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
25 #define POP_CACHE_LEN 10
26 /* maximal length of the server response (RFC1939) */
27 #define POP_CMD_RESPONSE 512
50 /* pop_fetch_data uses pop_query_status and this return value */
53 PQ_NOT_CONNECTED = -1,
58 CMD_NOT_AVAILABLE = 0,
60 CMD_UNKNOWN /* unknown whether it is available or not */
67 unsigned capabilities : 1;
68 unsigned use_stls : 2;
69 cmd_status cmd_capa : 2; /* optional command CAPA */
70 cmd_status cmd_stls : 2; /* optional command STLS */
71 cmd_status cmd_user : 2; /* optional command USER */
72 cmd_status cmd_uidl : 2; /* optional command UIDL */
73 cmd_status cmd_top : 2; /* optional command TOP */
74 unsigned resp_codes : 1; /* server supports extended response codes */
75 unsigned expire : 1; /* expire is greater than 0 */
76 unsigned 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 /* pop low level functions {{{ */
89 static void pop_error(pop_data_t *pop_data, const char *msg)
91 if (!m_strncmp(msg, "-ERR ", 5)) {
92 const char *s = skipspaces(msg + 5);
97 m_strcat(pop_data->err_msg, sizeof(pop_data->err_msg), msg);
98 m_strrtrim(pop_data->err_msg);
102 * Send data from buffer and receive answer to the same buffer
104 * -1 - conection lost,
105 * -2 - invalid command or execution error.
107 static pop_query_status pop_query(pop_data_t *pop_data, char *buf, ssize_t buflen)
111 if (pop_data->status != POP_CONNECTED)
112 return PQ_NOT_CONNECTED;
114 mutt_socket_write(pop_data->conn, buf);
116 c = strpbrk(buf, " \r\n");
118 snprintf(pop_data->err_msg, sizeof(pop_data->err_msg), "%s: ", buf);
120 if (mutt_socket_readln(buf, buflen, pop_data->conn) < 0) {
121 pop_data->status = POP_DISCONNECTED;
122 return PQ_NOT_CONNECTED;
124 if (!m_strncmp(buf, "+OK", 3))
127 pop_error(pop_data, buf);
134 * -1 - conection lost,
135 * -2 - invalid response.
137 static pop_query_status pop_connect(pop_data_t * pop_data)
139 char buf[LONG_STRING];
142 if (mutt_socket_open(pop_data->conn) < 0
143 || mutt_socket_readln(buf, sizeof(buf), pop_data->conn) < 0)
145 mutt_error(_("Error connecting to server: %s"),
146 pop_data->conn->account.host);
147 pop_data->status = POP_NONE;
148 return PQ_NOT_CONNECTED;
151 pop_data->status = POP_CONNECTED;
152 if (m_strncmp(buf, "+OK", 3)) {
153 pop_data->err_msg[0] = '\0';
154 pop_error(pop_data, buf);
155 mutt_error("%s", pop_data->err_msg);
159 p_delete(&pop_data->timestamp);
160 if ((p = strchr(buf, '<')) && (q = strchr(p, '>'))) {
161 pop_data->timestamp = p_dupstr(p, q - p);
166 static void pop_logout (CONTEXT * ctx)
168 pop_query_status ret = 0;
169 char buf[LONG_STRING];
170 pop_data_t *pop_data = (pop_data_t *) ctx->data;
172 if (pop_data->status == POP_CONNECTED) {
173 mutt_message _("Closing connection to POP server...");
176 m_strcpy(buf, sizeof(buf), "RSET\r\n");
177 ret = pop_query (pop_data, buf, sizeof (buf));
180 if (ret != PQ_NOT_CONNECTED) {
181 m_strcpy(buf, sizeof(buf), "QUIT\r\n");
182 pop_query (pop_data, buf, sizeof (buf));
188 pop_data->status = POP_DISCONNECTED;
195 * This function calls funct(*line, *data) for each received line,
196 * funct(NULL, *data) if rewind(*data) needs, exits when fail or done.
199 * -1 - conection lost,
200 * -2 - invalid command or execution error,
201 * -3 - error in funct(*line, *data)
203 static pop_query_status
204 pop_fetch_data(pop_data_t *pop_data, const char *query, progress_t *bar,
205 int (*funct)(char *, void *), void *data)
207 char buf[LONG_STRING];
210 pop_query_status ret;
215 m_strcpy(buf, sizeof(buf), query);
216 ret = pop_query (pop_data, buf, sizeof (buf));
220 inbuf = p_new(char, sizeof(buf));
224 mutt_socket_readln(buf, sizeof (buf), pop_data->conn);
226 pop_data->status = POP_DISCONNECTED;
227 ret = PQ_NOT_CONNECTED;
232 if (!lenbuf && buf[0] == '.') {
238 m_strcpy(inbuf + lenbuf,sizeof(buf), p);
241 if (chunk >= ssizeof(buf)) {
242 lenbuf += strlen (p);
245 mutt_progress_bar (bar, pos);
246 if (ret == 0 && funct (inbuf, data) < 0)
247 ret = PFD_FUNCT_ERROR;
251 p_realloc(&inbuf, lenbuf + sizeof(buf));
258 static int fetch_capa (char *line, void *data)
260 pop_data_t *pop_data = (pop_data_t *) data;
263 if (!ascii_strncasecmp (line, "SASL", 4)) {
264 p_delete(&pop_data->auth_list);
265 c = vskipspaces(line + 4);
266 pop_data->auth_list = m_strdup(c);
269 else if (!ascii_strncasecmp (line, "STLS", 4))
270 pop_data->cmd_stls = CMD_AVAILABLE;
272 else if (!ascii_strncasecmp (line, "USER", 4))
273 pop_data->cmd_user = CMD_AVAILABLE;
275 else if (!ascii_strncasecmp (line, "UIDL", 4))
276 pop_data->cmd_uidl = CMD_AVAILABLE;
278 else if (!ascii_strncasecmp (line, "TOP", 3))
279 pop_data->cmd_top = CMD_AVAILABLE;
284 static int fetch_auth (char *line, void *data)
286 pop_data_t *pop_data = (pop_data_t *) data;
287 ssize_t auth_list_len;
289 if (!pop_data->auth_list) {
290 auth_list_len = m_strlen(line) + 1;
291 pop_data->auth_list = p_new(char, auth_list_len);
293 auth_list_len = m_strlen(pop_data->auth_list) + m_strlen(line) + 2;
294 p_realloc(&pop_data->auth_list, auth_list_len);
295 m_strcat(pop_data->auth_list, auth_list_len, " ");
297 m_strcat(pop_data->auth_list, auth_list_len, line);
305 * -1 - conection lost,
306 * -2 - execution error.
308 static pop_query_status pop_capabilities (pop_data_t * pop_data, int mode)
310 char buf[LONG_STRING];
312 /* don't check capabilities on reconnect */
313 if (pop_data->capabilities)
316 /* init capabilities */
318 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
319 pop_data->cmd_stls = CMD_NOT_AVAILABLE;
320 pop_data->cmd_user = CMD_NOT_AVAILABLE;
321 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
322 pop_data->cmd_top = CMD_NOT_AVAILABLE;
323 pop_data->resp_codes = 0;
324 pop_data->expire = 1;
325 pop_data->login_delay = 0;
326 p_delete(&pop_data->auth_list);
329 /* Execute CAPA command */
330 if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) {
331 m_strcpy(buf, sizeof(buf), "CAPA\r\n");
332 switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) {
335 pop_data->cmd_capa = CMD_AVAILABLE;
338 case PFD_FUNCT_ERROR:
341 pop_data->cmd_capa = CMD_NOT_AVAILABLE;
344 case PQ_NOT_CONNECTED:
345 return PQ_NOT_CONNECTED;
349 /* CAPA not supported, use defaults */
350 if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) {
351 pop_data->cmd_user = CMD_UNKNOWN;
352 pop_data->cmd_uidl = CMD_UNKNOWN;
353 pop_data->cmd_top = CMD_UNKNOWN;
355 m_strcpy(buf, sizeof(buf), "AUTH\r\n");
356 if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == PQ_NOT_CONNECTED)
357 return PQ_NOT_CONNECTED;
360 /* Check capabilities */
364 if (!pop_data->expire)
365 msg = _("Unable to leave messages on server.");
366 if (pop_data->cmd_top == CMD_NOT_AVAILABLE)
367 msg = _("Command TOP is not supported by server.");
368 if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE)
369 msg = _("Command UIDL is not supported by server.");
370 if (msg && pop_data->cmd_capa != CMD_AVAILABLE) {
374 pop_data->capabilities = 1;
380 /* Authentication {{{ */
382 /* SASL authenticator */
383 static pop_auth_res_t pop_auth_sasl (pop_data_t * pop_data, const char *method)
385 sasl_conn_t *saslconn;
386 sasl_interact_t *interaction = NULL;
388 char buf[LONG_STRING];
389 char inbuf[LONG_STRING];
392 const char *pc = NULL;
393 unsigned int len, olen;
394 unsigned char client_start;
396 if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) {
397 return POP_A_FAILURE;
401 method = pop_data->auth_list;
404 rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen,
406 if (rc != SASL_INTERACT)
408 mutt_sasl_interact (interaction);
411 if (rc != SASL_OK && rc != SASL_CONTINUE) {
412 /* SASL doesn't support suggested mechanisms, so fall back */
413 return POP_A_UNAVAIL;
416 client_start = (olen > 0);
418 mutt_message _("Authenticating (SASL)...");
420 snprintf (buf, sizeof (buf), "AUTH %s", mech);
423 /* looping protocol */
425 m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n");
426 mutt_socket_write (pop_data->conn, buf);
427 if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) {
428 sasl_dispose (&saslconn);
429 pop_data->status = POP_DISCONNECTED;
433 if (rc != SASL_CONTINUE)
436 if (!m_strncmp(inbuf, "+ ", 2)
437 && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1,
445 rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen);
446 if (rc != SASL_INTERACT)
448 mutt_sasl_interact (interaction);
453 if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK))
456 /* send out response, or line break if none needed */
458 if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) {
462 /* sasl_client_st(art|ep) allocate pc with malloc, expect me to
464 p_delete((char **)&pc);
471 if (!m_strncmp(inbuf, "+OK", 3)) {
472 mutt_sasl_setup_conn (pop_data->conn, saslconn);
473 return POP_A_SUCCESS;
477 sasl_dispose (&saslconn);
479 /* terminate SASL sessoin if the last responce is not +OK nor -ERR */
480 if (!m_strncmp(inbuf, "+ ", 2)) {
481 snprintf (buf, sizeof (buf), "*\r\n");
482 if (pop_query (pop_data, buf, sizeof (buf)) == PQ_NOT_CONNECTED)
486 mutt_error _("SASL authentication failed.");
490 return POP_A_FAILURE;
493 /* APOP authenticator */
494 static pop_auth_res_t pop_auth_apop (pop_data_t * pop_data,
495 const char *method __attribute__ ((unused)))
498 unsigned char digest[16];
500 char buf[LONG_STRING];
503 if (!pop_data->timestamp)
504 return POP_A_UNAVAIL;
506 mutt_message _("Authenticating (APOP)...");
508 /* Compute the authentication hash to send to the server */
509 MD5Init (&mdContext);
510 MD5Update (&mdContext, (unsigned char *) pop_data->timestamp,
511 strlen (pop_data->timestamp));
512 MD5Update (&mdContext, (unsigned char *) pop_data->conn->account.pass,
513 strlen (pop_data->conn->account.pass));
514 MD5Final (digest, &mdContext);
516 for (i = 0; i < ssizeof(digest); i++)
517 sprintf (hash + 2 * i, "%02x", digest[i]);
519 /* Send APOP command to server */
520 snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user,
523 switch (pop_query (pop_data, buf, sizeof (buf))) {
525 return POP_A_SUCCESS;
526 case PQ_NOT_CONNECTED:
528 case PFD_FUNCT_ERROR:
534 mutt_error ("%s %s", _("APOP authentication failed."), pop_data->err_msg);
537 return POP_A_FAILURE;
540 /* USER authenticator */
541 static pop_auth_res_t pop_auth_user (pop_data_t * pop_data,
542 const char *method __attribute__ ((unused)))
544 char buf[LONG_STRING];
545 pop_query_status ret;
547 if (pop_data->cmd_user == CMD_NOT_AVAILABLE)
548 return POP_A_UNAVAIL;
550 mutt_message _("Logging in...");
552 snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user);
553 ret = pop_query (pop_data, buf, sizeof (buf));
555 if (pop_data->cmd_user == CMD_UNKNOWN) {
557 pop_data->cmd_user = CMD_AVAILABLE;
561 pop_data->cmd_user = CMD_NOT_AVAILABLE;
563 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
564 _("Command USER is not supported by server."));
569 snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass);
570 ret = pop_query (pop_data, buf, sizeof (buf));
575 return POP_A_SUCCESS;
576 case PQ_NOT_CONNECTED:
578 case PFD_FUNCT_ERROR:
584 mutt_error ("%s %s", _("Login failed."), pop_data->err_msg);
587 return POP_A_FAILURE;
591 /* do authentication, using named method or any available if method is NULL */
592 pop_auth_res_t (*authenticate) (pop_data_t *, const char *);
593 /* name of authentication method supported, NULL means variable. If this
594 * is not null, authenticate may ignore the second parameter. */
601 * -1 - conection lost,
603 * -3 - authentication canceled.
605 static pop_query_status pop_authenticate (pop_data_t * pop_data)
607 static pop_auth_t const pop_authenticators[] = {
608 {pop_auth_sasl, NULL},
609 {pop_auth_apop, "apop"},
610 {pop_auth_user, "user"},
614 ACCOUNT *act = &pop_data->conn->account;
615 const pop_auth_t *authenticator;
620 int ret = POP_A_UNAVAIL;
622 if (mutt_account_getuser (act) || !act->user[0] ||
623 mutt_account_getpass (act) || !act->pass[0])
624 return PFD_FUNCT_ERROR;
626 if (PopAuthenticators && *PopAuthenticators) {
627 /* Try user-specified list of authentication methods */
628 methods = m_strdup(PopAuthenticators);
632 comma = strchr (method, ':');
635 authenticator = pop_authenticators;
637 while (authenticator->authenticate) {
638 if (!authenticator->method ||
639 !ascii_strcasecmp (authenticator->method, method)) {
640 ret = authenticator->authenticate (pop_data, method);
641 if (ret == POP_A_SOCKET) {
642 if (pop_connect(pop_data) == PQ_OK) {
643 ret = authenticator->authenticate (pop_data, method);
649 if (ret != POP_A_UNAVAIL)
651 if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
652 (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) {
666 /* Fall back to default: any authenticator */
667 authenticator = pop_authenticators;
669 while (authenticator->authenticate) {
670 ret = authenticator->authenticate (pop_data, authenticator->method);
671 if (ret == POP_A_SOCKET) {
672 if (pop_connect(pop_data) == PQ_OK) {
673 ret = authenticator->authenticate(pop_data, authenticator->method);
679 if (ret != POP_A_UNAVAIL)
681 if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET ||
682 (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL)))
693 return PQ_NOT_CONNECTED;
696 mutt_error (_("No authenticators available"));
702 /* given an POP mailbox name, return host, port, username and password */
703 static int pop_parse_path (const char *path, ACCOUNT * act)
711 act->port = POP_PORT;
712 act->type = M_ACCT_TYPE_POP;
715 url_parse_ciss (&url, c);
717 if (url.scheme == U_POP || url.scheme == U_POPS) {
718 if (url.scheme == U_POPS) {
719 act->flags |= M_ACCT_SSL;
720 act->port = POP_SSL_PORT;
723 if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0)
734 * Open connection and authenticate
736 * -1 - conection lost,
737 * -2 - invalid command or execution error,
738 * -3 - authentication canceled.
740 static pop_query_status pop_open_connection (pop_data_t * pop_data)
742 pop_query_status ret;
743 unsigned int n, size;
744 char buf[LONG_STRING];
746 ret = pop_connect(pop_data);
752 ret = pop_capabilities (pop_data, 0);
753 if (ret == PQ_NOT_CONNECTED)
760 /* Attempt STLS if available and desired. */
761 if (!pop_data->conn->ssf && (pop_data->cmd_stls || mod_ssl.force_tls)) {
762 if (mod_ssl.force_tls)
763 pop_data->use_stls = 2;
764 if (pop_data->use_stls == 0) {
765 pop_data->use_stls = 1;
766 if (mod_ssl.starttls)
767 pop_data->use_stls = 2;
769 if (pop_data->use_stls == 2) {
770 m_strcpy(buf, sizeof(buf), "STLS\r\n");
771 ret = pop_query (pop_data, buf, sizeof (buf));
772 if (ret == PQ_NOT_CONNECTED)
775 mutt_error ("%s", pop_data->err_msg);
778 else if (mutt_ssl_starttls (pop_data->conn))
780 mutt_error (_("Could not negotiate TLS connection"));
785 /* recheck capabilities after STLS completes */
786 ret = pop_capabilities (pop_data, 1);
787 if (ret == PQ_NOT_CONNECTED)
797 if (mod_ssl.force_tls && !pop_data->conn->ssf) {
798 mutt_error _("Encrypted connection unavailable");
803 ret = pop_authenticate (pop_data);
804 if (ret == PQ_NOT_CONNECTED)
806 if (ret == PFD_FUNCT_ERROR)
811 /* recheck capabilities after authentication */
812 ret = pop_capabilities (pop_data, 2);
813 if (ret == PQ_NOT_CONNECTED)
820 /* get total size of mailbox */
821 m_strcpy(buf, sizeof(buf), "STAT\r\n");
822 ret = pop_query (pop_data, buf, sizeof (buf));
823 if (ret == PQ_NOT_CONNECTED)
826 mutt_error ("%s", pop_data->err_msg);
831 sscanf (buf, "+OK %u %u", &n, &size);
832 pop_data->size = size;
836 pop_data->status = POP_DISCONNECTED;
837 mutt_error _("Server closed connection!");
840 return PQ_NOT_CONNECTED;
843 /* find message with this UIDL and set refno */
844 static int check_uidl (char *line, void *data)
847 CONTEXT *ctx = (CONTEXT *)data;
849 sscanf (line, "%u %s", &idx, line);
850 for (i = 0; i < ctx->msgcount; i++) {
851 if (!m_strcmp(ctx->hdrs[i]->data, line)) {
852 ctx->hdrs[i]->refno = idx;
860 /* reconnect and verify indexes if connection was lost */
861 static pop_query_status pop_reconnect (CONTEXT * ctx)
863 pop_query_status ret;
864 pop_data_t *pop_data = (pop_data_t *) ctx->data;
867 if (pop_data->status == POP_CONNECTED)
869 if (pop_data->status == POP_BYE)
870 return PQ_NOT_CONNECTED;
873 mutt_socket_close (pop_data->conn);
875 ret = pop_open_connection(pop_data);
879 bar.msg = _("Verifying message indexes...");
881 mutt_progress_bar (&bar, 0);
883 for (i = 0; i < ctx->msgcount; i++)
884 ctx->hdrs[i]->refno = -1;
886 ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx);
888 mutt_error ("%s", pop_data->err_msg);
898 return PQ_NOT_CONNECTED;
900 if (query_quadoption (OPT_POPRECONNECT,
901 _("Connection lost. Reconnect to POP server?")) !=
903 return PQ_NOT_CONNECTED;
907 /* write line to file */
908 static int fetch_message (char *line, void *file)
910 FILE *f = (FILE *) file;
913 if (fputc ('\n', f) == EOF)
923 * -1 - conection lost,
924 * -2 - invalid command or execution error,
925 * -3 - error writing to tempfile
927 static pop_query_status pop_read_header (pop_data_t * pop_data, HEADER * h)
931 pop_query_status ret;
933 char buf[LONG_STRING];
934 char tempfile[_POSIX_PATH_MAX];
936 f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
938 mutt_error(_("Could not create temporary file"));
939 return PFD_FUNCT_ERROR;
942 snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno);
943 ret = pop_query (pop_data, buf, sizeof (buf));
945 sscanf (buf, "+OK %d %ld", &idx, &length);
947 snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno);
948 ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f);
950 if (pop_data->cmd_top == CMD_UNKNOWN) {
952 pop_data->cmd_top = CMD_AVAILABLE;
956 pop_data->cmd_top = CMD_NOT_AVAILABLE;
957 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
958 _("Command TOP is not supported by server."));
967 h->env = mutt_read_rfc822_header (f, h, 0, 0);
968 h->content->length = length - h->content->offset + 1;
971 h->content->length--;
972 fgets (buf, sizeof (buf), f);
978 mutt_error ("%s", pop_data->err_msg);
981 case PFD_FUNCT_ERROR:
983 mutt_error _("Can't write header to temporary file!");
987 case PQ_NOT_CONNECTED:
989 mutt_error _("Can't fetch header: Not connected!");
1000 static int fetch_uidl (char *line, void *data)
1003 CONTEXT *ctx = (CONTEXT *) data;
1004 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1006 sscanf (line, "%d %s", &idx, line);
1007 for (i = 0; i < ctx->msgcount; i++)
1008 if (!m_strcmp(line, ctx->hdrs[i]->data))
1011 if (i == ctx->msgcount) {
1012 if (i >= ctx->hdrmax)
1013 mx_alloc_memory (ctx);
1016 ctx->hdrs[i] = header_new();
1017 ctx->hdrs[i]->data = m_strdup(line);
1019 else if (ctx->hdrs[i]->index != idx - 1)
1020 pop_data->clear_cache = 1;
1022 ctx->hdrs[i]->refno = idx;
1023 ctx->hdrs[i]->index = idx - 1;
1032 * -1 - conection lost,
1033 * -2 - invalid command or execution error,
1034 * -3 - error writing to tempfile
1036 static int pop_fetch_headers (CONTEXT * ctx)
1038 int i, old_count, new_count;
1039 pop_query_status ret;
1040 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1042 time (&pop_data->check_time);
1043 pop_data->clear_cache = 0;
1045 for (i = 0; i < ctx->msgcount; i++)
1046 ctx->hdrs[i]->refno = -1;
1048 old_count = ctx->msgcount;
1049 ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx);
1050 new_count = ctx->msgcount;
1051 ctx->msgcount = old_count;
1053 if (pop_data->cmd_uidl == CMD_UNKNOWN) {
1055 pop_data->cmd_uidl = CMD_AVAILABLE;
1058 if (ret == PQ_ERR && pop_data->cmd_uidl == CMD_UNKNOWN) {
1059 pop_data->cmd_uidl = CMD_NOT_AVAILABLE;
1061 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg),
1062 _("Command UIDL is not supported by server."));
1067 for (i = 0; i < old_count; i++)
1068 if (ctx->hdrs[i]->refno == -1)
1069 ctx->hdrs[i]->deleted = 1;
1071 for (i = old_count; i < new_count; i++) {
1072 mutt_message (_("Fetching message headers... [%d/%d]"),
1073 i + 1 - old_count, new_count - old_count);
1075 ret = pop_read_header (pop_data, ctx->hdrs[i]);
1083 mx_update_context (ctx, i - old_count);
1087 for (i = ctx->msgcount; i < new_count; i++)
1088 header_delete(&ctx->hdrs[i]);
1092 mutt_clear_error ();
1093 return (new_count - old_count);
1096 /* delete all cached messages */
1097 static void pop_clear_cache (pop_data_t * pop_data)
1101 if (!pop_data->clear_cache)
1104 for (i = 0; i < POP_CACHE_LEN; i++) {
1105 if (pop_data->cache[i].path) {
1106 unlink (pop_data->cache[i].path);
1107 p_delete(&pop_data->cache[i].path);
1112 /* pop_mx functions {{{ */
1114 static int pop_is_magic(const char* path, struct stat* st)
1116 url_scheme_t s = url_check_scheme(NONULL(path));
1117 return s == U_POP || s == U_POPS ? M_POP : -1;
1120 static int pop_open_mailbox (CONTEXT * ctx)
1123 char buf[LONG_STRING];
1126 pop_data_t *pop_data;
1129 if (pop_parse_path (ctx->path, &act)) {
1130 mutt_error (_("%s is an invalid POP path"), ctx->path);
1135 mutt_account_tourl (&act, &url);
1137 url_ciss_tostring (&url, buf, sizeof (buf), 0);
1138 conn = mutt_conn_find (NULL, &act);
1142 p_delete(&ctx->path);
1143 ctx->path = m_strdup(buf);
1145 pop_data = p_new(pop_data_t, 1);
1146 pop_data->conn = conn;
1147 ctx->data = pop_data;
1149 if (pop_open_connection(pop_data) != PQ_OK)
1152 conn->data = pop_data;
1155 if (pop_reconnect (ctx) != PQ_OK)
1158 ctx->size = pop_data->size;
1160 mutt_message _("Fetching list of messages...");
1162 ret = pop_fetch_headers (ctx);
1174 static int pop_acl_check(CONTEXT *ctx, int bit)
1177 case ACL_DELETE: /* (un)deletion */
1178 case ACL_SEEN: /* mark as read */
1180 case ACL_INSERT: /* editing messages */
1181 case ACL_WRITE: /* change importance */
1187 static int pop_check_mailbox(CONTEXT * ctx, int *index_hint, int unused)
1190 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1192 if ((pop_data->check_time + PopCheckTimeout) > time (NULL))
1197 mutt_socket_close (pop_data->conn);
1199 if (pop_open_connection (pop_data) < 0)
1202 ctx->size = pop_data->size;
1204 mutt_message _("Checking for new messages...");
1206 ret = pop_fetch_headers (ctx);
1207 pop_clear_cache (pop_data);
1218 static void pop_close_mailbox (CONTEXT * ctx)
1220 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1227 if (pop_data->status != POP_NONE)
1228 mutt_socket_close (pop_data->conn);
1230 pop_data->status = POP_NONE;
1232 pop_data->clear_cache = 1;
1233 pop_clear_cache (pop_data);
1235 if (!pop_data->conn->data)
1236 mutt_socket_free (pop_data->conn);
1241 static int pop_sync_mailbox(CONTEXT * ctx, int unused, int *index_hint)
1244 pop_query_status ret;
1245 char buf[LONG_STRING];
1246 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1248 pop_data->check_time = 0;
1251 if (pop_reconnect (ctx) != PQ_OK)
1252 return PQ_NOT_CONNECTED;
1254 mutt_message (_("Marking %d messages deleted..."), ctx->deleted);
1256 for (i = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) {
1257 if (ctx->hdrs[i]->deleted) {
1258 snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno);
1259 ret = pop_query (pop_data, buf, sizeof (buf));
1264 m_strcpy(buf, sizeof(buf), "QUIT\r\n");
1265 ret = pop_query (pop_data, buf, sizeof (buf));
1269 pop_data->clear_cache = 1;
1270 pop_clear_cache (pop_data);
1271 pop_data->status = POP_DISCONNECTED;
1275 if (ret == PQ_ERR) {
1276 mutt_error ("%s", pop_data->err_msg);
1278 return PQ_NOT_CONNECTED;
1285 mx_t const pop_mx = {
1300 /* public API {{{ */
1302 void pop_fetch_mail (void)
1304 char buffer[LONG_STRING];
1305 char msgbuf[STRING];
1307 int i, delanswer, last = 0, msgs, bytes, rset = 0;
1308 pop_query_status ret;
1311 MESSAGE *msg = NULL;
1313 pop_data_t *pop_data;
1317 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 plen -= m_strcpy(url, plen, "pop://");
1328 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 m_strcpy(buffer, sizeof(buffer), "STAT\r\n");
1356 ret = pop_query (pop_data, buffer, sizeof (buffer));
1357 if (ret == PQ_NOT_CONNECTED)
1359 if (ret == PQ_ERR) {
1360 mutt_error ("%s", pop_data->err_msg);
1364 sscanf (buffer, "+OK %d %d", &msgs, &bytes);
1366 /* only get unread messages */
1367 if (msgs > 0 && option (OPTPOPLAST)) {
1368 m_strcpy(buffer, sizeof(buffer), "LAST\r\n");
1369 ret = pop_query (pop_data, buffer, sizeof (buffer));
1370 if (ret == PQ_NOT_CONNECTED)
1373 sscanf (buffer, "+OK %d", &last);
1377 mutt_message _("No new mail in POP mailbox.");
1382 if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL)
1386 query_quadoption (OPT_POPDELETE, _("Delete messages from server?"));
1388 snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."),
1390 mutt_message ("%s", msgbuf);
1392 for (i = last + 1; i <= msgs; i++) {
1393 if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL)
1396 snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i);
1397 ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp);
1398 if (ret == PFD_FUNCT_ERROR)
1401 if (ret == PQ_OK && mx_commit_message (msg, &ctx) != 0) {
1403 ret = PFD_FUNCT_ERROR;
1406 mx_close_message (&msg);
1409 if (ret == PQ_OK && delanswer == M_YES) {
1410 /* delete the message on the server */
1411 snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i);
1412 ret = pop_query (pop_data, buffer, sizeof (buffer));
1415 if (ret == PQ_NOT_CONNECTED) {
1416 mx_close_mailbox (&ctx, NULL);
1419 if (ret == PQ_ERR) {
1420 mutt_error ("%s", pop_data->err_msg);
1423 if (ret == -3) { /* this is -3 when ret != 0, because it will keep the value from before *gna* */
1424 mutt_error _("Error while writing mailbox!");
1429 mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last,
1433 mx_close_mailbox (&ctx, NULL);
1436 /* make sure no messages get deleted */
1437 m_strcpy(buffer, sizeof(buffer), "RSET\r\n");
1438 if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED)
1443 /* exit gracefully */
1444 m_strcpy(buffer, sizeof(buffer), "QUIT\r\n");
1445 if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED)
1447 mutt_socket_close (conn);
1448 p_delete(&pop_data);
1452 mutt_error _("Server closed connection!");
1453 mutt_socket_close (conn);
1454 p_delete(&pop_data);
1457 /* fetch message from POP server */
1458 int pop_fetch_message (MESSAGE * msg, CONTEXT * ctx, int msgno)
1462 char buf[LONG_STRING];
1463 char path[_POSIX_PATH_MAX];
1465 pop_data_t *pop_data = (pop_data_t *) ctx->data;
1467 HEADER *h = ctx->hdrs[msgno];
1469 /* see if we already have the message in our cache */
1470 cache = &pop_data->cache[h->index % POP_CACHE_LEN];
1473 if (cache->index == h->index) {
1474 /* yes, so just return a pointer to the message */
1475 msg->fp = fopen (cache->path, "r");
1479 mutt_perror (cache->path);
1484 /* clear the previous entry */
1485 unlink (cache->path);
1486 p_delete(&cache->path);
1491 if (pop_reconnect (ctx) != PQ_OK)
1494 /* verify that massage index is correct */
1497 _("The message index is incorrect. Try reopening the mailbox.");
1502 bar.size = h->content->length + h->content->offset - 1;
1503 bar.msg = _("Fetching message...");
1504 mutt_progress_bar (&bar, 0);
1506 msg->fp = m_tempfile(path, sizeof(path), NONULL(MCore.tmpdir), NULL);
1508 mutt_error(_("Could not create temporary file"));
1513 snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno);
1515 ret = pop_fetch_data (pop_data, buf, &bar, fetch_message, msg->fp);
1522 if (ret == PQ_ERR) {
1523 mutt_error ("%s", pop_data->err_msg);
1528 if (ret == PFD_FUNCT_ERROR) {
1529 mutt_error _("Can't write message to temporary file!");
1536 /* Update the header information. Previously, we only downloaded a
1537 * portion of the headers, those required for the main display.
1539 cache->index = h->index;
1540 cache->path = m_strdup(path);
1543 envelope_delete(&h->env);
1544 h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0);
1547 fgets (buf, sizeof (buf), msg->fp);
1548 while (!feof (msg->fp)) {
1549 ctx->hdrs[msgno]->lines++;
1550 fgets (buf, sizeof (buf), msg->fp);
1553 h->content->length = ftello (msg->fp) - h->content->offset;
1555 /* This needs to be done in case this is a multipart message */
1556 h->security = crypt_query (h->content);
1558 mutt_clear_error ();