/* imap_add_folder: add a folder name to the browser list, formatting it as * necessary. */ static void imap_add_folder (char delim, char *folder, int noselect, int noinferiors, struct browser_state *state, short isparent) { char tmp[LONG_STRING]; char relpath[LONG_STRING]; IMAP_MBOX mx; if (imap_parse_path (state->folder, &mx)) return; imap_unmunge_mbox_name (folder); if (state->entrylen + 1 == state->entrymax) { safe_realloc ((void **) &state->entry, sizeof (struct folder_file) * (state->entrymax += 256)); memset (state->entry + state->entrylen, 0, (sizeof (struct folder_file) * (state->entrymax - state->entrylen))); } /* render superiors as unix-standard ".." */ if (isparent) strfcpy (relpath, "../", sizeof (relpath)); /* strip current folder from target, to render a relative path */ else if (!mutt_strncmp (mx.mbox, folder, mutt_strlen (mx.mbox))) strfcpy (relpath, folder + mutt_strlen (mx.mbox), sizeof (relpath)); else strfcpy (relpath, folder, sizeof (relpath)); /* apply filemask filter. This should really be done at menu setup rather * than at scan, since it's so expensive to scan. But that's big changes * to browser.c */ if (!((regexec (Mask.rx, relpath, 0, NULL, 0) == 0) ^ Mask.not)) { FREE (&mx.mbox); return; } imap_qualify_path (tmp, sizeof (tmp), &mx, folder); (state->entry)[state->entrylen].name = safe_strdup (tmp); /* mark desc with delim in browser if it can have subfolders */ if (!isparent && !noinferiors && strlen (relpath) < sizeof (relpath) - 1) { relpath[strlen (relpath) + 1] = '\0'; relpath[strlen (relpath)] = delim; } (state->entry)[state->entrylen].desc = safe_strdup (relpath); (state->entry)[state->entrylen].imap = 1; /* delimiter at the root is useless. */ if (folder[0] == '\0') delim = '\0'; (state->entry)[state->entrylen].delim = delim; (state->entry)[state->entrylen].selectable = !noselect; (state->entry)[state->entrylen].inferiors = !noinferiors; (state->entrylen)++; FREE (&mx.mbox); }
/* args: * ctx Context info, used when recalling a message to which * we reply. * hdr envelope/attachment info for recalled message * cur if message was a reply, `cur' is set to the message which * `hdr' is in reply to * fcc fcc for the recalled message * fcclen max length of fcc * * return vals: * -1 error/no messages * 0 normal exit * SENDREPLY recalled message is a reply */ int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size_t fcclen) { HEADER *h; int code = SENDPOSTPONED; LIST *tmp; LIST *last = NULL; LIST *next; const char *p; int opt_delete; if (!Postponed) return (-1); if ((PostContext = mx_open_mailbox (Postponed, MUTT_NOSORT, NULL)) == NULL) { PostCount = 0; mutt_error _("No postponed messages."); return (-1); } if (! PostContext->msgcount) { PostCount = 0; mx_close_mailbox (PostContext, NULL); FREE (&PostContext); mutt_error _("No postponed messages."); return (-1); } if (PostContext->msgcount == 1) { /* only one message, so just use that one. */ h = PostContext->hdrs[0]; } else if ((h = select_msg ()) == NULL) { mx_close_mailbox (PostContext, NULL); FREE (&PostContext); return (-1); } if (mutt_prepare_template (NULL, PostContext, hdr, h, 0) < 0) { mx_fastclose_mailbox (PostContext); FREE (&PostContext); return (-1); } /* finished with this message, so delete it. */ mutt_set_flag (PostContext, h, MUTT_DELETE, 1); mutt_set_flag (PostContext, h, MUTT_PURGE, 1); /* update the count for the status display */ PostCount = PostContext->msgcount - PostContext->deleted; /* avoid the "purge deleted messages" prompt */ opt_delete = quadoption (OPT_DELETE); set_quadoption (OPT_DELETE, MUTT_YES); mx_close_mailbox (PostContext, NULL); set_quadoption (OPT_DELETE, opt_delete); FREE (&PostContext); for (tmp = hdr->env->userhdrs; tmp; ) { if (ascii_strncasecmp ("X-Mutt-References:", tmp->data, 18) == 0) { if (ctx) { /* if a mailbox is currently open, look to see if the orignal message the user attempted to reply to is in this mailbox */ p = skip_email_wsp(tmp->data + 18); if (!ctx->id_hash) ctx->id_hash = mutt_make_id_hash (ctx); *cur = hash_find (ctx->id_hash, p); } /* Remove the X-Mutt-References: header field. */ next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; if (*cur) code |= SENDREPLY; } else if (ascii_strncasecmp ("X-Mutt-Fcc:", tmp->data, 11) == 0) { p = skip_email_wsp(tmp->data + 11); strfcpy (fcc, p, fcclen); mutt_pretty_mailbox (fcc, fcclen); /* remove the X-Mutt-Fcc: header field */ next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; /* note that x-mutt-fcc was present. we do this because we want to add a * default fcc if the header was missing, but preserve the request of the * user to not make a copy if the header field is present, but empty. * see http://dev.mutt.org/trac/ticket/3653 */ code |= SENDPOSTPONEDFCC; } else if ((WithCrypto & APPLICATION_PGP) && (mutt_strncmp ("Pgp:", tmp->data, 4) == 0 /* this is generated * by old mutt versions */ || mutt_strncmp ("X-Mutt-PGP:", tmp->data, 11) == 0)) { hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1, APPLICATION_PGP); hdr->security |= APPLICATION_PGP; /* remove the pgp field */ next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; } else if ((WithCrypto & APPLICATION_SMIME) && mutt_strncmp ("X-Mutt-SMIME:", tmp->data, 13) == 0) { hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1, APPLICATION_SMIME); hdr->security |= APPLICATION_SMIME; /* remove the smime field */ next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; } #ifdef MIXMASTER else if (mutt_strncmp ("X-Mutt-Mix:", tmp->data, 11) == 0) { char *t; mutt_free_list (&hdr->chain); t = strtok (tmp->data + 11, " \t\n"); while (t) { hdr->chain = mutt_add_list (hdr->chain, t); t = strtok (NULL, " \t\n"); } next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; } #endif else { last = tmp; tmp = tmp->next; } } if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) crypt_opportunistic_encrypt (hdr); return (code); }
void mutt_fetchPopMail (void) { struct sockaddr_in sin; #if SIZEOF_LONG == 4 long n; #else int n; #endif struct hostent *he; char buffer[2048]; char msgbuf[SHORT_STRING]; int s, i, last = 0, msgs, bytes, err = 0; CONTEXT ctx; MESSAGE *msg = NULL; if (!PopHost) { mutt_error _("POP host is not defined."); return; } if (!PopUser) { mutt_error _("No POP username is defined."); return; } if (!getPass ()) return; s = socket (AF_INET, SOCK_STREAM, IPPROTO_IP); memset ((char *) &sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons (PopPort); if ((n = inet_addr (NONULL(PopHost))) == -1) { /* Must be a DNS name */ if ((he = gethostbyname (NONULL(PopHost))) == NULL) { mutt_error (_("Could not find address for host %s."), PopHost); return; } memcpy ((void *)&sin.sin_addr, *(he->h_addr_list), he->h_length); } else memcpy ((void *)&sin.sin_addr, (void *)&n, sizeof(n)); mutt_message (_("Connecting to %s"), inet_ntoa (sin.sin_addr)); if (connect (s, (struct sockaddr *) &sin, sizeof (struct sockaddr_in)) == -1) { mutt_perror ("connect"); return; } if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws (buffer); mutt_error ("%s", buffer); goto finish; } snprintf (buffer, sizeof(buffer), "user %s\r\n", PopUser); write (s, buffer, mutt_strlen (buffer)); if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws (buffer); mutt_error ("%s", buffer); goto finish; } snprintf (buffer, sizeof(buffer), "pass %s\r\n", NONULL(PopPass)); write (s, buffer, mutt_strlen (buffer)); if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) != 0) { if(PopPass) memset(PopPass, 0, mutt_strlen(PopPass)); safe_free((void **) &PopPass); /* void the given password */ mutt_remove_trailing_ws (buffer); mutt_error ("%s", buffer[0] ? buffer : _("Server closed connection!")); goto finish; } /* find out how many messages are in the mailbox. */ write (s, "stat\r\n", 6); if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws (buffer); mutt_error ("%s", buffer); goto finish; } sscanf (buffer, "+OK %d %d", &msgs, &bytes); if (msgs == 0) { mutt_message _("No new mail in POP mailbox."); goto finish; } if (mx_open_mailbox (NONULL(Spoolfile), M_APPEND, &ctx) == NULL) goto finish; /* only get unread messages */ if(option(OPTPOPLAST)) { write (s, "last\r\n", 6); if (getLine (s, buffer, sizeof (buffer)) == -1) goto fail; if (mutt_strncmp (buffer, "+OK", 3) == 0) sscanf (buffer, "+OK %d", &last); else /* ignore an error here and assume all messages are new */ last = 0; } snprintf (msgbuf, sizeof (msgbuf), msgs > 1 ? _("Reading new messages (%d bytes)...") : _("Reading new message (%d bytes)..."), bytes); mutt_message (msgbuf); for (i = last + 1 ; i <= msgs ; i++) { snprintf (buffer, sizeof(buffer), "retr %d\r\n", i); write (s, buffer, mutt_strlen (buffer)); if (getLine (s, buffer, sizeof (buffer)) == -1) { mx_fastclose_mailbox (&ctx); goto fail; } if (mutt_strncmp (buffer, "+OK", 3) != 0) { mutt_remove_trailing_ws (buffer); mutt_error ("%s", buffer); break; } if ((msg = mx_open_new_message (&ctx, NULL, M_ADD_FROM)) == NULL) { err = 1; break; } /* Now read the actual message. */ FOREVER { char *p; int chunk; if ((chunk = getLine (s, buffer, sizeof (buffer))) == -1) { mutt_error _("Error reading message!"); err = 1; break; } /* check to see if we got a full line */ if (buffer[chunk-2] == '\r' && buffer[chunk-1] == '\n') { if (mutt_strcmp(".\r\n", buffer) == 0) { /* end of message */ break; } /* change CRLF to just LF */ buffer[chunk-2] = '\n'; buffer[chunk-1] = 0; chunk--; /* see if the line was byte-stuffed */ if (buffer[0] == '.') { p = buffer + 1; chunk--; } else p = buffer; } else p = buffer; fwrite (p, 1, chunk, msg->fp); } if (mx_commit_message (msg, &ctx) != 0) { mutt_error _("Error while writing mailbox!"); err = 1; } mx_close_message (&msg); if (err) break; if (option (OPTPOPDELETE)) { /* delete the message on the server */ snprintf (buffer, sizeof(buffer), "dele %d\r\n", i); write (s, buffer, mutt_strlen (buffer)); /* eat the server response */ getLine (s, buffer, sizeof (buffer)); if (mutt_strncmp (buffer, "+OK", 3) != 0) { err = 1; mutt_remove_trailing_ws (buffer); mutt_error ("%s", buffer); break; } } if ( msgs > 1) mutt_message (_("%s [%d of %d messages read]"), msgbuf, i, msgs); else mutt_message (_("%s [%d message read]"), msgbuf, msgs); } if (msg) { if (mx_commit_message (msg, &ctx) != 0) err = 1; mx_close_message (&msg); } mx_close_mailbox (&ctx, NULL); if (err) { /* make sure no messages get deleted */ write (s, "rset\r\n", 6); getLine (s, buffer, sizeof (buffer)); /* snarf the response */ } finish: /* exit gracefully */ write (s, "quit\r\n", 6); getLine (s, buffer, sizeof (buffer)); /* snarf the response */ close (s); return; /* not reached */ fail: mutt_error _("Server closed connection!"); close (s); }
/* Ok, the only reason for not merging this with mutt_copy_header() * below is to avoid creating a HEADER structure in message_handler(). * Also, this one will wrap headers much more aggressively than the other one. */ int mutt_copy_hdr (FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags, const char *prefix) { int from = 0; int this_is_from; int ignore = 0; char buf[LONG_STRING]; /* should be long enough to get most fields in one pass */ char *nl; LIST *t; char **headers; int hdr_count; int x; char *this_one = NULL; size_t this_one_len = 0; int error; if (ftello (in) != off_start) fseeko (in, off_start, 0); buf[0] = '\n'; buf[1] = 0; if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | CH_WEED_DELIVERED)) == 0) { /* Without these flags to complicate things * we can do a more efficient line to line copying */ while (ftello (in) < off_end) { nl = strchr (buf, '\n'); if ((fgets (buf, sizeof (buf), in)) == NULL) break; /* Is it the beginning of a header? */ if (nl && buf[0] != ' ' && buf[0] != '\t') { ignore = 1; if (!from && mutt_strncmp ("From ", buf, 5) == 0) { if ((flags & CH_FROM) == 0) continue; from = 1; } else if (flags & (CH_NOQFROM) && ascii_strncasecmp (">From ", buf, 6) == 0) continue; else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) break; /* end of header */ if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) && (ascii_strncasecmp ("Status:", buf, 7) == 0 || ascii_strncasecmp ("X-Status:", buf, 9) == 0)) continue; if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) && (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 || ascii_strncasecmp ("Lines:", buf, 6) == 0)) continue; if ((flags & CH_UPDATE_REFS) && ascii_strncasecmp ("References:", buf, 11) == 0) continue; if ((flags & CH_UPDATE_IRT) && ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0) continue; ignore = 0; } if (!ignore && fputs (buf, out) == EOF) return (-1); } return 0; } hdr_count = 1; x = 0; error = FALSE; /* We are going to read and collect the headers in an array * so we are able to do re-ordering. * First count the number of entries in the array */ if (flags & CH_REORDER) { for (t = HeaderOrderList; t; t = t->next) { dprint(3, (debugfile, "Reorder list: %s\n", t->data)); hdr_count++; } } dprint (1, (debugfile, "WEED is %s\n", (flags & CH_WEED) ? "Set" : "Not")); headers = safe_calloc (hdr_count, sizeof (char *)); /* Read all the headers into the array */ while (ftello (in) < off_end) { nl = strchr (buf, '\n'); /* Read a line */ if ((fgets (buf, sizeof (buf), in)) == NULL) break; /* Is it the beginning of a header? */ if (nl && buf[0] != ' ' && buf[0] != '\t') { /* Do we have anything pending? */ if (this_one) { if (flags & CH_DECODE) { if (!address_header_decode (&this_one)) rfc2047_decode (&this_one); this_one_len = mutt_strlen (this_one); } if (!headers[x]) headers[x] = this_one; else { int hlen = mutt_strlen (headers[x]); safe_realloc (&headers[x], hlen + this_one_len + sizeof (char)); strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */ FREE (&this_one); } this_one = NULL; } ignore = 1; this_is_from = 0; if (!from && mutt_strncmp ("From ", buf, 5) == 0) { if ((flags & CH_FROM) == 0) continue; this_is_from = from = 1; } else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) break; /* end of header */ /* note: CH_FROM takes precedence over header weeding. */ if (!((flags & CH_FROM) && (flags & CH_FORCE_FROM) && this_is_from) && (flags & CH_WEED) && mutt_matches_ignore (buf, Ignore) && !mutt_matches_ignore (buf, UnIgnore)) continue; if ((flags & CH_WEED_DELIVERED) && ascii_strncasecmp ("Delivered-To:", buf, 13) == 0) continue; if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) && (ascii_strncasecmp ("Status:", buf, 7) == 0 || ascii_strncasecmp ("X-Status:", buf, 9) == 0)) continue; if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) && (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 || ascii_strncasecmp ("Lines:", buf, 6) == 0)) continue; if ((flags & CH_MIME) && ((ascii_strncasecmp ("content-", buf, 8) == 0 && (ascii_strncasecmp ("transfer-encoding:", buf + 8, 18) == 0 || ascii_strncasecmp ("type:", buf + 8, 5) == 0)) || ascii_strncasecmp ("mime-version:", buf, 13) == 0)) continue; if ((flags & CH_UPDATE_REFS) && ascii_strncasecmp ("References:", buf, 11) == 0) continue; if ((flags & CH_UPDATE_IRT) && ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0) continue; /* Find x -- the array entry where this header is to be saved */ if (flags & CH_REORDER) { for (t = HeaderOrderList, x = 0 ; (t) ; t = t->next, x++) { if (!ascii_strncasecmp (buf, t->data, mutt_strlen (t->data))) { dprint(2, (debugfile, "Reorder: %s matches %s\n", t->data, buf)); break; } } } ignore = 0; } /* If beginning of header */ if (!ignore) { dprint (2, (debugfile, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count)); if (!this_one) { this_one = safe_strdup (buf); this_one_len = mutt_strlen (this_one); } else { int blen = mutt_strlen (buf); safe_realloc (&this_one, this_one_len + blen + sizeof (char)); strcat (this_one + this_one_len, buf); /* __STRCAT_CHECKED__ */ this_one_len += blen; } } } /* while (ftello (in) < off_end) */ /* Do we have anything pending? -- XXX, same code as in above in the loop. */ if (this_one) { if (flags & CH_DECODE) { if (!address_header_decode (&this_one)) rfc2047_decode (&this_one); this_one_len = mutt_strlen (this_one); } if (!headers[x]) headers[x] = this_one; else { int hlen = mutt_strlen (headers[x]); safe_realloc (&headers[x], hlen + this_one_len + sizeof (char)); strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */ FREE (&this_one); } this_one = NULL; } /* Now output the headers in order */ for (x = 0; x < hdr_count; x++) { if (headers[x]) { #if 0 if (flags & CH_DECODE) rfc2047_decode (&headers[x]); #endif /* We couldn't do the prefixing when reading because RFC 2047 * decoding may have concatenated lines. */ if (flags & (CH_DECODE|CH_PREFIX)) { if (mutt_write_one_header (out, 0, headers[x], flags & CH_PREFIX ? prefix : 0, mutt_term_width (Wrap)-SidebarWidth, flags) == -1) { error = TRUE; break; } } else { if (fputs (headers[x], out) == EOF) { error = TRUE; break; } } } } /* Free in a separate loop to be sure that all headers are freed * in case of error. */ for (x = 0; x < hdr_count; x++) FREE (&headers[x]); FREE (&headers); if (error) return (-1); return (0); }
time_t is_from (const char *s, char *path, size_t pathlen) { struct tm tm; int yr; if (path) *path = 0; if (mutt_strncmp ("From ", s, 5) != 0) return 0; s = next_word (s); /* skip over the From part. */ if (!*s) return 0; dprint (3, (debugfile, "\nis_from(): parsing: %s", s)); if (!is_day_name (s)) { const char *p; size_t len; short q = 0; for (p = s; *p && (q || !ISSPACE (*p)); p++) { if (*p == '\\') { if (*++p == '\0') return 0; } else if (*p == '"') { q = !q; } } if (q || !*p) return 0; if (path) { len = (size_t) (p - s); if (len + 1 > pathlen) len = pathlen - 1; memcpy (path, s, len); path[len] = 0; dprint (3, (debugfile, "is_from(): got return path: %s\n", path)); } s = p + 1; SKIPWS (s); if (!*s) return 0; if (!is_day_name (s)) { dprint(1, (debugfile, "is_from(): expected weekday, got: %s\n", s)); return 0; } } s = next_word (s); if (!*s) return 0; /* do a quick check to make sure that this isn't really the day of the week. * this could happen when receiving mail from a local user whose login name * is the same as a three-letter abbreviation of the day of the week. */ if (is_day_name (s)) { s = next_word (s); if (!*s) return 0; } /* now we should be on the month. */ if ((tm.tm_mon = mutt_check_month (s)) < 0) return 0; /* day */ s = next_word (s); if (!*s) return 0; if (sscanf (s, "%d", &tm.tm_mday) != 1) return 0; /* time */ s = next_word (s); if (!*s) return 0; /* Accept either HH:MM or HH:MM:SS */ if (sscanf (s, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 3); else if (sscanf (s, "%d:%d", &tm.tm_hour, &tm.tm_min) == 2) tm.tm_sec = 0; else return 0; s = next_word (s); if (!*s) return 0; /* timezone? */ if (isalpha ((unsigned char) *s) || *s == '+' || *s == '-') { s = next_word (s); if (!*s) return 0; /* * some places have two timezone fields after the time, e.g. * From [email protected] Wed Aug 2 00:39:12 MET DST 1995 */ if (isalpha ((unsigned char) *s)) { s = next_word (s); if (!*s) return 0; } } /* year */ if (sscanf (s, "%d", &yr) != 1) return 0; tm.tm_year = yr > 1900 ? yr - 1900 : (yr < 70 ? yr + 100 : yr); dprint (3,(debugfile, "is_from(): month=%d, day=%d, hr=%d, min=%d, sec=%d, yr=%d.\n", tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_year)); tm.tm_isdst = -1; return (mutt_mktime (&tm, 0)); }
/* 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; }
/* check to see if the mailbox has changed on disk. * * return values: * M_REOPENED mailbox has been reopened * M_NEW_MAIL new mail has arrived! * M_LOCKED couldn't lock the file * 0 no change * -1 error */ int mbox_check_mailbox(CONTEXT *ctx, int *index_hint) { struct stat st; char buffer[LONG_STRING]; int unlock = 0; int modified = 0; if (stat(ctx->path, &st) == 0) { if ((st.st_mtime == ctx->mtime) && (st.st_size == ctx->size)) return 0; if (st.st_size == ctx->size) { /* the file was touched, but it is still the same length, so just exit */ ctx->mtime = st.st_mtime; return 0; } if (st.st_size > ctx->size) { /* lock the file if it isn't already */ if (!ctx->locked) { mutt_block_signals(); if (mbox_lock_mailbox(ctx, 0, 0) == -1) { mutt_unblock_signals(); /* we couldn't lock the mailbox, but nothing serious happened: * probably the new mail arrived: no reason to wait till we *can * parse it: we'll get it on the next pass */ return M_LOCKED; } unlock = 1; } /* * Check to make sure that the only change to the mailbox is that * message(s) were appended to this file. My heuristic is that we *should * see the message separator at *exactly* what used to be the end of *the * folder. */ if (fseeko(ctx->fp, ctx->size, SEEK_SET) != 0) dprint(1, "mbox_check_mailbox: fseek() failed\n"); if (fgets(buffer, sizeof(buffer), ctx->fp) != NULL) { if (((ctx->magic == M_MBOX) && (mutt_strncmp("From ", buffer, 5) == 0)) || ((ctx->magic == M_MMDF) && (mutt_strcmp(MMDF_SEP, buffer) == 0))) { if (fseeko(ctx->fp, ctx->size, SEEK_SET) != 0) dprint(1, "mbox_check_mailbox: fseek() failed\n"); if (ctx->magic == M_MBOX) mbox_parse_mailbox(ctx); else mmdf_parse_mailbox(ctx); /* Only unlock the folder if it was locked inside of this routine. * It may have been locked elsewhere, like in * mutt_checkpoint_mailbox(). */ if (unlock) { mbox_unlock_mailbox(ctx); mutt_unblock_signals(); } return M_NEW_MAIL; /* signal that new mail arrived */ } else modified = 1; } else { dprint(1, "mbox_check_mailbox: fgets returned NULL.\n"); modified = 1; } } else modified = 1; } if (modified) { if (mutt_reopen_mailbox(ctx, index_hint) != -1) { if (unlock) { mbox_unlock_mailbox(ctx); mutt_unblock_signals(); } return M_REOPENED; } } /* fatal error */ mbox_unlock_mailbox(ctx); mx_fastclose_mailbox(ctx); mutt_unblock_signals(); mutt_error _("Mailbox was corrupted!"); return -1; }
/* 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; }
/* args: * ctx Context info, used when recalling a message to which * we reply. * hdr envelope/attachment info for recalled message * cur if message was a reply, `cur' is set to the message which * `hdr' is in reply to * fcc fcc for the recalled message * fcclen max length of fcc * * return vals: * -1 error/no messages * 0 normal exit * SENDREPLY recalled message is a reply */ int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size_t fcclen) { HEADER *h; int code = SENDPOSTPONED; LIST *tmp; LIST *last = NULL; LIST *next; char *p; int opt_delete; #ifdef USE_IMAP char curpath[LONG_STRING]; int need_reopen = 0; #endif if (!Postponed) return (-1); #ifdef USE_IMAP /* if we're in an IMAP folder and the postponed folder is also IMAP, we may * need to take steps to avoid opening an additional connection to the same * server. */ if ((ctx && ctx->magic == M_IMAP) && mx_is_imap (Postponed)) { strfcpy (curpath, ctx->path, sizeof (curpath)); if (imap_select_mailbox (ctx, Postponed) < 0) return -1; need_reopen = 1; PostContext = ctx; } else #endif if ((PostContext = mx_open_mailbox (Postponed, M_NOSORT, NULL)) == NULL) { PostCount = 0; mutt_error _("No postponed messages."); return (-1); } if (! PostContext->msgcount) { PostCount = 0; mx_close_mailbox (PostContext, NULL); #ifdef USE_IMAP if (need_reopen) ctx = mx_open_mailbox (curpath, 0, PostContext); else #endif safe_free ((void **) &PostContext); mutt_error _("No postponed messages."); return (-1); } if (PostContext->msgcount == 1) { /* only one message, so just use that one. */ h = PostContext->hdrs[0]; } else if ((h = select_msg ()) == NULL) { mx_close_mailbox (PostContext, NULL); #ifdef USE_IMAP if (need_reopen) ctx = mx_open_mailbox (curpath, 0, PostContext); else #endif safe_free ((void **) &PostContext); return (-1); } if (mutt_prepare_template (NULL, PostContext, hdr, h, 0) < 0) { mx_fastclose_mailbox (PostContext); #ifdef USE_IMAP if (need_reopen) ctx = mx_open_mailbox (curpath, 0, NULL); else #endif safe_free ((void **) &PostContext); return (-1); } /* finished with this message, so delete it. */ mutt_set_flag (PostContext, h, M_DELETE, 1); /* update the count for the status display */ PostCount = PostContext->msgcount - PostContext->deleted; /* avoid the "purge deleted messages" prompt */ opt_delete = quadoption (OPT_DELETE); set_quadoption (OPT_DELETE, M_YES); mx_close_mailbox (PostContext, NULL); set_quadoption (OPT_DELETE, opt_delete); #ifdef USE_IMAP if (need_reopen) ctx = mx_open_mailbox (curpath, 0, PostContext); else #endif safe_free ((void **) &PostContext); for (tmp = hdr->env->userhdrs; tmp; ) { if (mutt_strncasecmp ("X-Mutt-References:", tmp->data, 18) == 0) { if (ctx) { /* if a mailbox is currently open, look to see if the orignal message the user attempted to reply to is in this mailbox */ p = tmp->data + 18; SKIPWS (p); *cur = hash_find (ctx->id_hash, p); } /* Remove the X-Mutt-References: header field. */ next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; if (*cur) code |= SENDREPLY; } else if (mutt_strncasecmp ("X-Mutt-Fcc:", tmp->data, 11) == 0) { p = tmp->data + 11; SKIPWS (p); strfcpy (fcc, p, fcclen); mutt_pretty_mailbox (fcc); /* remove the X-Mutt-Fcc: header field */ next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; } #ifdef HAVE_PGP else if (mutt_strncmp ("Pgp:", tmp->data, 4) == 0 /* this is generated * by old mutt versions */ || mutt_strncmp ("X-Mutt-PGP:", tmp->data, 11) == 0) { hdr->pgp = mutt_parse_pgp_hdr (strchr (tmp->data, ':') + 1, 1); /* remove the pgp field */ next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; } #endif /* HAVE_PGP */ #ifdef MIXMASTER else if (mutt_strncmp ("X-Mutt-Mix:", tmp->data, 11) == 0) { char *t; mutt_free_list (&hdr->chain); t = strtok (tmp->data + 11, " \t\n"); while (t) { hdr->chain = mutt_add_list (hdr->chain, t); t = strtok (NULL, " \t\n"); } next = tmp->next; if (last) last->next = tmp->next; else hdr->env->userhdrs = tmp->next; tmp->next = NULL; mutt_free_list (&tmp); tmp = next; } #endif else { last = tmp; tmp = tmp->next; } } return (code); }