/* * 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; }
/* 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; } } }
/** * mutt_progress_init - Set up a progress bar * @param progress Progress bar * @param msg Message to display * @param flags Flags, e.g. #MUTT_PROGRESS_SIZE * @param inc Increments to display (0 disables updates) * @param size Total size of expected file / traffic */ void mutt_progress_init(struct Progress *progress, const char *msg, unsigned short flags, unsigned short inc, size_t size) { struct timeval tv = { 0, 0 }; if (!progress) return; if (OptNoCurses) return; memset(progress, 0, sizeof(struct Progress)); progress->inc = inc; progress->flags = flags; progress->msg = msg; progress->size = size; if (progress->size != 0) { if (progress->flags & MUTT_PROGRESS_SIZE) { mutt_str_pretty_size(progress->sizestr, sizeof(progress->sizestr), progress->size); } else snprintf(progress->sizestr, sizeof(progress->sizestr), "%zu", progress->size); } if (inc == 0) { if (size != 0) mutt_message("%s (%s)", msg, progress->sizestr); else mutt_message(msg); return; } if (gettimeofday(&tv, NULL) < 0) mutt_debug(LL_DEBUG1, "gettimeofday failed: %d\n", errno); /* if timestamp is 0 no time-based suppression is done */ if (C_TimeInc != 0) { progress->timestamp = ((unsigned int) tv.tv_sec * 1000) + (unsigned int) (tv.tv_usec / 1000); } mutt_progress_update(progress, 0, 0); }
static void nm_progress_update(CONTEXT *ctx, notmuch_query_t *q) { struct nm_ctxdata *data = get_ctxdata(ctx); if (ctx->quiet || data->noprogress) return; if (!data->progress_ready && q) { static char msg[STRING]; snprintf(msg, sizeof(msg), _("Reading messages...")); mutt_progress_init(&data->progress, msg, M_PROGRESS_MSG, ReadInc, notmuch_query_count_messages(q)); data->progress_ready = 1; } if (data->progress_ready) mutt_progress_update(&data->progress, ctx->msgcount + data->ignmsgcount - data->oldmsgcount, -1); }
int imap_append_message (CONTEXT *ctx, MESSAGE *msg) { IMAP_DATA* idata; FILE *fp; char buf[LONG_STRING]; char mbox[LONG_STRING]; char mailbox[LONG_STRING]; char internaldate[IMAP_DATELEN]; char imap_flags[SHORT_STRING]; size_t len; progress_t progressbar; size_t sent; int c, last; IMAP_MBOX mx; int rc; idata = (IMAP_DATA*) ctx->data; if (imap_parse_path (ctx->path, &mx)) return -1; imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox)); if (!*mailbox) strfcpy (mailbox, "INBOX", sizeof (mailbox)); if ((fp = fopen (msg->path, "r")) == NULL) { mutt_perror (msg->path); goto fail; } /* currently we set the \Seen flag on all messages, but probably we * should scan the message Status header for flag info. Since we're * already rereading the whole file for length it isn't any more * expensive (it'd be nice if we had the file size passed in already * by the code that writes the file, but that's a lot of changes. * Ideally we'd have a HEADER structure with flag info here... */ for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c) { if(c == '\n' && last != '\r') len++; len++; } rewind (fp); mutt_progress_init (&progressbar, _("Uploading message..."), MUTT_PROGRESS_SIZE, NetInc, len); imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox); imap_make_date (internaldate, msg->received); imap_flags[0] = imap_flags[1] = 0; if (msg->flags.read) safe_strcat (imap_flags, sizeof (imap_flags), " \\Seen"); if (msg->flags.replied) safe_strcat (imap_flags, sizeof (imap_flags), " \\Answered"); if (msg->flags.flagged) safe_strcat (imap_flags, sizeof (imap_flags), " \\Flagged"); if (msg->flags.draft) safe_strcat (imap_flags, sizeof (imap_flags), " \\Draft"); snprintf (buf, sizeof (buf), "APPEND %s (%s) \"%s\" {%lu}", mbox, imap_flags + 1, internaldate, (unsigned long) len); imap_cmd_start (idata, buf); do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (rc != IMAP_CMD_RESPOND) { char *pc; dprint (1, (debugfile, "imap_append_message(): command failed: %s\n", idata->buf)); pc = idata->buf + SEQLEN; SKIPWS (pc); pc = imap_next_word (pc); mutt_error ("%s", pc); mutt_sleep (1); safe_fclose (&fp); goto fail; } for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c) { if (c == '\n' && last != '\r') buf[len++] = '\r'; buf[len++] = c; if (len > sizeof(buf) - 3) { sent += len; flush_buffer(buf, &len, idata->conn); mutt_progress_update (&progressbar, sent, -1); } } if (len) flush_buffer(buf, &len, idata->conn); mutt_socket_write (idata->conn, "\r\n"); safe_fclose (&fp); do rc = imap_cmd_step (idata); while (rc == IMAP_CMD_CONTINUE); if (!imap_code (idata->buf)) { char *pc; dprint (1, (debugfile, "imap_append_message(): command failed: %s\n", idata->buf)); pc = idata->buf + SEQLEN; SKIPWS (pc); pc = imap_next_word (pc); mutt_error ("%s", pc); mutt_sleep (1); goto fail; } FREE (&mx.mbox); return 0; fail: FREE (&mx.mbox); return -1; }
/* imap_read_headers: * Changed to read many headers instead of just one. It will return the * msgno of the last message read. It will return a value other than * msgend if mail comes in while downloading headers (in theory). */ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend) { CONTEXT* ctx; char *hdrreq = NULL; FILE *fp; char tempfile[_POSIX_PATH_MAX]; int msgno, idx = msgbegin - 1; IMAP_HEADER h; IMAP_STATUS* status; int rc, mfhrc, oldmsgcount; int fetchlast = 0; int maxuid = 0; static const char * const want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL"; progress_t progress; int retval = -1; #if USE_HCACHE char buf[LONG_STRING]; unsigned int *uid_validity = NULL; unsigned int *puidnext = NULL; unsigned int uidnext = 0; int evalhc = 0; #endif /* USE_HCACHE */ ctx = idata->ctx; if (mutt_bit_isset (idata->capabilities,IMAP4REV1)) { safe_asprintf (&hdrreq, "BODY.PEEK[HEADER.FIELDS (%s%s%s)]", want_headers, ImapHeaders ? " " : "", NONULL (ImapHeaders)); } else if (mutt_bit_isset (idata->capabilities,IMAP4)) { safe_asprintf (&hdrreq, "RFC822.HEADER.LINES (%s%s%s)", want_headers, ImapHeaders ? " " : "", NONULL (ImapHeaders)); } else { /* Unable to fetch headers for lower versions */ mutt_error _("Unable to fetch headers from this IMAP server version."); mutt_sleep (2); /* pause a moment to let the user see the error */ goto error_out_0; } /* instead of downloading all headers and then parsing them, we parse them * as they come in. */ mutt_mktemp (tempfile, sizeof (tempfile)); if (!(fp = safe_fopen (tempfile, "w+"))) { mutt_error (_("Could not create temporary file %s"), tempfile); mutt_sleep (2); goto error_out_0; } unlink (tempfile); /* make sure context has room to hold the mailbox */ while ((msgend) >= idata->ctx->hdrmax) mx_alloc_memory (idata->ctx); oldmsgcount = ctx->msgcount; idata->reopen &= ~(IMAP_REOPEN_ALLOW|IMAP_NEWMAIL_PENDING); idata->newMailCount = 0; #if USE_HCACHE idata->hcache = imap_hcache_open (idata, NULL); if (idata->hcache && !msgbegin) { uid_validity = mutt_hcache_fetch_raw (idata->hcache, "/UIDVALIDITY", imap_hcache_keylen); puidnext = mutt_hcache_fetch_raw (idata->hcache, "/UIDNEXT", imap_hcache_keylen); if (puidnext) { uidnext = *puidnext; FREE (&puidnext); } if (uid_validity && uidnext && *uid_validity == idata->uid_validity) evalhc = 1; FREE (&uid_validity); } if (evalhc) { /* L10N: Comparing the cached data with the IMAP server's data */ mutt_progress_init (&progress, _("Evaluating cache..."), MUTT_PROGRESS_MSG, ReadInc, msgend + 1); snprintf (buf, sizeof (buf), "UID FETCH 1:%u (UID FLAGS)", uidnext - 1); imap_cmd_start (idata, buf); rc = IMAP_CMD_CONTINUE; for (msgno = msgbegin; rc == IMAP_CMD_CONTINUE; msgno++) { mutt_progress_update (&progress, msgno + 1, -1); memset (&h, 0, sizeof (h)); h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA)); do { mfhrc = 0; rc = imap_cmd_step (idata); if (rc != IMAP_CMD_CONTINUE) { imap_free_header_data (&h.data); break; } /* hole in the header cache */ if (!evalhc) continue; if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, NULL)) == -1) continue; else if (mfhrc < 0) { imap_free_header_data (&h.data); break; } if (!h.data->uid) { dprint (2, (debugfile, "imap_read_headers: skipping hcache FETCH " "response for unknown message number %d\n", h.sid)); mfhrc = -1; continue; } idx++; ctx->hdrs[idx] = imap_hcache_get (idata, h.data->uid); if (ctx->hdrs[idx]) { ctx->hdrs[idx]->index = idx; /* messages which have not been expunged are ACTIVE (borrowed from mh * folders) */ ctx->hdrs[idx]->active = 1; ctx->hdrs[idx]->read = h.data->read; ctx->hdrs[idx]->old = h.data->old; ctx->hdrs[idx]->deleted = h.data->deleted; ctx->hdrs[idx]->flagged = h.data->flagged; ctx->hdrs[idx]->replied = h.data->replied; ctx->hdrs[idx]->changed = h.data->changed; /* ctx->hdrs[msgno]->received is restored from mutt_hcache_restore */ ctx->hdrs[idx]->data = (void *) (h.data); ctx->msgcount++; ctx->size += ctx->hdrs[idx]->content->length; } else { /* bad header in the cache, we'll have to refetch. */ dprint (3, (debugfile, "bad cache entry at %d, giving up\n", h.sid - 1)); imap_free_header_data(&h.data); evalhc = 0; idx--; } } while (rc != IMAP_CMD_OK && mfhrc == -1); if (rc == IMAP_CMD_OK) break; if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK))) { imap_free_header_data (&h.data); imap_hcache_close (idata); goto error_out_1; } } /* could also look for first null header in case hcache is holey */ msgbegin = ctx->msgcount; } #endif /* USE_HCACHE */ mutt_progress_init (&progress, _("Fetching message headers..."), MUTT_PROGRESS_MSG, ReadInc, msgend + 1); for (msgno = msgbegin; msgno <= msgend ; msgno++) { mutt_progress_update (&progress, msgno + 1, -1); /* we may get notification of new mail while fetching headers */ if (msgno + 1 > fetchlast) { char *cmd; fetchlast = msgend + 1; safe_asprintf (&cmd, "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", msgno + 1, fetchlast, hdrreq); imap_cmd_start (idata, cmd); FREE (&cmd); } rewind (fp); memset (&h, 0, sizeof (h)); h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA)); /* this DO loop does two things: * 1. handles untagged messages, so we can try again on the same msg * 2. fetches the tagged response at the end of the last message. */ do { mfhrc = 0; rc = imap_cmd_step (idata); if (rc != IMAP_CMD_CONTINUE) break; if ((mfhrc = msg_fetch_header (ctx, &h, idata->buf, fp)) == -1) continue; else if (mfhrc < 0) break; if (!ftello (fp)) { dprint (2, (debugfile, "msg_fetch_header: ignoring fetch response with no body\n")); mfhrc = -1; msgend--; continue; } /* make sure we don't get remnants from older larger message headers */ fputs ("\n\n", fp); idx++; if (idx > msgend) { dprint (1, (debugfile, "imap_read_headers: skipping FETCH response for " "unknown message number %d\n", h.sid)); mfhrc = -1; idx--; continue; } /* May receive FLAGS updates in a separate untagged response (#2935) */ if (idx < ctx->msgcount) { dprint (2, (debugfile, "imap_read_headers: message %d is not new\n", h.sid)); idx--; continue; } ctx->hdrs[idx] = mutt_new_header (); ctx->hdrs[idx]->index = h.sid - 1; /* messages which have not been expunged are ACTIVE (borrowed from mh * folders) */ ctx->hdrs[idx]->active = 1; ctx->hdrs[idx]->read = h.data->read; ctx->hdrs[idx]->old = h.data->old; ctx->hdrs[idx]->deleted = h.data->deleted; ctx->hdrs[idx]->flagged = h.data->flagged; ctx->hdrs[idx]->replied = h.data->replied; ctx->hdrs[idx]->changed = h.data->changed; ctx->hdrs[idx]->received = h.received; ctx->hdrs[idx]->data = (void *) (h.data); if (maxuid < h.data->uid) maxuid = h.data->uid; rewind (fp); /* NOTE: if Date: header is missing, mutt_read_rfc822_header depends * on h.received being set */ ctx->hdrs[idx]->env = mutt_read_rfc822_header (fp, ctx->hdrs[idx], 0, 0); /* content built as a side-effect of mutt_read_rfc822_header */ ctx->hdrs[idx]->content->length = h.content_length; ctx->size += h.content_length; #if USE_HCACHE imap_hcache_put (idata, ctx->hdrs[idx]); #endif /* USE_HCACHE */ ctx->msgcount++; } while ((rc != IMAP_CMD_OK) && ((mfhrc == -1) || ((msgno + 1) >= fetchlast))); if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK))) { imap_free_header_data (&h.data); #if USE_HCACHE imap_hcache_close (idata); #endif goto error_out_1; } /* in case we get new mail while fetching the headers */ if (idata->reopen & IMAP_NEWMAIL_PENDING) { msgend = idata->newMailCount - 1; while ((msgend) >= ctx->hdrmax) mx_alloc_memory (ctx); idata->reopen &= ~IMAP_NEWMAIL_PENDING; idata->newMailCount = 0; } } if (maxuid && (status = imap_mboxcache_get (idata, idata->mailbox, 0)) && (status->uidnext < maxuid + 1)) status->uidnext = maxuid + 1; #if USE_HCACHE mutt_hcache_store_raw (idata->hcache, "/UIDVALIDITY", &idata->uid_validity, sizeof (idata->uid_validity), imap_hcache_keylen); if (maxuid && idata->uidnext < maxuid + 1) { dprint (2, (debugfile, "Overriding UIDNEXT: %u -> %u\n", idata->uidnext, maxuid + 1)); idata->uidnext = maxuid + 1; } if (idata->uidnext > 1) mutt_hcache_store_raw (idata->hcache, "/UIDNEXT", &idata->uidnext, sizeof (idata->uidnext), imap_hcache_keylen); imap_hcache_close (idata); #endif /* USE_HCACHE */ if (ctx->msgcount > oldmsgcount) { mx_alloc_memory(ctx); mx_update_context (ctx, ctx->msgcount - oldmsgcount); } idata->reopen |= IMAP_REOPEN_ALLOW; retval = msgend; error_out_1: safe_fclose (&fp); error_out_0: FREE (&hdrreq); return retval; }
static int smtp_data (CONNECTION * conn, const char *msgfile) { char buf[1024]; FILE *fp = 0; progress_t progress; struct stat st; int r, term = 0; size_t buflen = 0; fp = fopen (msgfile, "r"); if (!fp) { mutt_error (_("SMTP session failed: unable to open %s"), msgfile); return -1; } stat (msgfile, &st); unlink (msgfile); mutt_progress_init (&progress, _("Sending message..."), M_PROGRESS_SIZE, NetInc, st.st_size); snprintf (buf, sizeof (buf), "DATA\r\n"); if (mutt_socket_write (conn, buf) == -1) { safe_fclose (&fp); return smtp_err_write; } if ((r = smtp_get_resp (conn))) { safe_fclose (&fp); return r; } while (fgets (buf, sizeof (buf) - 1, fp)) { buflen = mutt_strlen (buf); term = buf[buflen-1] == '\n'; if (buflen && buf[buflen-1] == '\n' && (buflen == 1 || buf[buflen - 2] != '\r')) snprintf (buf + buflen - 1, sizeof (buf) - buflen + 1, "\r\n"); if (buf[0] == '.') { if (mutt_socket_write_d (conn, ".", -1, M_SOCK_LOG_FULL) == -1) { safe_fclose (&fp); return smtp_err_write; } } if (mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL) == -1) { safe_fclose (&fp); return smtp_err_write; } mutt_progress_update (&progress, ftell (fp), -1); } if (!term && buflen && mutt_socket_write_d (conn, "\r\n", -1, M_SOCK_LOG_FULL) == -1) { safe_fclose (&fp); return smtp_err_write; } safe_fclose (&fp); /* terminate the message body */ if (mutt_socket_write (conn, ".\r\n") == -1) return smtp_err_write; if ((r = smtp_get_resp (conn))) return r; return 0; }
/** * mbox_parse_mailbox - Read a mailbox from disk * @param m Mailbox * @retval 0 Success * @retval -1 Error * @retval -2 Aborted * * Note that this function is also called when new mail is appended to the * currently open folder, and NOT just when the mailbox is initially read. * * NOTE: it is assumed that the mailbox being read has been locked before this * routine gets called. Strange things could happen if it's not! */ static int mbox_parse_mailbox(struct Mailbox *m) { if (!m) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; struct stat sb; char buf[8192], return_path[256]; struct Email *e_cur = NULL; time_t t; int count = 0, lines = 0; LOFF_T loc; struct Progress progress; /* Save information about the folder at the time we opened it. */ if (stat(m->path, &sb) == -1) { mutt_perror(m->path); return -1; } m->size = sb.st_size; mutt_file_get_stat_timespec(&m->mtime, &sb, MUTT_STAT_MTIME); mutt_file_get_stat_timespec(&adata->atime, &sb, MUTT_STAT_ATIME); if (!m->readonly) m->readonly = access(m->path, W_OK) ? true : false; if (!m->quiet) { char msgbuf[256]; snprintf(msgbuf, sizeof(msgbuf), _("Reading %s..."), m->path); mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, C_ReadInc, 0); } if (!m->emails) { /* Allocate some memory to get started */ m->email_max = m->msg_count; m->msg_count = 0; m->msg_unread = 0; m->vcount = 0; mx_alloc_memory(m); } loc = ftello(adata->fp); while ((fgets(buf, sizeof(buf), adata->fp)) && (SigInt != 1)) { if (is_from(buf, return_path, sizeof(return_path), &t)) { /* Save the Content-Length of the previous message */ if (count > 0) { struct Email *e = m->emails[m->msg_count - 1]; if (e->content->length < 0) { e->content->length = loc - e->content->offset - 1; if (e->content->length < 0) e->content->length = 0; } if (!e->lines) e->lines = lines ? lines - 1 : 0; } count++; if (!m->quiet) { mutt_progress_update(&progress, count, (int) (ftello(adata->fp) / (m->size / 100 + 1))); } if (m->msg_count == m->email_max) mx_alloc_memory(m); m->emails[m->msg_count] = mutt_email_new(); e_cur = m->emails[m->msg_count]; e_cur->received = t - mutt_date_local_tz(t); e_cur->offset = loc; e_cur->index = m->msg_count; e_cur->env = mutt_rfc822_read_header(adata->fp, e_cur, false, false); /* if we know how long this message is, either just skip over the body, * or if we don't know how many lines there are, count them now (this will * save time by not having to search for the next message marker). */ if (e_cur->content->length > 0) { LOFF_T tmploc; loc = ftello(adata->fp); /* The test below avoids a potential integer overflow if the * content-length is huge (thus necessarily invalid). */ tmploc = (e_cur->content->length < m->size) ? (loc + e_cur->content->length + 1) : -1; if ((tmploc > 0) && (tmploc < m->size)) { /* check to see if the content-length looks valid. we expect to * to see a valid message separator at this point in the stream */ if ((fseeko(adata->fp, tmploc, SEEK_SET) != 0) || !fgets(buf, sizeof(buf), adata->fp) || !mutt_str_startswith(buf, "From ", CASE_MATCH)) { mutt_debug(LL_DEBUG1, "bad content-length in message %d (cl=" OFF_T_FMT ")\n", e_cur->index, e_cur->content->length); mutt_debug(LL_DEBUG1, "\tLINE: %s", buf); /* nope, return the previous position */ if ((loc < 0) || (fseeko(adata->fp, loc, SEEK_SET) != 0)) { mutt_debug(LL_DEBUG1, "#1 fseek() failed\n"); } e_cur->content->length = -1; } } else if (tmploc != m->size) { /* content-length would put us past the end of the file, so it * must be wrong */ e_cur->content->length = -1; } if (e_cur->content->length != -1) { /* good content-length. check to see if we know how many lines * are in this message. */ if (e_cur->lines == 0) { int cl = e_cur->content->length; /* count the number of lines in this message */ if ((loc < 0) || (fseeko(adata->fp, loc, SEEK_SET) != 0)) mutt_debug(LL_DEBUG1, "#2 fseek() failed\n"); while (cl-- > 0) { if (fgetc(adata->fp) == '\n') e_cur->lines++; } } /* return to the offset of the next message separator */ if (fseeko(adata->fp, tmploc, SEEK_SET) != 0) mutt_debug(LL_DEBUG1, "#3 fseek() failed\n"); } } m->msg_count++; if (!e_cur->env->return_path && return_path[0]) { e_cur->env->return_path = mutt_addr_parse_list(e_cur->env->return_path, return_path); } if (!e_cur->env->from) e_cur->env->from = mutt_addr_copy_list(e_cur->env->return_path, false); lines = 0; } else lines++; loc = ftello(adata->fp); } /* Only set the content-length of the previous message if we have read more * than one message during _this_ invocation. If this routine is called * when new mail is received, we need to make sure not to clobber what * previously was the last message since the headers may be sorted. */ if (count > 0) { struct Email *e = m->emails[m->msg_count - 1]; if (e->content->length < 0) { e->content->length = ftello(adata->fp) - e->content->offset - 1; if (e->content->length < 0) e->content->length = 0; } if (!e->lines) e->lines = lines ? lines - 1 : 0; } if (SigInt == 1) { SigInt = 0; return -2; /* action aborted */ } return 0; }
/** * mmdf_parse_mailbox - Read a mailbox in MMDF format * @param m Mailbox * @retval 0 Success * @retval -1 Failure * @retval -2 Aborted */ static int mmdf_parse_mailbox(struct Mailbox *m) { if (!m) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; char buf[8192]; char return_path[1024]; int count = 0; int lines; time_t t; LOFF_T loc, tmploc; struct Email *e = NULL; struct stat sb; struct Progress progress; if (stat(m->path, &sb) == -1) { mutt_perror(m->path); return -1; } mutt_file_get_stat_timespec(&adata->atime, &sb, MUTT_STAT_ATIME); mutt_file_get_stat_timespec(&m->mtime, &sb, MUTT_STAT_MTIME); m->size = sb.st_size; buf[sizeof(buf) - 1] = '\0'; if (!m->quiet) { char msgbuf[256]; snprintf(msgbuf, sizeof(msgbuf), _("Reading %s..."), m->path); mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, C_ReadInc, 0); } while (true) { if (!fgets(buf, sizeof(buf) - 1, adata->fp)) break; if (SigInt == 1) break; if (mutt_str_strcmp(buf, MMDF_SEP) == 0) { loc = ftello(adata->fp); if (loc < 0) return -1; count++; if (!m->quiet) mutt_progress_update(&progress, count, (int) (loc / (m->size / 100 + 1))); if (m->msg_count == m->email_max) mx_alloc_memory(m); e = mutt_email_new(); m->emails[m->msg_count] = e; e->offset = loc; e->index = m->msg_count; if (!fgets(buf, sizeof(buf) - 1, adata->fp)) { /* TODO: memory leak??? */ mutt_debug(LL_DEBUG1, "unexpected EOF\n"); break; } return_path[0] = '\0'; if (!is_from(buf, return_path, sizeof(return_path), &t)) { if (fseeko(adata->fp, loc, SEEK_SET) != 0) { mutt_debug(LL_DEBUG1, "#1 fseek() failed\n"); mutt_error(_("Mailbox is corrupt")); return -1; } } else e->received = t - mutt_date_local_tz(t); e->env = mutt_rfc822_read_header(adata->fp, e, false, false); loc = ftello(adata->fp); if (loc < 0) return -1; if ((e->content->length > 0) && (e->lines > 0)) { tmploc = loc + e->content->length; if ((tmploc > 0) && (tmploc < m->size)) { if ((fseeko(adata->fp, tmploc, SEEK_SET) != 0) || !fgets(buf, sizeof(buf) - 1, adata->fp) || (mutt_str_strcmp(MMDF_SEP, buf) != 0)) { if (fseeko(adata->fp, loc, SEEK_SET) != 0) mutt_debug(LL_DEBUG1, "#2 fseek() failed\n"); e->content->length = -1; } } else e->content->length = -1; } else e->content->length = -1; if (e->content->length < 0) { lines = -1; do { loc = ftello(adata->fp); if (loc < 0) return -1; if (!fgets(buf, sizeof(buf) - 1, adata->fp)) break; lines++; } while (mutt_str_strcmp(buf, MMDF_SEP) != 0); e->lines = lines; e->content->length = loc - e->content->offset; } if (!e->env->return_path && return_path[0]) e->env->return_path = mutt_addr_parse_list(e->env->return_path, return_path); if (!e->env->from) e->env->from = mutt_addr_copy_list(e->env->return_path, false); m->msg_count++; } else { mutt_debug(LL_DEBUG1, "corrupt mailbox\n"); mutt_error(_("Mailbox is corrupt")); return -1; } } if (SigInt == 1) { SigInt = 0; return -2; /* action aborted */ } return 0; }
/** * mbox_mbox_sync - Implements MxOps::mbox_sync() */ static int mbox_mbox_sync(struct Mailbox *m, int *index_hint) { if (!m) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; char tempfile[PATH_MAX]; char buf[32]; int i, j; enum SortType save_sort = SORT_ORDER; int rc = -1; int need_sort = 0; /* flag to resort mailbox if new mail arrives */ int first = -1; /* first message to be written */ LOFF_T offset; /* location in mailbox to write changed messages */ struct stat statbuf; struct MUpdate *new_offset = NULL; struct MUpdate *old_offset = NULL; FILE *fp = NULL; struct Progress progress; char msgbuf[PATH_MAX + 64]; /* sort message by their position in the mailbox on disk */ if (C_Sort != SORT_ORDER) { save_sort = C_Sort; C_Sort = SORT_ORDER; mutt_mailbox_changed(m, MBN_RESORT); C_Sort = save_sort; need_sort = 1; } /* need to open the file for writing in such a way that it does not truncate * the file, so use read-write mode. */ adata->fp = freopen(m->path, "r+", adata->fp); if (!adata->fp) { mx_fastclose_mailbox(m); mutt_error(_("Fatal error! Could not reopen mailbox!")); return -1; } mutt_sig_block(); if (mbox_lock_mailbox(m, true, true) == -1) { mutt_sig_unblock(); mutt_error(_("Unable to lock mailbox")); goto bail; } /* Check to make sure that the file hasn't changed on disk */ i = mbox_mbox_check(m, index_hint); if ((i == MUTT_NEW_MAIL) || (i == MUTT_REOPENED)) { /* new mail arrived, or mailbox reopened */ rc = i; goto bail; } else if (i < 0) { /* fatal error */ return -1; } /* Create a temporary file to write the new version of the mailbox in. */ mutt_mktemp(tempfile, sizeof(tempfile)); int fd = open(tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600); if ((fd == -1) || !(fp = fdopen(fd, "w"))) { if (fd != -1) { close(fd); unlink(tempfile); } mutt_error(_("Could not create temporary file")); goto bail; } /* find the first deleted/changed message. we save a lot of time by only * rewriting the mailbox from the point where it has actually changed. */ for (i = 0; (i < m->msg_count) && !m->emails[i]->deleted && !m->emails[i]->changed && !m->emails[i]->attach_del; i++) { } if (i == m->msg_count) { /* this means ctx->changed or m->msg_deleted was set, but no * messages were found to be changed or deleted. This should * never happen, is we presume it is a bug in neomutt. */ mutt_error( _("sync: mbox modified, but no modified messages (report this bug)")); mutt_debug(LL_DEBUG1, "no modified messages\n"); unlink(tempfile); goto bail; } /* save the index of the first changed/deleted message */ first = i; /* where to start overwriting */ offset = m->emails[i]->offset; /* the offset stored in the header does not include the MMDF_SEP, so make * sure we seek to the correct location */ if (m->magic == MUTT_MMDF) offset -= (sizeof(MMDF_SEP) - 1); /* allocate space for the new offsets */ new_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate)); old_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate)); if (!m->quiet) { snprintf(msgbuf, sizeof(msgbuf), _("Writing %s..."), m->path); mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, C_WriteInc, m->msg_count); } for (i = first, j = 0; i < m->msg_count; i++) { if (!m->quiet) mutt_progress_update(&progress, i, (int) (ftello(adata->fp) / (m->size / 100 + 1))); /* back up some information which is needed to restore offsets when * something fails. */ old_offset[i - first].valid = true; old_offset[i - first].hdr = m->emails[i]->offset; old_offset[i - first].body = m->emails[i]->content->offset; old_offset[i - first].lines = m->emails[i]->lines; old_offset[i - first].length = m->emails[i]->content->length; if (!m->emails[i]->deleted) { j++; if (m->magic == MUTT_MMDF) { if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } } /* save the new offset for this message. we add 'offset' because the * temporary file only contains saved message which are located after * 'offset' in the real mailbox */ new_offset[i - first].hdr = ftello(fp) + offset; if (mutt_copy_message_ctx(fp, m, m->emails[i], MUTT_CM_UPDATE, CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0) { mutt_perror(tempfile); unlink(tempfile); goto bail; } /* Since messages could have been deleted, the offsets stored in memory * will be wrong, so update what we can, which is the offset of this * message, and the offset of the body. If this is a multipart message, * we just flush the in memory cache so that the message will be reparsed * if the user accesses it later. */ new_offset[i - first].body = ftello(fp) - m->emails[i]->content->length + offset; mutt_body_free(&m->emails[i]->content->parts); switch (m->magic) { case MUTT_MMDF: if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } break; default: if (fputs("\n", fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } } } } if (fclose(fp) != 0) { fp = NULL; mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n"); unlink(tempfile); mutt_perror(tempfile); goto bail; } fp = NULL; /* Save the state of this folder. */ if (stat(m->path, &statbuf) == -1) { mutt_perror(m->path); unlink(tempfile); goto bail; } fp = fopen(tempfile, "r"); if (!fp) { mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n"); mutt_perror(tempfile); FREE(&new_offset); FREE(&old_offset); return -1; } if ((fseeko(adata->fp, offset, SEEK_SET) != 0) || /* seek the append location */ /* do a sanity check to make sure the mailbox looks ok */ !fgets(buf, sizeof(buf), adata->fp) || ((m->magic == MUTT_MBOX) && !mutt_str_startswith(buf, "From ", CASE_MATCH)) || ((m->magic == MUTT_MMDF) && (mutt_str_strcmp(MMDF_SEP, buf) != 0))) { mutt_debug(LL_DEBUG1, "message not in expected position\n"); mutt_debug(LL_DEBUG1, "\tLINE: %s\n", buf); i = -1; } else { if (fseeko(adata->fp, offset, SEEK_SET) != 0) /* return to proper offset */ { i = -1; mutt_debug(LL_DEBUG1, "fseek() failed\n"); } else { /* copy the temp mailbox back into place starting at the first * change/deleted message */ if (!m->quiet) mutt_message(_("Committing changes...")); i = mutt_file_copy_stream(fp, adata->fp); if (ferror(adata->fp)) i = -1; } if (i == 0) { m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */ if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0)) { i = -1; mutt_debug(LL_DEBUG1, "ftruncate() failed\n"); } } } mutt_file_fclose(&fp); fp = NULL; mbox_unlock_mailbox(m); if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1)) { /* error occurred while writing the mailbox back, so keep the temp copy around */ char savefile[PATH_MAX]; snprintf(savefile, sizeof(savefile), "%s/neomutt.%s-%s-%u", NONULL(C_Tmpdir), NONULL(Username), NONULL(ShortHostname), (unsigned int) getpid()); rename(tempfile, savefile); mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_pretty_mailbox(savefile, sizeof(savefile)); mutt_error(_("Write failed! Saved partial mailbox to %s"), savefile); FREE(&new_offset); FREE(&old_offset); return -1; } /* Restore the previous access/modification times */ mbox_reset_atime(m, &statbuf); /* reopen the mailbox in read-only mode */ adata->fp = fopen(m->path, "r"); if (!adata->fp) { unlink(tempfile); mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_error(_("Fatal error! Could not reopen mailbox!")); FREE(&new_offset); FREE(&old_offset); return -1; } /* update the offsets of the rewritten messages */ for (i = first, j = first; i < m->msg_count; i++) { if (!m->emails[i]->deleted) { m->emails[i]->offset = new_offset[i - first].hdr; m->emails[i]->content->hdr_offset = new_offset[i - first].hdr; m->emails[i]->content->offset = new_offset[i - first].body; m->emails[i]->index = j++; } } FREE(&new_offset); FREE(&old_offset); unlink(tempfile); /* remove partial copy of the mailbox */ mutt_sig_unblock(); if (C_CheckMboxSize) { struct Mailbox *tmp = mutt_find_mailbox(m->path); if (tmp && !tmp->has_new) mutt_update_mailbox(tmp); } return 0; /* signal success */ bail: /* Come here in case of disaster */ mutt_file_fclose(&fp); /* restore offsets, as far as they are valid */ if ((first >= 0) && old_offset) { for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++) { m->emails[i]->offset = old_offset[i - first].hdr; m->emails[i]->content->hdr_offset = old_offset[i - first].hdr; m->emails[i]->content->offset = old_offset[i - first].body; m->emails[i]->lines = old_offset[i - first].lines; m->emails[i]->content->length = old_offset[i - first].length; } } /* this is ok to call even if we haven't locked anything */ mbox_unlock_mailbox(m); mutt_sig_unblock(); FREE(&new_offset); FREE(&old_offset); adata->fp = freopen(m->path, "r", adata->fp); if (!adata->fp) { mutt_error(_("Could not reopen mailbox")); mx_fastclose_mailbox(m); return -1; } if (need_sort) { /* if the mailbox was reopened, the thread tree will be invalid so make * sure to start threading from scratch. */ mutt_mailbox_changed(m, MBN_RESORT); } return rc; }
/* return values: * 0 success * -1 failure */ int mbox_sync_mailbox(CONTEXT *ctx, int *index_hint) { char tempfile[_POSIX_PATH_MAX]; char buf[32]; int i, j, save_sort = SORT_ORDER; int rc = -1; int need_sort = 0; /* flag to resort mailbox if new mail arrives */ int first = -1; /* first message to be written */ LOFF_T offset; /* location in mailbox to write changed messages */ struct stat statbuf; struct m_update_t *newOffset = NULL; struct m_update_t *oldOffset = NULL; FILE *fp = NULL; progress_t progress; char msgbuf[STRING]; /* sort message by their position in the mailbox on disk */ if (Sort != SORT_ORDER) { save_sort = Sort; Sort = SORT_ORDER; mutt_sort_headers(ctx, 0); Sort = save_sort; need_sort = 1; } /* need to open the file for writing in such a way that it does not truncate * the file, so use read-write mode. */ if ((ctx->fp = freopen(ctx->path, "r+", ctx->fp)) == NULL) { mx_fastclose_mailbox(ctx); mutt_error _("Fatal error! Could not reopen mailbox!"); return -1; } mutt_block_signals(); if (mbox_lock_mailbox(ctx, 1, 1) == -1) { mutt_unblock_signals(); mutt_error _("Unable to lock mailbox!"); goto bail; } /* Check to make sure that the file hasn't changed on disk */ if (((i = mbox_check_mailbox(ctx, index_hint)) == M_NEW_MAIL) || (i == M_REOPENED)) { /* new mail arrived, or mailbox reopened */ need_sort = i; rc = i; goto bail; } else if (i < 0) /* fatal error */ return -1; /* Create a temporary file to write the new version of the mailbox in. */ mutt_mktemp(tempfile, sizeof(tempfile)); if (((i = open(tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1) || ((fp = fdopen(i, "w")) == NULL)) { if (-1 != i) { close(i); unlink(tempfile); } mutt_error _("Could not create temporary file!"); mutt_sleep(5); goto bail; } /* find the first deleted/changed message. we save a lot of time by only * rewriting the mailbox from the point where it has actually changed. */ for (i = 0; i < ctx->msgcount && !ctx->hdrs[i]->deleted && !ctx->hdrs[i]->changed && !ctx->hdrs[i]->attach_del; i++) ; if (i == ctx->msgcount) { /* this means ctx->changed or ctx->deleted was set, but no * messages were found to be changed or deleted. This should * never happen, is we presume it is a bug in mutt. */ mutt_error _( "sync: mbox modified, but no modified messages! (report this bug)"); mutt_sleep(5); /* the mutt_error /will/ get cleared! */ dprint(1, "mbox_sync_mailbox(): no modified messages.\n"); unlink(tempfile); goto bail; } /* save the index of the first changed/deleted message */ first = i; /* where to start overwriting */ offset = ctx->hdrs[i]->offset; /* the offset stored in the header does not include the MMDF_SEP, so make * sure we seek to the correct location */ if (ctx->magic == M_MMDF) offset -= (sizeof MMDF_SEP - 1); /* allocate space for the new offsets */ newOffset = (__typeof__(newOffset)) safe_calloc(ctx->msgcount - first, sizeof(struct m_update_t)); oldOffset = (__typeof__(oldOffset)) safe_calloc(ctx->msgcount - first, sizeof(struct m_update_t)); if (!ctx->quiet) { snprintf(msgbuf, sizeof(msgbuf), _("Writing %s..."), ctx->path); mutt_progress_init(&progress, msgbuf, M_PROGRESS_MSG, WriteInc, ctx->msgcount); } for (i = first, j = 0; i < ctx->msgcount; i++) { if (!ctx->quiet) mutt_progress_update(&progress, i, (int)(ftello(ctx->fp) / (ctx->size / 100 + 1))); /* * back up some information which is needed to restore offsets when * something fails. */ oldOffset[i - first].valid = 1; oldOffset[i - first].hdr = ctx->hdrs[i]->offset; oldOffset[i - first].body = ctx->hdrs[i]->content->offset; oldOffset[i - first].lines = ctx->hdrs[i]->lines; oldOffset[i - first].length = ctx->hdrs[i]->content->length; if (!ctx->hdrs[i]->deleted) { j++; if (ctx->magic == M_MMDF) { if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); mutt_sleep(5); unlink(tempfile); goto bail; } } /* save the new offset for this message. we add `offset' because the * temporary file only contains saved message which are located *after * `offset' in the real mailbox */ newOffset[i - first].hdr = ftello(fp) + offset; if (mutt_copy_message(fp, ctx, ctx->hdrs[i], M_CM_UPDATE, CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0) { mutt_perror(tempfile); mutt_sleep(5); unlink(tempfile); goto bail; } /* Since messages could have been deleted, the offsets stored in memory * will be wrong, so update what we can, which is the offset of this * message, and the offset of the body. If this is a multipart *message, * we just flush the in memory cache so that the message will be *reparsed * if the user accesses it later. */ newOffset[i - first].body = ftello(fp) - ctx->hdrs[i]->content->length + offset; mutt_free_body(&ctx->hdrs[i]->content->parts); switch (ctx->magic) { case M_MMDF: if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); mutt_sleep(5); unlink(tempfile); goto bail; } break; default: if (fputs("\n", fp) == EOF) { mutt_perror(tempfile); mutt_sleep(5); unlink(tempfile); goto bail; } } } } if (fclose(fp) != 0) { fp = NULL; dprint(1, "mbox_sync_mailbox: safe_fclose (&) returned non-zero.\n"); unlink(tempfile); mutt_perror(tempfile); mutt_sleep(5); goto bail; } fp = NULL; /* Save the state of this folder. */ if (stat(ctx->path, &statbuf) == -1) { mutt_perror(ctx->path); mutt_sleep(5); unlink(tempfile); goto bail; } if ((fp = fopen(tempfile, "r")) == NULL) { mutt_unblock_signals(); mx_fastclose_mailbox(ctx); dprint(1, "mbox_sync_mailbox: unable to reopen temp copy of mailbox!\n"); mutt_perror(tempfile); mutt_sleep(5); return -1; } if ((fseeko(ctx->fp, offset, SEEK_SET) != 0) /* seek the append location */ ||/* do a sanity check to make sure the mailbox looks ok */ (fgets(buf, sizeof(buf), ctx->fp) == NULL) || ((ctx->magic == M_MBOX) && (mutt_strncmp("From ", buf, 5) != 0)) || ((ctx->magic == M_MMDF) && (mutt_strcmp(MMDF_SEP, buf) != 0))) { dprint(1, "mbox_sync_mailbox: message not in expected position."); dprint(1, "\tLINE: %s\n", buf); i = -1; } else { if (fseeko(ctx->fp, offset, SEEK_SET) != 0) { /* return to proper offset */ i = -1; dprint(1, "mbox_sync_mailbox: fseek() failed\n"); } else { /* copy the temp mailbox back into place starting at the first * change/deleted message */ if (!ctx->quiet) mutt_message _("Committing changes..."); i = mutt_copy_stream(fp, ctx->fp); if (ferror(ctx->fp)) i = -1; } if (i == 0) { ctx->size = ftello(ctx->fp); /* update the size of the mailbox */ ftruncate(fileno(ctx->fp), ctx->size); } } safe_fclose(&fp); fp = NULL; mbox_unlock_mailbox(ctx); if ((fclose(ctx->fp) != 0) || (i == -1)) { /* error occurred while writing the mailbox back, so keep the temp copy * around */ char savefile[_POSIX_PATH_MAX]; snprintf(savefile, sizeof(savefile), "%s/mutt.%s-%s-%u", NONULL(Tempdir), NONULL(Username), NONULL(Hostname), (unsigned int)getpid()); rename(tempfile, savefile); mutt_unblock_signals(); mx_fastclose_mailbox(ctx); mutt_pretty_mailbox(savefile, sizeof(savefile)); mutt_error(_("Write failed! Saved partial mailbox to %s"), savefile); mutt_sleep(5); return -1; } /* Restore the previous access/modification times */ mbox_reset_atime(ctx, &statbuf); /* reopen the mailbox in read-only mode */ if ((ctx->fp = fopen(ctx->path, "r")) == NULL) { unlink(tempfile); mutt_unblock_signals(); mx_fastclose_mailbox(ctx); mutt_error _("Fatal error! Could not reopen mailbox!"); return -1; } /* update the offsets of the rewritten messages */ for (i = first, j = first; i < ctx->msgcount; i++) { if (!ctx->hdrs[i]->deleted) { ctx->hdrs[i]->offset = newOffset[i - first].hdr; ctx->hdrs[i]->content->hdr_offset = newOffset[i - first].hdr; ctx->hdrs[i]->content->offset = newOffset[i - first].body; ctx->hdrs[i]->index = j++; } } safe_free(&newOffset); safe_free(&oldOffset); unlink(tempfile); /* remove partial copy of the mailbox */ mutt_unblock_signals(); return 0; /* signal success */ bail: /* Come here in case of disaster */ safe_fclose(&fp); /* restore offsets, as far as they are valid */ if ((first >= 0) && oldOffset) { for (i = first; i < ctx->msgcount && oldOffset[i - first].valid; i++) { ctx->hdrs[i]->offset = oldOffset[i - first].hdr; ctx->hdrs[i]->content->hdr_offset = oldOffset[i - first].hdr; ctx->hdrs[i]->content->offset = oldOffset[i - first].body; ctx->hdrs[i]->lines = oldOffset[i - first].lines; ctx->hdrs[i]->content->length = oldOffset[i - first].length; } } /* this is ok to call even if we haven't locked anything */ mbox_unlock_mailbox(ctx); mutt_unblock_signals(); safe_free(&newOffset); safe_free(&oldOffset); if ((ctx->fp = freopen(ctx->path, "r", ctx->fp)) == NULL) { mutt_error _("Could not reopen mailbox!"); mx_fastclose_mailbox(ctx); return -1; } if (need_sort) /* if the mailbox was reopened, the thread tree will be invalid so make * sure to start threading from scratch. */ mutt_sort_headers(ctx, (need_sort == M_REOPENED)); return rc; }
int mmdf_parse_mailbox(CONTEXT *ctx) { char buf[HUGE_STRING]; char return_path[LONG_STRING]; int count = 0, oldmsgcount = ctx->msgcount; int lines; time_t t; LOFF_T loc, tmploc; HEADER *hdr; struct stat sb; #ifdef NFS_ATTRIBUTE_HACK struct utimbuf newtime; #endif /* ifdef NFS_ATTRIBUTE_HACK */ progress_t progress; char msgbuf[STRING]; if (stat(ctx->path, &sb) == -1) { mutt_perror(ctx->path); return -1; } ctx->atime = sb.st_atime; ctx->mtime = sb.st_mtime; ctx->size = sb.st_size; #ifdef NFS_ATTRIBUTE_HACK if (sb.st_mtime > sb.st_atime) { newtime.modtime = sb.st_mtime; newtime.actime = time(NULL); utime(ctx->path, &newtime); } #endif /* ifdef NFS_ATTRIBUTE_HACK */ buf[sizeof(buf) - 1] = 0; if (!ctx->quiet) { snprintf(msgbuf, sizeof(msgbuf), _("Reading %s..."), ctx->path); mutt_progress_init(&progress, msgbuf, M_PROGRESS_MSG, ReadInc, 0); } FOREVER { if (fgets(buf, sizeof(buf) - 1, ctx->fp) == NULL) break; if (mutt_strcmp(buf, MMDF_SEP) == 0) { loc = ftello(ctx->fp); count++; if (!ctx->quiet) mutt_progress_update(&progress, count, (int)(loc / (ctx->size / 100 + 1))); if (ctx->msgcount == ctx->hdrmax) mx_alloc_memory(ctx); ctx->hdrs[ctx->msgcount] = hdr = mutt_new_header(); hdr->offset = loc; hdr->index = ctx->msgcount; if (fgets(buf, sizeof(buf) - 1, ctx->fp) == NULL) { /* TODO: memory leak??? */ dprint(1, "mmdf_parse_mailbox: unexpected EOF\n"); break; } return_path[0] = 0; if (!is_from(buf, return_path, sizeof(return_path), &t)) { if (fseeko(ctx->fp, loc, SEEK_SET) != 0) { dprint(1, "mmdf_parse_mailbox: fseek() failed\n"); mutt_error _("Mailbox is corrupt!"); return -1; } } else hdr->received = t - mutt_local_tz(t); hdr->env = mutt_read_rfc822_header(ctx->fp, hdr, 0, 0); loc = ftello(ctx->fp); if ((hdr->content->length > 0) && (hdr->lines > 0)) { tmploc = loc + hdr->content->length; if ((0 < tmploc) && (tmploc < ctx->size)) { if ((fseeko(ctx->fp, tmploc, SEEK_SET) != 0) || (fgets(buf, sizeof(buf) - 1, ctx->fp) == NULL) || (mutt_strcmp(MMDF_SEP, buf) != 0)) { if (fseeko(ctx->fp, loc, SEEK_SET) != 0) dprint(1, "mmdf_parse_mailbox: fseek() failed\n"); hdr->content->length = -1; } } else hdr->content->length = -1; } else hdr->content->length = -1; if (hdr->content->length < 0) { lines = -1; do { loc = ftello(ctx->fp); if (fgets(buf, sizeof(buf) - 1, ctx->fp) == NULL) break; lines++; } while (mutt_strcmp(buf, MMDF_SEP) != 0); hdr->lines = lines; hdr->content->length = loc - hdr->content->offset; } if (!hdr->env->return_path && return_path[0]) hdr->env->return_path = rfc822_parse_adrlist( hdr->env->return_path, return_path); if (!hdr->env->from) hdr->env->from = rfc822_cpy_adr(hdr->env->return_path, 0); ctx->msgcount++; } else { dprint(1, "mmdf_parse_mailbox: corrupt mailbox!\n"); mutt_error _("Mailbox is corrupt!"); return -1; } } if (ctx->msgcount > oldmsgcount) mx_update_context(ctx, ctx->msgcount - oldmsgcount); return 0; }
/* Note that this function is also called when new mail is appended to the * currently open folder, and NOT just when the mailbox is initially read. * * NOTE: it is assumed that the mailbox being read has been locked before * this routine gets called. Strange things could happen if it's not! */ int mbox_parse_mailbox(CONTEXT *ctx) { struct stat sb; char buf[HUGE_STRING], return_path[STRING]; HEADER *curhdr; time_t t; int count = 0, lines = 0; LOFF_T loc; #ifdef NFS_ATTRIBUTE_HACK struct utimbuf newtime; #endif /* ifdef NFS_ATTRIBUTE_HACK */ progress_t progress; char msgbuf[STRING]; /* Save information about the folder at the time we opened it. */ if (stat(ctx->path, &sb) == -1) { mutt_perror(ctx->path); return -1; } ctx->size = sb.st_size; ctx->mtime = sb.st_mtime; ctx->atime = sb.st_atime; #ifdef NFS_ATTRIBUTE_HACK if (sb.st_mtime > sb.st_atime) { newtime.modtime = sb.st_mtime; newtime.actime = time(NULL); utime(ctx->path, &newtime); } #endif /* ifdef NFS_ATTRIBUTE_HACK */ if (!ctx->readonly) ctx->readonly = access(ctx->path, W_OK) ? 1 : 0; if (!ctx->quiet) { snprintf(msgbuf, sizeof(msgbuf), _("Reading %s..."), ctx->path); mutt_progress_init(&progress, msgbuf, M_PROGRESS_MSG, ReadInc, 0); } loc = ftello(ctx->fp); while (fgets(buf, sizeof(buf), ctx->fp) != NULL) { if (is_from(buf, return_path, sizeof(return_path), &t)) { /* Save the Content-Length of the previous message */ if (count > 0) { #define PREV ctx->hdrs[ctx->msgcount - 1] if (PREV->content->length < 0) { PREV->content->length = loc - PREV->content->offset - 1; if (PREV->content->length < 0) PREV->content->length = 0; } if (!PREV->lines) PREV->lines = lines ? lines - 1 : 0; } count++; if (!ctx->quiet) mutt_progress_update(&progress, count, (int)(ftello(ctx->fp) / (ctx->size / 100 + 1))); if (ctx->msgcount == ctx->hdrmax) mx_alloc_memory(ctx); curhdr = ctx->hdrs[ctx->msgcount] = mutt_new_header(); curhdr->received = t - mutt_local_tz(t); curhdr->offset = loc; curhdr->index = ctx->msgcount; curhdr->env = mutt_read_rfc822_header(ctx->fp, curhdr, 0, 0); /* if we know how long this message is, either just skip over the body, * or if we don't know how many lines there are, count them now *(this will * save time by not having to search for the next message marker). */ if (curhdr->content->length > 0) { LOFF_T tmploc; loc = ftello(ctx->fp); tmploc = loc + curhdr->content->length + 1; if ((0 < tmploc) && (tmploc < ctx->size)) { /* * check to see if the content-length looks valid. we *expect to * to see a valid message separator at this point in the *stream */ if ((fseeko(ctx->fp, tmploc, SEEK_SET) != 0) || (fgets(buf, sizeof(buf), ctx->fp) == NULL) || (mutt_strncmp("From ", buf, 5) != 0)) { dprint(1, "mbox_parse_mailbox: bad content-length in message %d (cl=" OFF_T_FMT ")\n", curhdr->index, curhdr->content->length); dprint(1, "\tLINE: %s", buf); if (fseeko(ctx->fp, loc, SEEK_SET) != 0) { /* nope, return the previous position */ dprint(1, "mbox_parse_mailbox: fseek() failed\n"); } curhdr->content->length = -1; } } else if (tmploc != ctx->size) { /* content-length would put us past the end of the file, so it * must be wrong */ curhdr->content->length = -1; } if (curhdr->content->length != -1) { /* good content-length. check to see if we know how many lines * are in this message. */ if (curhdr->lines == 0) { int cl = curhdr->content->length; /* count the number of lines in this message */ if (fseeko(ctx->fp, loc, SEEK_SET) != 0) dprint(1, "mbox_parse_mailbox: fseek() failed\n"); while (cl-- > 0) { if (fgetc(ctx->fp) == '\n') curhdr->lines++; } } /* return to the offset of the next message separator */ if (fseeko(ctx->fp, tmploc, SEEK_SET) != 0) dprint(1, "mbox_parse_mailbox: fseek() failed\n"); } } ctx->msgcount++; if (!curhdr->env->return_path && return_path[0]) curhdr->env->return_path = rfc822_parse_adrlist( curhdr->env->return_path, return_path); if (!curhdr->env->from) curhdr->env->from = rfc822_cpy_adr(curhdr->env->return_path, 0); lines = 0; } else lines++; loc = ftello(ctx->fp); } /* * Only set the content-length of the previous message if we have read more * than one message during _this_ invocation. If this routine is called * when new mail is received, we need to make sure not to clobber what * previously was the last message since the headers may be sorted. */ if (count > 0) { if (PREV->content->length < 0) { PREV->content->length = ftello(ctx->fp) - PREV->content->offset - 1; if (PREV->content->length < 0) PREV->content->length = 0; } if (!PREV->lines) PREV->lines = lines ? lines - 1 : 0; mx_update_context(ctx, count); } return 0; }
/* * Read headers * returns: * 0 on success * -1 - connection lost, * -2 - invalid command or execution error, * -3 - error writing to tempfile */ static int pop_fetch_headers(CONTEXT *ctx) { int i, ret, old_count, new_count, deleted; unsigned short hcached = 0, bcached; POP_DATA *pop_data = (POP_DATA *)ctx->data; progress_t progress; 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 == 2) { if (ret == 0) { pop_data->cmd_uidl = 1; dprint(1, "pop_fetch_headers: set UIDL capability\n"); } if ((ret == -2) && (pop_data->cmd_uidl == 2)) { pop_data->cmd_uidl = 0; dprint(1, "pop_fetch_headers: unset UIDL capability\n"); snprintf(pop_data->err_msg, sizeof(pop_data->err_msg), _("Command UIDL is not supported by server.")); } } if (!ctx->quiet) mutt_progress_init(&progress, _("Fetching message headers..."), M_PROGRESS_MSG, ReadInc, new_count - old_count); if (ret == 0) { for (i = 0, deleted = 0; i < old_count; i++) { if (ctx->hdrs[i]->refno == -1) { ctx->hdrs[i]->deleted = 1; deleted++; } } if (deleted > 0) { mutt_error(_( "%d messages have been lost. Try reopening the mailbox."), deleted); mutt_sleep(2); } for (i = old_count; i < new_count; i++) { if (!ctx->quiet) mutt_progress_update(&progress, i + 1 - old_count, -1); if ((ret = pop_read_header(pop_data, ctx->hdrs[i])) < 0) break; /* * faked support for flags works like this: * - if 'hcached' is 1, we have the message in our hcache: * - if we also have a body: read * - if we don't have a body: old * (if $mark_old is set which is maybe wrong as * $mark_old should be considered for syncing the * folder and not when opening it XXX) * - if 'hcached' is 0, we don't have the message in our hcache: * - if we also have a body: read * - if we don't have a body: new */ bcached = mutt_bcache_exists(pop_data->bcache, ctx->hdrs[i]->data) == 0; ctx->hdrs[i]->old = 0; ctx->hdrs[i]->read = 0; if (hcached) { if (bcached) ctx->hdrs[i]->read = 1; else if (option(OPTMARKOLD)) ctx->hdrs[i]->old = 1; } else { if (bcached) ctx->hdrs[i]->read = 1; } ctx->msgcount++; } if (i > old_count) mx_update_context(ctx, i - old_count); } if (ret < 0) { for (i = ctx->msgcount; i < new_count; i++) mutt_free_header(&ctx->hdrs[i]); return ret; } /* after putting the result into our structures, * clean up cache, i.e. wipe messages deleted outside * the availability of our cache */ if (option(OPTMESSAGECACHECLEAN)) mutt_bcache_list(pop_data->bcache, msg_cache_check, (void *)ctx); mutt_clear_error(); return new_count - old_count; }
/* 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; } } }
/* * 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, ret, old_count, new_count, deleted; unsigned short hcached = 0, bcached; POP_DATA *pop_data = (POP_DATA *)ctx->data; progress_t progress; #ifdef USE_HCACHE header_cache_t *hc = NULL; void *data; hc = pop_hcache_open (pop_data, ctx->path); #endif 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 == 2) { if (ret == 0) { pop_data->cmd_uidl = 1; dprint (1, (debugfile, "pop_fetch_headers: set UIDL capability\n")); } if (ret == -2 && pop_data->cmd_uidl == 2) { pop_data->cmd_uidl = 0; dprint (1, (debugfile, "pop_fetch_headers: unset UIDL capability\n")); snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), _("Command UIDL is not supported by server.")); } } if (!ctx->quiet) mutt_progress_init (&progress, _("Fetching message headers..."), M_PROGRESS_MSG, ReadInc, new_count - old_count); if (ret == 0) { for (i = 0, deleted = 0; i < old_count; i++) { if (ctx->hdrs[i]->refno == -1) { ctx->hdrs[i]->deleted = 1; deleted++; } } if (deleted > 0) { mutt_error (_("%d messages have been lost. Try reopening the mailbox."), deleted); mutt_sleep (2); } for (i = old_count; i < new_count; i++) { if (!ctx->quiet) mutt_progress_update (&progress, i + 1 - old_count, -1); #if USE_HCACHE if ((data = mutt_hcache_fetch (hc, ctx->hdrs[i]->data, strlen))) { char *uidl = safe_strdup (ctx->hdrs[i]->data); int refno = ctx->hdrs[i]->refno; int index = ctx->hdrs[i]->index; /* * - POP dynamically numbers headers and relies on h->refno * to map messages; so restore header and overwrite restored * refno with current refno, same for index * - h->data needs to a separate pointer as it's driver-specific * data freed separately elsewhere * (the old h->data should point inside a malloc'd block from * hcache so there shouldn't be a memleak here) */ HEADER *h = mutt_hcache_restore ((unsigned char *) data, NULL); mutt_free_header (&ctx->hdrs[i]); ctx->hdrs[i] = h; ctx->hdrs[i]->refno = refno; ctx->hdrs[i]->index = index; ctx->hdrs[i]->data = uidl; ret = 0; hcached = 1; } else #endif if ((ret = pop_read_header (pop_data, ctx->hdrs[i])) < 0) break; #if USE_HCACHE else { mutt_hcache_store (hc, ctx->hdrs[i]->data, ctx->hdrs[i], 0, strlen, M_GENERATE_UIDVALIDITY); } FREE(&data); #endif /* * faked support for flags works like this: * - if 'hcached' is 1, we have the message in our hcache: * - if we also have a body: read * - if we don't have a body: old * (if $mark_old is set which is maybe wrong as * $mark_old should be considered for syncing the * folder and not when opening it XXX) * - if 'hcached' is 0, we don't have the message in our hcache: * - if we also have a body: read * - if we don't have a body: new */ bcached = mutt_bcache_exists (pop_data->bcache, ctx->hdrs[i]->data) == 0; ctx->hdrs[i]->old = 0; ctx->hdrs[i]->read = 0; if (hcached) { if (bcached) ctx->hdrs[i]->read = 1; else if (option (OPTMARKOLD)) ctx->hdrs[i]->old = 1; } else { if (bcached) ctx->hdrs[i]->read = 1; } ctx->msgcount++; } if (i > old_count) mx_update_context (ctx, i - old_count); } #if USE_HCACHE mutt_hcache_close (hc); #endif if (ret < 0) { for (i = ctx->msgcount; i < new_count; i++) mutt_free_header (&ctx->hdrs[i]); return ret; } /* after putting the result into our structures, * clean up cache, i.e. wipe messages deleted outside * the availability of our cache */ if (option (OPTMESSAGECACHECLEAN)) mutt_bcache_list (pop_data->bcache, msg_cache_check, (void*)ctx); mutt_clear_error (); return (new_count - old_count); }