NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
lib.c
Go to the documentation of this file.
1
24
30
31#include "config.h"
32#include <arpa/inet.h>
33#include <errno.h>
34#include <netdb.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include "private.h"
40#include "mutt/lib.h"
41#include "config/lib.h"
42#include "email/lib.h"
43#include "core/lib.h"
44#include "conn/lib.h"
45#include "question/lib.h"
46#include "progress/lib.h"
47#include "adata.h"
48#include "edata.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 || (account_from_url(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 buf_strcpy(&adata->auth_list, c);
154 }
155 else if (mutt_istr_startswith(line, "STLS"))
156 {
157 adata->cmd_stls = true;
158 }
159 else if (mutt_istr_startswith(line, "USER"))
160 {
161 adata->cmd_user = 1;
162 }
163 else if (mutt_istr_startswith(line, "UIDL"))
164 {
165 adata->cmd_uidl = 1;
166 }
167 else if (mutt_istr_startswith(line, "TOP"))
168 {
169 adata->cmd_top = 1;
170 }
171
172 return 0;
173}
174
181static int fetch_auth(const char *line, void *data)
182{
183 struct PopAccountData *adata = data;
184
185 if (!buf_is_empty(&adata->auth_list))
186 {
187 buf_addstr(&adata->auth_list, " ");
188 }
189 buf_addstr(&adata->auth_list, line);
190
191 return 0;
192}
193
202static int pop_capabilities(struct PopAccountData *adata, int mode)
203{
204 char buf[1024] = { 0 };
205
206 /* don't check capabilities on reconnect */
207 if (adata->capabilities)
208 return 0;
209
210 /* init capabilities */
211 if (mode == 0)
212 {
213 adata->cmd_capa = false;
214 adata->cmd_stls = false;
215 adata->cmd_user = 0;
216 adata->cmd_uidl = 0;
217 adata->cmd_top = 0;
218 adata->resp_codes = false;
219 adata->expire = true;
220 adata->login_delay = 0;
221 buf_init(&adata->auth_list);
222 }
223
224 /* Execute CAPA command */
225 if ((mode == 0) || adata->cmd_capa)
226 {
227 mutt_str_copy(buf, "CAPA\r\n", sizeof(buf));
228 switch (pop_fetch_data(adata, buf, NULL, fetch_capa, adata))
229 {
230 case 0:
231 {
232 adata->cmd_capa = true;
233 break;
234 }
235 case -1:
236 default:
237 return -1;
238 }
239 }
240
241 /* CAPA not supported, use defaults */
242 if ((mode == 0) && !adata->cmd_capa)
243 {
244 adata->cmd_user = 2;
245 adata->cmd_uidl = 2;
246 adata->cmd_top = 2;
247
248 mutt_str_copy(buf, "AUTH\r\n", sizeof(buf));
249 if (pop_fetch_data(adata, buf, NULL, fetch_auth, adata) == -1)
250 return -1;
251 }
252
253 /* Check capabilities */
254 if (mode == 2)
255 {
256 char *msg = NULL;
257
258 if (!adata->expire)
259 msg = _("Unable to leave messages on server");
260 if (adata->cmd_top == 0)
261 msg = _("Command TOP is not supported by server");
262 if (adata->cmd_uidl == 0)
263 msg = _("Command UIDL is not supported by server");
264 if (msg && adata->cmd_capa)
265 {
266 mutt_error("%s", msg);
267 return -2;
268 }
269 adata->capabilities = true;
270 }
271
272 return 0;
273}
274
283{
284 char buf[1024] = { 0 };
285
286 adata->status = POP_NONE;
287 if ((mutt_socket_open(adata->conn) < 0) ||
288 (mutt_socket_readln(buf, sizeof(buf), adata->conn) < 0))
289 {
290 mutt_error(_("Error connecting to server: %s"), adata->conn->account.host);
291 return -1;
292 }
293
294 adata->status = POP_CONNECTED;
295
296 if (!mutt_str_startswith(buf, "+OK"))
297 {
298 *adata->err_msg = '\0';
299 pop_error(adata, buf);
300 mutt_error("%s", adata->err_msg);
301 return -2;
302 }
303
305
306 return 0;
307}
308
318{
319 char buf[1024] = { 0 };
320
321 int rc = pop_connect(adata);
322 if (rc < 0)
323 return rc;
324
325 rc = pop_capabilities(adata, 0);
326 if (rc == -1)
327 goto err_conn;
328 if (rc == -2)
329 return -2;
330
331#ifdef USE_SSL
332 /* Attempt STLS if available and desired. */
333 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
334 if ((adata->conn->ssf == 0) && (adata->cmd_stls || c_ssl_force_tls))
335 {
336 if (c_ssl_force_tls)
337 adata->use_stls = 2;
338 if (adata->use_stls == 0)
339 {
340 enum QuadOption ans = query_quadoption(_("Secure connection with TLS?"),
341 NeoMutt->sub, "ssl_starttls");
342 if (ans == MUTT_ABORT)
343 return -2;
344 adata->use_stls = 1;
345 if (ans == MUTT_YES)
346 adata->use_stls = 2;
347 }
348 if (adata->use_stls == 2)
349 {
350 mutt_str_copy(buf, "STLS\r\n", sizeof(buf));
351 rc = pop_query(adata, buf, sizeof(buf), NULL);
352 // Clear any data after the STLS acknowledgement
354 if (rc == -1)
355 goto err_conn;
356 if (rc != 0)
357 {
358 mutt_error("%s", adata->err_msg);
359 }
360 else if (mutt_ssl_starttls(adata->conn) != 0)
361 {
362 mutt_error(_("Could not negotiate TLS connection"));
363 return -2;
364 }
365 else
366 {
367 /* recheck capabilities after STLS completes */
368 rc = pop_capabilities(adata, 1);
369 if (rc == -1)
370 goto err_conn;
371 if (rc == -2)
372 return -2;
373 }
374 }
375 }
376
377 if (c_ssl_force_tls && (adata->conn->ssf == 0))
378 {
379 mutt_error(_("Encrypted connection unavailable"));
380 return -2;
381 }
382#endif
383
385 if (rc == -1)
386 goto err_conn;
387 if (rc == -3)
389 if (rc != 0)
390 return rc;
391
392 /* recheck capabilities after authentication */
393 rc = pop_capabilities(adata, 2);
394 if (rc == -1)
395 goto err_conn;
396 if (rc == -2)
397 return -2;
398
399 /* get total size of mailbox */
400 mutt_str_copy(buf, "STAT\r\n", sizeof(buf));
401 rc = pop_query(adata, buf, sizeof(buf), NULL);
402 if (rc == -1)
403 goto err_conn;
404 if (rc == -2)
405 {
406 mutt_error("%s", adata->err_msg);
407 return rc;
408 }
409
410 unsigned int n = 0;
411 size_t size = 0;
412 sscanf(buf, "+OK %u %zu", &n, &size);
413 adata->size = size;
414 return 0;
415
416err_conn:
417 adata->status = POP_DISCONNECTED;
418 mutt_error(_("Server closed connection"));
419 return -1;
420}
421
426void pop_logout(struct Mailbox *m)
427{
429
430 if (adata->status == POP_CONNECTED)
431 {
432 int rc = 0;
433 char buf[1024] = { 0 };
434 mutt_message(_("Closing connection to POP server..."));
435
436 if (m->readonly)
437 {
438 mutt_str_copy(buf, "RSET\r\n", sizeof(buf));
439 rc = pop_query(adata, buf, sizeof(buf), NULL);
440 }
441
442 if (rc != -1)
443 {
444 mutt_str_copy(buf, "QUIT\r\n", sizeof(buf));
445 rc = pop_query(adata, buf, sizeof(buf), NULL);
446 }
447
448 if (rc < 0)
449 mutt_debug(LL_DEBUG1, "Error closing POP connection\n");
450
452 }
453
454 adata->status = POP_DISCONNECTED;
455}
456
467int pop_query(struct PopAccountData *adata, char *buf, size_t buflen, char *msg)
468{
469 if (adata->status != POP_CONNECTED)
470 return -1;
471
472 /* print msg instead of real command */
473 if (msg)
474 {
475 mutt_debug(MUTT_SOCK_LOG_CMD, "> %s", msg);
476 }
477
479
480 char *c = strpbrk(buf, " \r\n");
481 if (c)
482 *c = '\0';
483 snprintf(adata->err_msg, sizeof(adata->err_msg), "%s: ", buf);
484
485 if (mutt_socket_readln_d(buf, buflen, adata->conn, MUTT_SOCK_LOG_FULL) < 0)
486 {
487 adata->status = POP_DISCONNECTED;
488 return -1;
489 }
490 if (mutt_str_startswith(buf, "+OK"))
491 return 0;
492
493 pop_error(adata, buf);
494 return -2;
495}
496
512int pop_fetch_data(struct PopAccountData *adata, const char *query,
513 struct Progress *progress, pop_fetch_t callback, void *data)
514{
515 char buf[1024] = { 0 };
516 long pos = 0;
517 size_t lenbuf = 0;
518
519 mutt_str_copy(buf, query, sizeof(buf));
520 int rc = pop_query(adata, buf, sizeof(buf), NULL);
521 if (rc < 0)
522 return rc;
523
524 char *inbuf = MUTT_MEM_MALLOC(sizeof(buf), char);
525
526 while (true)
527 {
528 const int chunk = mutt_socket_readln_d(buf, sizeof(buf), adata->conn, MUTT_SOCK_LOG_FULL);
529 if (chunk < 0)
530 {
531 adata->status = POP_DISCONNECTED;
532 rc = -1;
533 break;
534 }
535
536 char *p = buf;
537 if (!lenbuf && (buf[0] == '.'))
538 {
539 if (buf[1] != '.')
540 break;
541 p++;
542 }
543
544 mutt_str_copy(inbuf + lenbuf, p, sizeof(buf));
545 pos += chunk;
546
547 /* cast is safe since we break out of the loop when chunk<=0 */
548 if ((size_t) chunk >= sizeof(buf))
549 {
550 lenbuf += strlen(p);
551 }
552 else
553 {
554 progress_update(progress, pos, -1);
555 if ((rc == 0) && (callback(inbuf, data) < 0))
556 rc = -3;
557 lenbuf = 0;
558 }
559
560 MUTT_MEM_REALLOC(&inbuf, lenbuf + sizeof(buf), char);
561 }
562
563 FREE(&inbuf);
564 return rc;
565}
566
576static int check_uidl(const char *line, void *data)
577{
578 if (!line || !data)
579 return -1;
580
581 char *endp = NULL;
582
583 errno = 0;
584 unsigned int index = strtoul(line, &endp, 10);
585 if ((errno != 0) || (endp == line))
586 return -1;
587 while (*endp == ' ')
588 endp++;
589
590 struct Mailbox *m = data;
591 for (int i = 0; i < m->msg_count; i++)
592 {
593 struct PopEmailData *edata = pop_edata_get(m->emails[i]);
594 if (mutt_str_equal(edata->uid, endp))
595 {
596 edata->refno = index;
597 break;
598 }
599 }
600
601 return 0;
602}
603
610int pop_reconnect(struct Mailbox *m)
611{
613
614 if (adata->status == POP_CONNECTED)
615 return 0;
616
617 while (true)
618 {
620
621 int rc = pop_open_connection(adata);
622 if (rc == 0)
623 {
624 struct Progress *progress = progress_new(MUTT_PROGRESS_NET, 0);
625 progress_set_message(progress, _("Verifying message indexes..."));
626
627 for (int i = 0; i < m->msg_count; i++)
628 {
629 struct PopEmailData *edata = pop_edata_get(m->emails[i]);
630 edata->refno = -1;
631 }
632
633 rc = pop_fetch_data(adata, "UIDL\r\n", progress, check_uidl, m);
634 progress_free(&progress);
635 if (rc == -2)
636 {
637 mutt_error("%s", adata->err_msg);
638 }
639 }
640
641 if (rc == 0)
642 return 0;
643
644 pop_logout(m);
645
646 if (rc < -1)
647 return -1;
648
649 if (query_quadoption(_("Connection lost. Reconnect to POP server?"),
650 NeoMutt->sub, "pop_reconnect") != MUTT_YES)
651 {
652 return -1;
653 }
654 }
655}
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition buffer.c:74
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
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
@ MUTT_ACCT_SSL
Account uses SSL/TLS.
Definition connaccount.h:51
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:1176
const char * pop_get_field(enum ConnAccountField field, void *gf_data)
Get connection login credentials - Implements ConnAccount::get_field() -.
Definition lib.c:56
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
static int fetch_capa(const char *line, void *data)
Parse CAPA response - Implements pop_fetch_t -.
Definition lib.c:146
static int fetch_auth(const char *line, void *data)
Parse AUTH response - Implements pop_fetch_t -.
Definition lib.c:181
static int check_uidl(const char *line, void *data)
Parse UIDL response - Implements pop_fetch_t -.
Definition lib.c:576
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_REALLOC(pptr, n, type)
Definition memory.h:55
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
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:571
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition string.c:614
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
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:587
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:246
int account_from_url(struct ConnAccount *cac, const struct Url *url)
Fill ConnAccount with information from url.
@ MUTT_ACCT_TYPE_POP
Pop Account.
void mutt_clear_error(void)
Clear the message line (bottom line of screen).
NeoMutt Logging.
struct PopAccountData * pop_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition adata.c:73
Pop-specific Account data.
void pop_apop_timestamp(struct PopAccountData *adata, char *buf)
Get the server timestamp for APOP authentication.
Definition auth.c:326
int pop_authenticate(struct PopAccountData *adata)
Authenticate with a POP server.
Definition auth.c:555
struct PopEmailData * pop_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:68
Pop-specific Email data.
int pop_connect(struct PopAccountData *adata)
Open connection.
Definition lib.c:282
int pop_open_connection(struct PopAccountData *adata)
Open connection and authenticate.
Definition lib.c:317
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:202
static void pop_error(struct PopAccountData *adata, char *msg)
Copy error message to err_msg buffer.
Definition lib.c:122
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:512
int pop_query(struct PopAccountData *adata, char *buf, size_t buflen, char *msg)
Send data from buffer and receive answer to the same buffer.
Definition lib.c:467
void pop_logout(struct Mailbox *m)
Logout from a POP server.
Definition lib.c:426
int pop_reconnect(struct Mailbox *m)
Reconnect and verify indexes if connection was lost.
Definition lib.c:610
POP network mailbox.
#define POP_SSL_PORT
Default port for POP3 over SSL.
Definition private.h:36
int(* pop_fetch_t)(const char *str, void *data)
Definition private.h:106
@ POP_DISCONNECTED
Disconnected from server.
Definition private.h:49
@ POP_CONNECTED
Connected to server.
Definition private.h:48
@ POP_NONE
No connected to server.
Definition private.h:47
#define POP_PORT
Default port for POP3.
Definition private.h:35
Progress Bar.
@ MUTT_PROGRESS_NET
Progress tracks bytes, according to $net_inc.
Definition lib.h:83
struct Progress * progress_new(enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition progress.c:139
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition progress.c:110
void progress_set_message(struct Progress *progress, const char *fmt,...) __attribute__((__format__(__printf__
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition progress.c:80
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(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:384
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition socket.c:100
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition socket.c:306
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition socket.c:76
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition socket.c:238
#define MUTT_SOCK_LOG_FULL
Log everything including full protocol.
Definition socket.h:53
#define mutt_socket_readln(buf, buflen, conn)
Definition socket.h:55
#define MUTT_SOCK_LOG_CMD
Log commands only.
Definition socket.h:51
#define mutt_socket_send_d(conn, buf, dbg)
Definition socket.h:57
void * adata
Private data (for Mailbox backends).
Definition account.h:42
Login details for a remote server.
Definition connaccount.h:59
const char * service
Name of the service, e.g. "imap".
Definition connaccount.h:67
const char *(* get_field)(enum ConnAccountField field, void *gf_data)
Definition connaccount.h:76
unsigned char type
Connection type, e.g. MUTT_ACCT_TYPE_IMAP.
Definition connaccount.h:65
MuttAccountFlags flags
Which fields are initialised, e.g. MUTT_ACCT_USER.
Definition connaccount.h:66
unsigned short port
Port to connect to.
Definition connaccount.h:64
void * edata
Driver-specific data.
Definition email.h:74
int index
The absolute (unsorted) message number.
Definition email.h:110
A mailbox.
Definition mailbox.h:81
int msg_count
Total number of messages.
Definition mailbox.h:90
struct Email ** emails
Array of Emails.
Definition mailbox.h:98
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:118
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
POP-specific Account data -.
Definition adata.h:37
size_t size
Mailbox size.
Definition adata.h:50
char err_msg[POP_CMD_RESPONSE]
Last error message.
Definition adata.h:57
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:242
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124
@ U_POPS
Url is pops://.
Definition url.h:38
@ U_POP
Url is pop://.
Definition url.h:37