From 9cb385d5621b95188fce2e0a7345004151486007 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 14 May 2007 22:02:58 +0200 Subject: [PATCH] there is absolutely *NO* reason for a separate pop library. Merge every pop/*.c into pop.c, reduce pop.h to the bare minimum. pop.c will be simplified soon. Signed-off-by: Pierre Habouzit --- Makefile.am | 8 +- configure.ac | 1 - lib-lib/Makefile.am | 6 +- lib-lib/lib-lib.h | 1 + {pop => lib-lib}/md5.c | 9 +- {pop => lib-lib}/md5.h | 6 +- lib-mx/mx.c | 2 +- lib-sys/mutt_socket.h | 1 + lib-ui/curs_main.c | 2 +- pop.c | 1580 ++++++++++++++++++++++++++++++++++++++++ pop.h | 21 + pop/Makefile.am | 15 - pop/pop.c | 676 ----------------- pop/pop.h | 112 --- pop/pop_auth.c | 352 --------- pop/pop_lib.c | 511 ------------- 16 files changed, 1615 insertions(+), 1688 deletions(-) rename {pop => lib-lib}/md5.c (99%) rename {pop => lib-lib}/md5.h (95%) create mode 100644 pop.c create mode 100644 pop.h delete mode 100644 pop/Makefile.am delete mode 100644 pop/pop.c delete mode 100644 pop/pop.h delete mode 100644 pop/pop_auth.c delete mode 100644 pop/pop_lib.c diff --git a/Makefile.am b/Makefile.am index 3c64513..9884cf3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,7 +10,7 @@ endif SUBDIRS = tools intl po \ lib-lua lib-mime lib-lib lib-mx lib-sys lib-ui \ - pop imap $(NNTP_SUBDIR) + imap $(NNTP_SUBDIR) BUILT_SOURCES = keymap_defs.h charset.gperf DISTCLEANFILES = $(BUILT_SOURCES) @@ -22,12 +22,11 @@ madmutt_SOURCES = $(BUILT_SOURCES) \ handler.c headers.c help.c hook.c main.c muttlib.c mutt_idna.c \ pager.c pattern.c postpone.c recvattach.c recvcmd.c score.c send.c \ sendlib.c send_smtp.c sort.c state.c thread.c account.c remailer.c \ - mutt_sasl.c + mutt_sasl.c pop.c madmutt_LDADD = $(top_builddir)/lib-mime/libmime.a \ $(top_builddir)/lib-mx/libmx.a \ $(top_builddir)/imap/libimap.a \ - $(top_builddir)/pop/libpop.a \ $(LIBNNTP) \ $(top_builddir)/lib-sys/libsys.a \ $(top_builddir)/lib-ui/libui.a \ @@ -42,7 +41,6 @@ madmutt_DEPENDENCIES = $(top_builddir)/lib-lib/liblib.a \ $(top_builddir)/lib-lua/liblua.a \ $(top_builddir)/lib-mx/libmx.a \ \ - $(top_builddir)/pop/libpop.a \ $(top_builddir)/imap/libimap.a \ $(LIBNNTPDEPS) $(INTLDEPS) @@ -95,4 +93,4 @@ include tools/cpkg2c.mk -include tools/cflags.mk wc: - @sloccount $(wildcard *.h *.c) $(wildcard lib-*/) lib imap nntp pop | grep 'ansic[=:]' + @sloccount $(wildcard *.h *.c) $(wildcard lib-*/) imap nntp | grep 'ansic[=:]' diff --git a/configure.ac b/configure.ac index beac5e1..d49abb1 100644 --- a/configure.ac +++ b/configure.ac @@ -558,6 +558,5 @@ AC_OUTPUT(Makefile lib-sys/Makefile lib-ui/Makefile imap/Makefile - pop/Makefile nntp/Makefile ) diff --git a/lib-lib/Makefile.am b/lib-lib/Makefile.am index 3346eeb..b74270d 100644 --- a/lib-lib/Makefile.am +++ b/lib-lib/Makefile.am @@ -1,10 +1,10 @@ noinst_LIBRARIES = liblib.a liblib_a_SOURCES = lib-lib.h mem.h \ - str.h utf8.h buffer.h hash.h array.h list.h file.h mapping.h date.h rx.h url.h \ - str.c utf8.c buffer.c hash.c array.c list.c file.c mapping.c date.c rx.c url.c + str.h md5.h utf8.h buffer.h hash.h array.h list.h file.h mapping.h date.h rx.h url.h \ + str.c md5.c utf8.c buffer.c hash.c array.c list.c file.c mapping.c date.c rx.c url.c noinst_HEADERS = lib-lib.h mem.h \ - str.h utf8.h buffer.h hash.h array.h list.h file.h mapping.h date.h rx.h url.h + str.h md5.h utf8.h buffer.h hash.h array.h list.h file.h mapping.h date.h rx.h url.h -include $(top_builddir)/tools/cflags.mk diff --git a/lib-lib/lib-lib.h b/lib-lib/lib-lib.h index 6dd3900..6a22306 100644 --- a/lib-lib/lib-lib.h +++ b/lib-lib/lib-lib.h @@ -118,6 +118,7 @@ typedef union __attribute__((transparent_union)) anytype { #include "file.h" #include "hash.h" #include "list.h" +#include "md5.h" #include "mapping.h" #include "rx.h" #include "url.h" diff --git a/pop/md5.c b/lib-lib/md5.c similarity index 99% rename from pop/md5.c rename to lib-lib/md5.c index 0ed0205..2e17790 100644 --- a/pop/md5.c +++ b/lib-lib/md5.c @@ -23,14 +23,9 @@ These notices must be retained in any copies of any part of this documentation and/or software. */ -#if HAVE_CONFIG_H -# include "config.h" -#endif +#include "lib-lib.h" -#include "md5.h" - -/* Constants for MD5Transform routine. - */ +/* Constants for MD5Transform routine. */ #define S11 7 #define S12 12 diff --git a/pop/md5.h b/lib-lib/md5.h similarity index 95% rename from pop/md5.h rename to lib-lib/md5.h index a803803..ce9fc1d 100644 --- a/pop/md5.h +++ b/lib-lib/md5.h @@ -23,10 +23,8 @@ These notices must be retained in any copies of any part of this documentation and/or software. */ -#ifndef MD5_H -#define MD5_H - -#include +#ifndef MUTT_LIB_LIB_MD5_H +#define MUTT_LIB_LIB_MD5_H /* MD5 context. */ typedef struct { diff --git a/lib-mx/mx.c b/lib-mx/mx.c index aa7e3e3..10d9d12 100644 --- a/lib-mx/mx.c +++ b/lib-mx/mx.c @@ -29,7 +29,7 @@ #include "dotlock.h" #include -#include +#include "pop.h" #ifdef USE_NNTP #include #endif diff --git a/lib-sys/mutt_socket.h b/lib-sys/mutt_socket.h index 1119b02..7df5d56 100644 --- a/lib-sys/mutt_socket.h +++ b/lib-sys/mutt_socket.h @@ -11,6 +11,7 @@ #ifndef _MUTT_SOCKET_H_ #define _MUTT_SOCKET_H_ 1 +#include #include "account.h" #include "mutt_ssl.li" diff --git a/lib-ui/curs_main.c b/lib-ui/curs_main.c index 0fa37cb..82eb7e4 100644 --- a/lib-ui/curs_main.c +++ b/lib-ui/curs_main.c @@ -14,7 +14,7 @@ #include #include -#include +#include "pop.h" #include "curses.h" #include "menu.h" diff --git a/pop.c b/pop.c new file mode 100644 index 0000000..bc857a3 --- /dev/null +++ b/pop.c @@ -0,0 +1,1580 @@ +/* + * Copyright notice from original mutt: + * Copyright (C) 2000-2002 Vsevolod Volkov + * + * This file is part of mutt-ng, see http://www.muttng.org/. + * It's licensed under the GNU General Public License, + * please see the file GPL in the top level source directory. + */ + +#include + +#include +#include + +#include +#include + +#include "crypt.h" +#include "mutt.h" +#include "mutt_sasl.h" +#include "pop.h" + +#define POP_PORT 110 +#define POP_SSL_PORT 995 + +/* number of entries in the hash table */ +#define POP_CACHE_LEN 10 + +/* maximal length of the server response (RFC1939) */ +#define POP_CMD_RESPONSE 512 + +enum { + /* Status */ + POP_NONE = 0, + POP_CONNECTED, + POP_DISCONNECTED, + POP_BYE +}; + +typedef enum { + POP_A_SUCCESS = 0, + POP_A_SOCKET, + POP_A_FAILURE, + POP_A_UNAVAIL +} pop_auth_res_t; + +typedef struct { + int index; + char *path; +} POP_CACHE; + +typedef enum pop_query_status_e { + PFD_FUNCT_ERROR = -3, /* pop_fetch_data uses pop_query_status and this return value */ + PQ_ERR = -2, + PQ_NOT_CONNECTED = -1, + PQ_OK = 0 +} pop_query_status; + +typedef enum cmd_status_e { + CMD_NOT_AVAILABLE = 0, + CMD_AVAILABLE, + CMD_UNKNOWN /* unknown whether it is available or not */ +} cmd_status; + +typedef struct { + CONNECTION *conn; + unsigned int status:2; + unsigned int capabilities:1; + unsigned int use_stls:2; + cmd_status cmd_capa; /* optional command CAPA */ + cmd_status cmd_stls; /* optional command STLS */ + cmd_status cmd_user; /* optional command USER */ + cmd_status cmd_uidl; /* optional command UIDL */ + cmd_status cmd_top; /* optional command TOP */ + unsigned int resp_codes:1; /* server supports extended response codes */ + unsigned int expire:1; /* expire is greater than 0 */ + unsigned int clear_cache:1; + size_t size; + time_t check_time; + time_t login_delay; /* minimal login delay capability */ + char *auth_list; /* list of auth mechanisms */ + char *timestamp; + char err_msg[POP_CMD_RESPONSE]; + POP_CACHE cache[POP_CACHE_LEN]; +} POP_DATA; + +/* Copy error message to err_msg buffer */ +static void pop_error (POP_DATA * pop_data, char *msg) +{ + char *t, *c, *c2; + + t = strchr (pop_data->err_msg, '\0'); + c = msg; + + if (!m_strncmp(msg, "-ERR ", 5)) { + c2 = vskipspaces(msg + 5); + if (*c2) + c = c2; + } + + m_strcpy(t, sizeof(pop_data->err_msg) - strlen(pop_data->err_msg), c); + m_strrtrim(pop_data->err_msg); +} + +/* + * Send data from buffer and receive answer to the same buffer + * 0 - successful, + * -1 - conection lost, + * -2 - invalid command or execution error. +*/ +static pop_query_status pop_query (POP_DATA * pop_data, char *buf, size_t buflen) +{ + char *c; + + if (pop_data->status != POP_CONNECTED) + return PQ_NOT_CONNECTED; + + mutt_socket_write(pop_data->conn, buf); + + c = strpbrk (buf, " \r\n"); + *c = '\0'; + snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf); + + if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0) { + pop_data->status = POP_DISCONNECTED; + return PQ_NOT_CONNECTED; + } + if (!m_strncmp(buf, "+OK", 3)) + return PQ_OK; + + pop_error (pop_data, buf); + return PQ_ERR; +} + +/* + * This function calls funct(*line, *data) for each received line, + * funct(NULL, *data) if rewind(*data) needs, exits when fail or done. + * Returned codes: + * 0 - successful, + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - error in funct(*line, *data) + */ +static pop_query_status +pop_fetch_data(POP_DATA *pop_data, const char *query, progress_t *bar, + int (*funct)(char *, void *), void *data) +{ + char buf[LONG_STRING]; + char *inbuf; + char *p; + pop_query_status ret; + int chunk = 0; + long pos = 0; + size_t lenbuf = 0; + + m_strcpy(buf, sizeof(buf), query); + ret = pop_query (pop_data, buf, sizeof (buf)); + if (ret != PQ_OK) + return ret; + + inbuf = p_new(char, sizeof(buf)); + + for (;;) { + chunk = + mutt_socket_readln(buf, sizeof (buf), pop_data->conn); + if (chunk < 0) { + pop_data->status = POP_DISCONNECTED; + ret = PQ_NOT_CONNECTED; + break; + } + + p = buf; + if (!lenbuf && buf[0] == '.') { + if (buf[1] != '.') + break; + p++; + } + + m_strcpy(inbuf + lenbuf,sizeof(buf), p); + pos += chunk; + + if (chunk >= ssizeof(buf)) { + lenbuf += strlen (p); + } else { + if (bar) + mutt_progress_bar (bar, pos); + if (ret == 0 && funct (inbuf, data) < 0) + ret = PFD_FUNCT_ERROR; + lenbuf = 0; + } + + p_realloc(&inbuf, lenbuf + sizeof(buf)); + } + + p_delete(&inbuf); + return ret; +} + +/* SASL authenticator */ +static pop_auth_res_t pop_auth_sasl (POP_DATA * pop_data, const char *method) +{ + sasl_conn_t *saslconn; + sasl_interact_t *interaction = NULL; + int rc; + char buf[LONG_STRING]; + char inbuf[LONG_STRING]; + const char *mech; + + const char *pc = NULL; + unsigned int len, olen; + unsigned char client_start; + + if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) { + return POP_A_FAILURE; + } + + if (!method) + method = pop_data->auth_list; + + for (;;) { + rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen, + &mech); + if (rc != SASL_INTERACT) + break; + mutt_sasl_interact (interaction); + } + + if (rc != SASL_OK && rc != SASL_CONTINUE) { + /* SASL doesn't support suggested mechanisms, so fall back */ + return POP_A_UNAVAIL; + } + + client_start = (olen > 0); + + mutt_message _("Authenticating (SASL)..."); + + snprintf (buf, sizeof (buf), "AUTH %s", mech); + olen = strlen (buf); + + /* looping protocol */ + for (;;) { + m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n"); + mutt_socket_write (pop_data->conn, buf); + if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) { + sasl_dispose (&saslconn); + pop_data->status = POP_DISCONNECTED; + return POP_A_SOCKET; + } + + if (rc != SASL_CONTINUE) + break; + + if (!m_strncmp(inbuf, "+ ", 2) + && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1, + &len) != SASL_OK) + { + goto bail; + } + + if (!client_start) + for (;;) { + rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen); + if (rc != SASL_INTERACT) + break; + mutt_sasl_interact (interaction); + } + else + client_start = 0; + + if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK)) + break; + + /* send out response, or line break if none needed */ + if (pc) { + if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) { + goto bail; + } + + /* sasl_client_st(art|ep) allocate pc with malloc, expect me to + * free it */ + p_delete((char **)&pc); + } + } + + if (rc != SASL_OK) + goto bail; + + if (!m_strncmp(inbuf, "+OK", 3)) { + mutt_sasl_setup_conn (pop_data->conn, saslconn); + return POP_A_SUCCESS; + } + +bail: + sasl_dispose (&saslconn); + + /* terminate SASL sessoin if the last responce is not +OK nor -ERR */ + if (!m_strncmp(inbuf, "+ ", 2)) { + snprintf (buf, sizeof (buf), "*\r\n"); + if (pop_query (pop_data, buf, sizeof (buf)) == PQ_NOT_CONNECTED) + return POP_A_SOCKET; + } + + mutt_error _("SASL authentication failed."); + + mutt_sleep (2); + + return POP_A_FAILURE; +} + +/* Get the server timestamp for APOP authentication */ +static void pop_apop_timestamp (POP_DATA * pop_data, char *buf) +{ + char *p1, *p2; + + p_delete(&pop_data->timestamp); + + if ((p1 = strchr (buf, '<')) && (p2 = strchr (p1, '>'))) { + p2[1] = '\0'; + pop_data->timestamp = m_strdup(p1); + } +} + +/* APOP authenticator */ +static pop_auth_res_t pop_auth_apop (POP_DATA * pop_data, + const char *method __attribute__ ((unused))) +{ + MD5_CTX mdContext; + unsigned char digest[16]; + char hash[33]; + char buf[LONG_STRING]; + int i; + + if (!pop_data->timestamp) + return POP_A_UNAVAIL; + + mutt_message _("Authenticating (APOP)..."); + + /* Compute the authentication hash to send to the server */ + MD5Init (&mdContext); + MD5Update (&mdContext, (unsigned char *) pop_data->timestamp, + strlen (pop_data->timestamp)); + MD5Update (&mdContext, (unsigned char *) pop_data->conn->account.pass, + strlen (pop_data->conn->account.pass)); + MD5Final (digest, &mdContext); + + for (i = 0; i < ssizeof(digest); i++) + sprintf (hash + 2 * i, "%02x", digest[i]); + + /* Send APOP command to server */ + snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user, + hash); + + switch (pop_query (pop_data, buf, sizeof (buf))) { + case PQ_OK: + return POP_A_SUCCESS; + case PQ_NOT_CONNECTED: + return POP_A_SOCKET; + case PFD_FUNCT_ERROR: + case PQ_ERR: + default: + break; + } + + mutt_error ("%s %s", _("APOP authentication failed."), pop_data->err_msg); + mutt_sleep (2); + + return POP_A_FAILURE; +} + +/* USER authenticator */ +static pop_auth_res_t pop_auth_user (POP_DATA * pop_data, + const char *method __attribute__ ((unused))) +{ + char buf[LONG_STRING]; + pop_query_status ret; + + if (pop_data->cmd_user == CMD_NOT_AVAILABLE) + return POP_A_UNAVAIL; + + mutt_message _("Logging in..."); + + snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user); + ret = pop_query (pop_data, buf, sizeof (buf)); + + if (pop_data->cmd_user == CMD_UNKNOWN) { + if (ret == PQ_OK) { + pop_data->cmd_user = CMD_AVAILABLE; + } + + if (ret == PQ_ERR) { + pop_data->cmd_user = CMD_NOT_AVAILABLE; + + snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), + _("Command USER is not supported by server.")); + } + } + + if (ret == PQ_OK) { + snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass); + ret = pop_query (pop_data, buf, sizeof (buf)); + } + + switch (ret) { + case PQ_OK: + return POP_A_SUCCESS; + case PQ_NOT_CONNECTED: + return POP_A_SOCKET; + case PFD_FUNCT_ERROR: + case PQ_ERR: + default: + break; + } + + mutt_error ("%s %s", _("Login failed."), pop_data->err_msg); + mutt_sleep (2); + + return POP_A_FAILURE; +} + +/* + * Open connection + * 0 - successful, + * -1 - conection lost, + * -2 - invalid response. +*/ +static pop_query_status pop_connect (POP_DATA * pop_data) +{ + char buf[LONG_STRING]; + + pop_data->status = POP_NONE; + if (mutt_socket_open (pop_data->conn) < 0 || + mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0) { + mutt_error (_("Error connecting to server: %s"), + pop_data->conn->account.host); + return PQ_NOT_CONNECTED; + } + + pop_data->status = POP_CONNECTED; + + if (m_strncmp(buf, "+OK", 3)) { + *pop_data->err_msg = '\0'; + pop_error (pop_data, buf); + mutt_error ("%s", pop_data->err_msg); + return PQ_ERR; + } + + pop_apop_timestamp (pop_data, buf); + + return PQ_OK; +} + +typedef struct { + /* do authentication, using named method or any available if method is NULL */ + pop_auth_res_t (*authenticate) (POP_DATA *, const char *); + /* name of authentication method supported, NULL means variable. If this + * is not null, authenticate may ignore the second parameter. */ + const char *method; +} pop_auth_t; + +/* + * Authentication + * 0 - successful, + * -1 - conection lost, + * -2 - login failed, + * -3 - authentication canceled. +*/ +static pop_query_status pop_authenticate (POP_DATA * pop_data) +{ + static pop_auth_t const pop_authenticators[] = { + {pop_auth_sasl, NULL}, + {pop_auth_apop, "apop"}, + {pop_auth_user, "user"}, + {NULL, NULL} + }; + + ACCOUNT *act = &pop_data->conn->account; + const pop_auth_t *authenticator; + char *methods; + char *comma; + char *method; + int attempts = 0; + int ret = POP_A_UNAVAIL; + + if (mutt_account_getuser (act) || !act->user[0] || + mutt_account_getpass (act) || !act->pass[0]) + return PFD_FUNCT_ERROR; + + if (PopAuthenticators && *PopAuthenticators) { + /* Try user-specified list of authentication methods */ + methods = m_strdup(PopAuthenticators); + method = methods; + + while (method) { + comma = strchr (method, ':'); + if (comma) + *comma++ = '\0'; + authenticator = pop_authenticators; + + while (authenticator->authenticate) { + if (!authenticator->method || + !ascii_strcasecmp (authenticator->method, method)) { + ret = authenticator->authenticate (pop_data, method); + if (ret == POP_A_SOCKET) + switch (pop_connect (pop_data)) { + case PQ_OK: + { + ret = authenticator->authenticate (pop_data, method); + break; + } + default: + ret = POP_A_FAILURE; + } + + if (ret != POP_A_UNAVAIL) + attempts++; + if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET || + (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) { + comma = NULL; + break; + } + } + authenticator++; + } + + method = comma; + } + + p_delete(&methods); + } + else { + /* Fall back to default: any authenticator */ + authenticator = pop_authenticators; + + while (authenticator->authenticate) { + ret = authenticator->authenticate (pop_data, authenticator->method); + if (ret == POP_A_SOCKET) + switch (pop_connect (pop_data)) { + case PQ_OK: + { + ret = + authenticator->authenticate (pop_data, authenticator->method); + break; + } + default: + ret = POP_A_FAILURE; + } + + if (ret != POP_A_UNAVAIL) + attempts++; + if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET || + (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) + break; + + authenticator++; + } + } + + switch (ret) { + case POP_A_SUCCESS: + return PQ_OK; + case POP_A_SOCKET: + return PQ_NOT_CONNECTED; + case POP_A_UNAVAIL: + if (!attempts) + mutt_error (_("No authenticators available")); + } + + return PQ_ERR; +} +/* given an POP mailbox name, return host, port, username and password */ +static int pop_parse_path (const char *path, ACCOUNT * act) +{ + ciss_url_t url; + char *c; + int ret = -1; + + /* Defaults */ + act->flags = 0; + act->port = POP_PORT; + act->type = M_ACCT_TYPE_POP; + + c = m_strdup(path); + url_parse_ciss (&url, c); + + if (url.scheme == U_POP || url.scheme == U_POPS) { + if (url.scheme == U_POPS) { + act->flags |= M_ACCT_SSL; + act->port = POP_SSL_PORT; + } + + if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0) + ret = 0; + } + + p_delete(&c); + return ret; +} + +/* Parse CAPA output */ +static int fetch_capa (char *line, void *data) +{ + POP_DATA *pop_data = (POP_DATA *) data; + char *c; + + if (!ascii_strncasecmp (line, "SASL", 4)) { + p_delete(&pop_data->auth_list); + c = vskipspaces(line + 4); + pop_data->auth_list = m_strdup(c); + } + + else if (!ascii_strncasecmp (line, "STLS", 4)) + pop_data->cmd_stls = CMD_AVAILABLE; + + else if (!ascii_strncasecmp (line, "USER", 4)) + pop_data->cmd_user = CMD_AVAILABLE; + + else if (!ascii_strncasecmp (line, "UIDL", 4)) + pop_data->cmd_uidl = CMD_AVAILABLE; + + else if (!ascii_strncasecmp (line, "TOP", 3)) + pop_data->cmd_top = CMD_AVAILABLE; + + return 0; +} + +/* Fetch list of the authentication mechanisms */ +static int fetch_auth (char *line, void *data) +{ + POP_DATA *pop_data = (POP_DATA *) data; + ssize_t auth_list_len; + + if (!pop_data->auth_list) { + auth_list_len = m_strlen(line) + 1; + pop_data->auth_list = p_new(char, auth_list_len); + } else { + auth_list_len = m_strlen(pop_data->auth_list) + m_strlen(line) + 2; + p_realloc(&pop_data->auth_list, auth_list_len); + m_strcat(pop_data->auth_list, auth_list_len, " "); + } + m_strcat(pop_data->auth_list, auth_list_len, line); + + return 0; +} + +/* + * Get capabilities + * 0 - successful, + * -1 - conection lost, + * -2 - execution error. +*/ +static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode) +{ + char buf[LONG_STRING]; + + /* don't check capabilities on reconnect */ + if (pop_data->capabilities) + return 0; + + /* init capabilities */ + if (mode == 0) { + pop_data->cmd_capa = CMD_NOT_AVAILABLE; + pop_data->cmd_stls = CMD_NOT_AVAILABLE; + pop_data->cmd_user = CMD_NOT_AVAILABLE; + pop_data->cmd_uidl = CMD_NOT_AVAILABLE; + pop_data->cmd_top = CMD_NOT_AVAILABLE; + pop_data->resp_codes = 0; + pop_data->expire = 1; + pop_data->login_delay = 0; + p_delete(&pop_data->auth_list); + } + + /* Execute CAPA command */ + if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) { + m_strcpy(buf, sizeof(buf), "CAPA\r\n"); + switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) { + case PQ_OK: + { + pop_data->cmd_capa = CMD_AVAILABLE; + break; + } + case PFD_FUNCT_ERROR: + case PQ_ERR: + { + pop_data->cmd_capa = CMD_NOT_AVAILABLE; + break; + } + case PQ_NOT_CONNECTED: + return PQ_NOT_CONNECTED; + } + } + + /* CAPA not supported, use defaults */ + if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) { + pop_data->cmd_user = CMD_UNKNOWN; + pop_data->cmd_uidl = CMD_UNKNOWN; + pop_data->cmd_top = CMD_UNKNOWN; + + m_strcpy(buf, sizeof(buf), "AUTH\r\n"); + if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == PQ_NOT_CONNECTED) + return PQ_NOT_CONNECTED; + } + + /* Check capabilities */ + if (mode == 2) { + char *msg = NULL; + + if (!pop_data->expire) + msg = _("Unable to leave messages on server."); + if (pop_data->cmd_top == CMD_NOT_AVAILABLE) + msg = _("Command TOP is not supported by server."); + if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE) + msg = _("Command UIDL is not supported by server."); + if (msg && pop_data->cmd_capa != CMD_AVAILABLE) { + mutt_error (msg); + return PQ_ERR; + } + pop_data->capabilities = 1; + } + + return PQ_OK; +} + +/* + * Open connection and authenticate + * 0 - successful, + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - authentication canceled. +*/ +static pop_query_status pop_open_connection (POP_DATA * pop_data) +{ + pop_query_status ret; + unsigned int n, size; + char buf[LONG_STRING]; + + ret = pop_connect (pop_data); + if (ret != PQ_OK) { + mutt_sleep (2); + return ret; + } + + ret = pop_capabilities (pop_data, 0); + if (ret == PQ_NOT_CONNECTED) + goto err_conn; + if (ret == PQ_ERR) { + mutt_sleep (2); + return PQ_ERR; + } + + /* Attempt STLS if available and desired. */ + if (!pop_data->conn->ssf && (pop_data->cmd_stls || mod_ssl.force_tls)) { + if (mod_ssl.force_tls) + pop_data->use_stls = 2; + if (pop_data->use_stls == 0) { + pop_data->use_stls = 1; + if (mod_ssl.starttls) + pop_data->use_stls = 2; + } + if (pop_data->use_stls == 2) { + m_strcpy(buf, sizeof(buf), "STLS\r\n"); + ret = pop_query (pop_data, buf, sizeof (buf)); + if (ret == PQ_NOT_CONNECTED) + goto err_conn; + if (ret != PQ_OK) { + mutt_error ("%s", pop_data->err_msg); + mutt_sleep (2); + } + else if (mutt_ssl_starttls (pop_data->conn)) + { + mutt_error (_("Could not negotiate TLS connection")); + mutt_sleep (2); + return PQ_ERR; + } + else { + /* recheck capabilities after STLS completes */ + ret = pop_capabilities (pop_data, 1); + if (ret == PQ_NOT_CONNECTED) + goto err_conn; + if (ret == PQ_ERR) { + mutt_sleep (2); + return PQ_ERR; + } + } + } + } + + if (mod_ssl.force_tls && !pop_data->conn->ssf) { + mutt_error _("Encrypted connection unavailable"); + mutt_sleep (1); + return -2; + } + + ret = pop_authenticate (pop_data); + if (ret == PQ_NOT_CONNECTED) + goto err_conn; + if (ret == PFD_FUNCT_ERROR) + mutt_clear_error (); + if (ret != PQ_OK) + return ret; + + /* recheck capabilities after authentication */ + ret = pop_capabilities (pop_data, 2); + if (ret == PQ_NOT_CONNECTED) + goto err_conn; + if (ret == PQ_ERR) { + mutt_sleep (2); + return PQ_ERR; + } + + /* get total size of mailbox */ + m_strcpy(buf, sizeof(buf), "STAT\r\n"); + ret = pop_query (pop_data, buf, sizeof (buf)); + if (ret == PQ_NOT_CONNECTED) + goto err_conn; + if (ret == PQ_ERR) { + mutt_error ("%s", pop_data->err_msg); + mutt_sleep (2); + return ret; + } + + sscanf (buf, "+OK %u %u", &n, &size); + pop_data->size = size; + return PQ_OK; + +err_conn: + pop_data->status = POP_DISCONNECTED; + mutt_error _("Server closed connection!"); + + mutt_sleep (2); + return PQ_NOT_CONNECTED; +} + +/* logout from POP server */ +static void pop_logout (CONTEXT * ctx) +{ + pop_query_status ret = 0; + char buf[LONG_STRING]; + POP_DATA *pop_data = (POP_DATA *) ctx->data; + + if (pop_data->status == POP_CONNECTED) { + mutt_message _("Closing connection to POP server..."); + + if (ctx->readonly) { + m_strcpy(buf, sizeof(buf), "RSET\r\n"); + ret = pop_query (pop_data, buf, sizeof (buf)); + } + + if (ret != PQ_NOT_CONNECTED) { + m_strcpy(buf, sizeof(buf), "QUIT\r\n"); + pop_query (pop_data, buf, sizeof (buf)); + } + + mutt_clear_error (); + } + + pop_data->status = POP_DISCONNECTED; + return; +} + +/* find message with this UIDL and set refno */ +static int check_uidl (char *line, void *data) +{ + int i, idx; + CONTEXT *ctx = (CONTEXT *)data; + + sscanf (line, "%u %s", &idx, line); + for (i = 0; i < ctx->msgcount; i++) { + if (!m_strcmp(ctx->hdrs[i]->data, line)) { + ctx->hdrs[i]->refno = idx; + break; + } + } + + return 0; +} + +/* reconnect and verify indexes if connection was lost */ +static pop_query_status pop_reconnect (CONTEXT * ctx) +{ + pop_query_status ret; + POP_DATA *pop_data = (POP_DATA *) ctx->data; + progress_t bar; + + if (pop_data->status == POP_CONNECTED) + return PQ_OK; + if (pop_data->status == POP_BYE) + return PQ_NOT_CONNECTED; + + for (;;) { + mutt_socket_close (pop_data->conn); + + ret = pop_open_connection (pop_data); + if (ret == PQ_OK) { + int i; + + bar.msg = _("Verifying message indexes..."); + bar.size = 0; + mutt_progress_bar (&bar, 0); + + for (i = 0; i < ctx->msgcount; i++) + ctx->hdrs[i]->refno = -1; + + ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx); + if (ret == PQ_ERR) { + mutt_error ("%s", pop_data->err_msg); + mutt_sleep (2); + } + } + if (ret == PQ_OK) + return PQ_OK; + + pop_logout (ctx); + + if (ret == PQ_ERR) + return PQ_NOT_CONNECTED; + + if (query_quadoption (OPT_POPRECONNECT, + _("Connection lost. Reconnect to POP server?")) != + M_YES) + return PQ_NOT_CONNECTED; + } +} +/* write line to file */ +static int fetch_message (char *line, void *file) +{ + FILE *f = (FILE *) file; + + fputs (line, f); + if (fputc ('\n', f) == EOF) + return -1; + + return 0; +} + +/* + * Read header + * returns: + * 0 on success + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - error writing to tempfile + */ +static pop_query_status pop_read_header (POP_DATA * pop_data, HEADER * h) +{ + FILE *f; + int idx; + pop_query_status ret; + long length; + char buf[LONG_STRING]; + char tempfile[_POSIX_PATH_MAX]; + + f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL); + if (!f) { + mutt_error(_("Could not create temporary file")); + return PFD_FUNCT_ERROR; + } + + snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno); + ret = pop_query (pop_data, buf, sizeof (buf)); + if (ret == PQ_OK) { + sscanf (buf, "+OK %d %ld", &idx, &length); + + snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno); + ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f); + + if (pop_data->cmd_top == CMD_UNKNOWN) { + if (ret == PQ_OK) { + pop_data->cmd_top = CMD_AVAILABLE; + } + + if (ret == PQ_ERR) { + pop_data->cmd_top = CMD_NOT_AVAILABLE; + snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), + _("Command TOP is not supported by server.")); + } + } + } + + switch (ret) { + case PQ_OK: + { + rewind (f); + h->env = mutt_read_rfc822_header (f, h, 0, 0); + h->content->length = length - h->content->offset + 1; + rewind (f); + while (!feof (f)) { + h->content->length--; + fgets (buf, sizeof (buf), f); + } + break; + } + case PQ_ERR: + { + mutt_error ("%s", pop_data->err_msg); + break; + } + case PFD_FUNCT_ERROR: + { + mutt_error _("Can't write header to temporary file!"); + + break; + } + case PQ_NOT_CONNECTED: + { + mutt_error _("Can't fetch header: Not connected!"); + break; + } + } + + m_fclose(&f); + unlink (tempfile); + return ret; +} + +/* parse UIDL */ +static int fetch_uidl (char *line, void *data) +{ + int i, idx; + CONTEXT *ctx = (CONTEXT *) data; + POP_DATA *pop_data = (POP_DATA *) ctx->data; + + sscanf (line, "%d %s", &idx, line); + for (i = 0; i < ctx->msgcount; i++) + if (!m_strcmp(line, ctx->hdrs[i]->data)) + break; + + if (i == ctx->msgcount) { + if (i >= ctx->hdrmax) + mx_alloc_memory (ctx); + + ctx->msgcount++; + ctx->hdrs[i] = header_new(); + ctx->hdrs[i]->data = m_strdup(line); + } + else if (ctx->hdrs[i]->index != idx - 1) + pop_data->clear_cache = 1; + + ctx->hdrs[i]->refno = idx; + ctx->hdrs[i]->index = idx - 1; + + return 0; +} + +/* + * Read headers + * returns: + * 0 on success + * -1 - conection lost, + * -2 - invalid command or execution error, + * -3 - error writing to tempfile + */ +static int pop_fetch_headers (CONTEXT * ctx) +{ + int i, old_count, new_count; + pop_query_status ret; + POP_DATA *pop_data = (POP_DATA *) ctx->data; + + time (&pop_data->check_time); + pop_data->clear_cache = 0; + + for (i = 0; i < ctx->msgcount; i++) + ctx->hdrs[i]->refno = -1; + + old_count = ctx->msgcount; + ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx); + new_count = ctx->msgcount; + ctx->msgcount = old_count; + + if (pop_data->cmd_uidl == CMD_UNKNOWN) { + if (ret == PQ_OK) { + pop_data->cmd_uidl = CMD_AVAILABLE; + } + + if (ret == PQ_ERR && pop_data->cmd_uidl == CMD_UNKNOWN) { + pop_data->cmd_uidl = CMD_NOT_AVAILABLE; + + snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), + _("Command UIDL is not supported by server.")); + } + } + + if (ret == PQ_OK) { + for (i = 0; i < old_count; i++) + if (ctx->hdrs[i]->refno == -1) + ctx->hdrs[i]->deleted = 1; + + for (i = old_count; i < new_count; i++) { + mutt_message (_("Fetching message headers... [%d/%d]"), + i + 1 - old_count, new_count - old_count); + + ret = pop_read_header (pop_data, ctx->hdrs[i]); + if (ret != PQ_OK) + break; + + ctx->msgcount++; + } + + if (i > old_count) + mx_update_context (ctx, i - old_count); + } + + if (ret != PQ_OK) { + for (i = ctx->msgcount; i < new_count; i++) + header_delete(&ctx->hdrs[i]); + return ret; + } + + mutt_clear_error (); + return (new_count - old_count); +} + +/* open POP mailbox - fetch only headers */ +static int pop_open_mailbox (CONTEXT * ctx) +{ + int ret; + char buf[LONG_STRING]; + CONNECTION *conn; + ACCOUNT act; + POP_DATA *pop_data; + ciss_url_t url; + + if (pop_parse_path (ctx->path, &act)) { + mutt_error (_("%s is an invalid POP path"), ctx->path); + mutt_sleep (2); + return -1; + } + + mutt_account_tourl (&act, &url); + url.path = NULL; + url_ciss_tostring (&url, buf, sizeof (buf), 0); + conn = mutt_conn_find (NULL, &act); + if (!conn) + return -1; + + p_delete(&ctx->path); + ctx->path = m_strdup(buf); + + pop_data = p_new(POP_DATA, 1); + pop_data->conn = conn; + ctx->data = pop_data; + + if (pop_open_connection (pop_data) < 0) + return -1; + + conn->data = pop_data; + + for (;;) { + if (pop_reconnect (ctx) != PQ_OK) + return -1; + + ctx->size = pop_data->size; + + mutt_message _("Fetching list of messages..."); + + ret = pop_fetch_headers (ctx); + + if (ret >= 0) + return 0; + + if (ret < -1) { + mutt_sleep (2); + return -1; + } + } +} + +/* delete all cached messages */ +static void pop_clear_cache (POP_DATA * pop_data) +{ + int i; + + if (!pop_data->clear_cache) + return; + + for (i = 0; i < POP_CACHE_LEN; i++) { + if (pop_data->cache[i].path) { + unlink (pop_data->cache[i].path); + p_delete(&pop_data->cache[i].path); + } + } +} + +/* close POP mailbox */ +static void pop_close_mailbox (CONTEXT * ctx) +{ + POP_DATA *pop_data = (POP_DATA *) ctx->data; + + if (!pop_data) + return; + + pop_logout (ctx); + + if (pop_data->status != POP_NONE) + mutt_socket_close (pop_data->conn); + + pop_data->status = POP_NONE; + + pop_data->clear_cache = 1; + pop_clear_cache (pop_data); + + if (!pop_data->conn->data) + mutt_socket_free (pop_data->conn); + + return; +} + +/* fetch message from POP server */ +int pop_fetch_message (MESSAGE * msg, CONTEXT * ctx, int msgno) +{ + int ret; + void *uidl; + char buf[LONG_STRING]; + char path[_POSIX_PATH_MAX]; + progress_t bar; + POP_DATA *pop_data = (POP_DATA *) ctx->data; + POP_CACHE *cache; + HEADER *h = ctx->hdrs[msgno]; + + /* see if we already have the message in our cache */ + cache = &pop_data->cache[h->index % POP_CACHE_LEN]; + + if (cache->path) { + if (cache->index == h->index) { + /* yes, so just return a pointer to the message */ + msg->fp = fopen (cache->path, "r"); + if (msg->fp) + return 0; + + mutt_perror (cache->path); + mutt_sleep (2); + return -1; + } + else { + /* clear the previous entry */ + unlink (cache->path); + p_delete(&cache->path); + } + } + + for (;;) { + if (pop_reconnect (ctx) != PQ_OK) + return -1; + + /* verify that massage index is correct */ + if (h->refno < 0) { + mutt_error + _("The message index is incorrect. Try reopening the mailbox."); + mutt_sleep (2); + return -1; + } + + bar.size = h->content->length + h->content->offset - 1; + bar.msg = _("Fetching message..."); + mutt_progress_bar (&bar, 0); + + msg->fp = m_tempfile(path, sizeof(path), NONULL(MCore.tmpdir), NULL); + if (!msg->fp) { + mutt_error(_("Could not create temporary file")); + mutt_sleep(2); + return -1; + } + + snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno); + + ret = pop_fetch_data (pop_data, buf, &bar, fetch_message, msg->fp); + if (ret == PQ_OK) + break; + + m_fclose(&msg->fp); + unlink (path); + + if (ret == PQ_ERR) { + mutt_error ("%s", pop_data->err_msg); + mutt_sleep (2); + return -1; + } + + if (ret == PFD_FUNCT_ERROR) { + mutt_error _("Can't write message to temporary file!"); + + mutt_sleep (2); + return -1; + } + } + + /* Update the header information. Previously, we only downloaded a + * portion of the headers, those required for the main display. + */ + cache->index = h->index; + cache->path = m_strdup(path); + rewind (msg->fp); + uidl = h->data; + envelope_delete(&h->env); + h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0); + h->data = uidl; + h->lines = 0; + fgets (buf, sizeof (buf), msg->fp); + while (!feof (msg->fp)) { + ctx->hdrs[msgno]->lines++; + fgets (buf, sizeof (buf), msg->fp); + } + + h->content->length = ftello (msg->fp) - h->content->offset; + + /* This needs to be done in case this is a multipart message */ + h->security = crypt_query (h->content); + + mutt_clear_error (); + rewind (msg->fp); + + return 0; +} + +/* update POP mailbox - delete messages from server */ +static pop_query_status +pop_sync_mailbox (CONTEXT * ctx, int unused __attribute__ ((unused)), + int *index_hint __attribute__ ((unused))) +{ + int i; + pop_query_status ret; + char buf[LONG_STRING]; + POP_DATA *pop_data = (POP_DATA *) ctx->data; + + pop_data->check_time = 0; + + for (;;) { + if (pop_reconnect (ctx) != PQ_OK) + return PQ_NOT_CONNECTED; + + mutt_message (_("Marking %d messages deleted..."), ctx->deleted); + + for (i = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) { + if (ctx->hdrs[i]->deleted) { + snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno); + ret = pop_query (pop_data, buf, sizeof (buf)); + } + } + + if (ret == PQ_OK) { + m_strcpy(buf, sizeof(buf), "QUIT\r\n"); + ret = pop_query (pop_data, buf, sizeof (buf)); + } + + if (ret == PQ_OK) { + pop_data->clear_cache = 1; + pop_clear_cache (pop_data); + pop_data->status = POP_DISCONNECTED; + return PQ_OK; + } + + if (ret == PQ_ERR) { + mutt_error ("%s", pop_data->err_msg); + mutt_sleep (2); + return PQ_NOT_CONNECTED; + } + } +} + +/* Check for new messages and fetch headers */ +static int pop_check_mailbox (CONTEXT * ctx, + int *index_hint __attribute__ ((unused)), + int unused __attribute__ ((unused))) +{ + int ret; + POP_DATA *pop_data = (POP_DATA *) ctx->data; + + if ((pop_data->check_time + PopCheckTimeout) > time (NULL)) + return 0; + + pop_logout (ctx); + + mutt_socket_close (pop_data->conn); + + if (pop_open_connection (pop_data) < 0) + return -1; + + ctx->size = pop_data->size; + + mutt_message _("Checking for new messages..."); + + ret = pop_fetch_headers (ctx); + pop_clear_cache (pop_data); + + if (ret < 0) + return -1; + + if (ret > 0) + return M_NEW_MAIL; + + return 0; +} + +/* Fetch messages and save them in $spoolfile */ +void pop_fetch_mail (void) +{ + char buffer[LONG_STRING]; + char msgbuf[STRING]; + char *url, *p; + int i, delanswer, last = 0, msgs, bytes, rset = 0; + pop_query_status ret; + CONNECTION *conn; + CONTEXT ctx; + MESSAGE *msg = NULL; + ACCOUNT act; + POP_DATA *pop_data; + ssize_t plen; + + if (!PopHost) { + mutt_error _("POP host is not defined."); + + return; + } + + plen = m_strlen(PopHost) + 7; + url = p = p_new(char, plen); + if (url_check_scheme (PopHost) == U_UNKNOWN) { + plen -= m_strcpy(url, plen, "pop://"); + p += plen; + } + m_strcpy(p, plen, PopHost); + + ret = pop_parse_path (url, &act); + p_delete(&url); + if (ret) { + mutt_error (_("%s is an invalid POP path"), PopHost); + return; + } + + conn = mutt_conn_find (NULL, &act); + if (!conn) + return; + + pop_data = p_new(POP_DATA, 1); + pop_data->conn = conn; + + if (pop_open_connection (pop_data) < 0) { + mutt_socket_free (pop_data->conn); + p_delete(&pop_data); + return; + } + + conn->data = pop_data; + + mutt_message _("Checking for new messages..."); + + /* find out how many messages are in the mailbox. */ + m_strcpy(buffer, sizeof(buffer), "STAT\r\n"); + ret = pop_query (pop_data, buffer, sizeof (buffer)); + if (ret == PQ_NOT_CONNECTED) + goto fail; + if (ret == PQ_ERR) { + mutt_error ("%s", pop_data->err_msg); + goto finish; + } + + sscanf (buffer, "+OK %d %d", &msgs, &bytes); + + /* only get unread messages */ + if (msgs > 0 && option (OPTPOPLAST)) { + m_strcpy(buffer, sizeof(buffer), "LAST\r\n"); + ret = pop_query (pop_data, buffer, sizeof (buffer)); + if (ret == PQ_NOT_CONNECTED) + goto fail; + if (ret == PQ_OK) + sscanf (buffer, "+OK %d", &last); + } + + if (msgs <= last) { + mutt_message _("No new mail in POP mailbox."); + + goto finish; + } + + if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL) + goto finish; + + delanswer = + query_quadoption (OPT_POPDELETE, _("Delete messages from server?")); + + snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."), + bytes); + mutt_message ("%s", msgbuf); + + for (i = last + 1; i <= msgs; i++) { + if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL) + ret = -3; + else { + snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i); + ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp); + if (ret == PFD_FUNCT_ERROR) + rset = 1; + + if (ret == PQ_OK && mx_commit_message (msg, &ctx) != 0) { + rset = 1; + ret = PFD_FUNCT_ERROR; + } + + mx_close_message (&msg); + } + + if (ret == PQ_OK && delanswer == M_YES) { + /* delete the message on the server */ + snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i); + ret = pop_query (pop_data, buffer, sizeof (buffer)); + } + + if (ret == PQ_NOT_CONNECTED) { + mx_close_mailbox (&ctx, NULL); + goto fail; + } + if (ret == PQ_ERR) { + mutt_error ("%s", pop_data->err_msg); + break; + } + if (ret == -3) { /* this is -3 when ret != 0, because it will keep the value from before *gna* */ + mutt_error _("Error while writing mailbox!"); + + break; + } + + mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last, + msgs - last); + } + + mx_close_mailbox (&ctx, NULL); + + if (rset) { + /* make sure no messages get deleted */ + m_strcpy(buffer, sizeof(buffer), "RSET\r\n"); + if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED) + goto fail; + } + +finish: + /* exit gracefully */ + m_strcpy(buffer, sizeof(buffer), "QUIT\r\n"); + if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED) + goto fail; + mutt_socket_close (conn); + p_delete(&pop_data); + return; + +fail: + mutt_error _("Server closed connection!"); + mutt_socket_close (conn); + p_delete(&pop_data); +} + +static int pop_is_magic (const char* path, struct stat* st __attribute__ ((unused))) { + url_scheme_t s = url_check_scheme (NONULL (path)); + return ((s == U_POP || s == U_POPS) ? M_POP : -1); +} + +static int acl_check_pop (CONTEXT* ctx __attribute__ ((unused)), int bit) { + switch (bit) { + case ACL_INSERT: /* editing messages */ + case ACL_WRITE: /* change importance */ + return (0); + case ACL_DELETE: /* (un)deletion */ + case ACL_SEEN: /* mark as read */ + return (1); + default: + return (0); + } +} + +mx_t const pop_mx = { + M_POP, + 0, + pop_is_magic, + NULL, + NULL, + pop_open_mailbox, + NULL, + acl_check_pop, + pop_check_mailbox, + pop_close_mailbox, + pop_sync_mailbox, + NULL, +}; diff --git a/pop.h b/pop.h new file mode 100644 index 0000000..396a81b --- /dev/null +++ b/pop.h @@ -0,0 +1,21 @@ +/* + * Copyright notice from original mutt: + * Copyright (C) 2000-2003 Vsevolod Volkov + * + * This file is part of mutt-ng, see http://www.muttng.org/. + * It's licensed under the GNU General Public License, + * please see the file GPL in the top level source directory. + */ + +#ifndef _POP_H +#define _POP_H 1 + +#include +#include + +extern mx_t const pop_mx; + +int pop_fetch_message (MESSAGE *, CONTEXT *, int); +void pop_fetch_mail (void); + +#endif diff --git a/pop/Makefile.am b/pop/Makefile.am deleted file mode 100644 index 45261db..0000000 --- a/pop/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -## Process this file with automake to produce Makefile.in - -AR=@AR@ - -AUTOMAKE_OPTIONS = foreign - -INCLUDES = -I$(top_srcdir) -I../intl - -noinst_LIBRARIES = libpop.a -noinst_HEADERS = pop.h md5.h - -libpop_a_SOURCES = pop.h md5.h \ - pop.c md5.c pop_auth.c pop_lib.c - --include $(top_builddir)/tools/cflags.mk diff --git a/pop/pop.c b/pop/pop.c deleted file mode 100644 index a09e1fc..0000000 --- a/pop/pop.c +++ /dev/null @@ -1,676 +0,0 @@ -/* - * Copyright notice from original mutt: - * Copyright (C) 2000-2002 Vsevolod Volkov - * - * This file is part of mutt-ng, see http://www.muttng.org/. - * It's licensed under the GNU General Public License, - * please see the file GPL in the top level source directory. - */ - -#include - -#include -#include - -#include "mutt.h" -#include "pop.h" -#include "crypt.h" - -/* write line to file */ -static int fetch_message (char *line, void *file) -{ - FILE *f = (FILE *) file; - - fputs (line, f); - if (fputc ('\n', f) == EOF) - return -1; - - return 0; -} - -/* - * Read header - * returns: - * 0 on success - * -1 - conection lost, - * -2 - invalid command or execution error, - * -3 - error writing to tempfile - */ -static pop_query_status pop_read_header (POP_DATA * pop_data, HEADER * h) -{ - FILE *f; - int idx; - pop_query_status ret; - long length; - char buf[LONG_STRING]; - char tempfile[_POSIX_PATH_MAX]; - - f = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL); - if (!f) { - mutt_error(_("Could not create temporary file")); - return PFD_FUNCT_ERROR; - } - - snprintf (buf, sizeof (buf), "LIST %d\r\n", h->refno); - ret = pop_query (pop_data, buf, sizeof (buf)); - if (ret == PQ_OK) { - sscanf (buf, "+OK %d %ld", &idx, &length); - - snprintf (buf, sizeof (buf), "TOP %d 0\r\n", h->refno); - ret = pop_fetch_data (pop_data, buf, NULL, fetch_message, f); - - if (pop_data->cmd_top == CMD_UNKNOWN) { - if (ret == PQ_OK) { - pop_data->cmd_top = CMD_AVAILABLE; - } - - if (ret == PQ_ERR) { - pop_data->cmd_top = CMD_NOT_AVAILABLE; - snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), - _("Command TOP is not supported by server.")); - } - } - } - - switch (ret) { - case PQ_OK: - { - rewind (f); - h->env = mutt_read_rfc822_header (f, h, 0, 0); - h->content->length = length - h->content->offset + 1; - rewind (f); - while (!feof (f)) { - h->content->length--; - fgets (buf, sizeof (buf), f); - } - break; - } - case PQ_ERR: - { - mutt_error ("%s", pop_data->err_msg); - break; - } - case PFD_FUNCT_ERROR: - { - mutt_error _("Can't write header to temporary file!"); - - break; - } - case PQ_NOT_CONNECTED: - { - mutt_error _("Can't fetch header: Not connected!"); - break; - } - } - - m_fclose(&f); - unlink (tempfile); - return ret; -} - -/* parse UIDL */ -static int fetch_uidl (char *line, void *data) -{ - int i, idx; - CONTEXT *ctx = (CONTEXT *) data; - POP_DATA *pop_data = (POP_DATA *) ctx->data; - - sscanf (line, "%d %s", &idx, line); - for (i = 0; i < ctx->msgcount; i++) - if (!m_strcmp(line, ctx->hdrs[i]->data)) - break; - - if (i == ctx->msgcount) { - if (i >= ctx->hdrmax) - mx_alloc_memory (ctx); - - ctx->msgcount++; - ctx->hdrs[i] = header_new(); - ctx->hdrs[i]->data = m_strdup(line); - } - else if (ctx->hdrs[i]->index != idx - 1) - pop_data->clear_cache = 1; - - ctx->hdrs[i]->refno = idx; - ctx->hdrs[i]->index = idx - 1; - - return 0; -} - -/* - * Read headers - * returns: - * 0 on success - * -1 - conection lost, - * -2 - invalid command or execution error, - * -3 - error writing to tempfile - */ -static int pop_fetch_headers (CONTEXT * ctx) -{ - int i, old_count, new_count; - pop_query_status ret; - POP_DATA *pop_data = (POP_DATA *) ctx->data; - - time (&pop_data->check_time); - pop_data->clear_cache = 0; - - for (i = 0; i < ctx->msgcount; i++) - ctx->hdrs[i]->refno = -1; - - old_count = ctx->msgcount; - ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx); - new_count = ctx->msgcount; - ctx->msgcount = old_count; - - if (pop_data->cmd_uidl == CMD_UNKNOWN) { - if (ret == PQ_OK) { - pop_data->cmd_uidl = CMD_AVAILABLE; - } - - if (ret == PQ_ERR && pop_data->cmd_uidl == CMD_UNKNOWN) { - pop_data->cmd_uidl = CMD_NOT_AVAILABLE; - - snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), - _("Command UIDL is not supported by server.")); - } - } - - if (ret == PQ_OK) { - for (i = 0; i < old_count; i++) - if (ctx->hdrs[i]->refno == -1) - ctx->hdrs[i]->deleted = 1; - - for (i = old_count; i < new_count; i++) { - mutt_message (_("Fetching message headers... [%d/%d]"), - i + 1 - old_count, new_count - old_count); - - ret = pop_read_header (pop_data, ctx->hdrs[i]); - if (ret != PQ_OK) - break; - - ctx->msgcount++; - } - - if (i > old_count) - mx_update_context (ctx, i - old_count); - } - - if (ret != PQ_OK) { - for (i = ctx->msgcount; i < new_count; i++) - header_delete(&ctx->hdrs[i]); - return ret; - } - - mutt_clear_error (); - return (new_count - old_count); -} - -/* open POP mailbox - fetch only headers */ -static int pop_open_mailbox (CONTEXT * ctx) -{ - int ret; - char buf[LONG_STRING]; - CONNECTION *conn; - ACCOUNT act; - POP_DATA *pop_data; - ciss_url_t url; - - if (pop_parse_path (ctx->path, &act)) { - mutt_error (_("%s is an invalid POP path"), ctx->path); - mutt_sleep (2); - return -1; - } - - mutt_account_tourl (&act, &url); - url.path = NULL; - url_ciss_tostring (&url, buf, sizeof (buf), 0); - conn = mutt_conn_find (NULL, &act); - if (!conn) - return -1; - - p_delete(&ctx->path); - ctx->path = m_strdup(buf); - - pop_data = p_new(POP_DATA, 1); - pop_data->conn = conn; - ctx->data = pop_data; - - if (pop_open_connection (pop_data) < 0) - return -1; - - conn->data = pop_data; - - for (;;) { - if (pop_reconnect (ctx) != PQ_OK) - return -1; - - ctx->size = pop_data->size; - - mutt_message _("Fetching list of messages..."); - - ret = pop_fetch_headers (ctx); - - if (ret >= 0) - return 0; - - if (ret < -1) { - mutt_sleep (2); - return -1; - } - } -} - -/* delete all cached messages */ -static void pop_clear_cache (POP_DATA * pop_data) -{ - int i; - - if (!pop_data->clear_cache) - return; - - for (i = 0; i < POP_CACHE_LEN; i++) { - if (pop_data->cache[i].path) { - unlink (pop_data->cache[i].path); - p_delete(&pop_data->cache[i].path); - } - } -} - -/* close POP mailbox */ -static void pop_close_mailbox (CONTEXT * ctx) -{ - POP_DATA *pop_data = (POP_DATA *) ctx->data; - - if (!pop_data) - return; - - pop_logout (ctx); - - if (pop_data->status != POP_NONE) - mutt_socket_close (pop_data->conn); - - pop_data->status = POP_NONE; - - pop_data->clear_cache = 1; - pop_clear_cache (pop_data); - - if (!pop_data->conn->data) - mutt_socket_free (pop_data->conn); - - return; -} - -/* fetch message from POP server */ -int pop_fetch_message (MESSAGE * msg, CONTEXT * ctx, int msgno) -{ - int ret; - void *uidl; - char buf[LONG_STRING]; - char path[_POSIX_PATH_MAX]; - progress_t bar; - POP_DATA *pop_data = (POP_DATA *) ctx->data; - POP_CACHE *cache; - HEADER *h = ctx->hdrs[msgno]; - - /* see if we already have the message in our cache */ - cache = &pop_data->cache[h->index % POP_CACHE_LEN]; - - if (cache->path) { - if (cache->index == h->index) { - /* yes, so just return a pointer to the message */ - msg->fp = fopen (cache->path, "r"); - if (msg->fp) - return 0; - - mutt_perror (cache->path); - mutt_sleep (2); - return -1; - } - else { - /* clear the previous entry */ - unlink (cache->path); - p_delete(&cache->path); - } - } - - for (;;) { - if (pop_reconnect (ctx) != PQ_OK) - return -1; - - /* verify that massage index is correct */ - if (h->refno < 0) { - mutt_error - _("The message index is incorrect. Try reopening the mailbox."); - mutt_sleep (2); - return -1; - } - - bar.size = h->content->length + h->content->offset - 1; - bar.msg = _("Fetching message..."); - mutt_progress_bar (&bar, 0); - - msg->fp = m_tempfile(path, sizeof(path), NONULL(MCore.tmpdir), NULL); - if (!msg->fp) { - mutt_error(_("Could not create temporary file")); - mutt_sleep(2); - return -1; - } - - snprintf (buf, sizeof (buf), "RETR %d\r\n", h->refno); - - ret = pop_fetch_data (pop_data, buf, &bar, fetch_message, msg->fp); - if (ret == PQ_OK) - break; - - m_fclose(&msg->fp); - unlink (path); - - if (ret == PQ_ERR) { - mutt_error ("%s", pop_data->err_msg); - mutt_sleep (2); - return -1; - } - - if (ret == PFD_FUNCT_ERROR) { - mutt_error _("Can't write message to temporary file!"); - - mutt_sleep (2); - return -1; - } - } - - /* Update the header information. Previously, we only downloaded a - * portion of the headers, those required for the main display. - */ - cache->index = h->index; - cache->path = m_strdup(path); - rewind (msg->fp); - uidl = h->data; - envelope_delete(&h->env); - h->env = mutt_read_rfc822_header (msg->fp, h, 0, 0); - h->data = uidl; - h->lines = 0; - fgets (buf, sizeof (buf), msg->fp); - while (!feof (msg->fp)) { - ctx->hdrs[msgno]->lines++; - fgets (buf, sizeof (buf), msg->fp); - } - - h->content->length = ftello (msg->fp) - h->content->offset; - - /* This needs to be done in case this is a multipart message */ - h->security = crypt_query (h->content); - - mutt_clear_error (); - rewind (msg->fp); - - return 0; -} - -/* update POP mailbox - delete messages from server */ -static pop_query_status -pop_sync_mailbox (CONTEXT * ctx, int unused __attribute__ ((unused)), - int *index_hint __attribute__ ((unused))) -{ - int i; - pop_query_status ret; - char buf[LONG_STRING]; - POP_DATA *pop_data = (POP_DATA *) ctx->data; - - pop_data->check_time = 0; - - for (;;) { - if (pop_reconnect (ctx) != PQ_OK) - return PQ_NOT_CONNECTED; - - mutt_message (_("Marking %d messages deleted..."), ctx->deleted); - - for (i = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) { - if (ctx->hdrs[i]->deleted) { - snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno); - ret = pop_query (pop_data, buf, sizeof (buf)); - } - } - - if (ret == PQ_OK) { - m_strcpy(buf, sizeof(buf), "QUIT\r\n"); - ret = pop_query (pop_data, buf, sizeof (buf)); - } - - if (ret == PQ_OK) { - pop_data->clear_cache = 1; - pop_clear_cache (pop_data); - pop_data->status = POP_DISCONNECTED; - return PQ_OK; - } - - if (ret == PQ_ERR) { - mutt_error ("%s", pop_data->err_msg); - mutt_sleep (2); - return PQ_NOT_CONNECTED; - } - } -} - -/* Check for new messages and fetch headers */ -static int pop_check_mailbox (CONTEXT * ctx, - int *index_hint __attribute__ ((unused)), - int unused __attribute__ ((unused))) -{ - int ret; - POP_DATA *pop_data = (POP_DATA *) ctx->data; - - if ((pop_data->check_time + PopCheckTimeout) > time (NULL)) - return 0; - - pop_logout (ctx); - - mutt_socket_close (pop_data->conn); - - if (pop_open_connection (pop_data) < 0) - return -1; - - ctx->size = pop_data->size; - - mutt_message _("Checking for new messages..."); - - ret = pop_fetch_headers (ctx); - pop_clear_cache (pop_data); - - if (ret < 0) - return -1; - - if (ret > 0) - return M_NEW_MAIL; - - return 0; -} - -/* Fetch messages and save them in $spoolfile */ -void pop_fetch_mail (void) -{ - char buffer[LONG_STRING]; - char msgbuf[STRING]; - char *url, *p; - int i, delanswer, last = 0, msgs, bytes, rset = 0; - pop_query_status ret; - CONNECTION *conn; - CONTEXT ctx; - MESSAGE *msg = NULL; - ACCOUNT act; - POP_DATA *pop_data; - ssize_t plen; - - if (!PopHost) { - mutt_error _("POP host is not defined."); - - return; - } - - plen = m_strlen(PopHost) + 7; - url = p = p_new(char, plen); - if (url_check_scheme (PopHost) == U_UNKNOWN) { - plen -= m_strcpy(url, plen, "pop://"); - p += plen; - } - m_strcpy(p, plen, PopHost); - - ret = pop_parse_path (url, &act); - p_delete(&url); - if (ret) { - mutt_error (_("%s is an invalid POP path"), PopHost); - return; - } - - conn = mutt_conn_find (NULL, &act); - if (!conn) - return; - - pop_data = p_new(POP_DATA, 1); - pop_data->conn = conn; - - if (pop_open_connection (pop_data) < 0) { - mutt_socket_free (pop_data->conn); - p_delete(&pop_data); - return; - } - - conn->data = pop_data; - - mutt_message _("Checking for new messages..."); - - /* find out how many messages are in the mailbox. */ - m_strcpy(buffer, sizeof(buffer), "STAT\r\n"); - ret = pop_query (pop_data, buffer, sizeof (buffer)); - if (ret == PQ_NOT_CONNECTED) - goto fail; - if (ret == PQ_ERR) { - mutt_error ("%s", pop_data->err_msg); - goto finish; - } - - sscanf (buffer, "+OK %d %d", &msgs, &bytes); - - /* only get unread messages */ - if (msgs > 0 && option (OPTPOPLAST)) { - m_strcpy(buffer, sizeof(buffer), "LAST\r\n"); - ret = pop_query (pop_data, buffer, sizeof (buffer)); - if (ret == PQ_NOT_CONNECTED) - goto fail; - if (ret == PQ_OK) - sscanf (buffer, "+OK %d", &last); - } - - if (msgs <= last) { - mutt_message _("No new mail in POP mailbox."); - - goto finish; - } - - if (mx_open_mailbox (NONULL (Spoolfile), M_APPEND, &ctx) == NULL) - goto finish; - - delanswer = - query_quadoption (OPT_POPDELETE, _("Delete messages from server?")); - - snprintf (msgbuf, sizeof (msgbuf), _("Reading new messages (%d bytes)..."), - bytes); - mutt_message ("%s", msgbuf); - - for (i = last + 1; i <= msgs; i++) { - if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL) - ret = -3; - else { - snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i); - ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp); - if (ret == PFD_FUNCT_ERROR) - rset = 1; - - if (ret == PQ_OK && mx_commit_message (msg, &ctx) != 0) { - rset = 1; - ret = PFD_FUNCT_ERROR; - } - - mx_close_message (&msg); - } - - if (ret == PQ_OK && delanswer == M_YES) { - /* delete the message on the server */ - snprintf (buffer, sizeof (buffer), "DELE %d\r\n", i); - ret = pop_query (pop_data, buffer, sizeof (buffer)); - } - - if (ret == PQ_NOT_CONNECTED) { - mx_close_mailbox (&ctx, NULL); - goto fail; - } - if (ret == PQ_ERR) { - mutt_error ("%s", pop_data->err_msg); - break; - } - if (ret == -3) { /* this is -3 when ret != 0, because it will keep the value from before *gna* */ - mutt_error _("Error while writing mailbox!"); - - break; - } - - mutt_message (_("%s [%d of %d messages read]"), msgbuf, i - last, - msgs - last); - } - - mx_close_mailbox (&ctx, NULL); - - if (rset) { - /* make sure no messages get deleted */ - m_strcpy(buffer, sizeof(buffer), "RSET\r\n"); - if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED) - goto fail; - } - -finish: - /* exit gracefully */ - m_strcpy(buffer, sizeof(buffer), "QUIT\r\n"); - if (pop_query (pop_data, buffer, sizeof (buffer)) == PQ_NOT_CONNECTED) - goto fail; - mutt_socket_close (conn); - p_delete(&pop_data); - return; - -fail: - mutt_error _("Server closed connection!"); - mutt_socket_close (conn); - p_delete(&pop_data); -} - -static int pop_is_magic (const char* path, struct stat* st __attribute__ ((unused))) { - url_scheme_t s = url_check_scheme (NONULL (path)); - return ((s == U_POP || s == U_POPS) ? M_POP : -1); -} - -static int acl_check_pop (CONTEXT* ctx __attribute__ ((unused)), int bit) { - switch (bit) { - case ACL_INSERT: /* editing messages */ - case ACL_WRITE: /* change importance */ - return (0); - case ACL_DELETE: /* (un)deletion */ - case ACL_SEEN: /* mark as read */ - return (1); - default: - return (0); - } -} - -mx_t const pop_mx = { - M_POP, - 0, - pop_is_magic, - NULL, - NULL, - pop_open_mailbox, - NULL, - acl_check_pop, - pop_check_mailbox, - pop_close_mailbox, - pop_sync_mailbox, - NULL, -}; diff --git a/pop/pop.h b/pop/pop.h deleted file mode 100644 index da52593..0000000 --- a/pop/pop.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright notice from original mutt: - * Copyright (C) 2000-2003 Vsevolod Volkov - * - * This file is part of mutt-ng, see http://www.muttng.org/. - * It's licensed under the GNU General Public License, - * please see the file GPL in the top level source directory. - */ - -#ifndef _POP_H -#define _POP_H 1 - -#include -#include -#include - - -extern mx_t const pop_mx; - - -#define POP_PORT 110 -#define POP_SSL_PORT 995 - -/* number of entries in the hash table */ -#define POP_CACHE_LEN 10 - -/* maximal length of the server response (RFC1939) */ -#define POP_CMD_RESPONSE 512 - -enum { - /* Status */ - POP_NONE = 0, - POP_CONNECTED, - POP_DISCONNECTED, - POP_BYE -}; - -typedef enum { - POP_A_SUCCESS = 0, - POP_A_SOCKET, - POP_A_FAILURE, - POP_A_UNAVAIL -} pop_auth_res_t; - -typedef struct { - int index; - char *path; -} POP_CACHE; - -typedef enum pop_query_status_e { - PFD_FUNCT_ERROR = -3, /* pop_fetch_data uses pop_query_status and this return value */ - PQ_ERR = -2, - PQ_NOT_CONNECTED = -1, - PQ_OK = 0 -} pop_query_status; - -typedef enum cmd_status_e { - CMD_NOT_AVAILABLE = 0, - CMD_AVAILABLE, - CMD_UNKNOWN /* unknown whether it is available or not */ -} cmd_status; - -typedef struct { - CONNECTION *conn; - unsigned int status:2; - unsigned int capabilities:1; - unsigned int use_stls:2; - cmd_status cmd_capa; /* optional command CAPA */ - cmd_status cmd_stls; /* optional command STLS */ - cmd_status cmd_user; /* optional command USER */ - cmd_status cmd_uidl; /* optional command UIDL */ - cmd_status cmd_top; /* optional command TOP */ - unsigned int resp_codes:1; /* server supports extended response codes */ - unsigned int expire:1; /* expire is greater than 0 */ - unsigned int clear_cache:1; - size_t size; - time_t check_time; - time_t login_delay; /* minimal login delay capability */ - char *auth_list; /* list of auth mechanisms */ - char *timestamp; - char err_msg[POP_CMD_RESPONSE]; - POP_CACHE cache[POP_CACHE_LEN]; -} POP_DATA; - -typedef struct { - /* do authentication, using named method or any available if method is NULL */ - pop_auth_res_t (*authenticate) (POP_DATA *, const char *); - /* name of authentication method supported, NULL means variable. If this - * is not null, authenticate may ignore the second parameter. */ - const char *method; -} pop_auth_t; - -/* pop_auth.c */ -int pop_authenticate (POP_DATA *); -void pop_apop_timestamp (POP_DATA *, char *); - -/* pop_lib.c */ -int pop_parse_path (const char *, ACCOUNT *); -int pop_connect (POP_DATA *); -pop_query_status pop_open_connection (POP_DATA *); -pop_query_status pop_query (POP_DATA *, char *, size_t); -pop_query_status pop_fetch_data (POP_DATA *, const char *, progress_t*, - int (*funct) (char *, void *), void *); -pop_query_status pop_reconnect (CONTEXT *); -void pop_logout (CONTEXT *); -void pop_error (POP_DATA *, char *); - -/* pop.c */ -int pop_fetch_message (MESSAGE *, CONTEXT *, int); -void pop_fetch_mail (void); - -#endif diff --git a/pop/pop_auth.c b/pop/pop_auth.c deleted file mode 100644 index 98dc1cc..0000000 --- a/pop/pop_auth.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright notice from original mutt: - * Copyright (C) 2000-2001 Vsevolod Volkov - * - * This file is part of mutt-ng, see http://www.muttng.org/. - * It's licensed under the GNU General Public License, - * please see the file GPL in the top level source directory. - */ - -#include - -#include -#include - -#include - -#include "md5.h" -#include "mutt.h" -#include "pop.h" -#include "mutt_sasl.h" - -/* SASL authenticator */ -static pop_auth_res_t pop_auth_sasl (POP_DATA * pop_data, const char *method) -{ - sasl_conn_t *saslconn; - sasl_interact_t *interaction = NULL; - int rc; - char buf[LONG_STRING]; - char inbuf[LONG_STRING]; - const char *mech; - - const char *pc = NULL; - unsigned int len, olen; - unsigned char client_start; - - if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) { - return POP_A_FAILURE; - } - - if (!method) - method = pop_data->auth_list; - - for (;;) { - rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen, - &mech); - if (rc != SASL_INTERACT) - break; - mutt_sasl_interact (interaction); - } - - if (rc != SASL_OK && rc != SASL_CONTINUE) { - /* SASL doesn't support suggested mechanisms, so fall back */ - return POP_A_UNAVAIL; - } - - client_start = (olen > 0); - - mutt_message _("Authenticating (SASL)..."); - - snprintf (buf, sizeof (buf), "AUTH %s", mech); - olen = strlen (buf); - - /* looping protocol */ - for (;;) { - m_strcpy(buf + olen, sizeof(buf) - olen, "\r\n"); - mutt_socket_write (pop_data->conn, buf); - if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) { - sasl_dispose (&saslconn); - pop_data->status = POP_DISCONNECTED; - return POP_A_SOCKET; - } - - if (rc != SASL_CONTINUE) - break; - - if (!m_strncmp(inbuf, "+ ", 2) - && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1, - &len) != SASL_OK) - { - goto bail; - } - - if (!client_start) - for (;;) { - rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen); - if (rc != SASL_INTERACT) - break; - mutt_sasl_interact (interaction); - } - else - client_start = 0; - - if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK)) - break; - - /* send out response, or line break if none needed */ - if (pc) { - if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) { - goto bail; - } - - /* sasl_client_st(art|ep) allocate pc with malloc, expect me to - * free it */ - p_delete((char **)&pc); - } - } - - if (rc != SASL_OK) - goto bail; - - if (!m_strncmp(inbuf, "+OK", 3)) { - mutt_sasl_setup_conn (pop_data->conn, saslconn); - return POP_A_SUCCESS; - } - -bail: - sasl_dispose (&saslconn); - - /* terminate SASL sessoin if the last responce is not +OK nor -ERR */ - if (!m_strncmp(inbuf, "+ ", 2)) { - snprintf (buf, sizeof (buf), "*\r\n"); - if (pop_query (pop_data, buf, sizeof (buf)) == PQ_NOT_CONNECTED) - return POP_A_SOCKET; - } - - mutt_error _("SASL authentication failed."); - - mutt_sleep (2); - - return POP_A_FAILURE; -} - -/* Get the server timestamp for APOP authentication */ -void pop_apop_timestamp (POP_DATA * pop_data, char *buf) -{ - char *p1, *p2; - - p_delete(&pop_data->timestamp); - - if ((p1 = strchr (buf, '<')) && (p2 = strchr (p1, '>'))) { - p2[1] = '\0'; - pop_data->timestamp = m_strdup(p1); - } -} - -/* APOP authenticator */ -static pop_auth_res_t pop_auth_apop (POP_DATA * pop_data, - const char *method __attribute__ ((unused))) -{ - MD5_CTX mdContext; - unsigned char digest[16]; - char hash[33]; - char buf[LONG_STRING]; - int i; - - if (!pop_data->timestamp) - return POP_A_UNAVAIL; - - mutt_message _("Authenticating (APOP)..."); - - /* Compute the authentication hash to send to the server */ - MD5Init (&mdContext); - MD5Update (&mdContext, (unsigned char *) pop_data->timestamp, - strlen (pop_data->timestamp)); - MD5Update (&mdContext, (unsigned char *) pop_data->conn->account.pass, - strlen (pop_data->conn->account.pass)); - MD5Final (digest, &mdContext); - - for (i = 0; i < ssizeof(digest); i++) - sprintf (hash + 2 * i, "%02x", digest[i]); - - /* Send APOP command to server */ - snprintf(buf, sizeof(buf), "APOP %s %s\r\n", pop_data->conn->account.user, - hash); - - switch (pop_query (pop_data, buf, sizeof (buf))) { - case PQ_OK: - return POP_A_SUCCESS; - case PQ_NOT_CONNECTED: - return POP_A_SOCKET; - case PFD_FUNCT_ERROR: - case PQ_ERR: - default: - break; - } - - mutt_error ("%s %s", _("APOP authentication failed."), pop_data->err_msg); - mutt_sleep (2); - - return POP_A_FAILURE; -} - -/* USER authenticator */ -static pop_auth_res_t pop_auth_user (POP_DATA * pop_data, - const char *method __attribute__ ((unused))) -{ - char buf[LONG_STRING]; - pop_query_status ret; - - if (pop_data->cmd_user == CMD_NOT_AVAILABLE) - return POP_A_UNAVAIL; - - mutt_message _("Logging in..."); - - snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user); - ret = pop_query (pop_data, buf, sizeof (buf)); - - if (pop_data->cmd_user == CMD_UNKNOWN) { - if (ret == PQ_OK) { - pop_data->cmd_user = CMD_AVAILABLE; - } - - if (ret == PQ_ERR) { - pop_data->cmd_user = CMD_NOT_AVAILABLE; - - snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), - _("Command USER is not supported by server.")); - } - } - - if (ret == PQ_OK) { - snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass); - ret = pop_query (pop_data, buf, sizeof (buf)); - } - - switch (ret) { - case PQ_OK: - return POP_A_SUCCESS; - case PQ_NOT_CONNECTED: - return POP_A_SOCKET; - case PFD_FUNCT_ERROR: - case PQ_ERR: - default: - break; - } - - mutt_error ("%s %s", _("Login failed."), pop_data->err_msg); - mutt_sleep (2); - - return POP_A_FAILURE; -} - -static pop_auth_t pop_authenticators[] = { - {pop_auth_sasl, NULL}, - {pop_auth_apop, "apop"}, - {pop_auth_user, "user"}, - {NULL, NULL} -}; - -/* - * Authentication - * 0 - successful, - * -1 - conection lost, - * -2 - login failed, - * -3 - authentication canceled. -*/ -pop_query_status pop_authenticate (POP_DATA * pop_data) -{ - ACCOUNT *act = &pop_data->conn->account; - pop_auth_t *authenticator; - char *methods; - char *comma; - char *method; - int attempts = 0; - int ret = POP_A_UNAVAIL; - - if (mutt_account_getuser (act) || !act->user[0] || - mutt_account_getpass (act) || !act->pass[0]) - return PFD_FUNCT_ERROR; - - if (PopAuthenticators && *PopAuthenticators) { - /* Try user-specified list of authentication methods */ - methods = m_strdup(PopAuthenticators); - method = methods; - - while (method) { - comma = strchr (method, ':'); - if (comma) - *comma++ = '\0'; - authenticator = pop_authenticators; - - while (authenticator->authenticate) { - if (!authenticator->method || - !ascii_strcasecmp (authenticator->method, method)) { - ret = authenticator->authenticate (pop_data, method); - if (ret == POP_A_SOCKET) - switch (pop_connect (pop_data)) { - case PQ_OK: - { - ret = authenticator->authenticate (pop_data, method); - break; - } - case PQ_ERR: - ret = POP_A_FAILURE; - } - - if (ret != POP_A_UNAVAIL) - attempts++; - if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET || - (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) { - comma = NULL; - break; - } - } - authenticator++; - } - - method = comma; - } - - p_delete(&methods); - } - else { - /* Fall back to default: any authenticator */ - authenticator = pop_authenticators; - - while (authenticator->authenticate) { - ret = authenticator->authenticate (pop_data, authenticator->method); - if (ret == POP_A_SOCKET) - switch (pop_connect (pop_data)) { - case PQ_OK: - { - ret = - authenticator->authenticate (pop_data, authenticator->method); - break; - } - case PQ_ERR: - ret = POP_A_FAILURE; - } - - if (ret != POP_A_UNAVAIL) - attempts++; - if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET || - (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) - break; - - authenticator++; - } - } - - switch (ret) { - case POP_A_SUCCESS: - return PQ_OK; - case POP_A_SOCKET: - return PQ_NOT_CONNECTED; - case POP_A_UNAVAIL: - if (!attempts) - mutt_error (_("No authenticators available")); - } - - return PQ_ERR; -} diff --git a/pop/pop_lib.c b/pop/pop_lib.c deleted file mode 100644 index 693259d..0000000 --- a/pop/pop_lib.c +++ /dev/null @@ -1,511 +0,0 @@ -/* - * Copyright notice from original mutt: - * Copyright (C) 2000-2003 Vsevolod Volkov - * - * This file is part of mutt-ng, see http://www.muttng.org/. - * It's licensed under the GNU General Public License, - * please see the file GPL in the top level source directory. - */ - -#include -#include - -#include "mutt.h" -#include "pop.h" - -/* given an POP mailbox name, return host, port, username and password */ -int pop_parse_path (const char *path, ACCOUNT * act) -{ - ciss_url_t url; - char *c; - int ret = -1; - - /* Defaults */ - act->flags = 0; - act->port = POP_PORT; - act->type = M_ACCT_TYPE_POP; - - c = m_strdup(path); - url_parse_ciss (&url, c); - - if (url.scheme == U_POP || url.scheme == U_POPS) { - if (url.scheme == U_POPS) { - act->flags |= M_ACCT_SSL; - act->port = POP_SSL_PORT; - } - - if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0) - ret = 0; - } - - p_delete(&c); - return ret; -} - -/* Copy error message to err_msg buffer */ -void pop_error (POP_DATA * pop_data, char *msg) -{ - char *t, *c, *c2; - - t = strchr (pop_data->err_msg, '\0'); - c = msg; - - if (!m_strncmp(msg, "-ERR ", 5)) { - c2 = vskipspaces(msg + 5); - if (*c2) - c = c2; - } - - m_strcpy(t, sizeof(pop_data->err_msg) - strlen(pop_data->err_msg), c); - m_strrtrim(pop_data->err_msg); -} - -/* Parse CAPA output */ -static int fetch_capa (char *line, void *data) -{ - POP_DATA *pop_data = (POP_DATA *) data; - char *c; - - if (!ascii_strncasecmp (line, "SASL", 4)) { - p_delete(&pop_data->auth_list); - c = vskipspaces(line + 4); - pop_data->auth_list = m_strdup(c); - } - - else if (!ascii_strncasecmp (line, "STLS", 4)) - pop_data->cmd_stls = CMD_AVAILABLE; - - else if (!ascii_strncasecmp (line, "USER", 4)) - pop_data->cmd_user = CMD_AVAILABLE; - - else if (!ascii_strncasecmp (line, "UIDL", 4)) - pop_data->cmd_uidl = CMD_AVAILABLE; - - else if (!ascii_strncasecmp (line, "TOP", 3)) - pop_data->cmd_top = CMD_AVAILABLE; - - return 0; -} - -/* Fetch list of the authentication mechanisms */ -static int fetch_auth (char *line, void *data) -{ - POP_DATA *pop_data = (POP_DATA *) data; - ssize_t auth_list_len; - - if (!pop_data->auth_list) { - auth_list_len = m_strlen(line) + 1; - pop_data->auth_list = p_new(char, auth_list_len); - } else { - auth_list_len = m_strlen(pop_data->auth_list) + m_strlen(line) + 2; - p_realloc(&pop_data->auth_list, auth_list_len); - m_strcat(pop_data->auth_list, auth_list_len, " "); - } - m_strcat(pop_data->auth_list, auth_list_len, line); - - return 0; -} - -/* - * Get capabilities - * 0 - successful, - * -1 - conection lost, - * -2 - execution error. -*/ -static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode) -{ - char buf[LONG_STRING]; - - /* don't check capabilities on reconnect */ - if (pop_data->capabilities) - return 0; - - /* init capabilities */ - if (mode == 0) { - pop_data->cmd_capa = CMD_NOT_AVAILABLE; - pop_data->cmd_stls = CMD_NOT_AVAILABLE; - pop_data->cmd_user = CMD_NOT_AVAILABLE; - pop_data->cmd_uidl = CMD_NOT_AVAILABLE; - pop_data->cmd_top = CMD_NOT_AVAILABLE; - pop_data->resp_codes = 0; - pop_data->expire = 1; - pop_data->login_delay = 0; - p_delete(&pop_data->auth_list); - } - - /* Execute CAPA command */ - if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) { - m_strcpy(buf, sizeof(buf), "CAPA\r\n"); - switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) { - case PQ_OK: - { - pop_data->cmd_capa = CMD_AVAILABLE; - break; - } - case PFD_FUNCT_ERROR: - case PQ_ERR: - { - pop_data->cmd_capa = CMD_NOT_AVAILABLE; - break; - } - case PQ_NOT_CONNECTED: - return PQ_NOT_CONNECTED; - } - } - - /* CAPA not supported, use defaults */ - if (mode == 0 && pop_data->cmd_capa == CMD_NOT_AVAILABLE) { - pop_data->cmd_user = CMD_UNKNOWN; - pop_data->cmd_uidl = CMD_UNKNOWN; - pop_data->cmd_top = CMD_UNKNOWN; - - m_strcpy(buf, sizeof(buf), "AUTH\r\n"); - if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == PQ_NOT_CONNECTED) - return PQ_NOT_CONNECTED; - } - - /* Check capabilities */ - if (mode == 2) { - char *msg = NULL; - - if (!pop_data->expire) - msg = _("Unable to leave messages on server."); - if (pop_data->cmd_top == CMD_NOT_AVAILABLE) - msg = _("Command TOP is not supported by server."); - if (pop_data->cmd_uidl == CMD_NOT_AVAILABLE) - msg = _("Command UIDL is not supported by server."); - if (msg && pop_data->cmd_capa != CMD_AVAILABLE) { - mutt_error (msg); - return PQ_ERR; - } - pop_data->capabilities = 1; - } - - return PQ_OK; -} - -/* - * Open connection - * 0 - successful, - * -1 - conection lost, - * -2 - invalid response. -*/ -pop_query_status pop_connect (POP_DATA * pop_data) -{ - char buf[LONG_STRING]; - - pop_data->status = POP_NONE; - if (mutt_socket_open (pop_data->conn) < 0 || - mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0) { - mutt_error (_("Error connecting to server: %s"), - pop_data->conn->account.host); - return PQ_NOT_CONNECTED; - } - - pop_data->status = POP_CONNECTED; - - if (m_strncmp(buf, "+OK", 3)) { - *pop_data->err_msg = '\0'; - pop_error (pop_data, buf); - mutt_error ("%s", pop_data->err_msg); - return PQ_ERR; - } - - pop_apop_timestamp (pop_data, buf); - - return PQ_OK; -} - -/* - * Open connection and authenticate - * 0 - successful, - * -1 - conection lost, - * -2 - invalid command or execution error, - * -3 - authentication canceled. -*/ -pop_query_status pop_open_connection (POP_DATA * pop_data) -{ - pop_query_status ret; - unsigned int n, size; - char buf[LONG_STRING]; - - ret = pop_connect (pop_data); - if (ret != PQ_OK) { - mutt_sleep (2); - return ret; - } - - ret = pop_capabilities (pop_data, 0); - if (ret == PQ_NOT_CONNECTED) - goto err_conn; - if (ret == PQ_ERR) { - mutt_sleep (2); - return PQ_ERR; - } - - /* Attempt STLS if available and desired. */ - if (!pop_data->conn->ssf && (pop_data->cmd_stls || mod_ssl.force_tls)) { - if (mod_ssl.force_tls) - pop_data->use_stls = 2; - if (pop_data->use_stls == 0) { - pop_data->use_stls = 1; - if (mod_ssl.starttls) - pop_data->use_stls = 2; - } - if (pop_data->use_stls == 2) { - m_strcpy(buf, sizeof(buf), "STLS\r\n"); - ret = pop_query (pop_data, buf, sizeof (buf)); - if (ret == PQ_NOT_CONNECTED) - goto err_conn; - if (ret != PQ_OK) { - mutt_error ("%s", pop_data->err_msg); - mutt_sleep (2); - } - else if (mutt_ssl_starttls (pop_data->conn)) - { - mutt_error (_("Could not negotiate TLS connection")); - mutt_sleep (2); - return PQ_ERR; - } - else { - /* recheck capabilities after STLS completes */ - ret = pop_capabilities (pop_data, 1); - if (ret == PQ_NOT_CONNECTED) - goto err_conn; - if (ret == PQ_ERR) { - mutt_sleep (2); - return PQ_ERR; - } - } - } - } - - if (mod_ssl.force_tls && !pop_data->conn->ssf) { - mutt_error _("Encrypted connection unavailable"); - mutt_sleep (1); - return -2; - } - - ret = pop_authenticate (pop_data); - if (ret == PQ_NOT_CONNECTED) - goto err_conn; - if (ret == PFD_FUNCT_ERROR) - mutt_clear_error (); - if (ret != PQ_OK) - return ret; - - /* recheck capabilities after authentication */ - ret = pop_capabilities (pop_data, 2); - if (ret == PQ_NOT_CONNECTED) - goto err_conn; - if (ret == PQ_ERR) { - mutt_sleep (2); - return PQ_ERR; - } - - /* get total size of mailbox */ - m_strcpy(buf, sizeof(buf), "STAT\r\n"); - ret = pop_query (pop_data, buf, sizeof (buf)); - if (ret == PQ_NOT_CONNECTED) - goto err_conn; - if (ret == PQ_ERR) { - mutt_error ("%s", pop_data->err_msg); - mutt_sleep (2); - return ret; - } - - sscanf (buf, "+OK %u %u", &n, &size); - pop_data->size = size; - return PQ_OK; - -err_conn: - pop_data->status = POP_DISCONNECTED; - mutt_error _("Server closed connection!"); - - mutt_sleep (2); - return PQ_NOT_CONNECTED; -} - -/* logout from POP server */ -void pop_logout (CONTEXT * ctx) -{ - pop_query_status ret = 0; - char buf[LONG_STRING]; - POP_DATA *pop_data = (POP_DATA *) ctx->data; - - if (pop_data->status == POP_CONNECTED) { - mutt_message _("Closing connection to POP server..."); - - if (ctx->readonly) { - m_strcpy(buf, sizeof(buf), "RSET\r\n"); - ret = pop_query (pop_data, buf, sizeof (buf)); - } - - if (ret != PQ_NOT_CONNECTED) { - m_strcpy(buf, sizeof(buf), "QUIT\r\n"); - pop_query (pop_data, buf, sizeof (buf)); - } - - mutt_clear_error (); - } - - pop_data->status = POP_DISCONNECTED; - return; -} - -/* - * Send data from buffer and receive answer to the same buffer - * 0 - successful, - * -1 - conection lost, - * -2 - invalid command or execution error. -*/ -pop_query_status pop_query (POP_DATA * pop_data, char *buf, size_t buflen) -{ - char *c; - - if (pop_data->status != POP_CONNECTED) - return PQ_NOT_CONNECTED; - - mutt_socket_write(pop_data->conn, buf); - - c = strpbrk (buf, " \r\n"); - *c = '\0'; - snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf); - - if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0) { - pop_data->status = POP_DISCONNECTED; - return PQ_NOT_CONNECTED; - } - if (!m_strncmp(buf, "+OK", 3)) - return PQ_OK; - - pop_error (pop_data, buf); - return PQ_ERR; -} - -/* - * This function calls funct(*line, *data) for each received line, - * funct(NULL, *data) if rewind(*data) needs, exits when fail or done. - * Returned codes: - * 0 - successful, - * -1 - conection lost, - * -2 - invalid command or execution error, - * -3 - error in funct(*line, *data) - */ -pop_query_status pop_fetch_data (POP_DATA * pop_data, const char *query, progress_t* bar, - int (*funct) (char *, void *), void *data) -{ - char buf[LONG_STRING]; - char *inbuf; - char *p; - pop_query_status ret; - int chunk = 0; - long pos = 0; - size_t lenbuf = 0; - - m_strcpy(buf, sizeof(buf), query); - ret = pop_query (pop_data, buf, sizeof (buf)); - if (ret != PQ_OK) - return ret; - - inbuf = p_new(char, sizeof(buf)); - - for (;;) { - chunk = - mutt_socket_readln(buf, sizeof (buf), pop_data->conn); - if (chunk < 0) { - pop_data->status = POP_DISCONNECTED; - ret = PQ_NOT_CONNECTED; - break; - } - - p = buf; - if (!lenbuf && buf[0] == '.') { - if (buf[1] != '.') - break; - p++; - } - - m_strcpy(inbuf + lenbuf,sizeof(buf), p); - pos += chunk; - - if (chunk >= ssizeof(buf)) { - lenbuf += strlen (p); - } else { - if (bar) - mutt_progress_bar (bar, pos); - if (ret == 0 && funct (inbuf, data) < 0) - ret = PFD_FUNCT_ERROR; - lenbuf = 0; - } - - p_realloc(&inbuf, lenbuf + sizeof(buf)); - } - - p_delete(&inbuf); - return ret; -} - -/* find message with this UIDL and set refno */ -static int check_uidl (char *line, void *data) -{ - int i, idx; - CONTEXT *ctx = (CONTEXT *)data; - - sscanf (line, "%u %s", &idx, line); - for (i = 0; i < ctx->msgcount; i++) { - if (!m_strcmp(ctx->hdrs[i]->data, line)) { - ctx->hdrs[i]->refno = idx; - break; - } - } - - return 0; -} - -/* reconnect and verify indexes if connection was lost */ -pop_query_status pop_reconnect (CONTEXT * ctx) -{ - pop_query_status ret; - POP_DATA *pop_data = (POP_DATA *) ctx->data; - progress_t bar; - - if (pop_data->status == POP_CONNECTED) - return PQ_OK; - if (pop_data->status == POP_BYE) - return PQ_NOT_CONNECTED; - - for (;;) { - mutt_socket_close (pop_data->conn); - - ret = pop_open_connection (pop_data); - if (ret == PQ_OK) { - int i; - - bar.msg = _("Verifying message indexes..."); - bar.size = 0; - mutt_progress_bar (&bar, 0); - - for (i = 0; i < ctx->msgcount; i++) - ctx->hdrs[i]->refno = -1; - - ret = pop_fetch_data(pop_data, "UIDL\r\n", &bar, check_uidl, ctx); - if (ret == PQ_ERR) { - mutt_error ("%s", pop_data->err_msg); - mutt_sleep (2); - } - } - if (ret == PQ_OK) - return PQ_OK; - - pop_logout (ctx); - - if (ret == PQ_ERR) - return PQ_NOT_CONNECTED; - - if (query_quadoption (OPT_POPRECONNECT, - _("Connection lost. Reconnect to POP server?")) != - M_YES) - return PQ_NOT_CONNECTED; - } -} -- 2.20.1