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.
11 #include <sasl/sasl.h>
12 #include <sasl/saslutil.h>
14 #include <lib-sys/mutt_socket.h>
15 #include <lib-ui/lib-ui.h>
19 #include "mutt_sasl.h"
23 #define POP_SSL_PORT 995
24 #define POP_CACHE_LEN 10
25 /* maximal length of the server response (RFC1939) */
26 #define POP_CMD_RESPONSE 512
49 /* pop_fetch_data uses pop_query_status and this return value */
52 PQ_NOT_CONNECTED = -1,
57 CMD_NOT_AVAILABLE = 0,
59 CMD_UNKNOWN /* unknown whether it is available or not */
66 unsigned capabilities : 1;
67 unsigned use_stls : 2;
68 cmd_status cmd_capa : 2; /* optional command CAPA */
69 cmd_status cmd_stls : 2; /* optional command STLS */
70 cmd_status cmd_uidl : 2; /* optional command UIDL */
71 cmd_status cmd_top : 2; /* optional command TOP */
72 cmd_status cmd_user : 2; /* optional command USER */
73 unsigned resp_codes : 1; /* server supports extended response codes */
74 unsigned clear_cache : 1;
78 char *auth_list; /* list of auth mechanisms */
80 char err_msg[POP_CMD_RESPONSE];
81 POP_CACHE cache[POP_CACHE_LEN];
84 /* pop low level functions {{{ */
86 static void pop_error(pop_data_t *pop_data, const char *msg)
88 if (!m_strncmp(msg, "-ERR ", 5)) {
89 const char *s = skipspaces(msg + 5);
94 m_strcat(pop_data->err_msg, sizeof(pop_data->err_msg), msg);
95 m_strrtrim(pop_data->err_msg);
99 * Send data from buffer and receive answer to the same buffer
101 * -1 - conection lost,
102 * -2 - invalid command or execution error.
104 static pop_query_status _pop_query(pop_data_t *pop_data, char *buf, ssize_t buflen)
106 if (pop_data->status != POP_CONNECTED)
107 return PQ_NOT_CONNECTED;
109 mutt_socket_write(pop_data->conn, buf);
110 snprintf(pop_data->err_msg, sizeof(pop_data->err_msg), "%.*s: ",
111 (int)(strpbrk(buf, " \r\n") - buf), buf);
113 if (mutt_socket_readln(buf, buflen, pop_data->conn) < 0) {
114 pop_data->status = POP_DISCONNECTED;
115 return PQ_NOT_CONNECTED;
117 if (!m_strncmp(buf, "+OK", 3))
120 pop_error(pop_data, buf);
123 #define pop_query(pd, b, l, fmt, ...) \
124 (snprintf(b, l, fmt "\r\n", ##__VA_ARGS__), _pop_query(pd, b, l))
129 * -1 - conection lost,
130 * -2 - invalid response.
132 static pop_query_status pop_connect(pop_data_t * pop_data)
134 char buf[LONG_STRING];
137 if (mutt_socket_open(pop_data->conn) < 0
138 || mutt_socket_readln(buf, sizeof(buf), pop_data->conn) < 0)
140 mutt_error(_("Error connecting to server: %s"),
141 pop_data->conn->account.host);
142 pop_data->status = POP_NONE;
143 return PQ_NOT_CONNECTED;
146 pop_data->status = POP_CONNECTED;
147 if (m_strncmp(buf, "+OK", 3)) {
148 pop_data->err_msg[0] = '\0';
149 pop_error(pop_data, buf);
150 mutt_error("%s", pop_data->err_msg);
154 p_delete(&pop_data->timestamp);
155 if ((p = strchr(buf, '<')) && (q = strchr(p, '>'))) {
156 pop_data->timestamp = p_dupstr(p, q + 1 - p);
161 static void pop_logout (CONTEXT * ctx)
163 pop_query_status ret = 0;
164 char buf[LONG_STRING];
165 pop_data_t *pop_data = (pop_data_t *) ctx->data;
167 if (pop_data->status == POP_CONNECTED) {
168 mutt_message _("Closing connection to POP server...");
171 ret = pop_query(pop_data, buf, sizeof(buf), "RSET");
174 if (ret != PQ_NOT_CONNECTED) {
175 pop_query(pop_data, buf, sizeof(buf), "QUIT");
181 pop_data->status = POP_DISCONNECTED;
186 /* Authentication {{{ */
188 static pop_auth_res_t pop_auth_sasl(pop_data_t *pop_data, const char *method)
190 sasl_conn_t *saslconn;
191 sasl_interact_t *interaction = NULL;
193 char buf[LONG_STRING], inbuf[LONG_STRING];
194 const char *mech, *pc = NULL;
195 unsigned int len, olen;
196 unsigned char client_start;
199 if (mutt_sasl_client_new(pop_data->conn, &saslconn) < 0) {
200 return POP_A_FAILURE;
204 method = pop_data->auth_list;
207 rc = sasl_client_start(saslconn, method, &interaction, &pc, &olen,
209 if (rc != SASL_INTERACT)
211 mutt_sasl_interact(interaction);
214 if (rc != SASL_OK && rc != SASL_CONTINUE) {
215 return POP_A_UNAVAIL;
218 client_start = (olen > 0);
219 mutt_message(_("Authenticating (SASL)..."));
220 snprintf(buf, sizeof (buf), "AUTH %s", mech);
223 /* looping protocol */
225 m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n");
226 mutt_socket_write(pop_data->conn, buf);
227 if (mutt_socket_readln(inbuf, sizeof(inbuf), pop_data->conn) < 0) {
228 sasl_dispose(&saslconn);
229 pop_data->status = POP_DISCONNECTED;
233 if (rc != SASL_CONTINUE)
236 if (!m_strncmp(inbuf, "+ ", 2)
237 && sasl_decode64(inbuf, strlen(inbuf),
238 buf, sizeof(buf) - 1, &len) != SASL_OK)
245 rc = sasl_client_step(saslconn, buf, len, &interaction, &pc,
247 if (rc != SASL_INTERACT)
249 mutt_sasl_interact(interaction);
255 if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK))
258 /* send out response, or line break if none needed */
260 if (sasl_encode64(pc, olen, buf, sizeof(buf), &olen) != SASL_OK) {
263 p_delete((char **)&pc);
270 if (!m_strncmp(inbuf, "+OK", 3)) {
271 mutt_sasl_setup_conn(pop_data->conn, saslconn);
272 return POP_A_SUCCESS;
276 sasl_dispose(&saslconn);
277 p_delete((char **)&pc);
279 /* terminate SASL session if the last responce is not +OK nor -ERR */
280 if (!m_strncmp(inbuf, "+ ", 2)) {
281 if (pop_query(pop_data, buf, sizeof(buf), "*") == PQ_NOT_CONNECTED)
285 mutt_error(_("SASL authentication failed."));
288 return POP_A_FAILURE;
291 static pop_auth_res_t pop_auth_apop(pop_data_t *pop_data, const char *method)
294 unsigned char digest[16];
296 char buf[LONG_STRING];
299 if (!pop_data->timestamp)
300 return POP_A_UNAVAIL;
302 mutt_message _("Authenticating (APOP)...");
304 /* Compute the authentication hash to send to the server */
306 MD5Update(&mdContext, (unsigned char *)pop_data->timestamp,
307 strlen(pop_data->timestamp));
308 MD5Update(&mdContext, (unsigned char *)pop_data->conn->account.pass,
309 strlen(pop_data->conn->account.pass));
310 MD5Final(digest, &mdContext);
312 for (i = 0; i < countof(digest); i++)
313 sprintf(hash + 2 * i, "%02x", digest[i]);
315 switch (pop_query(pop_data, buf, sizeof(buf), "APOP %s %s",
316 pop_data->conn->account.user, hash))
319 return POP_A_SUCCESS;
320 case PQ_NOT_CONNECTED:
323 mutt_error("%s %s", _("APOP authentication failed."), pop_data->err_msg);
325 return POP_A_FAILURE;
329 static pop_auth_res_t pop_auth_user(pop_data_t *pop_data, const char *method)
331 char buf[LONG_STRING];
332 pop_query_status ret;
334 if (pop_data->cmd_user == CMD_NOT_AVAILABLE)
335 return POP_A_UNAVAIL;
337 mutt_message _("Authenticating (USER)...");
338 ret = pop_query(pop_data, buf, sizeof(buf), "USER %s",
339 pop_data->conn->account.user);
341 if (pop_data->cmd_user == CMD_UNKNOWN) {
343 pop_data->cmd_user = CMD_AVAILABLE;
345 pop_data->cmd_user = CMD_NOT_AVAILABLE;
349 ret = pop_query(pop_data, buf, sizeof(buf), "PASS %s",
350 pop_data->conn->account.pass);
355 return POP_A_SUCCESS;
356 case PQ_NOT_CONNECTED:
359 mutt_error("%s %s", _("USER authentication failed."), pop_data->err_msg);
361 return POP_A_FAILURE;
366 pop_auth_res_t (*do_auth)(pop_data_t *, const char *);
370 static pop_query_status pop_authenticate (pop_data_t * pop_data)
372 static pop_auth_t const pop_authenticators[] = {
373 {pop_auth_sasl, NULL},
374 {pop_auth_apop, "apop"},
375 {pop_auth_user, "user"},
379 ACCOUNT *act = &pop_data->conn->account;
380 const pop_auth_t *auth;
383 if (mutt_account_getuser(act) || !act->user[0]
384 || mutt_account_getpass(act) || !act->pass[0])
386 return PFD_FUNCT_ERROR;
389 if (!m_strisempty(PopAuthenticators)) {
393 for (p = PopAuthenticators; ; p = q) {
399 q = strchrnul(p, ':');
400 m_strncpy(buf, sizeof(buf), p, q - p);
402 for (auth = pop_authenticators; auth->do_auth; auth++) {
403 if (auth->method && ascii_strcasecmp(auth->method, buf))
406 switch (auth->do_auth(pop_data, buf)) {
410 return PQ_NOT_CONNECTED;
417 mutt_socket_close(pop_data->conn);
421 for (auth = pop_authenticators; auth->do_auth; auth++) {
422 switch (auth->do_auth(pop_data, auth->method)) {
426 return PQ_NOT_CONNECTED;
433 mutt_socket_close(pop_data->conn);
438 mutt_error(_("No authenticators available"));
445 * This function calls funct(*line, *data) for each received line,
446 * funct(NULL, *data) if rewind(*data) needs, exits when fail or done.
449 * -1 - conection lost,
450 * -2 - invalid command or execution error,
451 * -3 - error in funct(*line, *data)
453 static pop_query_status
454 pop_fetch_data(pop_data_t *pop_data, const char *query, progress_t *bar,
455 int (*funct)(char *, void *), void *data)
457 pop_query_status ret;
458 char buf[LONG_STRING];
464 m_strcpy(buf, sizeof(buf), query);
465 ret = _pop_query(pop_data, buf, sizeof(buf));
472 if (mutt_socket_readln2(&inbuf, pop_data->conn) < 0) {
473 pop_data->status = POP_DISCONNECTED;
474 ret = PQ_NOT_CONNECTED;
479 mutt_progress_bar(bar, pos += inbuf.len);
482 if (inbuf.data[0] == '.') {
483 if (inbuf.data[1] != '.')
488 if (funct(inbuf.data + dot, data) < 0) {
490 ret = PFD_FUNCT_ERROR;
494 buffer_reset(&inbuf);
501 static int fetch_capa (char *line, void *data)
503 pop_data_t *pop_data = (pop_data_t *) data;
506 if (!ascii_strncasecmp (line, "SASL", 4)) {
507 p_delete(&pop_data->auth_list);
508 c = vskipspaces(line + 4);
509 pop_data->auth_list = m_strdup(c);
512 else if (!ascii_strncasecmp (line, "STLS", 4))
513 pop_data->cmd_stls = CMD_AVAILABLE;
515 else if (!ascii_strncasecmp (line, "UIDL", 4))
516 pop_data->cmd_uidl = CMD_AVAILABLE;
518 else if (!ascii_strncasecmp (line, "TOP", 3))
519 pop_data->cmd_top = CMD_AVAILABLE;
524 static int fetch_auth (char *line, void *data)
526 pop_data_t *pop_data = (pop_data_t *) data;
527 ssize_t auth_list_len;
529 if (!pop_data->auth_list) {
530 auth_list_len = m_strlen(line) + 1;
531 pop_data->auth_list = p_new(char, auth_list_len);
533 auth_list_len = m_strlen(pop_data->auth_list) + m_strlen(line) + 2;
534 p_realloc(&pop_data->auth_list, auth_list_len);
535 m_strcat(pop_data->auth_list, auth_list_len, " ");
537 m_strcat(pop_data->auth_list, auth_list_len, line);
545 * -1 - conection lost,
546 * -2 - execution error.
548 static pop_query_status pop_capabilities(pop_data_t * pop_data, int mode)
550 /* don't check capabilities on reconnect */
551 if (pop_data->capabilities)
554 /* init capabilities */
556 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
557 pop_data->cmd_stls = CMD_NOT_AVAILABLE;
558 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
559 pop_data->cmd_top = CMD_NOT_AVAILABLE;
560 pop_data->cmd_user = CMD_NOT_AVAILABLE;
561 pop_data->resp_codes = 0;
562 p_delete(&pop_data->auth_list);
565 /* Execute CAPA command */
566 if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) {
567 switch (pop_fetch_data(pop_data, "CAPA\r\n", NULL, fetch_capa, pop_data)) {
569 pop_data->cmd_capa = CMD_AVAILABLE;
571 case PFD_FUNCT_ERROR:
573 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
575 case PQ_NOT_CONNECTED:
576 return PQ_NOT_CONNECTED;
580 /* CAPA not supported, use defaults */
581 if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) {
582 pop_data->cmd_uidl = CMD_UNKNOWN;
583 pop_data->cmd_top = CMD_UNKNOWN;
585 if (pop_fetch_data(pop_data, "AUTH\r\n", NULL, fetch_auth, pop_data) ==
587 return PQ_NOT_CONNECTED;
590 /* Check capabilities */
592 const char *msg = NULL;
594 if (pop_data->cmd_top == CMD_NOT_AVAILABLE)
595 msg = _("Command TOP is not supported by server.");
596 if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE)
597 msg = _("Command UIDL is not supported by server.");
598 if (msg && pop_data->cmd_capa != CMD_AVAILABLE) {
602 pop_data->capabilities = 1;
608 /* given an POP mailbox name, return host, port, username and password */
609 static int pop_parse_path (const char *path, ACCOUNT * act)
617 act->port = POP_PORT;
618 act->type = M_ACCT_TYPE_POP;
621 url_parse_ciss (&url, c);
623 if (url.scheme == U_POP || url.scheme == U_POPS) {
624 if (url.scheme == U_POPS) {
626 act->port = POP_SSL_PORT;
629 if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0)
638 * Open connection and authenticate
640 * -1 - conection lost,
641 * -2 - invalid command or execution error,
642 * -3 - authentication canceled.
644 static pop_query_status pop_open_connection (pop_data_t * pop_data)
646 pop_query_status ret;
648 char buf[LONG_STRING];
650 ret = pop_connect(pop_data);
656 ret = pop_capabilities (pop_data, 0);
657 if (ret == PQ_NOT_CONNECTED)
664 /* Attempt STLS if available and desired. */
665 if (!pop_data->conn->ssf && (pop_data->cmd_stls || mod_ssl.force_tls)) {
666 if (mod_ssl.force_tls)
667 pop_data->use_stls = 2;
668 if (pop_data->use_stls == 0) {
669 pop_data->use_stls = 1;
670 if (mod_ssl.starttls)
671 pop_data->use_stls = 2;
673 if (pop_data->use_stls == 2) {
674 ret = pop_query(pop_data, buf, sizeof(buf), "STLS");
675 if (ret == PQ_NOT_CONNECTED)
678 mutt_error ("%s", pop_data->err_msg);
681 else if (mutt_ssl_starttls (pop_data->conn))
683 mutt_error (_("Could not negotiate TLS connection"));
688 /* recheck capabilities after STLS completes */
689 ret = pop_capabilities (pop_data, 1);
690 if (ret == PQ_NOT_CONNECTED)
700 if (mod_ssl.force_tls && !pop_data->conn->ssf) {
701 mutt_error _("Encrypted connection unavailable");
706 ret = pop_authenticate (pop_data);
707 if (ret == PQ_NOT_CONNECTED)
709 if (ret == PFD_FUNCT_ERROR)
714 /* recheck capabilities after authentication */
715 ret = pop_capabilities (pop_data, 2);
716 if (ret == PQ_NOT_CONNECTED)
723 /* get total size of mailbox */
724 ret = pop_query(pop_data, buf, sizeof(buf), "STAT");
725 if (ret == PQ_NOT_CONNECTED)
728 mutt_error ("%s", pop_data->err_msg);
733 sscanf (buf, "+OK %u %u", &n, &size);
734 pop_data->size = size;
738 pop_data->status = POP_DISCONNECTED;
739 mutt_error _("Server closed connection!");
742 return PQ_NOT_CONNECTED;
745 /* find message with this UIDL and set refno */
746 static int check_uidl (char *line, void *data)
749 CONTEXT *ctx = (CONTEXT *)data;
751 sscanf (line, "%u %s", &idx, line);
752 for (i = 0; i < ctx->msgcount; i++) {
753 if (!m_strcmp(ctx->hdrs[i]->data, line)) {
754 ctx->hdrs[i]->refno = idx;
762 /* reconnect and verify indexes if connection was lost */
763 static pop_query_status pop_reconnect (CONTEXT * ctx)
765 pop_query_status ret;
766 pop_data_t *pop_data = (pop_data_t *) ctx->data;
769 if (pop_data->status == POP_CONNECTED)
771 if (pop_data->status == POP_BYE)
772 return PQ_NOT_CONNECTED;
775 mutt_socket_close (pop_data->conn);
777 ret = pop_open_connection(pop_data);
781 bar.msg = _("Verifying message indexes...");
783 mutt_progress_bar (&bar, 0);
785 for (i = 0; i < ctx->msgcount; i++)
786 ctx->hdrs[i]->refno = -1;
788 ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx);
790 mutt_error ("%s", pop_data->err_msg);
800 return PQ_NOT_CONNECTED;
802 if (query_quadoption (OPT_POPRECONNECT,
803 _("Connection lost. Reconnect to POP server?")) !=
805 return PQ_NOT_CONNECTED;
809 /* write line to file */
810 static int fetch_message (char *line, void *file)
812 FILE *f = (FILE *) file;
815 if (fputc ('\n', f) == EOF)
825 * -1 - conection lost,
826 * -2 - invalid command or execution error,
827 * -3 - error writing to tempfile
829 static pop_query_status pop_read_header (pop_data_t * pop_data, HEADER * h)
833 pop_query_status ret;
835 char buf[LONG_STRING];
839 mutt_error(_("Could not create temporary file"));
840 return PFD_FUNCT_ERROR;
843 ret = pop_query(pop_data, buf, sizeof(buf), "LIST %d", h->refno);
845 sscanf (buf, "+OK %d %ld", &idx, &length);
847 snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno);
848 ret = pop_fetch_data(pop_data, buf, NULL, fetch_message, f);
850 if (pop_data->cmd_top == CMD_UNKNOWN) {
852 pop_data->cmd_top = CMD_AVAILABLE;
856 pop_data->cmd_top = CMD_NOT_AVAILABLE;
857 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
858 _("Command TOP is not supported by server."));
867 h->env = mutt_read_rfc822_header (f, h, 0, 0);
868 h->content->length = length - h->content->offset + 1;
871 h->content->length--;
872 fgets (buf, sizeof (buf), f);
878 mutt_error ("%s", pop_data->err_msg);
881 case PFD_FUNCT_ERROR:
883 mutt_error _("Can't write header to temporary file!");
887 case PQ_NOT_CONNECTED:
889 mutt_error _("Can't fetch header: Not connected!");
899 static int fetch_uidl (char *line, void *data)
902 CONTEXT *ctx = (CONTEXT *) data;
903 pop_data_t *pop_data = (pop_data_t *) ctx->data;
905 sscanf (line, "%d %s", &idx, line);
906 for (i = 0; i < ctx->msgcount; i++)
907 if (!m_strcmp(line, ctx->hdrs[i]->data))
910 if (i == ctx->msgcount) {
911 if (i >= ctx->hdrmax)
912 mx_alloc_memory (ctx);
915 ctx->hdrs[i] = header_new();
916 ctx->hdrs[i]->data = m_strdup(line);
918 else if (ctx->hdrs[i]->index != idx - 1)
919 pop_data->clear_cache = 1;
921 ctx->hdrs[i]->refno = idx;
922 ctx->hdrs[i]->index = idx - 1;
931 * -1 - conection lost,
932 * -2 - invalid command or execution error,
933 * -3 - error writing to tempfile
935 static int pop_fetch_headers (CONTEXT * ctx)
937 int i, old_count, new_count;
938 pop_query_status ret;
939 pop_data_t *pop_data = (pop_data_t *) ctx->data;
941 time (&pop_data->check_time);
942 pop_data->clear_cache = 0;
944 for (i = 0; i < ctx->msgcount; i++)
945 ctx->hdrs[i]->refno = -1;
947 old_count = ctx->msgcount;
948 ret = pop_fetch_data(pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx);
949 new_count = ctx->msgcount;
950 ctx->msgcount = old_count;
952 if (pop_data->cmd_uidl == CMD_UNKNOWN) {
954 pop_data->cmd_uidl = CMD_AVAILABLE;
957 if (ret == PQ_ERR && pop_data->cmd_uidl == CMD_UNKNOWN) {
958 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
960 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
961 _("Command UIDL is not supported by server."));
966 for (i = 0; i < old_count; i++)
967 if (ctx->hdrs[i]->refno == -1)
968 ctx->hdrs[i]->deleted = 1;
970 for (i = old_count; i < new_count; i++) {
971 mutt_message (_("Fetching message headers... [%d/%d]"),
972 i + 1 - old_count, new_count - old_count);
974 ret = pop_read_header (pop_data, ctx->hdrs[i]);
982 mx_update_context (ctx, i - old_count);
986 for (i = ctx->msgcount; i < new_count; i++)
987 header_delete(&ctx->hdrs[i]);
992 return new_count - old_count;
995 /* delete all cached messages */
996 static void pop_clear_cache (pop_data_t * pop_data)
1000 if (!pop_data->clear_cache)
1003 for (i = 0; i < POP_CACHE_LEN; i++) {
1004 if (pop_data->cache[i].path) {
1005 unlink (pop_data->cache[i].path);
1006 p_delete(&pop_data->cache[i].path);
1011 /* pop_mx functions {{{ */
1013 static int pop_is_magic(const char* path, struct stat* st)
1015 url_scheme_t s = url_check_scheme(NONULL(path));
1016 return s == U_POP || s == U_POPS ? M_POP : -1;
1019 static int pop_open_mailbox (CONTEXT * ctx)
1022 char buf[LONG_STRING];
1025 pop_data_t *pop_data;
1028 if (pop_parse_path (ctx->path, &act)) {
1029 mutt_error (_("%s is an invalid POP path"), ctx->path);
1034 mutt_account_tourl (&act, &url);
1036 url_ciss_tostring (&url, buf, sizeof (buf), 0);
1037 conn = mutt_conn_find (NULL, &act);
1041 p_delete(&ctx->path);
1042 ctx->path = m_strdup(buf);
1044 pop_data = p_new(pop_data_t, 1);
1045 pop_data->conn = conn;
1046 ctx->data = pop_data;
1048 if (pop_open_connection(pop_data) != PQ_OK)
1051 conn->data = pop_data;
1054 if (pop_reconnect (ctx) != PQ_OK)
1057 mutt_message _("Fetching list of messages...");
1058 ctx->size = pop_data->size;
1059 ret = pop_fetch_headers (ctx);
1071 static int pop_acl_check(CONTEXT *ctx, int bit)
1074 case ACL_DELETE: /* (un)deletion */
1075 case ACL_SEEN: /* mark as read */
1077 case ACL_INSERT: /* editing messages */
1078 case ACL_WRITE: /* change importance */
1084 static int pop_check_mailbox(CONTEXT * ctx, int *index_hint, int unused)
1087 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1089 if ((pop_data->check_time + PopCheckTimeout) > time (NULL))
1094 mutt_socket_close (pop_data->conn);
1096 if (pop_open_connection (pop_data) < 0)
1099 mutt_message _("Checking for new messages...");
1100 ctx->size = pop_data->size;
1101 ret = pop_fetch_headers (ctx);
1102 pop_clear_cache (pop_data);
1113 static void pop_close_mailbox (CONTEXT * ctx)
1115 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1122 if (pop_data->status != POP_NONE)
1123 mutt_socket_close (pop_data->conn);
1125 pop_data->status = POP_NONE;
1127 pop_data->clear_cache = 1;
1128 pop_clear_cache (pop_data);
1130 if (!pop_data->conn->data)
1131 mutt_socket_free (pop_data->conn);
1136 static int pop_sync_mailbox(CONTEXT * ctx, int unused, int *index_hint)
1139 pop_query_status ret;
1140 char buf[LONG_STRING];
1141 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1143 pop_data->check_time = 0;
1146 if (pop_reconnect (ctx) != PQ_OK)
1147 return PQ_NOT_CONNECTED;
1149 mutt_message (_("Marking %d messages deleted..."), ctx->deleted);
1151 for (i = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) {
1152 if (ctx->hdrs[i]->deleted) {
1153 ret = pop_query(pop_data, buf, sizeof(buf), "DELE %d",
1154 ctx->hdrs[i]->refno);
1159 ret = pop_query(pop_data, buf, sizeof(buf), "QUIT");
1163 pop_data->clear_cache = 1;
1164 pop_clear_cache (pop_data);
1165 pop_data->status = POP_DISCONNECTED;
1169 if (ret == PQ_ERR) {
1170 mutt_error ("%s", pop_data->err_msg);
1172 return PQ_NOT_CONNECTED;
1177 /* fetch message from POP server */
1178 static int pop_fetch_message (MESSAGE * msg, CONTEXT * ctx, int msgno)
1182 char buf[LONG_STRING];
1183 char path[_POSIX_PATH_MAX];
1185 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1187 HEADER *h = ctx->hdrs[msgno];
1189 /* see if we already have the message in our cache */
1190 cache = &pop_data->cache[h->index % POP_CACHE_LEN];
1193 if (cache->index == h->index) {
1194 /* yes, so just return a pointer to the message */
1195 msg->fp = fopen (cache->path, "r");
1199 mutt_perror (cache->path);
1204 /* clear the previous entry */
1205 unlink (cache->path);
1206 p_delete(&cache->path);
1211 if (pop_reconnect (ctx) != PQ_OK)
1214 /* verify that massage index is correct */
1217 _("The message index is incorrect. Try reopening the mailbox.");
1222 bar.size = h->content->length + h->content->offset - 1;
1223 bar.msg = _("Fetching message...");
1224 mutt_progress_bar (&bar, 0);
1226 msg->fp = m_tempfile(path, sizeof(path), NONULL(mod_core.tmpdir), NULL);
1228 mutt_error(_("Could not create temporary file"));
1233 snprintf(buf, sizeof(buf), "RETR %d\r\n", h->refno);
1234 ret = pop_fetch_data(pop_data, buf, &bar, fetch_message, msg->fp);
1241 if (ret == PQ_ERR) {
1242 mutt_error ("%s", pop_data->err_msg);
1247 if (ret == PFD_FUNCT_ERROR) {
1248 mutt_error _("Can't write message to temporary file!");
1255 /* Update the header information. Previously, we only downloaded a
1256 * portion of the headers, those required for the main display.
1258 cache->index = h->index;
1259 cache->path = m_strdup(path);
1262 envelope_delete(&h->env);
1263 h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
1266 fgets (buf, sizeof (buf), msg->fp);
1267 while (!feof (msg->fp)) {
1268 ctx->hdrs[msgno]->lines++;
1269 fgets (buf, sizeof (buf), msg->fp);
1272 h->content->length = ftello (msg->fp) - h->content->offset;
1274 /* This needs to be done in case this is a multipart message */
1275 h->security = crypt_query (h->content);
1277 mutt_clear_error ();
1285 mx_t const pop_mx = {
1301 /* public API {{{ */
1303 void pop_fetch_mail (void)
1305 char buffer[LONG_STRING];
1306 char msgbuf[STRING];
1308 int i, delanswer, last = 0, msgs, bytes, rset = 0;
1309 pop_query_status ret;
1312 MESSAGE *msg = NULL;
1314 pop_data_t *pop_data;
1317 if (m_strisempty(PopHost)) {
1318 mutt_error _("POP host is not defined.");
1322 plen = m_strlen(PopHost) + 7;
1323 url = p = p_new(char, plen);
1324 if (url_check_scheme (PopHost) == U_UNKNOWN) {
1325 snprintf(p, plen, "pop://%s", PopHost);
1327 m_strcpy(p, plen, PopHost);
1330 ret = pop_parse_path (url, &act);
1333 mutt_error (_("%s is an invalid POP path"), PopHost);
1337 conn = mutt_conn_find (NULL, &act);
1341 pop_data = p_new(pop_data_t, 1);
1342 pop_data->conn = conn;
1344 if (pop_open_connection (pop_data) < 0) {
1345 mutt_socket_free (pop_data->conn);
1346 p_delete(&pop_data);
1350 conn->data = pop_data;
1352 mutt_message _("Checking for new messages...");
1354 /* find out how many messages are in the mailbox. */
1355 ret = pop_query(pop_data, buffer, sizeof(buffer), "STAT");
1356 if (ret == PQ_NOT_CONNECTED)
1358 if (ret == PQ_ERR) {
1359 mutt_error ("%s", pop_data->err_msg);
1363 sscanf (buffer, "+OK %d %d", &msgs, &bytes);
1365 /* only get unread messages */
1366 if (msgs > 0 && option (OPTPOPLAST)) {
1367 ret = pop_query(pop_data, buffer, sizeof(buffer), "LAST");
1368 if (ret == PQ_NOT_CONNECTED)
1371 sscanf (buffer, "+OK %d", &last);
1375 mutt_message _("No new mail in POP mailbox.");
1380 if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL)
1384 query_quadoption (OPT_POPDELETE, _("Delete messages from server?"));
1386 snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."),
1388 mutt_message ("%s", msgbuf);
1390 for (i = last + 1; i <= msgs; i++) {
1391 if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL)
1394 snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i);
1395 ret = pop_fetch_data(pop_data, buffer, NULL, fetch_message, msg->fp);
1396 if (ret == PFD_FUNCT_ERROR)
1399 if (ret == PQ_OK && mx_commit_message (msg, &ctx) != 0) {
1401 ret = PFD_FUNCT_ERROR;
1404 mx_close_message (&msg);
1407 if (ret == PQ_OK && delanswer == M_YES) {
1408 ret = pop_query(pop_data, buffer, sizeof(buffer), "DELE %d", i);
1411 if (ret == PQ_NOT_CONNECTED) {
1412 mx_close_mailbox (&ctx, NULL);
1415 if (ret == PQ_ERR) {
1416 mutt_error ("%s", pop_data->err_msg);
1419 if (ret == -3) { /* this is -3 when ret != 0, because it will keep the value from before *gna* */
1420 mutt_error _("Error while writing mailbox!");
1425 mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last,
1429 mx_close_mailbox (&ctx, NULL);
1432 /* make sure no messages get deleted */
1433 if (pop_query(pop_data, buffer, sizeof(buffer), "RSET") ==
1439 if (pop_query(pop_data, buffer, sizeof(buffer), "QUIT") == PQ_NOT_CONNECTED)
1441 mutt_socket_close (conn);
1442 p_delete(&pop_data);
1446 mutt_error _("Server closed connection!");
1447 mutt_socket_close (conn);
1448 p_delete(&pop_data);