/* logout from POP server */ void pop_logout (CONTEXT *ctx) { int 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) { strfcpy (buf, "RSET\r\n", sizeof (buf)); ret = pop_query (pop_data, buf, sizeof (buf)); } if (ret != -1) { strfcpy (buf, "QUIT\r\n", sizeof (buf)); pop_query (pop_data, buf, sizeof (buf)); } mutt_clear_error (); } pop_data->status = POP_DISCONNECTED; return; }
/* update POP mailbox - delete messages from server */ int pop_sync_mailbox(CONTEXT *ctx, int *index_hint) { int i, j, ret = 0; char buf[LONG_STRING]; POP_DATA *pop_data = (POP_DATA *)ctx->data; progress_t progress; pop_data->check_time = 0; FOREVER { if (pop_reconnect(ctx) < 0) return -1; mutt_progress_init(&progress, _("Marking messages deleted..."), M_PROGRESS_MSG, WriteInc, ctx->deleted); for (i = 0, j = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) { if (ctx->hdrs[i]->deleted && (ctx->hdrs[i]->refno != -1)) { j++; if (!ctx->quiet) mutt_progress_update(&progress, j, -1); snprintf(buf, sizeof(buf), "DELE %d\r\n", ctx->hdrs[i]->refno); if ((ret = pop_query(pop_data, buf, sizeof(buf))) == 0) { mutt_bcache_del(pop_data->bcache, ctx->hdrs[i]->data); } } } if (ret == 0) { strfcpy(buf, "QUIT\r\n", sizeof(buf)); ret = pop_query(pop_data, buf, sizeof(buf)); } if (ret == 0) { pop_data->clear_cache = 1; pop_clear_cache(pop_data); pop_data->status = POP_DISCONNECTED; return 0; } if (ret == -2) { mutt_error("%s", pop_data->err_msg); mutt_sleep(2); return -1; } } }
/* * 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 - connection lost, * -2 - invalid command or execution error, * -3 - error in funct(*line, *data) */ int pop_fetch_data (POP_DATA *pop_data, char *query, progress_t *progressbar, int (*funct) (char *, void *), void *data) { char buf[LONG_STRING]; char *inbuf; char *p; int ret, chunk = 0; long pos = 0; size_t lenbuf = 0; strfcpy (buf, query, sizeof (buf)); ret = pop_query (pop_data, buf, sizeof (buf)); if (ret < 0) return ret; inbuf = safe_malloc (sizeof (buf)); FOREVER { chunk = mutt_socket_readln_d (buf, sizeof (buf), pop_data->conn, M_SOCK_LOG_HDR); if (chunk < 0) { pop_data->status = POP_DISCONNECTED; ret = -1; break; } p = buf; if (!lenbuf && buf[0] == '.') { if (buf[1] != '.') break; p++; } strfcpy (inbuf + lenbuf, p, sizeof (buf)); pos += chunk; /* cast is safe since we break out of the loop when chunk<=0 */ if ((size_t)chunk >= sizeof (buf)) { lenbuf += strlen (p); } else { if (progressbar) mutt_progress_update (progressbar, pos, -1); if (ret == 0 && funct (inbuf, data) < 0) ret = -3; lenbuf = 0; } safe_realloc (&inbuf, lenbuf + sizeof (buf)); } FREE (&inbuf); return ret; }
/* * Open connection and authenticate * 0 - successful, * -1 - conection lost, * -2 - invalid command or execution error, * -3 - authentication canceled. */ int pop_open_connection (POP_DATA *pop_data) { int ret; unsigned int n, size; char buf[LONG_STRING]; ret = pop_connect (pop_data); if (ret < 0) { mutt_sleep (2); return ret; } ret = pop_capabilities (pop_data, 0); if (ret == -1) goto err_conn; if (ret == -2) { mutt_sleep (2); return -2; } #if defined(USE_SSL) /* Attempt STLS if available and desired. */ if (!pop_data->conn->ssf && (pop_data->cmd_stls || option(OPTSSLFORCETLS))) { if (option(OPTSSLFORCETLS)) pop_data->use_stls = 2; if (pop_data->use_stls == 0) { ret = query_quadoption (OPT_SSLSTARTTLS, _("Secure connection with TLS?")); if (ret == -1) return -2; pop_data->use_stls = 1; if (ret == M_YES) pop_data->use_stls = 2; } if (pop_data->use_stls == 2) { strfcpy (buf, "STLS\r\n", sizeof (buf)); ret = pop_query (pop_data, buf, sizeof (buf)); if (ret == -1) goto err_conn; if (ret != 0) { 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 -2; } else { /* recheck capabilities after STLS completes */ ret = pop_capabilities (pop_data, 1); if (ret == -1) goto err_conn; if (ret == -2) { mutt_sleep (2); return -2; } } } } if (option(OPTSSLFORCETLS) && !pop_data->conn->ssf) { mutt_error _("Encrypted connection unavailable"); mutt_sleep (1); return -2; } #endif ret = pop_authenticate (pop_data); if (ret == -1) goto err_conn; if (ret == -3) mutt_clear_error (); if (ret != 0) return ret; /* recheck capabilities after authentication */ ret = pop_capabilities (pop_data, 2); if (ret == -1) goto err_conn; if (ret == -2) { mutt_sleep (2); return -2; } /* get total size of mailbox */ strfcpy (buf, "STAT\r\n", sizeof (buf)); ret = pop_query (pop_data, buf, sizeof (buf)); if (ret == -1) goto err_conn; if (ret == -2) { mutt_error ("%s", pop_data->err_msg); mutt_sleep (2); return ret; } sscanf (buf, "+OK %u %u", &n, &size); pop_data->size = size; return 0; err_conn: pop_data->status = POP_DISCONNECTED; mutt_error _("Server closed connection!"); mutt_sleep (2); return -1; }
/* Fetch messages and save them in $spoolfile */ void pop_fetch_mail(void) { char buffer[LONG_STRING]; char msgbuf[SHORT_STRING]; char *url, *p; int i, delanswer, last = 0, msgs, bytes, rset = 0, ret; CONNECTION *conn; CONTEXT ctx; MESSAGE *msg = NULL; ACCOUNT acct; POP_DATA *pop_data; if (!PopHost) { mutt_error _("POP host is not defined."); return; } url = p = safe_calloc(strlen(PopHost) + 7, sizeof(char)); if (url_check_scheme(PopHost) == U_UNKNOWN) { strcpy(url, "pop://"); /* __STRCPY_CHECKED__ */ p = strchr(url, '\0'); } strcpy(p, PopHost); /* __STRCPY_CHECKED__ */ ret = pop_parse_path(url, &acct); safe_free(&url); if (ret) { mutt_error(_("%s is an invalid POP path"), PopHost); return; } conn = mutt_conn_find(NULL, &acct); if (!conn) return; pop_data = safe_calloc(1, sizeof(POP_DATA)); pop_data->conn = conn; if (pop_open_connection(pop_data) < 0) { mutt_socket_free(pop_data->conn); safe_free(&pop_data); return; } conn->data = pop_data; mutt_message _("Checking for new messages..."); /* find out how many messages are in the mailbox. */ strfcpy(buffer, "STAT\r\n", sizeof(buffer)); ret = pop_query(pop_data, buffer, sizeof(buffer)); if (ret == -1) goto fail; if (ret == -2) { 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)) { strfcpy(buffer, "LAST\r\n", sizeof(buffer)); ret = pop_query(pop_data, buffer, sizeof(buffer)); if (ret == -1) goto fail; if (ret == 0) 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 == -3) rset = 1; if ((ret == 0) && (mx_commit_message(msg, &ctx) != 0)) { rset = 1; ret = -3; } mx_close_message(&msg); } if ((ret == 0) && (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 == -1) { mx_close_mailbox(&ctx, NULL); goto fail; } if (ret == -2) { mutt_error("%s", pop_data->err_msg); break; } if (ret == -3) { 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 */ strfcpy(buffer, "RSET\r\n", sizeof(buffer)); if (pop_query(pop_data, buffer, sizeof(buffer)) == -1) goto fail; } finish: /* exit gracefully */ strfcpy(buffer, "QUIT\r\n", sizeof(buffer)); if (pop_query(pop_data, buffer, sizeof(buffer)) == -1) goto fail; mutt_socket_close(conn); safe_free(&pop_data); return; fail: mutt_error _("Server closed connection!"); mutt_socket_close(conn); safe_free(&pop_data); }
/* * Read header * returns: * 0 on success * -1 - connection lost, * -2 - invalid command or execution error, * -3 - error writing to tempfile */ static int pop_read_header(POP_DATA *pop_data, HEADER *h) { FILE *f; int ret, index; long length; char buf[LONG_STRING]; char tempfile[_POSIX_PATH_MAX]; mutt_mktemp(tempfile, sizeof(tempfile)); if (!(f = safe_fopen(tempfile, "w+"))) { mutt_perror(tempfile); return -3; } snprintf(buf, sizeof(buf), "LIST %d\r\n", h->refno); ret = pop_query(pop_data, buf, sizeof(buf)); if (ret == 0) { sscanf(buf, "+OK %d %ld", &index, &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 == 2) { if (ret == 0) { pop_data->cmd_top = 1; dprint(1, "pop_read_header: set TOP capability\n"); } if (ret == -2) { pop_data->cmd_top = 0; dprint(1, "pop_read_header: unset TOP capability\n"); snprintf(pop_data->err_msg, sizeof(pop_data->err_msg), _("Command TOP is not supported by server.")); } } } switch (ret) { case 0: { 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 -2: { mutt_error("%s", pop_data->err_msg); break; } case -3: { mutt_error _("Can't write header to temporary file!"); break; } } safe_fclose(&f); unlink(tempfile); return ret; }
/* update POP mailbox - delete messages from server */ int pop_sync_mailbox (CONTEXT *ctx, int *index_hint) { int i, j, ret = 0; char buf[LONG_STRING]; POP_DATA *pop_data = (POP_DATA *)ctx->data; progress_t progress; #ifdef USE_HCACHE header_cache_t *hc = NULL; #endif pop_data->check_time = 0; FOREVER { if (pop_reconnect (ctx) < 0) return -1; mutt_progress_init (&progress, _("Marking messages deleted..."), M_PROGRESS_MSG, WriteInc, ctx->deleted); #if USE_HCACHE hc = pop_hcache_open (pop_data, ctx->path); #endif for (i = 0, j = 0, ret = 0; ret == 0 && i < ctx->msgcount; i++) { if (ctx->hdrs[i]->deleted && ctx->hdrs[i]->refno != -1) { j++; if (!ctx->quiet) mutt_progress_update (&progress, j, -1); snprintf (buf, sizeof (buf), "DELE %d\r\n", ctx->hdrs[i]->refno); if ((ret = pop_query (pop_data, buf, sizeof (buf))) == 0) { mutt_bcache_del (pop_data->bcache, ctx->hdrs[i]->data); #if USE_HCACHE mutt_hcache_delete (hc, ctx->hdrs[i]->data, strlen); #endif } } #if USE_HCACHE if (ctx->hdrs[i]->changed) { mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, M_GENERATE_UIDVALIDITY); } #endif } #if USE_HCACHE mutt_hcache_close (hc); #endif if (ret == 0) { strfcpy (buf, "QUIT\r\n", sizeof (buf)); ret = pop_query (pop_data, buf, sizeof (buf)); } if (ret == 0) { pop_data->clear_cache = 1; pop_clear_cache (pop_data); pop_data->status = POP_DISCONNECTED; return 0; } if (ret == -2) { mutt_error ("%s", pop_data->err_msg); mutt_sleep (2); return -1; } } }