X-Git-Url: http://git.madism.org/?p=apps%2Fmadmutt.git;a=blobdiff_plain;f=pop.c;fp=pop.c;h=0000000000000000000000000000000000000000;hp=2258d0fe5025d1fd28313e69d1aca81798a8a0a3;hb=799c3f5aec72b8230f9c3c284e2b2e9d67ecf366;hpb=e6131516658a6c59a5bf1fe5fb73a9d1714a6f02 diff --git a/pop.c b/pop.c deleted file mode 100644 index 2258d0f..0000000 --- a/pop.c +++ /dev/null @@ -1,1455 +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 -#include - -#include "crypt.h" -#include "mutt.h" -#include "mutt_sasl.h" -#include "pop.h" - -#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 - -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_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_NOT_AVAILABLE = 0, - CMD_AVAILABLE, - CMD_UNKNOWN /* unknown whether it is available or not */ -} cmd_status; - -typedef struct { - 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) -{ - if (!m_strncmp(msg, "-ERR ", 5)) { - const char *s = skipspaces(msg + 5); - if (*s) - msg = s; - } - - m_strcat(pop_data->err_msg, sizeof(pop_data->err_msg), msg); - 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_t *pop_data, char *buf, ssize_t buflen) -{ - if (pop_data->status != POP_CONNECTED) - return PQ_NOT_CONNECTED; - - 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); - - 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; -} -#define pop_query(pd, b, l, fmt, ...) \ - (snprintf(b, l, fmt "\r\n", ##__VA_ARGS__), _pop_query(pd, b, l)) - -/* - * Open connection - * 0 - successful, - * -1 - conection lost, - * -2 - invalid response. -*/ -static pop_query_status pop_connect(pop_data_t * pop_data) -{ - char buf[LONG_STRING]; - const char *p, *q; - - 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; - } - - 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; - } - - p_delete(&pop_data->timestamp); - if ((p = strchr(buf, '<')) && (q = strchr(p, '>'))) { - pop_data->timestamp = p_dupstr(p, q + 1 - p); - } - return PQ_OK; -} - -static void pop_logout (CONTEXT * ctx) -{ - pop_query_status ret = 0; - char buf[LONG_STRING]; - pop_data_t *pop_data = (pop_data_t *) ctx->data; - - if (pop_data->status == POP_CONNECTED) { - mutt_message _("Closing connection to POP server..."); - - if (ctx->readonly) { - ret = pop_query(pop_data, buf, sizeof(buf), "RSET"); - } - - if (ret != PQ_NOT_CONNECTED) { - pop_query(pop_data, buf, sizeof(buf), "QUIT"); - } - - mutt_clear_error (); - } - - pop_data->status = POP_DISCONNECTED; - return; -} - -/* }}} */ -/* Authentication {{{ */ - -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; - - char buf[LONG_STRING], inbuf[LONG_STRING]; - const char *mech, *pc = NULL; - unsigned int len, olen; - unsigned char client_start; - int rc; - - 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) { - 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, sizeof(buf) - 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; - } - 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); - p_delete((char **)&pc); - - /* 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; - } - - mutt_error(_("SASL authentication failed.")); - mutt_sleep(2); - - return POP_A_FAILURE; -} - -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 < 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; - } -} - -static pop_auth_res_t pop_auth_user(pop_data_t *pop_data, const char *method) -{ - char buf[LONG_STRING]; - pop_query_status ret; - - if (pop_data->cmd_user == CMD_NOT_AVAILABLE) - return POP_A_UNAVAIL; - - mutt_message _("Authenticating (USER)..."); - ret = pop_query(pop_data, buf, sizeof(buf), "USER %s", - pop_data->conn->account.user); - - 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 (ret == PQ_OK) { - ret = pop_query(pop_data, buf, sizeof(buf), "PASS %s", - pop_data->conn->account.pass); - } - - 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; - } -} - -typedef struct { - pop_auth_res_t (*do_auth)(pop_data_t *, const char *); - const char *method; -} pop_auth_t; - -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; - } - - if (!m_strisempty(PopAuthenticators)) { - const char *p, *q; - char buf[STRING]; - - for (p = PopAuthenticators; ; p = q) { - while (*p == ':') - p++; - if (!*p) - break; - - 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); - } - } - - if (!attempts) - mutt_error(_("No authenticators available")); - 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_t *pop_data, const char *query, progress_t *bar, - int (*funct)(char *, void *), void *data) -{ - pop_query_status ret; - char buf[LONG_STRING]; - buffer_t inbuf; - ssize_t pos = 0; - - buffer_init(&inbuf); - - m_strcpy(buf, sizeof(buf), query); - ret = _pop_query(pop_data, buf, sizeof(buf)); - if (ret != PQ_OK) - return ret; - - for (;;) { - int dot = 0; - - if (mutt_socket_readln2(&inbuf, pop_data->conn) < 0) { - pop_data->status = POP_DISCONNECTED; - ret = PQ_NOT_CONNECTED; - break; - } - - if (bar) { - mutt_progress_bar(bar, pos += inbuf.len); - } - - if (inbuf.data[0] == '.') { - if (inbuf.data[1] != '.') - break; - dot = 1; - } - - if (funct(inbuf.data + dot, data) < 0) { - buffer_wipe(&inbuf); - ret = PFD_FUNCT_ERROR; - break; - } - - buffer_reset(&inbuf); - } - - buffer_wipe(&inbuf); - return ret; -} - -static int fetch_capa (char *line, void *data) -{ - pop_data_t *pop_data = (pop_data_t *) 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, "UIDL", 4)) - pop_data->cmd_uidl = CMD_AVAILABLE; - - else if (!ascii_strncasecmp (line, "TOP", 3)) - pop_data->cmd_top = CMD_AVAILABLE; - - return 0; -} - -static int fetch_auth (char *line, void *data) -{ - pop_data_t *pop_data = (pop_data_t *) 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_t * pop_data, int mode) -{ - /* 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_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; - p_delete(&pop_data->auth_list); - } - - /* Execute CAPA command */ - if (mode == 0 || pop_data->cmd_capa != CMD_NOT_AVAILABLE) { - switch (pop_fetch_data(pop_data, "CAPA\r\n", 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_uidl = CMD_UNKNOWN; - pop_data->cmd_top = CMD_UNKNOWN; - - 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) { - const 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; -} - -/* 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->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); - 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) { - ret = pop_query(pop_data, buf, sizeof(buf), "STLS"); - 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 */ - ret = pop_query(pop_data, buf, sizeof(buf), "STAT"); - 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; -} - -/* 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_t *pop_data = (pop_data_t *) 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_t * pop_data, HEADER * h) -{ - FILE *f; - int idx; - pop_query_status ret; - long length; - char buf[LONG_STRING]; - - f = tmpfile(); - if (!f) { - mutt_error(_("Could not create temporary file")); - return PFD_FUNCT_ERROR; - } - - 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); - - 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); - return ret; -} - -/* parse UIDL */ -static int fetch_uidl (char *line, void *data) -{ - int i, idx; - CONTEXT *ctx = (CONTEXT *) data; - pop_data_t *pop_data = (pop_data_t *) 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_t *pop_data = (pop_data_t *) 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; -} - -/* 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_t *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_t, 1); - pop_data->conn = conn; - ctx->data = pop_data; - - if (pop_open_connection(pop_data) != PQ_OK) - return -1; - - conn->data = pop_data; - - for (;;) { - if (pop_reconnect (ctx) != PQ_OK) - return -1; - - mutt_message _("Fetching list of messages..."); - ctx->size = pop_data->size; - ret = pop_fetch_headers (ctx); - - if (ret >= 0) - return 0; - - if (ret < -1) { - mutt_sleep (2); - return -1; - } - } -} - -static int pop_acl_check(CONTEXT *ctx, int bit) -{ - 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; - } -} - -static int pop_check_mailbox(CONTEXT * ctx, int *index_hint, int unused) -{ - int ret; - pop_data_t *pop_data = (pop_data_t *) 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; - - 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; -} - -static void pop_close_mailbox (CONTEXT * ctx) -{ - pop_data_t *pop_data = (pop_data_t *) 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; -} - -static int pop_sync_mailbox(CONTEXT * ctx, int unused, int *index_hint) -{ - int i; - pop_query_status ret; - char buf[LONG_STRING]; - pop_data_t *pop_data = (pop_data_t *) 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) { - ret = pop_query(pop_data, buf, sizeof(buf), "DELE %d", - ctx->hdrs[i]->refno); - } - } - - if (ret == PQ_OK) { - ret = pop_query(pop_data, buf, sizeof(buf), "QUIT"); - } - - 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; - } - } -} - -/* }}} */ - -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, -}; - -/* public API {{{ */ - -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_t *pop_data; - ssize_t plen; - - if (m_strisempty(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) { - snprintf(p, plen, "pop://%s", PopHost); - } else { - 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_t, 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. */ - ret = pop_query(pop_data, buffer, sizeof(buffer), "STAT"); - 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)) { - ret = pop_query(pop_data, buffer, sizeof(buffer), "LAST"); - 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) { - ret = pop_query(pop_data, buffer, sizeof(buffer), "DELE %d", i); - } - - 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 */ - if (pop_query(pop_data, buffer, sizeof(buffer), "RSET") == - PQ_NOT_CONNECTED) - goto fail; - } - -finish: - if (pop_query(pop_data, buffer, sizeof(buffer), "QUIT") == 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); -} - -/* 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]; - - 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; -} - -/* }}} */