NeoMutt  2022-04-29-215-gc12b98
Teaching an old dog new tricks
DOXYGEN
lib.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <errno.h>
32#include <netdb.h>
33#include <netinet/in.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
51struct Progress;
52
56const char *pop_get_field(enum ConnAccountField field, void *gf_data)
57{
58 switch (field)
59 {
60 case MUTT_CA_LOGIN:
61 case MUTT_CA_USER:
62 return cs_subset_string(NeoMutt->sub, "pop_user");
63 case MUTT_CA_PASS:
64 return cs_subset_string(NeoMutt->sub, "pop_pass");
66 return cs_subset_string(NeoMutt->sub, "pop_oauth_refresh_command");
67 case MUTT_CA_HOST:
68 default:
69 return NULL;
70 }
71}
72
82int pop_parse_path(const char *path, struct ConnAccount *cac)
83{
84 /* Defaults */
85 cac->flags = 0;
87 cac->port = 0;
88 cac->service = "pop";
90
91 struct Url *url = url_parse(path);
92
93 if (!url || ((url->scheme != U_POP) && (url->scheme != U_POPS)) ||
94 !url->host || (mutt_account_fromurl(cac, url) < 0))
95 {
96 url_free(&url);
97 mutt_error(_("Invalid POP URL: %s"), path);
98 return -1;
99 }
100
101 if (url->scheme == U_POPS)
102 cac->flags |= MUTT_ACCT_SSL;
103
104 struct servent *service = getservbyname((url->scheme == U_POP) ? "pop3" : "pop3s", "tcp");
105 if (cac->port == 0)
106 {
107 if (service)
108 cac->port = ntohs(service->s_port);
109 else
110 cac->port = (url->scheme == U_POP) ? POP_PORT : POP_SSL_PORT;
111 }
112
113 url_free(&url);
114 return 0;
115}
116
122static void pop_error(struct PopAccountData *adata, char *msg)
123{
124 char *t = strchr(adata->err_msg, '\0');
125 char *c = msg;
126
127 size_t plen = mutt_str_startswith(msg, "-ERR ");
128 if (plen != 0)
129 {
130 char *c2 = mutt_str_skip_email_wsp(msg + plen);
131
132 if (*c2)
133 c = c2;
134 }
135
136 mutt_str_copy(t, c, sizeof(adata->err_msg) - strlen(adata->err_msg));
138}
139
146static int fetch_capa(const char *line, void *data)
147{
148 struct PopAccountData *adata = data;
149
150 if (mutt_istr_startswith(line, "SASL"))
151 {
152 const char *c = mutt_str_skip_email_wsp(line + 4);
153 mutt_buffer_strcpy(&adata->auth_list, c);
154 }
155 else if (mutt_istr_startswith(line, "STLS"))
156 adata->cmd_stls = true;
157 else if (mutt_istr_startswith(line, "USER"))
158 adata->cmd_user = 1;
159 else if (mutt_istr_startswith(line, "UIDL"))
160 adata->cmd_uidl = 1;
161 else if (mutt_istr_startswith(line, "TOP"))
162 adata->cmd_top = 1;
163
164 return 0;
165}
166
173static int fetch_auth(const char *line, void *data)
174{
175 struct PopAccountData *adata = data;
176
177 if (!mutt_buffer_is_empty(&adata->auth_list))
178 {
179 mutt_buffer_addstr(&adata->auth_list, " ");
180 }
181 mutt_buffer_addstr(&adata->auth_list, line);
182
183 return 0;
184}
185
194static int pop_capabilities(struct PopAccountData *adata, int mode)
195{
196 char buf[1024] = { 0 };
197
198 /* don't check capabilities on reconnect */
199 if (adata->capabilities)
200 return 0;
201
202 /* init capabilities */
203 if (mode == 0)
204 {
205 adata->cmd_capa = false;
206 adata->cmd_stls = false;
207 adata->cmd_user = 0;
208 adata->cmd_uidl = 0;
209 adata->cmd_top = 0;
210 adata->resp_codes = false;
211 adata->expire = true;
212 adata->login_delay = 0;
213 mutt_buffer_init(&adata->auth_list);
214 }
215
216 /* Execute CAPA command */
217 if ((mode == 0) || adata->cmd_capa)
218 {
219 mutt_str_copy(buf, "CAPA\r\n", sizeof(buf));
220 switch (pop_fetch_data(adata, buf, NULL, fetch_capa, adata))
221 {
222 case 0:
223 {
224 adata->cmd_capa = true;
225 break;
226 }
227 case -1:
228 return -1;
229 }
230 }
231
232 /* CAPA not supported, use defaults */
233 if ((mode == 0) && !adata->cmd_capa)
234 {
235 adata->cmd_user = 2;
236 adata->cmd_uidl = 2;
237 adata->cmd_top = 2;
238
239 mutt_str_copy(buf, "AUTH\r\n", sizeof(buf));
240 if (pop_fetch_data(adata, buf, NULL, fetch_auth, adata) == -1)
241 return -1;
242 }
243
244 /* Check capabilities */
245 if (mode == 2)
246 {
247 char *msg = NULL;
248
249 if (!adata->expire)
250 msg = _("Unable to leave messages on server");
251 if (adata->cmd_top == 0)
252 msg = _("Command TOP is not supported by server");
253 if (adata->cmd_uidl == 0)
254 msg = _("Command UIDL is not supported by server");
255 if (msg && adata->cmd_capa)
256 {
257 mutt_error(msg);
258 return -2;
259 }
260 adata->capabilities = true;
261 }
262
263 return 0;
264}
265
274{
275 char buf[1024] = { 0 };
276
277 adata->status = POP_NONE;
278 if ((mutt_socket_open(adata->conn) < 0) ||
279 (mutt_socket_readln(buf, sizeof(buf), adata->conn) < 0))
280 {
281 mutt_error(_("Error connecting to server: %s"), adata->conn->account.host);
282 return -1;
283 }
284
285 adata->status = POP_CONNECTED;
286
287 if (!mutt_str_startswith(buf, "+OK"))
288 {
289 *adata->err_msg = '\0';
290 pop_error(adata, buf);
291 mutt_error("%s", adata->err_msg);
292 return -2;
293 }
294
296
297 return 0;
298}
299
309{
310 char buf[1024] = { 0 };
311
312 int rc = pop_connect(adata);
313 if (rc < 0)
314 return rc;
315
316 rc = pop_capabilities(adata, 0);
317 if (rc == -1)
318 goto err_conn;
319 if (rc == -2)
320 return -2;
321
322#ifdef USE_SSL
323 /* Attempt STLS if available and desired. */
324 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
325 if ((adata->conn->ssf == 0) && (adata->cmd_stls || c_ssl_force_tls))
326 {
327 if (c_ssl_force_tls)
328 adata->use_stls = 2;
329 if (adata->use_stls == 0)
330 {
331 const enum QuadOption c_ssl_starttls = cs_subset_quad(NeoMutt->sub, "ssl_starttls");
332 enum QuadOption ans = query_quadoption(c_ssl_starttls, _("Secure connection with TLS?"));
333 if (ans == MUTT_ABORT)
334 return -2;
335 adata->use_stls = 1;
336 if (ans == MUTT_YES)
337 adata->use_stls = 2;
338 }
339 if (adata->use_stls == 2)
340 {
341 mutt_str_copy(buf, "STLS\r\n", sizeof(buf));
342 rc = pop_query(adata, buf, sizeof(buf));
343 // Clear any data after the STLS acknowledgement
345 if (rc == -1)
346 goto err_conn;
347 if (rc != 0)
348 {
349 mutt_error("%s", adata->err_msg);
350 }
351 else if (mutt_ssl_starttls(adata->conn))
352 {
353 mutt_error(_("Could not negotiate TLS connection"));
354 return -2;
355 }
356 else
357 {
358 /* recheck capabilities after STLS completes */
359 rc = pop_capabilities(adata, 1);
360 if (rc == -1)
361 goto err_conn;
362 if (rc == -2)
363 return -2;
364 }
365 }
366 }
367
368 if (c_ssl_force_tls && (adata->conn->ssf == 0))
369 {
370 mutt_error(_("Encrypted connection unavailable"));
371 return -2;
372 }
373#endif
374
376 if (rc == -1)
377 goto err_conn;
378 if (rc == -3)
380 if (rc != 0)
381 return rc;
382
383 /* recheck capabilities after authentication */
384 rc = pop_capabilities(adata, 2);
385 if (rc == -1)
386 goto err_conn;
387 if (rc == -2)
388 return -2;
389
390 /* get total size of mailbox */
391 mutt_str_copy(buf, "STAT\r\n", sizeof(buf));
392 rc = pop_query(adata, buf, sizeof(buf));
393 if (rc == -1)
394 goto err_conn;
395 if (rc == -2)
396 {
397 mutt_error("%s", adata->err_msg);
398 return rc;
399 }
400
401 unsigned int n = 0, size = 0;
402 sscanf(buf, "+OK %u %u", &n, &size);
403 adata->size = size;
404 return 0;
405
406err_conn:
407 adata->status = POP_DISCONNECTED;
408 mutt_error(_("Server closed connection"));
409 return -1;
410}
411
416void pop_logout(struct Mailbox *m)
417{
419
420 if (adata->status == POP_CONNECTED)
421 {
422 int rc = 0;
423 char buf[1024] = { 0 };
424 mutt_message(_("Closing connection to POP server..."));
425
426 if (m->readonly)
427 {
428 mutt_str_copy(buf, "RSET\r\n", sizeof(buf));
429 rc = pop_query(adata, buf, sizeof(buf));
430 }
431
432 if (rc != -1)
433 {
434 mutt_str_copy(buf, "QUIT\r\n", sizeof(buf));
435 rc = pop_query(adata, buf, sizeof(buf));
436 }
437
438 if (rc < 0)
439 mutt_debug(LL_DEBUG1, "Error closing POP connection\n");
440
442 }
443
444 adata->status = POP_DISCONNECTED;
445}
446
457int pop_query_d(struct PopAccountData *adata, char *buf, size_t buflen, char *msg)
458{
459 if (adata->status != POP_CONNECTED)
460 return -1;
461
462 /* print msg instead of real command */
463 if (msg)
464 {
465 mutt_debug(MUTT_SOCK_LOG_CMD, "> %s", msg);
466 }
467
469
470 char *c = strpbrk(buf, " \r\n");
471 if (c)
472 *c = '\0';
473 snprintf(adata->err_msg, sizeof(adata->err_msg), "%s: ", buf);
474
475 if (mutt_socket_readln_d(buf, buflen, adata->conn, MUTT_SOCK_LOG_FULL) < 0)
476 {
477 adata->status = POP_DISCONNECTED;
478 return -1;
479 }
480 if (mutt_str_startswith(buf, "+OK"))
481 return 0;
482
483 pop_error(adata, buf);
484 return -2;
485}
486
502int pop_fetch_data(struct PopAccountData *adata, const char *query,
503 struct Progress *progress, pop_fetch_t callback, void *data)
504{
505 char buf[1024] = { 0 };
506 long pos = 0;
507 size_t lenbuf = 0;
508
509 mutt_str_copy(buf, query, sizeof(buf));
510 int rc = pop_query(adata, buf, sizeof(buf));
511 if (rc < 0)
512 return rc;
513
514 char *inbuf = mutt_mem_malloc(sizeof(buf));
515
516 while (true)
517 {
518 const int chunk = mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
519 if (chunk < 0)
520 {
521 adata->status = POP_DISCONNECTED;
522 rc = -1;
523 break;
524 }
525
526 char *p = buf;
527 if (!lenbuf && (buf[0] == '.'))
528 {
529 if (buf[1] != '.')
530 break;
531 p++;
532 }
533
534 mutt_str_copy(inbuf + lenbuf, p, sizeof(buf));
535 pos += chunk;
536
537 /* cast is safe since we break out of the loop when chunk<=0 */
538 if ((size_t) chunk >= sizeof(buf))
539 {
540 lenbuf += strlen(p);
541 }
542 else
543 {
544 if (progress)
545 progress_update(progress, pos, -1);
546 if ((rc == 0) && (callback(inbuf, data) < 0))
547 rc = -3;
548 lenbuf = 0;
549 }
550
551 mutt_mem_realloc(&inbuf, lenbuf + sizeof(buf));
552 }
553
554 FREE(&inbuf);
555 return rc;
556}
557
565static int check_uidl(const char *line, void *data)
566{
567 if (!line || !data)
568 return -1;
569
570 char *endp = NULL;
571
572 errno = 0;
573 unsigned int index = strtoul(line, &endp, 10);
574 if (errno != 0)
575 return -1;
576 while (*endp == ' ')
577 endp++;
578
579 struct Mailbox *m = data;
580 for (int i = 0; i < m->msg_count; i++)
581 {
582 struct PopEmailData *edata = pop_edata_get(m->emails[i]);
583 if (mutt_str_equal(edata->uid, endp))
584 {
585 edata->refno = index;
586 break;
587 }
588 }
589
590 return 0;
591}
592
599int pop_reconnect(struct Mailbox *m)
600{
602
603 if (adata->status == POP_CONNECTED)
604 return 0;
605
606 while (true)
607 {
609
610 int rc = pop_open_connection(adata);
611 if (rc == 0)
612 {
613 struct Progress *progress = progress_new(_("Verifying message indexes..."),
615
616 for (int i = 0; i < m->msg_count; i++)
617 {
618 struct PopEmailData *edata = pop_edata_get(m->emails[i]);
619 edata->refno = -1;
620 }
621
622 rc = pop_fetch_data(adata, "UIDL\r\n", progress, check_uidl, m);
623 progress_free(&progress);
624 if (rc == -2)
625 {
626 mutt_error("%s", adata->err_msg);
627 }
628 }
629
630 if (rc == 0)
631 return 0;
632
633 pop_logout(m);
634
635 if (rc < -1)
636 return -1;
637
638 const enum QuadOption c_pop_reconnect = cs_subset_quad(NeoMutt->sub, "pop_reconnect");
639 if (query_quadoption(c_pop_reconnect, _("Connection lost. Reconnect to POP server?")) != MUTT_YES)
640 {
641 return -1;
642 }
643 }
644}
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:260
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:327
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:52
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
Connection Library.
ConnAccountField
Login credentials.
Definition: connaccount.h:33
@ MUTT_CA_OAUTH_CMD
OAuth refresh command.
Definition: connaccount.h:38
@ MUTT_CA_USER
User name.
Definition: connaccount.h:36
@ MUTT_CA_LOGIN
Login name.
Definition: connaccount.h:35
@ MUTT_CA_HOST
Server name.
Definition: connaccount.h:34
@ MUTT_CA_PASS
Password.
Definition: connaccount.h:37
#define MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition: connaccount.h:47
Convenience wrapper for the core headers.
Structs that make up an email.
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1143
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
static int fetch_capa(const char *line, void *data)
Parse CAPA output - Implements pop_fetch_t -.
Definition: lib.c:146
static int fetch_auth(const char *line, void *data)
Fetch list of the authentication mechanisms - Implements pop_fetch_t -.
Definition: lib.c:173
static int check_uidl(const char *line, void *data)
Find message with this UIDL and set refno - Implements pop_fetch_t -.
Definition: lib.c:565
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:636
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:679
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:652
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
int mutt_account_fromurl(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
Definition: mutt_account.c:43
ConnAccount object used by POP and IMAP.
@ MUTT_ACCT_TYPE_POP
Pop Account.
Definition: mutt_account.h:38
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
NeoMutt Logging.
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
struct PopAccountData * pop_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:64
Pop-specific Account data.
void pop_apop_timestamp(struct PopAccountData *adata, char *buf)
Get the server timestamp for APOP authentication.
Definition: auth.c:204
int pop_authenticate(struct PopAccountData *adata)
Authenticate with a POP server.
Definition: auth.c:424
struct PopEmailData * pop_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:65
Pop-specific Email data.
int pop_connect(struct PopAccountData *adata)
Open connection.
Definition: lib.c:273
const char * pop_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field()
Definition: lib.c:56
int pop_open_connection(struct PopAccountData *adata)
Open connection and authenticate.
Definition: lib.c:308
int pop_parse_path(const char *path, struct ConnAccount *cac)
Parse a POP mailbox name.
Definition: lib.c:82
static int pop_capabilities(struct PopAccountData *adata, int mode)
Get capabilities from a POP server.
Definition: lib.c:194
static void pop_error(struct PopAccountData *adata, char *msg)
Copy error message to err_msg buffer.
Definition: lib.c:122
int pop_query_d(struct PopAccountData *adata, char *buf, size_t buflen, char *msg)
Send data from buffer and receive answer to the same buffer.
Definition: lib.c:457
int pop_fetch_data(struct PopAccountData *adata, const char *query, struct Progress *progress, pop_fetch_t callback, void *data)
Read Headers with callback function.
Definition: lib.c:502
void pop_logout(struct Mailbox *m)
Logout from a POP server.
Definition: lib.c:416
int pop_reconnect(struct Mailbox *m)
Reconnect and verify indexes if connection was lost.
Definition: lib.c:599
#define POP_SSL_PORT
Definition: private.h:36
#define pop_query(adata, buf, buflen)
Definition: private.h:109
int(* pop_fetch_t)(const char *str, void *data)
Definition: private.h:106
@ POP_DISCONNECTED
Disconnected from server.
Definition: private.h:51
@ POP_CONNECTED
Connected to server.
Definition: private.h:50
@ POP_NONE
No connected to server.
Definition: private.h:49
#define POP_PORT
Definition: private.h:35
Progress bar.
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc
Definition: lib.h:51
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:86
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:73
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:118
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition: quad.h:37
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:386
GUI display the mailboxes in a side panel.
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:98
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:314
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:77
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition: socket.c:247
#define MUTT_SOCK_LOG_FULL
Definition: socket.h:56
#define mutt_socket_readln(buf, buflen, conn)
Definition: socket.h:58
#define MUTT_SOCK_LOG_CMD
Definition: socket.h:54
#define mutt_socket_send_d(conn, buf, dbg)
Definition: socket.h:60
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
Login details for a remote server.
Definition: connaccount.h:53
const char * service
Name of the service, e.g. "imap".
Definition: connaccount.h:61
const char *(* get_field)(enum ConnAccountField field, void *gf_data)
Function to get some login credentials.
Definition: connaccount.h:68
unsigned char type
Connection type, e.g. MUTT_ACCT_TYPE_IMAP.
Definition: connaccount.h:59
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition: connaccount.h:60
unsigned short port
Port to connect to.
Definition: connaccount.h:58
void * edata
Driver-specific data.
Definition: email.h:72
int index
The absolute (unsorted) message number.
Definition: email.h:110
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:115
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
POP-specific Account data -.
Definition: adata.h:37
size_t size
Definition: adata.h:50
char err_msg[POP_CMD_RESPONSE]
Definition: adata.h:56
POP-specific Email data -.
Definition: edata.h:32
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:69
char * host
Host.
Definition: url.h:73
char * path
Path.
Definition: url.h:75
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition: url.h:70
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
@ U_POPS
Url is pops://.
Definition: url.h:38
@ U_POP
Url is pop://.
Definition: url.h:37