X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=blobdiff_plain;f=pop.c;h=a066f6e5dd05562de916dd3f760a506e089e4613;hp=bc857a367b271f90d9def2ebe0fa8ea0024e8ea6;hb=981e10e224fde4de5d40adcee1deda89df2715ca;hpb=9cb385d5621b95188fce2e0a7345004151486007 diff --git a/pop.c b/pop.c index bc857a3..a066f6e 100644 --- a/pop.c +++ b/pop.c @@ -13,35 +13,32 @@ #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 - +#define POP_PORT 110 +#define POP_SSL_PORT 995 +#define POP_CACHE_LEN 10 /* maximal length of the server response (RFC1939) */ -#define POP_CMD_RESPONSE 512 +#define POP_CMD_RESPONSE 512 enum { - /* Status */ - POP_NONE = 0, - POP_CONNECTED, - POP_DISCONNECTED, - POP_BYE + /* 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_A_SUCCESS = 0, + POP_A_SOCKET, + POP_A_FAILURE, + POP_A_UNAVAIL } pop_auth_res_t; typedef struct { @@ -49,57 +46,55 @@ typedef struct { 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 +typedef enum { + /* pop_fetch_data uses pop_query_status and this return value */ + PFD_FUNCT_ERROR = -3, + 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 */ +typedef enum { + 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) + CONNECTION *conn; + + unsigned status : 2; + unsigned capabilities : 1; + unsigned use_stls : 2; + cmd_status cmd_capa : 2; /* optional command CAPA */ + cmd_status cmd_stls : 2; /* optional command STLS */ + cmd_status cmd_uidl : 2; /* optional command UIDL */ + cmd_status cmd_top : 2; /* optional command TOP */ + cmd_status cmd_user : 2; /* optional command USER */ + unsigned resp_codes : 1; /* server supports extended response codes */ + unsigned expire : 1; /* expire is greater than 0 */ + unsigned clear_cache : 1; + + ssize_t size; + time_t check_time; + char *auth_list; /* list of auth mechanisms */ + char *timestamp; + char err_msg[POP_CMD_RESPONSE]; + POP_CACHE cache[POP_CACHE_LEN]; +} pop_data_t; + +/* pop low level functions {{{ */ + +static void pop_error(pop_data_t *pop_data, const 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; - } + if (!m_strncmp(msg, "-ERR ", 5)) { + const char *s = skipspaces(msg + 5); + if (*s) + msg = s; + } - m_strcpy(t, sizeof(pop_data->err_msg) - strlen(pop_data->err_msg), c); - m_strrtrim(pop_data->err_msg); + m_strcat(pop_data->err_msg, sizeof(pop_data->err_msg), msg); + m_strrtrim(pop_data->err_msg); } /* @@ -108,498 +103,406 @@ static void pop_error (POP_DATA * pop_data, char *msg) * -1 - conection lost, * -2 - invalid command or execution error. */ -static pop_query_status pop_query (POP_DATA * pop_data, char *buf, size_t buflen) +static pop_query_status _pop_query(pop_data_t *pop_data, char *buf, ssize_t buflen) { - char *c; - - if (pop_data->status != POP_CONNECTED) - return PQ_NOT_CONNECTED; + if (pop_data->status != POP_CONNECTED) + return PQ_NOT_CONNECTED; - mutt_socket_write(pop_data->conn, buf); + mutt_socket_write(pop_data->conn, buf); + snprintf(pop_data->err_msg, sizeof(pop_data->err_msg), "%.*s: ", + (int)(strpbrk(buf, " \r\n") - buf), 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; + 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; + pop_error(pop_data, buf); + return PQ_ERR; } +#define pop_query(pd, b, l, fmt, ...) \ + (snprintf(b, l, fmt "\r\n", ##__VA_ARGS__), _pop_query(pd, b, l)) /* - * This function calls funct(*line, *data) for each received line, - * funct(NULL, *data) if rewind(*data) needs, exits when fail or done. - * Returned codes: + * Open connection * 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) + * -2 - invalid response. +*/ +static pop_query_status pop_connect(pop_data_t * pop_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; + char buf[LONG_STRING]; + const char *p, *q; - 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; + 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); + pop_data->status = POP_NONE; + return PQ_NOT_CONNECTED; } - p = buf; - if (!lenbuf && buf[0] == '.') { - if (buf[1] != '.') - break; - p++; + pop_data->status = POP_CONNECTED; + if (m_strncmp(buf, "+OK", 3)) { + pop_data->err_msg[0] = '\0'; + pop_error(pop_data, buf); + mutt_error("%s", pop_data->err_msg); + return PQ_ERR; } - 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_delete(&pop_data->timestamp); + if ((p = strchr(buf, '<')) && (q = strchr(p, '>'))) { + pop_data->timestamp = p_dupstr(p, q + 1 - p); } - - p_realloc(&inbuf, lenbuf + sizeof(buf)); - } - - p_delete(&inbuf); - return ret; + return PQ_OK; } -/* SASL authenticator */ -static pop_auth_res_t pop_auth_sasl (POP_DATA * pop_data, const char *method) +static void pop_logout (CONTEXT * ctx) { - sasl_conn_t *saslconn; - sasl_interact_t *interaction = NULL; - int rc; + pop_query_status ret = 0; char buf[LONG_STRING]; - char inbuf[LONG_STRING]; - const char *mech; + pop_data_t *pop_data = (pop_data_t *) ctx->data; - const char *pc = NULL; - unsigned int len, olen; - unsigned char client_start; + if (pop_data->status == POP_CONNECTED) { + mutt_message _("Closing connection to POP server..."); - if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) { - return POP_A_FAILURE; - } + if (ctx->readonly) { + ret = pop_query(pop_data, buf, sizeof(buf), "RSET"); + } - if (!method) - method = pop_data->auth_list; + if (ret != PQ_NOT_CONNECTED) { + pop_query(pop_data, buf, sizeof(buf), "QUIT"); + } - for (;;) { - rc = sasl_client_start (saslconn, method, &interaction, &pc, &olen, - &mech); - if (rc != SASL_INTERACT) - break; - mutt_sasl_interact (interaction); + mutt_clear_error (); } - if (rc != SASL_OK && rc != SASL_CONTINUE) { - /* SASL doesn't support suggested mechanisms, so fall back */ - return POP_A_UNAVAIL; - } + pop_data->status = POP_DISCONNECTED; + return; +} - client_start = (olen > 0); +/* }}} */ +/* Authentication {{{ */ - mutt_message _("Authenticating (SASL)..."); +static pop_auth_res_t pop_auth_sasl(pop_data_t *pop_data, const char *method) +{ + sasl_conn_t *saslconn; + sasl_interact_t *interaction = NULL; - snprintf (buf, sizeof (buf), "AUTH %s", mech); - olen = strlen (buf); + char buf[LONG_STRING], inbuf[LONG_STRING]; + const char *mech, *pc = NULL; + unsigned int len, olen; + unsigned char client_start; + int rc; - /* 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 (mutt_sasl_client_new(pop_data->conn, &saslconn) < 0) { + return POP_A_FAILURE; } - if (rc != SASL_CONTINUE) - break; + if (!method) + method = pop_data->auth_list; - if (!m_strncmp(inbuf, "+ ", 2) - && sasl_decode64 (inbuf, strlen (inbuf), buf, LONG_STRING - 1, - &len) != SASL_OK) - { - goto bail; + for (;;) { + rc = sasl_client_start(saslconn, method, &interaction, &pc, &olen, + &mech); + if (rc != SASL_INTERACT) + break; + mutt_sasl_interact(interaction); } - 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; + if (rc != SASL_OK && rc != SASL_CONTINUE) { + return POP_A_UNAVAIL; + } - /* send out response, or line break if none needed */ - if (pc) { - if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK) { - goto bail; - } + 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; + } - /* sasl_client_st(art|ep) allocate pc with malloc, expect me to - * free it */ - p_delete((char **)&pc); - } - } + if (rc != SASL_CONTINUE) + break; - if (rc != SASL_OK) - goto bail; + if (!m_strncmp(inbuf, "+ ", 2) + && sasl_decode64(inbuf, strlen(inbuf), + buf, sizeof(buf) - 1, &len) != SASL_OK) + { + goto bail; + } - if (!m_strncmp(inbuf, "+OK", 3)) { - mutt_sasl_setup_conn (pop_data->conn, saslconn); - return POP_A_SUCCESS; - } + 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; + } -bail: - sasl_dispose (&saslconn); + if (rc != SASL_CONTINUE && (olen == 0 || rc != SASL_OK)) + break; - /* 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; - } + /* send out response, or line break if none needed */ + if (pc) { + if (sasl_encode64(pc, olen, buf, sizeof(buf), &olen) != SASL_OK) { + goto bail; + } + p_delete((char **)&pc); + } + } - mutt_error _("SASL authentication failed."); + if (rc != SASL_OK) + goto bail; - mutt_sleep (2); + if (!m_strncmp(inbuf, "+OK", 3)) { + mutt_sasl_setup_conn(pop_data->conn, saslconn); + return POP_A_SUCCESS; + } - return POP_A_FAILURE; -} + bail: + sasl_dispose(&saslconn); + p_delete((char **)&pc); -/* Get the server timestamp for APOP authentication */ -static void pop_apop_timestamp (POP_DATA * pop_data, char *buf) -{ - char *p1, *p2; + /* terminate SASL session if the last responce is not +OK nor -ERR */ + if (!m_strncmp(inbuf, "+ ", 2)) { + if (pop_query(pop_data, buf, sizeof(buf), "*") == PQ_NOT_CONNECTED) + return POP_A_SOCKET; + } - p_delete(&pop_data->timestamp); + mutt_error(_("SASL authentication failed.")); + mutt_sleep(2); - if ((p1 = strchr (buf, '<')) && (p2 = strchr (p1, '>'))) { - p2[1] = '\0'; - pop_data->timestamp = m_strdup(p1); - } + return POP_A_FAILURE; } -/* APOP authenticator */ -static pop_auth_res_t pop_auth_apop (POP_DATA * pop_data, - const char *method __attribute__ ((unused))) +static pop_auth_res_t pop_auth_apop(pop_data_t *pop_data, const char *method) { - 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; + 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 < countof(digest); i++) + sprintf(hash + 2 * i, "%02x", digest[i]); + + switch (pop_query(pop_data, buf, sizeof(buf), "APOP %s %s", + pop_data->conn->account.user, hash)) + { + case PQ_OK: + return POP_A_SUCCESS; + case PQ_NOT_CONNECTED: + return POP_A_SOCKET; + default: + 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))) +static pop_auth_res_t pop_auth_user(pop_data_t *pop_data, const char *method) { - char buf[LONG_STRING]; - pop_query_status ret; + char buf[LONG_STRING]; + pop_query_status ret; - if (pop_data->cmd_user == CMD_NOT_AVAILABLE) - return POP_A_UNAVAIL; + if (pop_data->cmd_user == CMD_NOT_AVAILABLE) + return POP_A_UNAVAIL; - mutt_message _("Logging in..."); + mutt_message _("Authenticating (USER)..."); + ret = pop_query(pop_data, buf, sizeof(buf), "USER %s", + pop_data->conn->account.user); - 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; + } - if (pop_data->cmd_user == CMD_UNKNOWN) { if (ret == PQ_OK) { - pop_data->cmd_user = CMD_AVAILABLE; + ret = pop_query(pop_data, buf, sizeof(buf), "PASS %s", + pop_data->conn->account.pass); } - 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.")); + switch (ret) { + case PQ_OK: + return POP_A_SUCCESS; + case PQ_NOT_CONNECTED: + return POP_A_SOCKET; + default: + mutt_error("%s %s", _("USER authentication failed."), pop_data->err_msg); + mutt_sleep(2); + return POP_A_FAILURE; } - } - - 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]; +typedef struct { + pop_auth_res_t (*do_auth)(pop_data_t *, const char *); + const char *method; +} pop_auth_t; - 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; - } +static pop_query_status pop_authenticate (pop_data_t * 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 *auth; + int attempts = 0; + + if (mutt_account_getuser(act) || !act->user[0] + || mutt_account_getpass(act) || !act->pass[0]) + { + return PFD_FUNCT_ERROR; + } - pop_data->status = POP_CONNECTED; + if (!m_strisempty(PopAuthenticators)) { + const char *p, *q; + char buf[STRING]; - 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; - } + for (p = PopAuthenticators; ; p = q) { + while (*p == ':') + p++; + if (!*p) + break; - pop_apop_timestamp (pop_data, buf); + q = strchrnul(p, ':'); + m_strncpy(buf, sizeof(buf), p, q - p); + + for (auth = pop_authenticators; auth->do_auth; auth++) { + if (auth->method && ascii_strcasecmp(auth->method, buf)) + continue; + + switch (auth->do_auth(pop_data, buf)) { + case POP_A_SUCCESS: + return PQ_OK; + case POP_A_SOCKET: + return PQ_NOT_CONNECTED; + case POP_A_UNAVAIL: + break; + case POP_A_FAILURE: + attempts++; + break; + } + mutt_socket_close(pop_data->conn); + } + } + } else { + for (auth = pop_authenticators; auth->do_auth; auth++) { + switch (auth->do_auth(pop_data, auth->method)) { + case POP_A_SUCCESS: + return PQ_OK; + case POP_A_SOCKET: + return PQ_NOT_CONNECTED; + case POP_A_UNAVAIL: + break; + case POP_A_FAILURE: + attempts++; + break; + } + mutt_socket_close(pop_data->conn); + } + } - return PQ_OK; + if (!attempts) + mutt_error(_("No authenticators available")); + return PQ_ERR; } -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 + * 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 - login failed, - * -3 - authentication canceled. -*/ -static pop_query_status pop_authenticate (POP_DATA * pop_data) + * -2 - invalid command or execution error, + * -3 - error in funct(*line, *data) + */ +static pop_query_status +pop_fetch_data(pop_data_t *pop_data, const char *query, progress_t *bar, + int (*funct)(char *, void *), void *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; + pop_query_status ret; + char buf[LONG_STRING]; + buffer_t inbuf; + ssize_t pos = 0; - 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; - } + buffer_init(&inbuf); - 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++; - } + m_strcpy(buf, sizeof(buf), query); + ret = _pop_query(pop_data, buf, sizeof(buf)); + if (ret != PQ_OK) + return ret; - method = comma; - } + for (;;) { + int dot = 0; - 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); + if (mutt_socket_readln2(&inbuf, pop_data->conn) < 0) { + pop_data->status = POP_DISCONNECTED; + ret = PQ_NOT_CONNECTED; 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; + if (bar) { + mutt_progress_bar(bar, pos += inbuf.len); + } - /* Defaults */ - act->flags = 0; - act->port = POP_PORT; - act->type = M_ACCT_TYPE_POP; + if (inbuf.data[0] == '.') { + if (inbuf.data[1] != '.') + break; + dot = 1; + } - c = m_strdup(path); - url_parse_ciss (&url, c); + if (funct(inbuf.data + dot, data) < 0) { + buffer_wipe(&inbuf); + ret = PFD_FUNCT_ERROR; + break; + } - if (url.scheme == U_POP || url.scheme == U_POPS) { - if (url.scheme == U_POPS) { - act->flags |= M_ACCT_SSL; - act->port = POP_SSL_PORT; + buffer_reset(&inbuf); } - if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0) - ret = 0; - } - - p_delete(&c); - return ret; + buffer_wipe(&inbuf); + return ret; } -/* Parse CAPA output */ static int fetch_capa (char *line, void *data) { - POP_DATA *pop_data = (POP_DATA *) data; + pop_data_t *pop_data = (pop_data_t *) data; char *c; if (!ascii_strncasecmp (line, "SASL", 4)) { @@ -611,9 +514,6 @@ static int fetch_capa (char *line, void *data) 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; @@ -623,10 +523,9 @@ static int fetch_capa (char *line, void *data) return 0; } -/* Fetch list of the authentication mechanisms */ static int fetch_auth (char *line, void *data) { - POP_DATA *pop_data = (POP_DATA *) data; + pop_data_t *pop_data = (pop_data_t *) data; ssize_t auth_list_len; if (!pop_data->auth_list) { @@ -648,42 +547,34 @@ static int fetch_auth (char *line, void *data) * -1 - conection lost, * -2 - execution error. */ -static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode) +static pop_query_status pop_capabilities(pop_data_t * 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->cmd_capa = CMD_NOT_AVAILABLE; + pop_data->cmd_stls = CMD_NOT_AVAILABLE; + pop_data->cmd_uidl = CMD_NOT_AVAILABLE; + pop_data->cmd_top = CMD_NOT_AVAILABLE; + pop_data->cmd_user = CMD_NOT_AVAILABLE; pop_data->resp_codes = 0; - pop_data->expire = 1; - pop_data->login_delay = 0; + pop_data->expire = 1; 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)) { + switch (pop_fetch_data(pop_data, "CAPA\r\n", NULL, fetch_capa, pop_data)) { case PQ_OK: - { - pop_data->cmd_capa = CMD_AVAILABLE; - break; - } + pop_data->cmd_capa = CMD_AVAILABLE; + break; case PFD_FUNCT_ERROR: case PQ_ERR: - { - pop_data->cmd_capa = CMD_NOT_AVAILABLE; - break; - } + pop_data->cmd_capa = CMD_NOT_AVAILABLE; + break; case PQ_NOT_CONNECTED: return PQ_NOT_CONNECTED; } @@ -691,18 +582,17 @@ static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode) /* 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) + if (pop_fetch_data(pop_data, "AUTH\r\n", NULL, fetch_auth, pop_data) == + PQ_NOT_CONNECTED) return PQ_NOT_CONNECTED; } /* Check capabilities */ if (mode == 2) { - char *msg = NULL; + const char *msg = NULL; if (!pop_data->expire) msg = _("Unable to leave messages on server."); @@ -720,23 +610,52 @@ static pop_query_status pop_capabilities (POP_DATA * pop_data, int mode) 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) +/* given an POP mailbox name, return host, port, username and password */ +static int pop_parse_path (const char *path, ACCOUNT * act) { - pop_query_status ret; - unsigned int n, size; - char buf[LONG_STRING]; + ciss_url_t url; + char *c; + int ret = -1; - ret = pop_connect (pop_data); - if (ret != PQ_OK) { - mutt_sleep (2); - return ret; + /* 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->has_ssl = 1; + act->port = POP_SSL_PORT; + } + + if ((!url.path || !*url.path) && mutt_account_fromurl (act, &url) == 0) + ret = 0; + } + + p_delete(&c); + return ret; +} + +/* + * 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_t * pop_data) +{ + pop_query_status ret; + 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); @@ -757,8 +676,7 @@ static pop_query_status pop_open_connection (POP_DATA * pop_data) 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)); + ret = pop_query(pop_data, buf, sizeof(buf), "STLS"); if (ret == PQ_NOT_CONNECTED) goto err_conn; if (ret != PQ_OK) { @@ -808,8 +726,7 @@ static pop_query_status pop_open_connection (POP_DATA * pop_data) } /* get total size of mailbox */ - m_strcpy(buf, sizeof(buf), "STAT\r\n"); - ret = pop_query (pop_data, buf, sizeof (buf)); + ret = pop_query(pop_data, buf, sizeof(buf), "STAT"); if (ret == PQ_NOT_CONNECTED) goto err_conn; if (ret == PQ_ERR) { @@ -830,33 +747,6 @@ err_conn: 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) { @@ -878,7 +768,7 @@ static int check_uidl (char *line, void *data) static pop_query_status pop_reconnect (CONTEXT * ctx) { pop_query_status ret; - POP_DATA *pop_data = (POP_DATA *) ctx->data; + pop_data_t *pop_data = (pop_data_t *) ctx->data; progress_t bar; if (pop_data->status == POP_CONNECTED) @@ -889,7 +779,7 @@ static pop_query_status pop_reconnect (CONTEXT * ctx) for (;;) { mutt_socket_close (pop_data->conn); - ret = pop_open_connection (pop_data); + ret = pop_open_connection(pop_data); if (ret == PQ_OK) { int i; @@ -920,6 +810,7 @@ static pop_query_status pop_reconnect (CONTEXT * ctx) return PQ_NOT_CONNECTED; } } + /* write line to file */ static int fetch_message (char *line, void *file) { @@ -940,28 +831,26 @@ static int fetch_message (char *line, void *file) * -2 - invalid command or execution error, * -3 - error writing to tempfile */ -static pop_query_status pop_read_header (POP_DATA * pop_data, HEADER * h) +static pop_query_status pop_read_header (pop_data_t * 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); + f = tmpfile(); 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)); + ret = pop_query(pop_data, buf, sizeof(buf), "LIST %d", h->refno); 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); + ret = pop_fetch_data(pop_data, buf, NULL, fetch_message, f); if (pop_data->cmd_top == CMD_UNKNOWN) { if (ret == PQ_OK) { @@ -1008,7 +897,6 @@ static pop_query_status pop_read_header (POP_DATA * pop_data, HEADER * h) } m_fclose(&f); - unlink (tempfile); return ret; } @@ -1017,7 +905,7 @@ static int fetch_uidl (char *line, void *data) { int i, idx; CONTEXT *ctx = (CONTEXT *) data; - POP_DATA *pop_data = (POP_DATA *) ctx->data; + pop_data_t *pop_data = (pop_data_t *) ctx->data; sscanf (line, "%d %s", &idx, line); for (i = 0; i < ctx->msgcount; i++) @@ -1053,7 +941,7 @@ 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; + pop_data_t *pop_data = (pop_data_t *) ctx->data; time (&pop_data->check_time); pop_data->clear_cache = 0; @@ -1062,7 +950,7 @@ static int pop_fetch_headers (CONTEXT * ctx) ctx->hdrs[i]->refno = -1; old_count = ctx->msgcount; - ret = pop_fetch_data (pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx); + ret = pop_fetch_data(pop_data, "UIDL\r\n", NULL, fetch_uidl, ctx); new_count = ctx->msgcount; ctx->msgcount = old_count; @@ -1109,14 +997,37 @@ static int pop_fetch_headers (CONTEXT * ctx) return (new_count - old_count); } -/* open POP mailbox - fetch only headers */ +/* delete all cached messages */ +static void pop_clear_cache (pop_data_t * 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); + } + } +} + +/* pop_mx functions {{{ */ + +static int pop_is_magic(const char* path, struct stat* st) +{ + url_scheme_t s = url_check_scheme(NONULL(path)); + return s == U_POP || s == U_POPS ? M_POP : -1; +} + static int pop_open_mailbox (CONTEXT * ctx) { int ret; char buf[LONG_STRING]; CONNECTION *conn; ACCOUNT act; - POP_DATA *pop_data; + pop_data_t *pop_data; ciss_url_t url; if (pop_parse_path (ctx->path, &act)) { @@ -1135,11 +1046,11 @@ static int pop_open_mailbox (CONTEXT * ctx) p_delete(&ctx->path); ctx->path = m_strdup(buf); - pop_data = p_new(POP_DATA, 1); + pop_data = p_new(pop_data_t, 1); pop_data->conn = conn; ctx->data = pop_data; - if (pop_open_connection (pop_data) < 0) + if (pop_open_connection(pop_data) != PQ_OK) return -1; conn->data = pop_data; @@ -1148,10 +1059,8 @@ static int pop_open_mailbox (CONTEXT * ctx) if (pop_reconnect (ctx) != PQ_OK) return -1; - ctx->size = pop_data->size; - mutt_message _("Fetching list of messages..."); - + ctx->size = pop_data->size; ret = pop_fetch_headers (ctx); if (ret >= 0) @@ -1164,26 +1073,51 @@ static int pop_open_mailbox (CONTEXT * ctx) } } -/* delete all cached messages */ -static void pop_clear_cache (POP_DATA * pop_data) +static int pop_acl_check(CONTEXT *ctx, int bit) { - int i; + switch (bit) { + case ACL_DELETE: /* (un)deletion */ + case ACL_SEEN: /* mark as read */ + return 1; + case ACL_INSERT: /* editing messages */ + case ACL_WRITE: /* change importance */ + default: + return 0; + } +} - if (!pop_data->clear_cache) - return; +static int pop_check_mailbox(CONTEXT * ctx, int *index_hint, int unused) +{ + int ret; + pop_data_t *pop_data = (pop_data_t *) ctx->data; - 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); - } - } + 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; + + mutt_message _("Checking for new messages..."); + ctx->size = pop_data->size; + ret = pop_fetch_headers (ctx); + pop_clear_cache (pop_data); + + if (ret < 0) + return -1; + + if (ret > 0) + return M_NEW_MAIL; + + return 0; } -/* close POP mailbox */ static void pop_close_mailbox (CONTEXT * ctx) { - POP_DATA *pop_data = (POP_DATA *) ctx->data; + pop_data_t *pop_data = (pop_data_t *) ctx->data; if (!pop_data) return; @@ -1204,122 +1138,12 @@ static void pop_close_mailbox (CONTEXT * ctx) 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))) +static int pop_sync_mailbox(CONTEXT * ctx, int unused, int *index_hint) { int i; pop_query_status ret; char buf[LONG_STRING]; - POP_DATA *pop_data = (POP_DATA *) ctx->data; + pop_data_t *pop_data = (pop_data_t *) ctx->data; pop_data->check_time = 0; @@ -1331,14 +1155,13 @@ pop_sync_mailbox (CONTEXT * ctx, int unused __attribute__ ((unused)), 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)); + ret = pop_query(pop_data, buf, sizeof(buf), "DELE %d", + ctx->hdrs[i]->refno); } } if (ret == PQ_OK) { - m_strcpy(buf, sizeof(buf), "QUIT\r\n"); - ret = pop_query (pop_data, buf, sizeof (buf)); + ret = pop_query(pop_data, buf, sizeof(buf), "QUIT"); } if (ret == PQ_OK) { @@ -1356,41 +1179,25 @@ pop_sync_mailbox (CONTEXT * ctx, int unused __attribute__ ((unused)), } } -/* 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; +mx_t const pop_mx = { + M_POP, + 0, + pop_is_magic, + NULL, + NULL, + pop_open_mailbox, + NULL, + pop_acl_check, + pop_check_mailbox, + pop_close_mailbox, + pop_sync_mailbox, + NULL, +}; - if (ret > 0) - return M_NEW_MAIL; +/* public API {{{ */ - return 0; -} - -/* Fetch messages and save them in $spoolfile */ void pop_fetch_mail (void) { char buffer[LONG_STRING]; @@ -1402,22 +1209,21 @@ void pop_fetch_mail (void) CONTEXT ctx; MESSAGE *msg = NULL; ACCOUNT act; - POP_DATA *pop_data; + pop_data_t *pop_data; ssize_t plen; - if (!PopHost) { + if (m_strisempty(PopHost)) { mutt_error _("POP host is not defined."); - return; } plen = m_strlen(PopHost) + 7; - url = p = p_new(char, plen); + url = p = p_new(char, plen); if (url_check_scheme (PopHost) == U_UNKNOWN) { - plen -= m_strcpy(url, plen, "pop://"); - p += plen; + snprintf(p, plen, "pop://%s", PopHost); + } else { + m_strcpy(p, plen, PopHost); } - m_strcpy(p, plen, PopHost); ret = pop_parse_path (url, &act); p_delete(&url); @@ -1430,7 +1236,7 @@ void pop_fetch_mail (void) if (!conn) return; - pop_data = p_new(POP_DATA, 1); + pop_data = p_new(pop_data_t, 1); pop_data->conn = conn; if (pop_open_connection (pop_data) < 0) { @@ -1444,8 +1250,7 @@ void pop_fetch_mail (void) 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)); + ret = pop_query(pop_data, buffer, sizeof(buffer), "STAT"); if (ret == PQ_NOT_CONNECTED) goto fail; if (ret == PQ_ERR) { @@ -1457,8 +1262,7 @@ void pop_fetch_mail (void) /* 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)); + ret = pop_query(pop_data, buffer, sizeof(buffer), "LAST"); if (ret == PQ_NOT_CONNECTED) goto fail; if (ret == PQ_OK) @@ -1486,7 +1290,7 @@ void pop_fetch_mail (void) ret = -3; else { snprintf (buffer, sizeof (buffer), "RETR %d\r\n", i); - ret = pop_fetch_data (pop_data, buffer, NULL, fetch_message, msg->fp); + ret = pop_fetch_data(pop_data, buffer, NULL, fetch_message, msg->fp); if (ret == PFD_FUNCT_ERROR) rset = 1; @@ -1499,9 +1303,7 @@ void pop_fetch_mail (void) } 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)); + ret = pop_query(pop_data, buffer, sizeof(buffer), "DELE %d", i); } if (ret == PQ_NOT_CONNECTED) { @@ -1526,15 +1328,13 @@ void pop_fetch_mail (void) 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) + if (pop_query(pop_data, buffer, sizeof(buffer), "RSET") == + 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) + if (pop_query(pop_data, buffer, sizeof(buffer), "QUIT") == PQ_NOT_CONNECTED) goto fail; mutt_socket_close (conn); p_delete(&pop_data); @@ -1546,35 +1346,110 @@ fail: 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); -} +/* 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_t *pop_data = (pop_data_t *) 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]; -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); + 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(mod_core.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; } -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, -}; +/* }}} */