2 * Copyright notice from original mutt:
3 * Copyright (C) 2000-2002 Vsevolod Volkov <vvv@mutt.org.ua>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 #include <lib-lib/lib-lib.h>
12 #include <sasl/sasl.h>
13 #include <sasl/saslutil.h>
15 #include <lib-sys/mutt_socket.h>
16 #include <lib-ui/curses.h>
20 #include "mutt_sasl.h"
24 #define POP_SSL_PORT 995
26 /* number of entries in the hash table */
27 #define POP_CACHE_LEN 10
29 /* maximal length of the server response (RFC1939) */
30 #define POP_CMD_RESPONSE 512
52 typedef enum pop_query_status_e {
53 PFD_FUNCT_ERROR = -3, /* pop_fetch_data uses pop_query_status and this return value */
55 PQ_NOT_CONNECTED = -1,
59 typedef enum cmd_status_e {
60 CMD_NOT_AVAILABLE = 0,
62 CMD_UNKNOWN /* unknown whether it is available or not */
67 unsigned int status:2;
68 unsigned int capabilities:1;
69 unsigned int use_stls:2;
70 cmd_status cmd_capa; /* optional command CAPA */
71 cmd_status cmd_stls; /* optional command STLS */
72 cmd_status cmd_user; /* optional command USER */
73 cmd_status cmd_uidl; /* optional command UIDL */
74 cmd_status cmd_top; /* optional command TOP */
75 unsigned int resp_codes:1; /* server supports extended response codes */
76 unsigned int expire:1; /* expire is greater than 0 */
77 unsigned int clear_cache:1;
80 time_t login_delay; /* minimal login delay capability */
81 char *auth_list; /* list of auth mechanisms */
83 char err_msg[POP_CMD_RESPONSE];
84 POP_CACHE cache[POP_CACHE_LEN];
87 /* Copy error message to err_msg buffer */
88 static void pop_error (POP_DATA * pop_data, char *msg)
92 t = strchr (pop_data->err_msg, '\0');
95 if (!m_strncmp(msg, "-ERR ", 5)) {
96 c2 = vskipspaces(msg + 5);
101 m_strcpy(t, sizeof(pop_data->err_msg) - strlen(pop_data->err_msg), c);
102 m_strrtrim(pop_data->err_msg);
106 * Send data from buffer and receive answer to the same buffer
108 * -1 - conection lost,
109 * -2 - invalid command or execution error.
111 static pop_query_status pop_query (POP_DATA * pop_data, char *buf, size_t buflen)
115 if (pop_data->status != POP_CONNECTED)
116 return PQ_NOT_CONNECTED;
118 mutt_socket_write(pop_data->conn, buf);
120 c = strpbrk (buf, " \r\n");
122 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf);
124 if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0) {
125 pop_data->status = POP_DISCONNECTED;
126 return PQ_NOT_CONNECTED;
128 if (!m_strncmp(buf, "+OK", 3))
131 pop_error (pop_data, buf);
136 * This function calls funct(*line, *data) for each received line,
137 * funct(NULL, *data) if rewind(*data) needs, exits when fail or done.
140 * -1 - conection lost,
141 * -2 - invalid command or execution error,
142 * -3 - error in funct(*line, *data)
144 static pop_query_status
145 pop_fetch_data(POP_DATA *pop_data, const char *query, progress_t *bar,
146 int (*funct)(char *, void *), void *data)
148 char buf[LONG_STRING];
151 pop_query_status ret;
156 m_strcpy(buf, sizeof(buf), query);
157 ret = pop_query (pop_data, buf, sizeof (buf));
161 inbuf = p_new(char, sizeof(buf));
165 mutt_socket_readln(buf, sizeof (buf), pop_data->conn);
167 pop_data->status = POP_DISCONNECTED;
168 ret = PQ_NOT_CONNECTED;
173 if (!lenbuf && buf[0] == '.') {
179 m_strcpy(inbuf + lenbuf,sizeof(buf), p);
182 if (chunk >= ssizeof(buf)) {
183 lenbuf += strlen (p);
186 mutt_progress_bar (bar, pos);
187 if (ret == 0 && funct (inbuf, data) < 0)
188 ret = PFD_FUNCT_ERROR;
192 p_realloc(&inbuf, lenbuf + sizeof(buf));
199 /* SASL authenticator */
200 static pop_auth_res_t pop_auth_sasl (POP_DATA * pop_data, const char *method)
202 sasl_conn_t *saslconn;
203 sasl_interact_t *interaction = NULL;
205 char buf[LONG_STRING];
206 char inbuf[LONG_STRING];
209 const char *pc = NULL;
210 unsigned int len, olen;
211 unsigned char client_start;
213 if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) {
214 return POP_A_FAILURE;
218 method = pop_data->auth_list;
221 rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen,
223 if (rc != SASL_INTERACT)
225 mutt_sasl_interact (interaction);
228 if (rc != SASL_OK && rc != SASL_CONTINUE) {
229 /* SASL doesn't support suggested mechanisms, so fall back */
230 return POP_A_UNAVAIL;
233 client_start = (olen > 0);
235 mutt_message _("Authenticating (SASL)...");
237 snprintf (buf, sizeof (buf), "AUTH %s", mech);
240 /* looping protocol */
242 m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n");
243 mutt_socket_write (pop_data->conn, buf);
244 if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) {
245 sasl_dispose (&saslconn);
246 pop_data->status = POP_DISCONNECTED;
250 if (rc != SASL_CONTINUE)
253 if (!m_strncmp(inbuf, "+ ", 2)
254 && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1,
262 rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen);
263 if (rc != SASL_INTERACT)
265 mutt_sasl_interact (interaction);
270 if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK))
273 /* send out response, or line break if none needed */
275 if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) {
279 /* sasl_client_st(art|ep) allocate pc with malloc, expect me to
281 p_delete((char **)&pc);
288 if (!m_strncmp(inbuf, "+OK", 3)) {
289 mutt_sasl_setup_conn (pop_data->conn, saslconn);
290 return POP_A_SUCCESS;
294 sasl_dispose (&saslconn);
296 /* terminate SASL sessoin if the last responce is not +OK nor -ERR */
297 if (!m_strncmp(inbuf, "+ ", 2)) {
298 snprintf (buf, sizeof (buf), "*\r\n");
299 if (pop_query (pop_data, buf, sizeof (buf)) == PQ_NOT_CONNECTED)
303 mutt_error _("SASL authentication failed.");
307 return POP_A_FAILURE;
310 /* Get the server timestamp for APOP authentication */
311 static void pop_apop_timestamp (POP_DATA * pop_data, char *buf)
315 p_delete(&pop_data->timestamp);
317 if ((p1 = strchr (buf, '<')) && (p2 = strchr (p1, '>'))) {
319 pop_data->timestamp = m_strdup(p1);
323 /* APOP authenticator */
324 static pop_auth_res_t pop_auth_apop (POP_DATA * pop_data,
325 const char *method __attribute__ ((unused)))
328 unsigned char digest[16];
330 char buf[LONG_STRING];
333 if (!pop_data->timestamp)
334 return POP_A_UNAVAIL;
336 mutt_message _("Authenticating (APOP)...");
338 /* Compute the authentication hash to send to the server */
339 MD5Init (&mdContext);
340 MD5Update (&mdContext, (unsigned char *) pop_data->timestamp,
341 strlen (pop_data->timestamp));
342 MD5Update (&mdContext, (unsigned char *) pop_data->conn->account.pass,
343 strlen (pop_data->conn->account.pass));
344 MD5Final (digest, &mdContext);
346 for (i = 0; i < ssizeof(digest); i++)
347 sprintf (hash + 2 * i, "%02x", digest[i]);
349 /* Send APOP command to server */
350 snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user,
353 switch (pop_query (pop_data, buf, sizeof (buf))) {
355 return POP_A_SUCCESS;
356 case PQ_NOT_CONNECTED:
358 case PFD_FUNCT_ERROR:
364 mutt_error ("%s %s", _("APOP authentication failed."), pop_data->err_msg);
367 return POP_A_FAILURE;
370 /* USER authenticator */
371 static pop_auth_res_t pop_auth_user (POP_DATA * pop_data,
372 const char *method __attribute__ ((unused)))
374 char buf[LONG_STRING];
375 pop_query_status ret;
377 if (pop_data->cmd_user == CMD_NOT_AVAILABLE)
378 return POP_A_UNAVAIL;
380 mutt_message _("Logging in...");
382 snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user);
383 ret = pop_query (pop_data, buf, sizeof (buf));
385 if (pop_data->cmd_user == CMD_UNKNOWN) {
387 pop_data->cmd_user = CMD_AVAILABLE;
391 pop_data->cmd_user = CMD_NOT_AVAILABLE;
393 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
394 _("Command USER is not supported by server."));
399 snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass);
400 ret = pop_query (pop_data, buf, sizeof (buf));
405 return POP_A_SUCCESS;
406 case PQ_NOT_CONNECTED:
408 case PFD_FUNCT_ERROR:
414 mutt_error ("%s %s", _("Login failed."), pop_data->err_msg);
417 return POP_A_FAILURE;
423 * -1 - conection lost,
424 * -2 - invalid response.
426 static pop_query_status pop_connect (POP_DATA * pop_data)
428 char buf[LONG_STRING];
430 pop_data->status = POP_NONE;
431 if (mutt_socket_open (pop_data->conn) < 0 ||
432 mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0) {
433 mutt_error (_("Error connecting to server: %s"),
434 pop_data->conn->account.host);
435 return PQ_NOT_CONNECTED;
438 pop_data->status = POP_CONNECTED;
440 if (m_strncmp(buf, "+OK", 3)) {
441 *pop_data->err_msg = '\0';
442 pop_error (pop_data, buf);
443 mutt_error ("%s", pop_data->err_msg);
447 pop_apop_timestamp (pop_data, buf);
453 /* do authentication, using named method or any available if method is NULL */
454 pop_auth_res_t (*authenticate) (POP_DATA *, const char *);
455 /* name of authentication method supported, NULL means variable. If this
456 * is not null, authenticate may ignore the second parameter. */
463 * -1 - conection lost,
465 * -3 - authentication canceled.
467 static pop_query_status pop_authenticate (POP_DATA * pop_data)
469 static pop_auth_t const pop_authenticators[] = {
470 {pop_auth_sasl, NULL},
471 {pop_auth_apop, "apop"},
472 {pop_auth_user, "user"},
476 ACCOUNT *act = &pop_data->conn->account;
477 const pop_auth_t *authenticator;
482 int ret = POP_A_UNAVAIL;
484 if (mutt_account_getuser (act) || !act->user[0] ||
485 mutt_account_getpass (act) || !act->pass[0])
486 return PFD_FUNCT_ERROR;
488 if (PopAuthenticators && *PopAuthenticators) {
489 /* Try user-specified list of authentication methods */
490 methods = m_strdup(PopAuthenticators);
494 comma = strchr (method, ':');
497 authenticator = pop_authenticators;
499 while (authenticator->authenticate) {
500 if (!authenticator->method ||
501 !ascii_strcasecmp (authenticator->method, method)) {
502 ret = authenticator->authenticate (pop_data, method);
503 if (ret == POP_A_SOCKET)
504 switch (pop_connect (pop_data)) {
507 ret = authenticator->authenticate (pop_data, method);
514 if (ret != POP_A_UNAVAIL)
516 if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
517 (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) {
531 /* Fall back to default: any authenticator */
532 authenticator = pop_authenticators;
534 while (authenticator->authenticate) {
535 ret = authenticator->authenticate (pop_data, authenticator->method);
536 if (ret == POP_A_SOCKET)
537 switch (pop_connect (pop_data)) {
541 authenticator->authenticate (pop_data, authenticator->method);
548 if (ret != POP_A_UNAVAIL)
550 if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
551 (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL)))
562 return PQ_NOT_CONNECTED;
565 mutt_error (_("No authenticators available"));
570 /* given an POP mailbox name, return host, port, username and password */
571 static int pop_parse_path (const char *path, ACCOUNT * act)
579 act->port = POP_PORT;
580 act->type = M_ACCT_TYPE_POP;
583 url_parse_ciss (&url, c);
585 if (url.scheme == U_POP || url.scheme == U_POPS) {
586 if (url.scheme == U_POPS) {
587 act->flags |= M_ACCT_SSL;
588 act->port = POP_SSL_PORT;
591 if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0)
599 /* Parse CAPA output */
600 static int fetch_capa (char *line, void *data)
602 POP_DATA *pop_data = (POP_DATA *) data;
605 if (!ascii_strncasecmp (line, "SASL", 4)) {
606 p_delete(&pop_data->auth_list);
607 c = vskipspaces(line + 4);
608 pop_data->auth_list = m_strdup(c);
611 else if (!ascii_strncasecmp (line, "STLS", 4))
612 pop_data->cmd_stls = CMD_AVAILABLE;
614 else if (!ascii_strncasecmp (line, "USER", 4))
615 pop_data->cmd_user = CMD_AVAILABLE;
617 else if (!ascii_strncasecmp (line, "UIDL", 4))
618 pop_data->cmd_uidl = CMD_AVAILABLE;
620 else if (!ascii_strncasecmp (line, "TOP", 3))
621 pop_data->cmd_top = CMD_AVAILABLE;
626 /* Fetch list of the authentication mechanisms */
627 static int fetch_auth (char *line, void *data)
629 POP_DATA *pop_data = (POP_DATA *) data;
630 ssize_t auth_list_len;
632 if (!pop_data->auth_list) {
633 auth_list_len = m_strlen(line) + 1;
634 pop_data->auth_list = p_new(char, auth_list_len);
636 auth_list_len = m_strlen(pop_data->auth_list) + m_strlen(line) + 2;
637 p_realloc(&pop_data->auth_list, auth_list_len);
638 m_strcat(pop_data->auth_list, auth_list_len, " ");
640 m_strcat(pop_data->auth_list, auth_list_len, line);
648 * -1 - conection lost,
649 * -2 - execution error.
651 static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode)
653 char buf[LONG_STRING];
655 /* don't check capabilities on reconnect */
656 if (pop_data->capabilities)
659 /* init capabilities */
661 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
662 pop_data->cmd_stls = CMD_NOT_AVAILABLE;
663 pop_data->cmd_user = CMD_NOT_AVAILABLE;
664 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
665 pop_data->cmd_top = CMD_NOT_AVAILABLE;
666 pop_data->resp_codes = 0;
667 pop_data->expire = 1;
668 pop_data->login_delay = 0;
669 p_delete(&pop_data->auth_list);
672 /* Execute CAPA command */
673 if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) {
674 m_strcpy(buf, sizeof(buf), "CAPA\r\n");
675 switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) {
678 pop_data->cmd_capa = CMD_AVAILABLE;
681 case PFD_FUNCT_ERROR:
684 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
687 case PQ_NOT_CONNECTED:
688 return PQ_NOT_CONNECTED;
692 /* CAPA not supported, use defaults */
693 if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) {
694 pop_data->cmd_user = CMD_UNKNOWN;
695 pop_data->cmd_uidl = CMD_UNKNOWN;
696 pop_data->cmd_top = CMD_UNKNOWN;
698 m_strcpy(buf, sizeof(buf), "AUTH\r\n");
699 if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == PQ_NOT_CONNECTED)
700 return PQ_NOT_CONNECTED;
703 /* Check capabilities */
707 if (!pop_data->expire)
708 msg = _("Unable to leave messages on server.");
709 if (pop_data->cmd_top == CMD_NOT_AVAILABLE)
710 msg = _("Command TOP is not supported by server.");
711 if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE)
712 msg = _("Command UIDL is not supported by server.");
713 if (msg && pop_data->cmd_capa != CMD_AVAILABLE) {
717 pop_data->capabilities = 1;
724 * Open connection and authenticate
726 * -1 - conection lost,
727 * -2 - invalid command or execution error,
728 * -3 - authentication canceled.
730 static pop_query_status pop_open_connection (POP_DATA * pop_data)
732 pop_query_status ret;
733 unsigned int n, size;
734 char buf[LONG_STRING];
736 ret = pop_connect (pop_data);
742 ret = pop_capabilities (pop_data, 0);
743 if (ret == PQ_NOT_CONNECTED)
750 /* Attempt STLS if available and desired. */
751 if (!pop_data->conn->ssf && (pop_data->cmd_stls || mod_ssl.force_tls)) {
752 if (mod_ssl.force_tls)
753 pop_data->use_stls = 2;
754 if (pop_data->use_stls == 0) {
755 pop_data->use_stls = 1;
756 if (mod_ssl.starttls)
757 pop_data->use_stls = 2;
759 if (pop_data->use_stls == 2) {
760 m_strcpy(buf, sizeof(buf), "STLS\r\n");
761 ret = pop_query (pop_data, buf, sizeof (buf));
762 if (ret == PQ_NOT_CONNECTED)
765 mutt_error ("%s", pop_data->err_msg);
768 else if (mutt_ssl_starttls (pop_data->conn))
770 mutt_error (_("Could not negotiate TLS connection"));
775 /* recheck capabilities after STLS completes */
776 ret = pop_capabilities (pop_data, 1);
777 if (ret == PQ_NOT_CONNECTED)
787 if (mod_ssl.force_tls && !pop_data->conn->ssf) {
788 mutt_error _("Encrypted connection unavailable");
793 ret = pop_authenticate (pop_data);
794 if (ret == PQ_NOT_CONNECTED)
796 if (ret == PFD_FUNCT_ERROR)
801 /* recheck capabilities after authentication */
802 ret = pop_capabilities (pop_data, 2);
803 if (ret == PQ_NOT_CONNECTED)
810 /* get total size of mailbox */
811 m_strcpy(buf, sizeof(buf), "STAT\r\n");
812 ret = pop_query (pop_data, buf, sizeof (buf));
813 if (ret == PQ_NOT_CONNECTED)
816 mutt_error ("%s", pop_data->err_msg);
821 sscanf (buf, "+OK %u %u", &n, &size);
822 pop_data->size = size;
826 pop_data->status = POP_DISCONNECTED;
827 mutt_error _("Server closed connection!");
830 return PQ_NOT_CONNECTED;
833 /* logout from POP server */
834 static void pop_logout (CONTEXT * ctx)
836 pop_query_status ret = 0;
837 char buf[LONG_STRING];
838 POP_DATA *pop_data = (POP_DATA *) ctx->data;
840 if (pop_data->status == POP_CONNECTED) {
841 mutt_message _("Closing connection to POP server...");
844 m_strcpy(buf, sizeof(buf), "RSET\r\n");
845 ret = pop_query (pop_data, buf, sizeof (buf));
848 if (ret != PQ_NOT_CONNECTED) {
849 m_strcpy(buf, sizeof(buf), "QUIT\r\n");
850 pop_query (pop_data, buf, sizeof (buf));
856 pop_data->status = POP_DISCONNECTED;
860 /* find message with this UIDL and set refno */
861 static int check_uidl (char *line, void *data)
864 CONTEXT *ctx = (CONTEXT *)data;
866 sscanf (line, "%u %s", &idx, line);
867 for (i = 0; i < ctx->msgcount; i++) {
868 if (!m_strcmp(ctx->hdrs[i]->data, line)) {
869 ctx->hdrs[i]->refno = idx;
877 /* reconnect and verify indexes if connection was lost */
878 static pop_query_status pop_reconnect (CONTEXT * ctx)
880 pop_query_status ret;
881 POP_DATA *pop_data = (POP_DATA *) ctx->data;
884 if (pop_data->status == POP_CONNECTED)
886 if (pop_data->status == POP_BYE)
887 return PQ_NOT_CONNECTED;
890 mutt_socket_close (pop_data->conn);
892 ret = pop_open_connection (pop_data);
896 bar.msg = _("Verifying message indexes...");
898 mutt_progress_bar (&bar, 0);
900 for (i = 0; i < ctx->msgcount; i++)
901 ctx->hdrs[i]->refno = -1;
903 ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx);
905 mutt_error ("%s", pop_data->err_msg);
915 return PQ_NOT_CONNECTED;
917 if (query_quadoption (OPT_POPRECONNECT,
918 _("Connection lost. Reconnect to POP server?")) !=
920 return PQ_NOT_CONNECTED;
923 /* write line to file */
924 static int fetch_message (char *line, void *file)
926 FILE *f = (FILE *) file;
929 if (fputc ('\n', f) == EOF)
939 * -1 - conection lost,
940 * -2 - invalid command or execution error,
941 * -3 - error writing to tempfile
943 static pop_query_status pop_read_header (POP_DATA * pop_data, HEADER * h)
947 pop_query_status ret;
949 char buf[LONG_STRING];
950 char tempfile[_POSIX_PATH_MAX];
952 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
954 mutt_error(_("Could not create temporary file"));
955 return PFD_FUNCT_ERROR;
958 snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno);
959 ret = pop_query (pop_data, buf, sizeof (buf));
961 sscanf (buf, "+OK %d %ld", &idx, &length);
963 snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno);
964 ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f);
966 if (pop_data->cmd_top == CMD_UNKNOWN) {
968 pop_data->cmd_top = CMD_AVAILABLE;
972 pop_data->cmd_top = CMD_NOT_AVAILABLE;
973 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
974 _("Command TOP is not supported by server."));
983 h->env = mutt_read_rfc822_header (f, h, 0, 0);
984 h->content->length = length - h->content->offset + 1;
987 h->content->length--;
988 fgets (buf, sizeof (buf), f);
994 mutt_error ("%s", pop_data->err_msg);
997 case PFD_FUNCT_ERROR:
999 mutt_error _("Can't write header to temporary file!");
1003 case PQ_NOT_CONNECTED:
1005 mutt_error _("Can't fetch header: Not connected!");
1016 static int fetch_uidl (char *line, void *data)
1019 CONTEXT *ctx = (CONTEXT *) data;
1020 POP_DATA *pop_data = (POP_DATA *) ctx->data;
1022 sscanf (line, "%d %s", &idx, line);
1023 for (i = 0; i < ctx->msgcount; i++)
1024 if (!m_strcmp(line, ctx->hdrs[i]->data))
1027 if (i == ctx->msgcount) {
1028 if (i >= ctx->hdrmax)
1029 mx_alloc_memory (ctx);
1032 ctx->hdrs[i] = header_new();
1033 ctx->hdrs[i]->data = m_strdup(line);
1035 else if (ctx->hdrs[i]->index != idx - 1)
1036 pop_data->clear_cache = 1;
1038 ctx->hdrs[i]->refno = idx;
1039 ctx->hdrs[i]->index = idx - 1;
1048 * -1 - conection lost,
1049 * -2 - invalid command or execution error,
1050 * -3 - error writing to tempfile
1052 static int pop_fetch_headers (CONTEXT * ctx)
1054 int i, old_count, new_count;
1055 pop_query_status ret;
1056 POP_DATA *pop_data = (POP_DATA *) ctx->data;
1058 time (&pop_data->check_time);
1059 pop_data->clear_cache = 0;
1061 for (i = 0; i < ctx->msgcount; i++)
1062 ctx->hdrs[i]->refno = -1;
1064 old_count = ctx->msgcount;
1065 ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx);
1066 new_count = ctx->msgcount;
1067 ctx->msgcount = old_count;
1069 if (pop_data->cmd_uidl == CMD_UNKNOWN) {
1071 pop_data->cmd_uidl = CMD_AVAILABLE;
1074 if (ret == PQ_ERR && pop_data->cmd_uidl == CMD_UNKNOWN) {
1075 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
1077 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
1078 _("Command UIDL is not supported by server."));
1083 for (i = 0; i < old_count; i++)
1084 if (ctx->hdrs[i]->refno == -1)
1085 ctx->hdrs[i]->deleted = 1;
1087 for (i = old_count; i < new_count; i++) {
1088 mutt_message (_("Fetching message headers... [%d/%d]"),
1089 i + 1 - old_count, new_count - old_count);
1091 ret = pop_read_header (pop_data, ctx->hdrs[i]);
1099 mx_update_context (ctx, i - old_count);
1103 for (i = ctx->msgcount; i < new_count; i++)
1104 header_delete(&ctx->hdrs[i]);
1108 mutt_clear_error ();
1109 return (new_count - old_count);
1112 /* open POP mailbox - fetch only headers */
1113 static int pop_open_mailbox (CONTEXT * ctx)
1116 char buf[LONG_STRING];
1122 if (pop_parse_path (ctx->path, &act)) {
1123 mutt_error (_("%s is an invalid POP path"), ctx->path);
1128 mutt_account_tourl (&act, &url);
1130 url_ciss_tostring (&url, buf, sizeof (buf), 0);
1131 conn = mutt_conn_find (NULL, &act);
1135 p_delete(&ctx->path);
1136 ctx->path = m_strdup(buf);
1138 pop_data = p_new(POP_DATA, 1);
1139 pop_data->conn = conn;
1140 ctx->data = pop_data;
1142 if (pop_open_connection (pop_data) < 0)
1145 conn->data = pop_data;
1148 if (pop_reconnect (ctx) != PQ_OK)
1151 ctx->size = pop_data->size;
1153 mutt_message _("Fetching list of messages...");
1155 ret = pop_fetch_headers (ctx);
1167 /* delete all cached messages */
1168 static void pop_clear_cache (POP_DATA * pop_data)
1172 if (!pop_data->clear_cache)
1175 for (i = 0; i < POP_CACHE_LEN; i++) {
1176 if (pop_data->cache[i].path) {
1177 unlink (pop_data->cache[i].path);
1178 p_delete(&pop_data->cache[i].path);
1183 /* close POP mailbox */
1184 static void pop_close_mailbox (CONTEXT * ctx)
1186 POP_DATA *pop_data = (POP_DATA *) ctx->data;
1193 if (pop_data->status != POP_NONE)
1194 mutt_socket_close (pop_data->conn);
1196 pop_data->status = POP_NONE;
1198 pop_data->clear_cache = 1;
1199 pop_clear_cache (pop_data);
1201 if (!pop_data->conn->data)
1202 mutt_socket_free (pop_data->conn);
1207 /* fetch message from POP server */
1208 int pop_fetch_message (MESSAGE * msg, CONTEXT * ctx, int msgno)
1212 char buf[LONG_STRING];
1213 char path[_POSIX_PATH_MAX];
1215 POP_DATA *pop_data = (POP_DATA *) ctx->data;
1217 HEADER *h = ctx->hdrs[msgno];
1219 /* see if we already have the message in our cache */
1220 cache = &pop_data->cache[h->index % POP_CACHE_LEN];
1223 if (cache->index == h->index) {
1224 /* yes, so just return a pointer to the message */
1225 msg->fp = fopen (cache->path, "r");
1229 mutt_perror (cache->path);
1234 /* clear the previous entry */
1235 unlink (cache->path);
1236 p_delete(&cache->path);
1241 if (pop_reconnect (ctx) != PQ_OK)
1244 /* verify that massage index is correct */
1247 _("The message index is incorrect. Try reopening the mailbox.");
1252 bar.size = h->content->length + h->content->offset - 1;
1253 bar.msg = _("Fetching message...");
1254 mutt_progress_bar (&bar, 0);
1256 msg->fp = m_tempfile(path, sizeof(path), NONULL(MCore.tmpdir), NULL);
1258 mutt_error(_("Could not create temporary file"));
1263 snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno);
1265 ret = pop_fetch_data (pop_data, buf, &bar, fetch_message, msg->fp);
1272 if (ret == PQ_ERR) {
1273 mutt_error ("%s", pop_data->err_msg);
1278 if (ret == PFD_FUNCT_ERROR) {
1279 mutt_error _("Can't write message to temporary file!");
1286 /* Update the header information. Previously, we only downloaded a
1287 * portion of the headers, those required for the main display.
1289 cache->index = h->index;
1290 cache->path = m_strdup(path);
1293 envelope_delete(&h->env);
1294 h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
1297 fgets (buf, sizeof (buf), msg->fp);
1298 while (!feof (msg->fp)) {
1299 ctx->hdrs[msgno]->lines++;
1300 fgets (buf, sizeof (buf), msg->fp);
1303 h->content->length = ftello (msg->fp) - h->content->offset;
1305 /* This needs to be done in case this is a multipart message */
1306 h->security = crypt_query (h->content);
1308 mutt_clear_error ();
1314 /* update POP mailbox - delete messages from server */
1315 static pop_query_status
1316 pop_sync_mailbox (CONTEXT * ctx, int unused __attribute__ ((unused)),
1317 int *index_hint __attribute__ ((unused)))
1320 pop_query_status ret;
1321 char buf[LONG_STRING];
1322 POP_DATA *pop_data = (POP_DATA *) ctx->data;
1324 pop_data->check_time = 0;
1327 if (pop_reconnect (ctx) != PQ_OK)
1328 return PQ_NOT_CONNECTED;
1330 mutt_message (_("Marking %d messages deleted..."), ctx->deleted);
1332 for (i = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) {
1333 if (ctx->hdrs[i]->deleted) {
1334 snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno);
1335 ret = pop_query (pop_data, buf, sizeof (buf));
1340 m_strcpy(buf, sizeof(buf), "QUIT\r\n");
1341 ret = pop_query (pop_data, buf, sizeof (buf));
1345 pop_data->clear_cache = 1;
1346 pop_clear_cache (pop_data);
1347 pop_data->status = POP_DISCONNECTED;
1351 if (ret == PQ_ERR) {
1352 mutt_error ("%s", pop_data->err_msg);
1354 return PQ_NOT_CONNECTED;
1359 /* Check for new messages and fetch headers */
1360 static int pop_check_mailbox (CONTEXT * ctx,
1361 int *index_hint __attribute__ ((unused)),
1362 int unused __attribute__ ((unused)))
1365 POP_DATA *pop_data = (POP_DATA *) ctx->data;
1367 if ((pop_data->check_time + PopCheckTimeout) > time (NULL))
1372 mutt_socket_close (pop_data->conn);
1374 if (pop_open_connection (pop_data) < 0)
1377 ctx->size = pop_data->size;
1379 mutt_message _("Checking for new messages...");
1381 ret = pop_fetch_headers (ctx);
1382 pop_clear_cache (pop_data);
1393 /* Fetch messages and save them in $spoolfile */
1394 void pop_fetch_mail (void)
1396 char buffer[LONG_STRING];
1397 char msgbuf[STRING];
1399 int i, delanswer, last = 0, msgs, bytes, rset = 0;
1400 pop_query_status ret;
1403 MESSAGE *msg = NULL;
1409 mutt_error _("POP host is not defined.");
1414 plen = m_strlen(PopHost) + 7;
1415 url = p = p_new(char, plen);
1416 if (url_check_scheme (PopHost) == U_UNKNOWN) {
1417 plen -= m_strcpy(url, plen, "pop://");
1420 m_strcpy(p, plen, PopHost);
1422 ret = pop_parse_path (url, &act);
1425 mutt_error (_("%s is an invalid POP path"), PopHost);
1429 conn = mutt_conn_find (NULL, &act);
1433 pop_data = p_new(POP_DATA, 1);
1434 pop_data->conn = conn;
1436 if (pop_open_connection (pop_data) < 0) {
1437 mutt_socket_free (pop_data->conn);
1438 p_delete(&pop_data);
1442 conn->data = pop_data;
1444 mutt_message _("Checking for new messages...");
1446 /* find out how many messages are in the mailbox. */
1447 m_strcpy(buffer, sizeof(buffer), "STAT\r\n");
1448 ret = pop_query (pop_data, buffer, sizeof (buffer));
1449 if (ret == PQ_NOT_CONNECTED)
1451 if (ret == PQ_ERR) {
1452 mutt_error ("%s", pop_data->err_msg);
1456 sscanf (buffer, "+OK %d %d", &msgs, &bytes);
1458 /* only get unread messages */
1459 if (msgs > 0 && option (OPTPOPLAST)) {
1460 m_strcpy(buffer, sizeof(buffer), "LAST\r\n");
1461 ret = pop_query (pop_data, buffer, sizeof (buffer));
1462 if (ret == PQ_NOT_CONNECTED)
1465 sscanf (buffer, "+OK %d", &last);
1469 mutt_message _("No new mail in POP mailbox.");
1474 if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL)
1478 query_quadoption (OPT_POPDELETE, _("Delete messages from server?"));
1480 snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."),
1482 mutt_message ("%s", msgbuf);
1484 for (i = last + 1; i <= msgs; i++) {
1485 if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL)
1488 snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i);
1489 ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp);
1490 if (ret == PFD_FUNCT_ERROR)
1493 if (ret == PQ_OK && mx_commit_message (msg, &ctx) != 0) {
1495 ret = PFD_FUNCT_ERROR;
1498 mx_close_message (&msg);
1501 if (ret == PQ_OK && delanswer == M_YES) {
1502 /* delete the message on the server */
1503 snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i);
1504 ret = pop_query (pop_data, buffer, sizeof (buffer));
1507 if (ret == PQ_NOT_CONNECTED) {
1508 mx_close_mailbox (&ctx, NULL);
1511 if (ret == PQ_ERR) {
1512 mutt_error ("%s", pop_data->err_msg);
1515 if (ret == -3) { /* this is -3 when ret != 0, because it will keep the value from before *gna* */
1516 mutt_error _("Error while writing mailbox!");
1521 mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last,
1525 mx_close_mailbox (&ctx, NULL);
1528 /* make sure no messages get deleted */
1529 m_strcpy(buffer, sizeof(buffer), "RSET\r\n");
1530 if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED)
1535 /* exit gracefully */
1536 m_strcpy(buffer, sizeof(buffer), "QUIT\r\n");
1537 if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED)
1539 mutt_socket_close (conn);
1540 p_delete(&pop_data);
1544 mutt_error _("Server closed connection!");
1545 mutt_socket_close (conn);
1546 p_delete(&pop_data);
1549 static int pop_is_magic (const char* path, struct stat* st __attribute__ ((unused))) {
1550 url_scheme_t s = url_check_scheme (NONULL (path));
1551 return ((s == U_POP || s == U_POPS) ? M_POP : -1);
1554 static int acl_check_pop (CONTEXT* ctx __attribute__ ((unused)), int bit) {
1556 case ACL_INSERT: /* editing messages */
1557 case ACL_WRITE: /* change importance */
1559 case ACL_DELETE: /* (un)deletion */
1560 case ACL_SEEN: /* mark as read */
1567 mx_t const pop_mx = {