Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * POP helper routines |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 2000-2003 Vsevolod Volkov <vvv@mutt.org.ua> |
7 | | * Copyright (C) 2018 Richard Russon <rich@flatcap.org> |
8 | | * |
9 | | * @copyright |
10 | | * This program is free software: you can redistribute it and/or modify it under |
11 | | * the terms of the GNU General Public License as published by the Free Software |
12 | | * Foundation, either version 2 of the License, or (at your option) any later |
13 | | * version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
16 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
17 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
18 | | * details. |
19 | | * |
20 | | * You should have received a copy of the GNU General Public License along with |
21 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
22 | | */ |
23 | | |
24 | | /** |
25 | | * @page pop_lib POP helper routines |
26 | | * |
27 | | * POP helper routines |
28 | | */ |
29 | | |
30 | | #include "config.h" |
31 | | #include <arpa/inet.h> |
32 | | #include <errno.h> |
33 | | #include <netdb.h> |
34 | | #include <stdbool.h> |
35 | | #include <stdio.h> |
36 | | #include <stdlib.h> |
37 | | #include <string.h> |
38 | | #include "private.h" |
39 | | #include "mutt/lib.h" |
40 | | #include "config/lib.h" |
41 | | #include "email/lib.h" |
42 | | #include "core/lib.h" |
43 | | #include "conn/lib.h" |
44 | | #include "question/lib.h" |
45 | | #include "progress/lib.h" |
46 | | #include "adata.h" |
47 | | #include "edata.h" |
48 | | #include "mutt_account.h" |
49 | | #include "mutt_logging.h" |
50 | | |
51 | | struct Progress; |
52 | | |
53 | | /** |
54 | | * pop_get_field - Get connection login credentials - Implements ConnAccount::get_field() |
55 | | */ |
56 | | const char *pop_get_field(enum ConnAccountField field, void *gf_data) |
57 | 0 | { |
58 | 0 | switch (field) |
59 | 0 | { |
60 | 0 | case MUTT_CA_LOGIN: |
61 | 0 | case MUTT_CA_USER: |
62 | 0 | return cs_subset_string(NeoMutt->sub, "pop_user"); |
63 | 0 | case MUTT_CA_PASS: |
64 | 0 | return cs_subset_string(NeoMutt->sub, "pop_pass"); |
65 | 0 | case MUTT_CA_OAUTH_CMD: |
66 | 0 | return cs_subset_string(NeoMutt->sub, "pop_oauth_refresh_command"); |
67 | 0 | case MUTT_CA_HOST: |
68 | 0 | default: |
69 | 0 | return NULL; |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | | /** |
74 | | * pop_parse_path - Parse a POP mailbox name |
75 | | * @param path Path to parse |
76 | | * @param cac Account to store details |
77 | | * @retval 0 success |
78 | | * @retval -1 error |
79 | | * |
80 | | * Split a POP path into host, port, username and password |
81 | | */ |
82 | | int pop_parse_path(const char *path, struct ConnAccount *cac) |
83 | 0 | { |
84 | | /* Defaults */ |
85 | 0 | cac->flags = 0; |
86 | 0 | cac->type = MUTT_ACCT_TYPE_POP; |
87 | 0 | cac->port = 0; |
88 | 0 | cac->service = "pop"; |
89 | 0 | cac->get_field = pop_get_field; |
90 | |
|
91 | 0 | struct Url *url = url_parse(path); |
92 | |
|
93 | 0 | if (!url || ((url->scheme != U_POP) && (url->scheme != U_POPS)) || |
94 | 0 | !url->host || (mutt_account_fromurl(cac, url) < 0)) |
95 | 0 | { |
96 | 0 | url_free(&url); |
97 | 0 | mutt_error(_("Invalid POP URL: %s"), path); |
98 | 0 | return -1; |
99 | 0 | } |
100 | | |
101 | 0 | if (url->scheme == U_POPS) |
102 | 0 | cac->flags |= MUTT_ACCT_SSL; |
103 | |
|
104 | 0 | struct servent *service = getservbyname((url->scheme == U_POP) ? "pop3" : "pop3s", "tcp"); |
105 | 0 | if (cac->port == 0) |
106 | 0 | { |
107 | 0 | if (service) |
108 | 0 | cac->port = ntohs(service->s_port); |
109 | 0 | else |
110 | 0 | cac->port = (url->scheme == U_POP) ? POP_PORT : POP_SSL_PORT; |
111 | 0 | } |
112 | |
|
113 | 0 | url_free(&url); |
114 | 0 | return 0; |
115 | 0 | } |
116 | | |
117 | | /** |
118 | | * pop_error - Copy error message to err_msg buffer |
119 | | * @param adata POP Account data |
120 | | * @param msg Error message to save |
121 | | */ |
122 | | static void pop_error(struct PopAccountData *adata, char *msg) |
123 | 0 | { |
124 | 0 | char *t = strchr(adata->err_msg, '\0'); |
125 | 0 | char *c = msg; |
126 | |
|
127 | 0 | size_t plen = mutt_str_startswith(msg, "-ERR "); |
128 | 0 | if (plen != 0) |
129 | 0 | { |
130 | 0 | char *c2 = mutt_str_skip_email_wsp(msg + plen); |
131 | |
|
132 | 0 | if (*c2) |
133 | 0 | c = c2; |
134 | 0 | } |
135 | |
|
136 | 0 | mutt_str_copy(t, c, sizeof(adata->err_msg) - strlen(adata->err_msg)); |
137 | 0 | mutt_str_remove_trailing_ws(adata->err_msg); |
138 | 0 | } |
139 | | |
140 | | /** |
141 | | * fetch_capa - Parse CAPA output - Implements ::pop_fetch_t - @ingroup pop_fetch_api |
142 | | * @param line List of capabilities |
143 | | * @param data POP data |
144 | | * @retval 0 (always) |
145 | | */ |
146 | | static int fetch_capa(const char *line, void *data) |
147 | 0 | { |
148 | 0 | struct PopAccountData *adata = data; |
149 | |
|
150 | 0 | if (mutt_istr_startswith(line, "SASL")) |
151 | 0 | { |
152 | 0 | const char *c = mutt_str_skip_email_wsp(line + 4); |
153 | 0 | buf_strcpy(&adata->auth_list, c); |
154 | 0 | } |
155 | 0 | else if (mutt_istr_startswith(line, "STLS")) |
156 | 0 | { |
157 | 0 | adata->cmd_stls = true; |
158 | 0 | } |
159 | 0 | else if (mutt_istr_startswith(line, "USER")) |
160 | 0 | { |
161 | 0 | adata->cmd_user = 1; |
162 | 0 | } |
163 | 0 | else if (mutt_istr_startswith(line, "UIDL")) |
164 | 0 | { |
165 | 0 | adata->cmd_uidl = 1; |
166 | 0 | } |
167 | 0 | else if (mutt_istr_startswith(line, "TOP")) |
168 | 0 | { |
169 | 0 | adata->cmd_top = 1; |
170 | 0 | } |
171 | |
|
172 | 0 | return 0; |
173 | 0 | } |
174 | | |
175 | | /** |
176 | | * fetch_auth - Fetch list of the authentication mechanisms - Implements ::pop_fetch_t - @ingroup pop_fetch_api |
177 | | * @param line List of authentication methods |
178 | | * @param data POP data |
179 | | * @retval 0 (always) |
180 | | */ |
181 | | static int fetch_auth(const char *line, void *data) |
182 | 0 | { |
183 | 0 | struct PopAccountData *adata = data; |
184 | |
|
185 | 0 | if (!buf_is_empty(&adata->auth_list)) |
186 | 0 | { |
187 | 0 | buf_addstr(&adata->auth_list, " "); |
188 | 0 | } |
189 | 0 | buf_addstr(&adata->auth_list, line); |
190 | |
|
191 | 0 | return 0; |
192 | 0 | } |
193 | | |
194 | | /** |
195 | | * pop_capabilities - Get capabilities from a POP server |
196 | | * @param adata POP Account data |
197 | | * @param mode Initial capabilities |
198 | | * @retval 0 Successful |
199 | | * @retval -1 Connection lost |
200 | | * @retval -2 Execution error |
201 | | */ |
202 | | static int pop_capabilities(struct PopAccountData *adata, int mode) |
203 | 0 | { |
204 | 0 | char buf[1024] = { 0 }; |
205 | | |
206 | | /* don't check capabilities on reconnect */ |
207 | 0 | if (adata->capabilities) |
208 | 0 | return 0; |
209 | | |
210 | | /* init capabilities */ |
211 | 0 | if (mode == 0) |
212 | 0 | { |
213 | 0 | adata->cmd_capa = false; |
214 | 0 | adata->cmd_stls = false; |
215 | 0 | adata->cmd_user = 0; |
216 | 0 | adata->cmd_uidl = 0; |
217 | 0 | adata->cmd_top = 0; |
218 | 0 | adata->resp_codes = false; |
219 | 0 | adata->expire = true; |
220 | 0 | adata->login_delay = 0; |
221 | 0 | buf_init(&adata->auth_list); |
222 | 0 | } |
223 | | |
224 | | /* Execute CAPA command */ |
225 | 0 | if ((mode == 0) || adata->cmd_capa) |
226 | 0 | { |
227 | 0 | mutt_str_copy(buf, "CAPA\r\n", sizeof(buf)); |
228 | 0 | switch (pop_fetch_data(adata, buf, NULL, fetch_capa, adata)) |
229 | 0 | { |
230 | 0 | case 0: |
231 | 0 | { |
232 | 0 | adata->cmd_capa = true; |
233 | 0 | break; |
234 | 0 | } |
235 | 0 | case -1: |
236 | 0 | return -1; |
237 | 0 | } |
238 | 0 | } |
239 | | |
240 | | /* CAPA not supported, use defaults */ |
241 | 0 | if ((mode == 0) && !adata->cmd_capa) |
242 | 0 | { |
243 | 0 | adata->cmd_user = 2; |
244 | 0 | adata->cmd_uidl = 2; |
245 | 0 | adata->cmd_top = 2; |
246 | |
|
247 | 0 | mutt_str_copy(buf, "AUTH\r\n", sizeof(buf)); |
248 | 0 | if (pop_fetch_data(adata, buf, NULL, fetch_auth, adata) == -1) |
249 | 0 | return -1; |
250 | 0 | } |
251 | | |
252 | | /* Check capabilities */ |
253 | 0 | if (mode == 2) |
254 | 0 | { |
255 | 0 | char *msg = NULL; |
256 | |
|
257 | 0 | if (!adata->expire) |
258 | 0 | msg = _("Unable to leave messages on server"); |
259 | 0 | if (adata->cmd_top == 0) |
260 | 0 | msg = _("Command TOP is not supported by server"); |
261 | 0 | if (adata->cmd_uidl == 0) |
262 | 0 | msg = _("Command UIDL is not supported by server"); |
263 | 0 | if (msg && adata->cmd_capa) |
264 | 0 | { |
265 | 0 | mutt_error(msg); |
266 | 0 | return -2; |
267 | 0 | } |
268 | 0 | adata->capabilities = true; |
269 | 0 | } |
270 | | |
271 | 0 | return 0; |
272 | 0 | } |
273 | | |
274 | | /** |
275 | | * pop_connect - Open connection |
276 | | * @param adata POP Account data |
277 | | * @retval 0 Successful |
278 | | * @retval -1 Connection lost |
279 | | * @retval -2 Invalid response |
280 | | */ |
281 | | int pop_connect(struct PopAccountData *adata) |
282 | 0 | { |
283 | 0 | char buf[1024] = { 0 }; |
284 | |
|
285 | 0 | adata->status = POP_NONE; |
286 | 0 | if ((mutt_socket_open(adata->conn) < 0) || |
287 | 0 | (mutt_socket_readln(buf, sizeof(buf), adata->conn) < 0)) |
288 | 0 | { |
289 | 0 | mutt_error(_("Error connecting to server: %s"), adata->conn->account.host); |
290 | 0 | return -1; |
291 | 0 | } |
292 | | |
293 | 0 | adata->status = POP_CONNECTED; |
294 | |
|
295 | 0 | if (!mutt_str_startswith(buf, "+OK")) |
296 | 0 | { |
297 | 0 | *adata->err_msg = '\0'; |
298 | 0 | pop_error(adata, buf); |
299 | 0 | mutt_error("%s", adata->err_msg); |
300 | 0 | return -2; |
301 | 0 | } |
302 | | |
303 | 0 | pop_apop_timestamp(adata, buf); |
304 | |
|
305 | 0 | return 0; |
306 | 0 | } |
307 | | |
308 | | /** |
309 | | * pop_open_connection - Open connection and authenticate |
310 | | * @param adata POP Account data |
311 | | * @retval 0 Successful |
312 | | * @retval -1 Connection lost |
313 | | * @retval -2 Invalid command or execution error |
314 | | * @retval -3 Authentication cancelled |
315 | | */ |
316 | | int pop_open_connection(struct PopAccountData *adata) |
317 | 0 | { |
318 | 0 | char buf[1024] = { 0 }; |
319 | |
|
320 | 0 | int rc = pop_connect(adata); |
321 | 0 | if (rc < 0) |
322 | 0 | return rc; |
323 | | |
324 | 0 | rc = pop_capabilities(adata, 0); |
325 | 0 | if (rc == -1) |
326 | 0 | goto err_conn; |
327 | 0 | if (rc == -2) |
328 | 0 | return -2; |
329 | | |
330 | | #ifdef USE_SSL |
331 | | /* Attempt STLS if available and desired. */ |
332 | | const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls"); |
333 | | if ((adata->conn->ssf == 0) && (adata->cmd_stls || c_ssl_force_tls)) |
334 | | { |
335 | | if (c_ssl_force_tls) |
336 | | adata->use_stls = 2; |
337 | | if (adata->use_stls == 0) |
338 | | { |
339 | | const enum QuadOption c_ssl_starttls = cs_subset_quad(NeoMutt->sub, "ssl_starttls"); |
340 | | enum QuadOption ans = query_quadoption(c_ssl_starttls, _("Secure connection with TLS?")); |
341 | | if (ans == MUTT_ABORT) |
342 | | return -2; |
343 | | adata->use_stls = 1; |
344 | | if (ans == MUTT_YES) |
345 | | adata->use_stls = 2; |
346 | | } |
347 | | if (adata->use_stls == 2) |
348 | | { |
349 | | mutt_str_copy(buf, "STLS\r\n", sizeof(buf)); |
350 | | rc = pop_query(adata, buf, sizeof(buf)); |
351 | | // Clear any data after the STLS acknowledgement |
352 | | mutt_socket_empty(adata->conn); |
353 | | if (rc == -1) |
354 | | goto err_conn; |
355 | | if (rc != 0) |
356 | | { |
357 | | mutt_error("%s", adata->err_msg); |
358 | | } |
359 | | else if (mutt_ssl_starttls(adata->conn)) |
360 | | { |
361 | | mutt_error(_("Could not negotiate TLS connection")); |
362 | | return -2; |
363 | | } |
364 | | else |
365 | | { |
366 | | /* recheck capabilities after STLS completes */ |
367 | | rc = pop_capabilities(adata, 1); |
368 | | if (rc == -1) |
369 | | goto err_conn; |
370 | | if (rc == -2) |
371 | | return -2; |
372 | | } |
373 | | } |
374 | | } |
375 | | |
376 | | if (c_ssl_force_tls && (adata->conn->ssf == 0)) |
377 | | { |
378 | | mutt_error(_("Encrypted connection unavailable")); |
379 | | return -2; |
380 | | } |
381 | | #endif |
382 | | |
383 | 0 | rc = pop_authenticate(adata); |
384 | 0 | if (rc == -1) |
385 | 0 | goto err_conn; |
386 | 0 | if (rc == -3) |
387 | 0 | mutt_clear_error(); |
388 | 0 | if (rc != 0) |
389 | 0 | return rc; |
390 | | |
391 | | /* recheck capabilities after authentication */ |
392 | 0 | rc = pop_capabilities(adata, 2); |
393 | 0 | if (rc == -1) |
394 | 0 | goto err_conn; |
395 | 0 | if (rc == -2) |
396 | 0 | return -2; |
397 | | |
398 | | /* get total size of mailbox */ |
399 | 0 | mutt_str_copy(buf, "STAT\r\n", sizeof(buf)); |
400 | 0 | rc = pop_query(adata, buf, sizeof(buf)); |
401 | 0 | if (rc == -1) |
402 | 0 | goto err_conn; |
403 | 0 | if (rc == -2) |
404 | 0 | { |
405 | 0 | mutt_error("%s", adata->err_msg); |
406 | 0 | return rc; |
407 | 0 | } |
408 | | |
409 | 0 | unsigned int n = 0, size = 0; |
410 | 0 | sscanf(buf, "+OK %u %u", &n, &size); |
411 | 0 | adata->size = size; |
412 | 0 | return 0; |
413 | | |
414 | 0 | err_conn: |
415 | 0 | adata->status = POP_DISCONNECTED; |
416 | 0 | mutt_error(_("Server closed connection")); |
417 | 0 | return -1; |
418 | 0 | } |
419 | | |
420 | | /** |
421 | | * pop_logout - Logout from a POP server |
422 | | * @param m Mailbox |
423 | | */ |
424 | | void pop_logout(struct Mailbox *m) |
425 | 0 | { |
426 | 0 | struct PopAccountData *adata = pop_adata_get(m); |
427 | |
|
428 | 0 | if (adata->status == POP_CONNECTED) |
429 | 0 | { |
430 | 0 | int rc = 0; |
431 | 0 | char buf[1024] = { 0 }; |
432 | 0 | mutt_message(_("Closing connection to POP server...")); |
433 | |
|
434 | 0 | if (m->readonly) |
435 | 0 | { |
436 | 0 | mutt_str_copy(buf, "RSET\r\n", sizeof(buf)); |
437 | 0 | rc = pop_query(adata, buf, sizeof(buf)); |
438 | 0 | } |
439 | |
|
440 | 0 | if (rc != -1) |
441 | 0 | { |
442 | 0 | mutt_str_copy(buf, "QUIT\r\n", sizeof(buf)); |
443 | 0 | rc = pop_query(adata, buf, sizeof(buf)); |
444 | 0 | } |
445 | |
|
446 | 0 | if (rc < 0) |
447 | 0 | mutt_debug(LL_DEBUG1, "Error closing POP connection\n"); |
448 | |
|
449 | 0 | mutt_clear_error(); |
450 | 0 | } |
451 | |
|
452 | 0 | adata->status = POP_DISCONNECTED; |
453 | 0 | } |
454 | | |
455 | | /** |
456 | | * pop_query_d - Send data from buffer and receive answer to the same buffer |
457 | | * @param adata POP Account data |
458 | | * @param buf Buffer to send/store data |
459 | | * @param buflen Buffer length |
460 | | * @param msg Progress message |
461 | | * @retval 0 Successful |
462 | | * @retval -1 Connection lost |
463 | | * @retval -2 Invalid command or execution error |
464 | | */ |
465 | | int pop_query_d(struct PopAccountData *adata, char *buf, size_t buflen, char *msg) |
466 | 0 | { |
467 | 0 | if (adata->status != POP_CONNECTED) |
468 | 0 | return -1; |
469 | | |
470 | | /* print msg instead of real command */ |
471 | 0 | if (msg) |
472 | 0 | { |
473 | 0 | mutt_debug(MUTT_SOCK_LOG_CMD, "> %s", msg); |
474 | 0 | } |
475 | |
|
476 | 0 | mutt_socket_send_d(adata->conn, buf, MUTT_SOCK_LOG_FULL); |
477 | |
|
478 | 0 | char *c = strpbrk(buf, " \r\n"); |
479 | 0 | if (c) |
480 | 0 | *c = '\0'; |
481 | 0 | snprintf(adata->err_msg, sizeof(adata->err_msg), "%s: ", buf); |
482 | |
|
483 | 0 | if (mutt_socket_readln_d(buf, buflen, adata->conn, MUTT_SOCK_LOG_FULL) < 0) |
484 | 0 | { |
485 | 0 | adata->status = POP_DISCONNECTED; |
486 | 0 | return -1; |
487 | 0 | } |
488 | 0 | if (mutt_str_startswith(buf, "+OK")) |
489 | 0 | return 0; |
490 | | |
491 | 0 | pop_error(adata, buf); |
492 | 0 | return -2; |
493 | 0 | } |
494 | | |
495 | | /** |
496 | | * pop_fetch_data - Read Headers with callback function |
497 | | * @param adata POP Account data |
498 | | * @param query POP query to send to server |
499 | | * @param progress Progress bar |
500 | | * @param callback Function called for each header read |
501 | | * @param data Data to pass to the callback |
502 | | * @retval 0 Successful |
503 | | * @retval -1 Connection lost |
504 | | * @retval -2 Invalid command or execution error |
505 | | * @retval -3 Error in callback(*line, *data) |
506 | | * |
507 | | * This function calls callback(*line, *data) for each received line, |
508 | | * callback(NULL, *data) if rewind(*data) needs, exits when fail or done. |
509 | | */ |
510 | | int pop_fetch_data(struct PopAccountData *adata, const char *query, |
511 | | struct Progress *progress, pop_fetch_t callback, void *data) |
512 | 0 | { |
513 | 0 | char buf[1024] = { 0 }; |
514 | 0 | long pos = 0; |
515 | 0 | size_t lenbuf = 0; |
516 | |
|
517 | 0 | mutt_str_copy(buf, query, sizeof(buf)); |
518 | 0 | int rc = pop_query(adata, buf, sizeof(buf)); |
519 | 0 | if (rc < 0) |
520 | 0 | return rc; |
521 | | |
522 | 0 | char *inbuf = mutt_mem_malloc(sizeof(buf)); |
523 | |
|
524 | 0 | while (true) |
525 | 0 | { |
526 | 0 | const int chunk = mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL); |
527 | 0 | if (chunk < 0) |
528 | 0 | { |
529 | 0 | adata->status = POP_DISCONNECTED; |
530 | 0 | rc = -1; |
531 | 0 | break; |
532 | 0 | } |
533 | | |
534 | 0 | char *p = buf; |
535 | 0 | if (!lenbuf && (buf[0] == '.')) |
536 | 0 | { |
537 | 0 | if (buf[1] != '.') |
538 | 0 | break; |
539 | 0 | p++; |
540 | 0 | } |
541 | | |
542 | 0 | mutt_str_copy(inbuf + lenbuf, p, sizeof(buf)); |
543 | 0 | pos += chunk; |
544 | | |
545 | | /* cast is safe since we break out of the loop when chunk<=0 */ |
546 | 0 | if ((size_t) chunk >= sizeof(buf)) |
547 | 0 | { |
548 | 0 | lenbuf += strlen(p); |
549 | 0 | } |
550 | 0 | else |
551 | 0 | { |
552 | 0 | if (progress) |
553 | 0 | progress_update(progress, pos, -1); |
554 | 0 | if ((rc == 0) && (callback(inbuf, data) < 0)) |
555 | 0 | rc = -3; |
556 | 0 | lenbuf = 0; |
557 | 0 | } |
558 | |
|
559 | 0 | mutt_mem_realloc(&inbuf, lenbuf + sizeof(buf)); |
560 | 0 | } |
561 | |
|
562 | 0 | FREE(&inbuf); |
563 | 0 | return rc; |
564 | 0 | } |
565 | | |
566 | | /** |
567 | | * check_uidl - Find message with this UIDL and set refno - Implements ::pop_fetch_t - @ingroup pop_fetch_api |
568 | | * @param line String containing UIDL |
569 | | * @param data POP data |
570 | | * @retval 0 Success |
571 | | * @retval -1 Error |
572 | | */ |
573 | | static int check_uidl(const char *line, void *data) |
574 | 0 | { |
575 | 0 | if (!line || !data) |
576 | 0 | return -1; |
577 | | |
578 | 0 | char *endp = NULL; |
579 | |
|
580 | 0 | errno = 0; |
581 | 0 | unsigned int index = strtoul(line, &endp, 10); |
582 | 0 | if (errno != 0) |
583 | 0 | return -1; |
584 | 0 | while (*endp == ' ') |
585 | 0 | endp++; |
586 | |
|
587 | 0 | struct Mailbox *m = data; |
588 | 0 | for (int i = 0; i < m->msg_count; i++) |
589 | 0 | { |
590 | 0 | struct PopEmailData *edata = pop_edata_get(m->emails[i]); |
591 | 0 | if (mutt_str_equal(edata->uid, endp)) |
592 | 0 | { |
593 | 0 | edata->refno = index; |
594 | 0 | break; |
595 | 0 | } |
596 | 0 | } |
597 | |
|
598 | 0 | return 0; |
599 | 0 | } |
600 | | |
601 | | /** |
602 | | * pop_reconnect - Reconnect and verify indexes if connection was lost |
603 | | * @param m Mailbox |
604 | | * @retval 0 Success |
605 | | * @retval -1 Error |
606 | | */ |
607 | | int pop_reconnect(struct Mailbox *m) |
608 | 0 | { |
609 | 0 | struct PopAccountData *adata = pop_adata_get(m); |
610 | |
|
611 | 0 | if (adata->status == POP_CONNECTED) |
612 | 0 | return 0; |
613 | | |
614 | 0 | while (true) |
615 | 0 | { |
616 | 0 | mutt_socket_close(adata->conn); |
617 | |
|
618 | 0 | int rc = pop_open_connection(adata); |
619 | 0 | if (rc == 0) |
620 | 0 | { |
621 | 0 | struct Progress *progress = progress_new(_("Verifying message indexes..."), |
622 | 0 | MUTT_PROGRESS_NET, 0); |
623 | |
|
624 | 0 | for (int i = 0; i < m->msg_count; i++) |
625 | 0 | { |
626 | 0 | struct PopEmailData *edata = pop_edata_get(m->emails[i]); |
627 | 0 | edata->refno = -1; |
628 | 0 | } |
629 | |
|
630 | 0 | rc = pop_fetch_data(adata, "UIDL\r\n", progress, check_uidl, m); |
631 | 0 | progress_free(&progress); |
632 | 0 | if (rc == -2) |
633 | 0 | { |
634 | 0 | mutt_error("%s", adata->err_msg); |
635 | 0 | } |
636 | 0 | } |
637 | |
|
638 | 0 | if (rc == 0) |
639 | 0 | return 0; |
640 | | |
641 | 0 | pop_logout(m); |
642 | |
|
643 | 0 | if (rc < -1) |
644 | 0 | return -1; |
645 | | |
646 | 0 | const enum QuadOption c_pop_reconnect = cs_subset_quad(NeoMutt->sub, "pop_reconnect"); |
647 | 0 | if (query_quadoption(c_pop_reconnect, _("Connection lost. Reconnect to POP server?")) != MUTT_YES) |
648 | 0 | { |
649 | 0 | return -1; |
650 | 0 | } |
651 | 0 | } |
652 | 0 | } |