static int check_attachments(ATTACHPTR **idx, short idxlen) { int i, r; struct stat st; char pretty[_POSIX_PATH_MAX], msg[_POSIX_PATH_MAX + SHORT_STRING]; for (i = 0; i < idxlen; i++) { strfcpy(pretty, idx[i]->content->filename, sizeof(pretty)); if(stat(idx[i]->content->filename, &st) != 0) { mutt_pretty_mailbox(pretty, sizeof (pretty)); mutt_error(_("%s [#%d] no longer exists!"), pretty, i+1); return -1; } if(idx[i]->content->stamp < st.st_mtime) { mutt_pretty_mailbox(pretty, sizeof (pretty)); snprintf(msg, sizeof(msg), _("%s [#%d] modified. Update encoding?"), pretty, i+1); if((r = mutt_yesorno(msg, M_YES)) == M_YES) mutt_update_encoding(idx[i]->content); else if(r == -1) return -1; } } return 0; }
void mutt_select_fcc (char *path, size_t pathlen, HEADER *hdr) { ADDRESS *adr; char buf[_POSIX_PATH_MAX]; ENVELOPE *env = hdr->env; if (mutt_addr_hook (path, pathlen, M_FCCHOOK, NULL, hdr) != 0) { if ((option (OPTSAVENAME) || option (OPTFORCENAME)) && (env->to || env->cc || env->bcc)) { adr = env->to ? env->to : (env->cc ? env->cc : env->bcc); mutt_safe_path (buf, sizeof (buf), adr); mutt_concat_path (path, NONULL(Maildir), buf, pathlen); if (!option (OPTFORCENAME) && mx_access (path, W_OK) != 0) strfcpy (path, NONULL (Outbox), pathlen); } else strfcpy (path, NONULL (Outbox), pathlen); } mutt_pretty_mailbox (path, pathlen); }
/* %b = number of incoming folders with unread messages [option] * %d = number of deleted messages [option] * %f = full mailbox path * %F = number of flagged messages [option] * %h = hostname * %l = length of mailbox (in bytes) [option] * %m = total number of messages [option] * %M = number of messages shown (virtual message count when limiting) [option] * %n = number of new messages [option] * %o = number of old unread messages [option] * %p = number of postponed messages [option] * %P = percent of way through index * %r = readonly/wontwrite/changed flag * %s = current sorting method ($sort) * %S = current aux sorting method ($sort_aux) * %t = # of tagged messages [option] * %u = number of unread messages [option] * %v = Mutt version * %V = currently active limit pattern [option] */ static const char * status_format_str(char *buf, size_t buflen, size_t col, char op, const char *src, const char *prefix, const char *ifstring, const char *elsestring, unsigned long data, format_flag flags) { char fmt[SHORT_STRING], tmp[SHORT_STRING], *cp; int count, optional = (flags & M_FORMAT_OPTIONAL); MUTTMENU *menu = (MUTTMENU *)data; *buf = 0; switch (op) { case 'b': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, mutt_buffy_check(0)); } else if (!mutt_buffy_check(0)) optional = 0; break; case 'd': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, Context ? Context->deleted : 0); } else if (!Context || !Context->deleted) optional = 0; break; case 'f': snprintf(fmt, sizeof(fmt), "%%%ss", prefix); if (Context && Context->path) { strfcpy(tmp, Context->path, sizeof(tmp)); mutt_pretty_mailbox(tmp, sizeof(tmp)); } else strfcpy(tmp, _("(no mailbox)"), sizeof(tmp)); snprintf(buf, buflen, fmt, tmp); break; case 'F': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, Context ? Context->flagged : 0); } else if (!Context || !Context->flagged) optional = 0; break; case 'h': snprintf(fmt, sizeof(fmt), "%%%ss", prefix); snprintf(buf, buflen, fmt, NONULL(Hostname)); break; case 'l': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%ss", prefix); mutt_pretty_size(tmp, sizeof(tmp), Context ? Context->size : 0); snprintf(buf, buflen, fmt, tmp); } else if (!Context || !Context->size) optional = 0; break; case 'L': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%ss", prefix); mutt_pretty_size(tmp, sizeof(tmp), Context ? Context->vsize : 0); snprintf(buf, buflen, fmt, tmp); } else if (!Context || !Context->pattern) optional = 0; break; case 'm': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, Context ? Context->msgcount : 0); } else if (!Context || !Context->msgcount) optional = 0; break; case 'M': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, Context ? Context->vcount : 0); } else if (!Context || !Context->pattern) optional = 0; break; case 'n': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, Context ? Context->new_messages : 0); } else if (!Context || !Context->new_messages) optional = 0; break; case 'o': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, Context ? Context->unread - Context->new_messages : -1); } else if (!Context || !(Context->unread - Context->new_messages)) optional = 0; break; case 'p': count = mutt_num_postponed(0); if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, count); } else if (!count) optional = 0; break; case 'P': if (!menu) break; if (menu->top + menu->pagelen >= menu->max) cp = (char *) (menu->top ? (char *)"end" : (char *)"all"); else { count = (100 * (menu->top + menu->pagelen)) / menu->max; snprintf(tmp, sizeof(tmp), "%d%%", count); cp = tmp; } snprintf(fmt, sizeof(fmt), "%%%ss", prefix); snprintf(buf, buflen, fmt, cp); break; case 'r': { size_t i = 0; if (Context) { i = globals.has_option(OPTATTACHMSG) ? 3 : ((Context->readonly || Context->dontwrite) ? 2 : ( Context->changed ||/* deleted doesn't necessarily mean changed in IMAP */ ( Context->magic != M_IMAP && Context->deleted)) ? 1 : 0); } if (!StChars) buf[0] = 0; else if (i >= mutt_strlen(StChars)) buf[0] = StChars[0]; else buf[0] = StChars[i]; buf[1] = 0; break; } case 's': snprintf(fmt, sizeof(fmt), "%%%ss", prefix); snprintf(buf, buflen, fmt, get_sort_str(tmp, sizeof(tmp), Sort)); break; case 'S': snprintf(fmt, sizeof(fmt), "%%%ss", prefix); snprintf(buf, buflen, fmt, get_sort_str(tmp, sizeof(tmp), SortAux)); break; case 't': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, Context ? Context->tagged : 0); } else if (!Context || !Context->tagged) optional = 0; break; case 'u': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%sd", prefix); snprintf(buf, buflen, fmt, Context ? Context->unread : 0); } else if (!Context || !Context->unread) optional = 0; break; case 'v': snprintf(fmt, sizeof(fmt), "Mutt %%s"); snprintf(buf, buflen, fmt, MUTT_VERSION); break; case 'V': if (!optional) { snprintf(fmt, sizeof(fmt), "%%%ss", prefix); snprintf(buf, buflen, fmt, (Context && Context->pattern) ? Context->pattern : ""); } else if (!Context || !Context->pattern) optional = 0; break; case 0: *buf = 0; return src; default: snprintf(buf, buflen, "%%%s%c", prefix, op); break; } if (optional) _menu_status_line(buf, buflen, col, menu, ifstring); else if (flags & M_FORMAT_OPTIONAL) _menu_status_line(buf, buflen, col, menu, elsestring); return src; }
/* 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, M_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, 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); 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); }
/* return values: * * 1 message should be postponed * 0 normal exit * -1 abort message */ int mutt_compose_menu (HEADER *msg, /* structure for new message */ char *fcc, /* where to save a copy of the message */ size_t fcclen, HEADER *cur, /* current message */ int flags) { char helpstr[LONG_STRING]; char buf[LONG_STRING]; char fname[_POSIX_PATH_MAX]; MUTTMENU *menu; ATTACHPTR **idx = NULL; short idxlen = 0; short idxmax = 0; int i, close = 0; int r = -1; /* return value */ int op = 0; int loop = 1; int fccSet = 0; /* has the user edited the Fcc: field ? */ CONTEXT *ctx = NULL, *this = NULL; /* Sort, SortAux could be changed in mutt_index_menu() */ int oldSort, oldSortAux; struct stat st; mutt_attach_init (msg->content); idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1); menu = mutt_new_menu (MENU_COMPOSE); menu->offset = HDR_ATTACH; menu->max = idxlen; menu->make_entry = snd_entry; menu->tag = mutt_tag_attach; menu->data = idx; menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp); while (loop) { switch (op = mutt_menuLoop (menu)) { case OP_REDRAW: draw_envelope (msg, fcc); menu->offset = HDR_ATTACH; menu->pagelen = LINES - HDR_ATTACH - 2; break; case OP_COMPOSE_EDIT_FROM: menu->redraw = edit_address_list (HDR_FROM, &msg->env->from); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_TO: menu->redraw = edit_address_list (HDR_TO, &msg->env->to); if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) { crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_BCC: menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc); if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) { crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_CC: menu->redraw = edit_address_list (HDR_CC, &msg->env->cc); if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) { crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_SUBJECT: if (msg->env->subject) strfcpy (buf, msg->env->subject, sizeof (buf)); else buf[0] = 0; if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0) { mutt_str_replace (&msg->env->subject, buf); move (HDR_SUBJECT, HDR_XOFFSET); if (msg->env->subject) mutt_paddstr (W, msg->env->subject); else clrtoeol(); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_REPLY_TO: menu->redraw = edit_address_list (HDR_REPLYTO, &msg->env->reply_to); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_FCC: strfcpy (buf, fcc, sizeof (buf)); if (mutt_get_field ("Fcc: ", buf, sizeof (buf), M_FILE | M_CLEAR) == 0) { strfcpy (fcc, buf, fcclen); mutt_pretty_mailbox (fcc, fcclen); move (HDR_FCC, HDR_XOFFSET); mutt_paddstr (W, fcc); fccSet = 1; } MAYBE_REDRAW (menu->redraw); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_MESSAGE: if (Editor && (mutt_strcmp ("builtin", Editor) != 0) && !option (OPTEDITHDRS)) { mutt_edit_file (Editor, msg->content->filename); mutt_update_encoding (msg->content); menu->redraw = REDRAW_FULL; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; } /* fall through */ case OP_COMPOSE_EDIT_HEADERS: if (mutt_strcmp ("builtin", Editor) != 0 && (op == OP_COMPOSE_EDIT_HEADERS || (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS)))) { char *tag = NULL, *err = NULL; mutt_env_to_local (msg->env); mutt_edit_headers (NONULL (Editor), msg->content->filename, msg, fcc, fcclen); if (mutt_env_to_intl (msg->env, &tag, &err)) { mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err); FREE (&err); } if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) crypt_opportunistic_encrypt (msg); } else { /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the attachment list could change if the user invokes ~v to edit the message with headers, in which we need to execute the code below to regenerate the index array */ mutt_builtin_editor (msg->content->filename, msg, cur); } mutt_update_encoding (msg->content); /* attachments may have been added */ if (idxlen && idx[idxlen - 1]->content->next) { for (i = 0; i < idxlen; i++) { FREE (&idx[i]->tree); FREE (&idx[i]); } idxlen = 0; idx = mutt_gen_attach_list (msg->content, -1, idx, &idxlen, &idxmax, 0, 1); menu->data = idx; menu->max = idxlen; } menu->redraw = REDRAW_FULL; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_ATTACH_KEY: if (!(WithCrypto & APPLICATION_PGP)) break; if (idxlen == idxmax) { safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5)); menu->data = idx; } idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); if ((idx[idxlen]->content = crypt_pgp_make_key_attachment(NULL)) != NULL) { update_idx (menu, idx, idxlen++); menu->redraw |= REDRAW_INDEX; } else FREE (&idx[idxlen]); menu->redraw |= REDRAW_STATUS; if (option(OPTNEEDREDRAW)) { menu->redraw = REDRAW_FULL; unset_option(OPTNEEDREDRAW); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_ATTACH_FILE: { char *prompt, **files; int error, numfiles; fname[0] = 0; prompt = _("Attach file"); numfiles = 0; files = NULL; if (_mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 0, 1, &files, &numfiles) == -1 || *fname == '\0') break; if (idxlen + numfiles >= idxmax) { safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + numfiles)); menu->data = idx; } error = 0; if (numfiles > 1) mutt_message _("Attaching selected files..."); for (i = 0; i < numfiles; i++) { char *att = files[i]; idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); idx[idxlen]->unowned = 1; idx[idxlen]->content = mutt_make_file_attach (att); if (idx[idxlen]->content != NULL) update_idx (menu, idx, idxlen++); else { error = 1; mutt_error (_("Unable to attach %s!"), att); FREE (&idx[idxlen]); } } FREE (&files); if (!error) mutt_clear_error (); menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_ATTACH_MESSAGE: { char *prompt; HEADER *h; fname[0] = 0; prompt = _("Open mailbox to attach message from"); if (Context) { strfcpy (fname, NONULL (Context->path), sizeof (fname)); mutt_pretty_mailbox (fname, sizeof (fname)); } if (mutt_enter_fname (prompt, fname, sizeof (fname), &menu->redraw, 1) == -1 || !fname[0]) break; mutt_expand_path (fname, sizeof (fname)); #ifdef USE_IMAP if (!mx_is_imap (fname)) #endif #ifdef USE_POP if (!mx_is_pop (fname)) #endif /* check to make sure the file exists and is readable */ if (access (fname, R_OK) == -1) { mutt_perror (fname); break; } menu->redraw = REDRAW_FULL; ctx = mx_open_mailbox (fname, M_READONLY, NULL); if (ctx == NULL) { mutt_error (_("Unable to open mailbox %s"), fname); break; } if (!ctx->msgcount) { mx_close_mailbox (ctx, NULL); FREE (&ctx); mutt_error _("No messages in that folder."); break; } this = Context; /* remember current folder and sort methods*/ oldSort = Sort; oldSortAux = SortAux; Context = ctx; set_option(OPTATTACHMSG); mutt_message _("Tag the messages you want to attach!"); close = mutt_index_menu (); unset_option(OPTATTACHMSG); if (!Context) { /* go back to the folder we started from */ Context = this; /* Restore old $sort and $sort_aux */ Sort = oldSort; SortAux = oldSortAux; menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; break; } if (idxlen + Context->tagged >= idxmax) { safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5 + Context->tagged)); menu->data = idx; } for (i = 0; i < Context->msgcount; i++) { h = Context->hdrs[i]; if (h->tagged) { idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); idx[idxlen]->content = mutt_make_message_attach (Context, h, 1); if (idx[idxlen]->content != NULL) update_idx (menu, idx, idxlen++); else { mutt_error _("Unable to attach!"); FREE (&idx[idxlen]); } } } menu->redraw |= REDRAW_FULL; if (close == OP_QUIT) mx_close_mailbox (Context, NULL); else mx_fastclose_mailbox (Context); FREE (&Context); /* go back to the folder we started from */ Context = this; /* Restore old $sort and $sort_aux */ Sort = oldSort; SortAux = oldSortAux; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_DELETE: CHECK_COUNT; if (idx[menu->current]->unowned) idx[menu->current]->content->unlink = 0; if (delete_attachment (menu, &idxlen, menu->current) == -1) break; mutt_update_tree (idx, idxlen); if (idxlen) { if (menu->current > idxlen - 1) menu->current = idxlen - 1; } else menu->current = 0; if (menu->current == 0) msg->content = idx[0]->content; menu->redraw |= REDRAW_STATUS; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; #define CURRENT idx[menu->current]->content case OP_COMPOSE_TOGGLE_RECODE: { CHECK_COUNT; if (!mutt_is_text_part (CURRENT)) { mutt_error (_("Recoding only affects text attachments.")); break; } CURRENT->noconv = !CURRENT->noconv; if (CURRENT->noconv) mutt_message (_("The current attachment won't be converted.")); else mutt_message (_("The current attachment will be converted.")); menu->redraw = REDRAW_CURRENT; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; } #undef CURRENT case OP_COMPOSE_EDIT_DESCRIPTION: CHECK_COUNT; strfcpy (buf, idx[menu->current]->content->description ? idx[menu->current]->content->description : "", sizeof (buf)); /* header names should not be translated */ if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0) { mutt_str_replace (&idx[menu->current]->content->description, buf); menu->redraw = REDRAW_CURRENT; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_UPDATE_ENCODING: CHECK_COUNT; if (menu->tagprefix) { BODY *top; for (top = msg->content; top; top = top->next) { if (top->tagged) mutt_update_encoding (top); } menu->redraw = REDRAW_FULL; } else { mutt_update_encoding(idx[menu->current]->content); menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_TOGGLE_DISPOSITION: /* toggle the content-disposition between inline/attachment */ idx[menu->current]->content->disposition = (idx[menu->current]->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE; menu->redraw = REDRAW_CURRENT; break; case OP_EDIT_TYPE: CHECK_COUNT; { mutt_edit_content_type (NULL, idx[menu->current]->content, NULL); /* this may have been a change to text/something */ mutt_update_encoding (idx[menu->current]->content); menu->redraw = REDRAW_CURRENT; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_ENCODING: CHECK_COUNT; strfcpy (buf, ENCODING (idx[menu->current]->content->encoding), sizeof (buf)); if (mutt_get_field ("Content-Transfer-Encoding: ", buf, sizeof (buf), 0) == 0 && buf[0]) { if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED) { idx[menu->current]->content->encoding = i; menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; mutt_clear_error(); } else mutt_error _("Invalid encoding."); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_SEND_MESSAGE: /* Note: We don't invoke send2-hook here, since we want to leave * users an opportunity to change settings from the ":" prompt. */ if(check_attachments(idx, idxlen) != 0) { menu->redraw = REDRAW_FULL; break; } #ifdef MIXMASTER if (msg->chain && mix_check_message (msg) != 0) break; #endif if (!fccSet && *fcc) { if ((i = query_quadoption (OPT_COPY, _("Save a copy of this message?"))) == -1) break; else if (i == M_NO) *fcc = 0; } loop = 0; r = 0; break; case OP_COMPOSE_EDIT_FILE: CHECK_COUNT; mutt_edit_file (NONULL(Editor), idx[menu->current]->content->filename); mutt_update_encoding (idx[menu->current]->content); menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_TOGGLE_UNLINK: CHECK_COUNT; idx[menu->current]->content->unlink = !idx[menu->current]->content->unlink; #if 0 /* OPTRESOLVE is otherwise ignored on this menu. * Where's the bug? */ if (option (OPTRESOLVE) && menu->current + 1 < menu->max) menu->current++; # endif menu->redraw = REDRAW_INDEX; /* No send2hook since this doesn't change the message. */ break; case OP_COMPOSE_GET_ATTACHMENT: CHECK_COUNT; if(menu->tagprefix) { BODY *top; for(top = msg->content; top; top = top->next) { if(top->tagged) mutt_get_tmp_attachment(top); } menu->redraw = REDRAW_FULL; } else if (mutt_get_tmp_attachment(idx[menu->current]->content) == 0) menu->redraw = REDRAW_CURRENT; /* No send2hook since this doesn't change the message. */ break; case OP_COMPOSE_RENAME_FILE: CHECK_COUNT; strfcpy (fname, idx[menu->current]->content->filename, sizeof (fname)); mutt_pretty_mailbox (fname, sizeof (fname)); if (mutt_get_field (_("Rename to: "), fname, sizeof (fname), M_FILE) == 0 && fname[0]) { if (stat(idx[menu->current]->content->filename, &st) == -1) { /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */ mutt_error (_("Can't stat %s: %s"), fname, strerror (errno)); break; } mutt_expand_path (fname, sizeof (fname)); if(mutt_rename_file (idx[menu->current]->content->filename, fname)) break; mutt_str_replace (&idx[menu->current]->content->filename, fname); menu->redraw = REDRAW_CURRENT; if(idx[menu->current]->content->stamp >= st.st_mtime) mutt_stamp_attachment(idx[menu->current]->content); } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_NEW_MIME: { char type[STRING]; char *p; int itype; FILE *fp; CLEARLINE (LINES-1); fname[0] = 0; if (mutt_get_field (_("New file: "), fname, sizeof (fname), M_FILE) != 0 || !fname[0]) continue; mutt_expand_path (fname, sizeof (fname)); /* Call to lookup_mime_type () ? maybe later */ type[0] = 0; if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0 || !type[0]) continue; if (!(p = strchr (type, '/'))) { mutt_error _("Content-Type is of the form base/sub"); continue; } *p++ = 0; if ((itype = mutt_check_mime_type (type)) == TYPEOTHER) { mutt_error (_("Unknown Content-Type %s"), type); continue; } if (idxlen == idxmax) { safe_realloc (&idx, sizeof (ATTACHPTR *) * (idxmax += 5)); menu->data = idx; } idx[idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); /* Touch the file */ if (!(fp = safe_fopen (fname, "w"))) { mutt_error (_("Can't create file %s"), fname); FREE (&idx[idxlen]); continue; } safe_fclose (&fp); if ((idx[idxlen]->content = mutt_make_file_attach (fname)) == NULL) { mutt_error _("What we have here is a failure to make an attachment"); continue; } update_idx (menu, idx, idxlen++); idx[menu->current]->content->type = itype; mutt_str_replace (&idx[menu->current]->content->subtype, p); idx[menu->current]->content->unlink = 1; menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; if (mutt_compose_attachment (idx[menu->current]->content)) { mutt_update_encoding (idx[menu->current]->content); menu->redraw = REDRAW_FULL; } } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_MIME: CHECK_COUNT; if (mutt_edit_attachment (idx[menu->current]->content)) { mutt_update_encoding (idx[menu->current]->content); menu->redraw = REDRAW_FULL; } mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_VIEW_ATTACH: case OP_DISPLAY_HEADERS: CHECK_COUNT; mutt_attach_display_loop (menu, op, NULL, NULL, NULL, &idx, &idxlen, NULL, 0); menu->redraw = REDRAW_FULL; /* no send2hook, since this doesn't modify the message */ break; case OP_SAVE: CHECK_COUNT; mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content, NULL, menu); MAYBE_REDRAW (menu->redraw); /* no send2hook, since this doesn't modify the message */ break; case OP_PRINT: CHECK_COUNT; mutt_print_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content); /* no send2hook, since this doesn't modify the message */ break; case OP_PIPE: case OP_FILTER: CHECK_COUNT; mutt_pipe_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : idx[menu->current]->content, op == OP_FILTER); if (op == OP_FILTER) /* cte might have changed */ menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT; menu->redraw |= REDRAW_STATUS; mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_EXIT: if ((i = query_quadoption (OPT_POSTPONE, _("Postpone this message?"))) == M_NO) { for (i = 0; i < idxlen; i++) if (idx[i]->unowned) idx[i]->content->unlink = 0; if (!(flags & M_COMPOSE_NOFREEHEADER)) { while (idxlen-- > 0) { /* avoid freeing other attachments */ idx[idxlen]->content->next = NULL; idx[idxlen]->content->parts = NULL; mutt_free_body (&idx[idxlen]->content); FREE (&idx[idxlen]->tree); FREE (&idx[idxlen]); } FREE (&idx); idxlen = 0; idxmax = 0; } r = -1; loop = 0; break; } else if (i == -1) break; /* abort */ /* fall through to postpone! */ case OP_COMPOSE_POSTPONE_MESSAGE: if(check_attachments(idx, idxlen) != 0) { menu->redraw = REDRAW_FULL; break; } loop = 0; r = 1; break; case OP_COMPOSE_ISPELL: endwin (); snprintf (buf, sizeof (buf), "%s -x %s", NONULL(Ispell), msg->content->filename); if (mutt_system (buf) == -1) mutt_error (_("Error running \"%s\"!"), buf); else { mutt_update_encoding (msg->content); menu->redraw |= REDRAW_STATUS; } break; case OP_COMPOSE_WRITE_MESSAGE: fname[0] = '\0'; if (Context) { strfcpy (fname, NONULL (Context->path), sizeof (fname)); mutt_pretty_mailbox (fname, sizeof (fname)); } if (idxlen) msg->content = idx[0]->content; if (mutt_enter_fname (_("Write message to mailbox"), fname, sizeof (fname), &menu->redraw, 1) != -1 && fname[0]) { mutt_message (_("Writing message to %s ..."), fname); mutt_expand_path (fname, sizeof (fname)); if (msg->content->next) msg->content = mutt_make_multipart (msg->content); if (mutt_write_fcc (fname, msg, NULL, 0, NULL) < 0) msg->content = mutt_remove_multipart (msg->content); else mutt_message _("Message written."); } break; case OP_COMPOSE_PGP_MENU: if (!(WithCrypto & APPLICATION_PGP)) break; if ((WithCrypto & APPLICATION_SMIME) && (msg->security & APPLICATION_SMIME)) { if (msg->security & (ENCRYPT | SIGN)) { if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "), M_YES) != M_YES) { mutt_clear_error (); break; } msg->security &= ~(ENCRYPT | SIGN); } msg->security &= ~APPLICATION_SMIME; msg->security |= APPLICATION_PGP; crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } msg->security = crypt_pgp_send_menu (msg, &menu->redraw); redraw_crypt_lines (msg); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_FORGET_PASSPHRASE: crypt_forget_passphrase (); break; case OP_COMPOSE_SMIME_MENU: if (!(WithCrypto & APPLICATION_SMIME)) break; if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP)) { if (msg->security & (ENCRYPT | SIGN)) { if (mutt_yesorno (_("PGP already selected. Clear & continue ? "), M_YES) != M_YES) { mutt_clear_error (); break; } msg->security &= ~(ENCRYPT | SIGN); } msg->security &= ~APPLICATION_PGP; msg->security |= APPLICATION_SMIME; crypt_opportunistic_encrypt (msg); redraw_crypt_lines (msg); } msg->security = crypt_smime_send_menu(msg, &menu->redraw); redraw_crypt_lines (msg); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; #ifdef MIXMASTER case OP_COMPOSE_MIX: mix_make_chain (&msg->chain, &menu->redraw); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; #endif } /* Draw formatted compose status line */ if (menu->redraw & REDRAW_STATUS) { compose_status_line (buf, sizeof (buf), 0, menu, NONULL(ComposeFormat)); move(option (OPTSTATUSONTOP) ? 0 : LINES-2, 0); SETCOLOR (MT_COLOR_STATUS); mutt_paddstr (COLS, buf); NORMAL_COLOR; menu->redraw &= ~REDRAW_STATUS; } } mutt_menuDestroy (&menu); if (idxlen) { msg->content = idx[0]->content; for (i = 0; i < idxlen; i++) { idx[i]->content->aptr = NULL; FREE (&idx[i]->tree); FREE (&idx[i]); } } else msg->content = NULL; FREE (&idx); return (r); }
/* 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; if (!Postponed) return (-1); 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); mem_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); mem_free (&PostContext); return (-1); } if (mutt_prepare_template (NULL, PostContext, hdr, h, 0) < 0) { mx_fastclose_mailbox (PostContext); mem_free (&PostContext); return (-1); } /* finished with this message, so delete it. */ mutt_set_flag (PostContext, h, M_DELETE, 1); /* and consider it saved, so that it won't be moved to the trash folder */ mutt_set_flag (PostContext, h, M_APPENDED, 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); mem_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 = tmp->data + 18; SKIPWS (p); 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 = 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; } else if ((WithCrypto & APPLICATION_PGP) && (str_ncmp ("Pgp:", tmp->data, 4) == 0 /* this is generated * by old mutt versions */ || str_ncmp ("X-Mutt-PGP:", tmp->data, 11) == 0)) { hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1); 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) && str_ncmp ("X-Mutt-SMIME:", tmp->data, 13) == 0) { hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1); 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 (str_ncmp ("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); }
void mutt_edit_headers (const char *editor, const char *body, HEADER *msg, char *fcc, size_t fcclen) { char path[_POSIX_PATH_MAX]; /* tempfile used to edit headers + body */ char buffer[LONG_STRING]; char *p; FILE *ifp, *ofp; int i, keep; int in_reply_to = 0; /* did we see the in-reply-to field ? */ ENVELOPE *n; time_t mtime; struct stat st; LIST *cur, *last = NULL, *tmp; mutt_mktemp (path); if ((ofp = safe_fopen (path, "w")) == NULL) { mutt_perror (path); return; } mutt_write_rfc822_header (ofp, msg->env, NULL, 1, 0); fputc ('\n', ofp); /* tie off the header. */ /* now copy the body of the message. */ if ((ifp = fopen (body, "r")) == NULL) { mutt_perror (body); return; } mutt_copy_stream (ifp, ofp); fclose (ifp); fclose (ofp); if (stat (path, &st) == -1) { mutt_perror (path); return; } mtime = st.st_mtime; mutt_edit_file (editor, path); stat (path, &st); if (mtime == st.st_mtime) { dprint (1, (debugfile, "ci_edit_headers(): temp file was not modified.\n")); /* the file has not changed! */ mutt_unlink (path); return; } mutt_unlink (body); mutt_free_list (&msg->env->userhdrs); /* Read the temp file back in */ if ((ifp = fopen (path, "r")) == NULL) { mutt_perror (path); return; } if ((ofp = safe_fopen (body, "w")) == NULL) { /* intentionally leak a possible temporary file here */ fclose (ifp); mutt_perror (body); return; } n = mutt_read_rfc822_header (ifp, NULL, 1, 0); while ((i = fread (buffer, 1, sizeof (buffer), ifp)) > 0) fwrite (buffer, 1, i, ofp); fclose (ofp); fclose (ifp); mutt_unlink (path); /* restore old info. */ n->references = msg->env->references; msg->env->references = NULL; mutt_free_envelope (&msg->env); msg->env = n; mutt_expand_aliases_env (msg->env); /* search through the user defined headers added to see if either a * fcc: or attach-file: field was specified. */ cur = msg->env->userhdrs; while (cur) { keep = 1; /* keep track of whether or not we see the in-reply-to field. if we did * not, remove the references: field later so that we can generate a new * message based upon this one. */ if (mutt_strncasecmp ("in-reply-to. good thing you replied:", cur->data, 12) == 0) in_reply_to = 1; else if (fcc && mutt_strncasecmp ("fcc:", cur->data, 4) == 0) { p = cur->data + 4; SKIPWS (p); if (*p) { strfcpy (fcc, p, fcclen); mutt_pretty_mailbox (fcc); } keep = 0; } else if (mutt_strncasecmp ("attach file:", cur->data, 7) == 0) { BODY *body; BODY *parts; char *q; p = cur->data + 7; SKIPWS (p); if (*p) { if ((q = strpbrk (p, " \t"))) { mutt_substrcpy (path, p, q, sizeof (path)); SKIPWS (q); } else strfcpy (path, p, sizeof (path)); mutt_expand_path (path, sizeof (path)); if ((body = mutt_make_file_attach (path))) { body->description = safe_strdup (q); for (parts = msg->content; parts->next; parts = parts->next) ; parts->next = body; } else { mutt_pretty_mailbox (path); mutt_error (_("%s: unable to attach file. try again"), path); } } keep = 0; } #ifdef HAVE_PGP else if (mutt_strncasecmp ("pgp:", cur->data, 4) == 0) { msg->pgp = mutt_parse_pgp_hdr (cur->data + 4, 0); keep = 0; } #endif if (keep) { last = cur; cur = cur->next; } else { if (last) last->next = cur->next; else msg->env->userhdrs = cur->next; tmp = cur; cur = cur->next; tmp->next = NULL; mutt_free_list (&tmp); } } if (!in_reply_to) mutt_free_list (&msg->env->references); }
int ci_send_message (int flags, /* send mode */ HEADER *msg, /* template to use for new message */ char *tempfile, /* file specified by -i or -H */ CONTEXT *ctx, /* current mailbox */ HEADER *cur) /* current message */ { char buffer[LONG_STRING]; char fcc[_POSIX_PATH_MAX] = ""; /* where to copy this message */ FILE *tempfp = NULL; BODY *pbody; int i, killfrom = 0; int fcc_error = 0; int free_clear_content = 0; BODY *save_content = NULL; BODY *clear_content = NULL; char *pgpkeylist = NULL; /* save current value of "pgp_sign_as" */ char *signas = NULL; char *tag = NULL, *err = NULL; char *ctype; int rv = -1; if (!flags && !msg && quadoption (OPT_RECALL) != M_NO && mutt_num_postponed (1)) { /* If the user is composing a new message, check to see if there * are any postponed messages first. */ if ((i = query_quadoption (OPT_RECALL, _("Recall postponed message?"))) == -1) return rv; if(i == M_YES) flags |= SENDPOSTPONED; } if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED)) signas = safe_strdup(PgpSignAs); /* Delay expansion of aliases until absolutely necessary--shouldn't * be necessary unless we are prompting the user or about to execute a * send-hook. */ if (!msg) { msg = mutt_new_header (); if (flags == SENDPOSTPONED) { if ((flags = mutt_get_postponed (ctx, msg, &cur, fcc, sizeof (fcc))) < 0) goto cleanup; } if (flags & (SENDPOSTPONED|SENDRESEND)) { if ((tempfp = safe_fopen (msg->content->filename, "a+")) == NULL) { mutt_perror (msg->content->filename); goto cleanup; } } if (!msg->env) msg->env = mutt_new_envelope (); } /* Parse and use an eventual list-post header */ if ((flags & SENDLISTREPLY) && cur && cur->env && cur->env->list_post) { /* Use any list-post header as a template */ url_parse_mailto (msg->env, NULL, cur->env->list_post); /* We don't let them set the sender's address. */ rfc822_free_address (&msg->env->from); } if (! (flags & (SENDKEY | SENDPOSTPONED | SENDRESEND))) { pbody = mutt_new_body (); pbody->next = msg->content; /* don't kill command-line attachments */ msg->content = pbody; if (!(ctype = safe_strdup (ContentType))) ctype = safe_strdup ("text/plain"); mutt_parse_content_type (ctype, msg->content); FREE (&ctype); msg->content->unlink = 1; msg->content->use_disp = 0; msg->content->disposition = DISPINLINE; if (!tempfile) { mutt_mktemp (buffer, sizeof (buffer)); tempfp = safe_fopen (buffer, "w+"); msg->content->filename = safe_strdup (buffer); } else { tempfp = safe_fopen (tempfile, "a+"); msg->content->filename = safe_strdup (tempfile); } if (!tempfp) { dprint(1,(debugfile, "newsend_message: can't create tempfile %s (errno=%d)\n", msg->content->filename, errno)); mutt_perror (msg->content->filename); goto cleanup; } } /* this is handled here so that the user can match ~f in send-hook */ if (cur && option (OPTREVNAME) && !(flags & (SENDPOSTPONED|SENDRESEND))) { /* we shouldn't have to worry about freeing `msg->env->from' before * setting it here since this code will only execute when doing some * sort of reply. the pointer will only be set when using the -H command * line option. * * We shouldn't have to worry about alias expansion here since we are * either replying to a real or postponed message, therefore no aliases * should exist since the user has not had the opportunity to add * addresses to the list. We just have to ensure the postponed messages * have their aliases expanded. */ msg->env->from = set_reverse_name (cur->env); } if (! (flags & (SENDPOSTPONED|SENDRESEND))) { if ((flags & (SENDREPLY | SENDFORWARD)) && ctx && envelope_defaults (msg->env, ctx, cur, flags) == -1) goto cleanup; if (option (OPTHDRS)) process_user_recips (msg->env); /* Expand aliases and remove duplicates/crossrefs */ mutt_expand_aliases_env (msg->env); if (flags & SENDREPLY) mutt_fix_reply_recipients (msg->env); if (! (flags & (SENDMAILX|SENDBATCH)) && ! (option (OPTAUTOEDIT) && option (OPTEDITHDRS)) && ! ((flags & SENDREPLY) && option (OPTFASTREPLY))) { if (edit_envelope (msg->env) == -1) goto cleanup; } /* the from address must be set here regardless of whether or not * $use_from is set so that the `~P' (from you) operator in send-hook * patterns will work. if $use_from is unset, the from address is killed * after send-hooks are evaulated */ if (!msg->env->from) { msg->env->from = mutt_default_from (); killfrom = 1; } if ((flags & SENDREPLY) && cur) { /* change setting based upon message we are replying to */ mutt_message_hook (ctx, cur, M_REPLYHOOK); /* * set the replied flag for the message we are generating so that the * user can use ~Q in a send-hook to know when reply-hook's are also * being used. */ msg->replied = 1; } /* change settings based upon recipients */ mutt_message_hook (NULL, msg, M_SENDHOOK); /* * Unset the replied flag from the message we are composing since it is * no longer required. This is done here because the FCC'd copy of * this message was erroneously get the 'R'eplied flag when stored in * a maildir-style mailbox. */ msg->replied = 0; if (! (flags & SENDKEY)) { if (option (OPTTEXTFLOWED) && msg->content->type == TYPETEXT && !ascii_strcasecmp (msg->content->subtype, "plain")) mutt_set_parameter ("format", "flowed", &msg->content->parameter); } /* $use_from and/or $from might have changed in a send-hook */ if (killfrom) { rfc822_free_address (&msg->env->from); if (option (OPTUSEFROM) && !(flags & (SENDPOSTPONED|SENDRESEND))) msg->env->from = mutt_default_from (); killfrom = 0; } if (option (OPTHDRS)) process_user_header (msg->env); if (flags & SENDBATCH) mutt_copy_stream (stdin, tempfp); if (option (OPTSIGONTOP) && ! (flags & (SENDMAILX|SENDKEY|SENDBATCH)) && Editor && mutt_strcmp (Editor, "builtin") != 0) append_signature (tempfp); /* include replies/forwarded messages, unless we are given a template */ if (!tempfile && (ctx || !(flags & (SENDREPLY|SENDFORWARD))) && generate_body (tempfp, msg, flags, ctx, cur) == -1) goto cleanup; if (!option (OPTSIGONTOP) && ! (flags & (SENDMAILX|SENDKEY|SENDBATCH)) && Editor && mutt_strcmp (Editor, "builtin") != 0) append_signature (tempfp); } /* * This hook is even called for postponed messages, and can, e.g., be * used for setting the editor, the sendmail path, or the * envelope sender. */ mutt_message_hook (NULL, msg, M_SEND2HOOK); /* wait until now to set the real name portion of our return address so that $realname can be set in a send-hook */ if (msg->env->from && !msg->env->from->personal && !(flags & (SENDRESEND|SENDPOSTPONED))) msg->env->from->personal = safe_strdup (Realname); if (!((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY))) safe_fclose (&tempfp); if (flags & SENDMAILX) { if (mutt_builtin_editor (msg->content->filename, msg, cur) == -1) goto cleanup; } else if (! (flags & SENDBATCH)) { struct stat st; time_t mtime = mutt_decrease_mtime (msg->content->filename, NULL); mutt_update_encoding (msg->content); /* * Select whether or not the user's editor should be called now. We * don't want to do this when: * 1) we are sending a key/cert * 2) we are forwarding a message and the user doesn't want to edit it. * This is controlled by the quadoption $forward_edit. However, if * both $edit_headers and $autoedit are set, we want to ignore the * setting of $forward_edit because the user probably needs to add the * recipients. */ if (! (flags & SENDKEY) && ((flags & SENDFORWARD) == 0 || (option (OPTEDITHDRS) && option (OPTAUTOEDIT)) || query_quadoption (OPT_FORWEDIT, _("Edit forwarded message?")) == M_YES)) { /* If the this isn't a text message, look for a mailcap edit command */ if (mutt_needs_mailcap (msg->content)) { if (!mutt_edit_attachment (msg->content)) goto cleanup; } else if (!Editor || mutt_strcmp ("builtin", Editor) == 0) mutt_builtin_editor (msg->content->filename, msg, cur); else if (option (OPTEDITHDRS)) { mutt_env_to_local (msg->env); mutt_edit_headers (Editor, msg->content->filename, msg, fcc, sizeof (fcc)); mutt_env_to_idna (msg->env, NULL, NULL); } else { mutt_edit_file (Editor, msg->content->filename); if (stat (msg->content->filename, &st) == 0) { if (mtime != st.st_mtime) fix_end_of_file (msg->content->filename); } else mutt_perror (msg->content->filename); } /* If using format=flowed, perform space stuffing. Avoid stuffing when * recalling a postponed message where the stuffing was already * performed. If it has already been performed, the format=flowed * parameter will be present. */ if (option (OPTTEXTFLOWED) && msg->content->type == TYPETEXT && !ascii_strcasecmp("plain", msg->content->subtype)) { char *p = mutt_get_parameter("format", msg->content->parameter); if (ascii_strcasecmp("flowed", NONULL(p))) rfc3676_space_stuff (msg); } mutt_message_hook (NULL, msg, M_SEND2HOOK); } if (! (flags & (SENDPOSTPONED | SENDFORWARD | SENDKEY | SENDRESEND))) { if (stat (msg->content->filename, &st) == 0) { /* if the file was not modified, bail out now */ if (mtime == st.st_mtime && !msg->content->next && query_quadoption (OPT_ABORT, _("Abort unmodified message?")) == M_YES) { mutt_message _("Aborted unmodified message."); goto cleanup; } } else mutt_perror (msg->content->filename); } } /* * Set the message security unless: * 1) crypto support is not enabled (WithCrypto==0) * 2) pgp: header field was present during message editing with $edit_headers (msg->security != 0) * 3) we are resending a message * 4) we are recalling a postponed message (don't override the user's saved settings) * 5) we are in mailx mode * 6) we are in batch mode * * This is done after allowing the user to edit the message so that security * settings can be configured with send2-hook and $edit_headers. */ if (WithCrypto && (msg->security == 0) && !(flags & (SENDBATCH | SENDMAILX | SENDPOSTPONED | SENDRESEND))) { if (option (OPTCRYPTAUTOSIGN)) msg->security |= SIGN; if (option (OPTCRYPTAUTOENCRYPT)) msg->security |= ENCRYPT; if (option (OPTCRYPTREPLYENCRYPT) && cur && (cur->security & ENCRYPT)) msg->security |= ENCRYPT; if (option (OPTCRYPTREPLYSIGN) && cur && (cur->security & SIGN)) msg->security |= SIGN; if (option (OPTCRYPTREPLYSIGNENCRYPTED) && cur && (cur->security & ENCRYPT)) msg->security |= SIGN; if (WithCrypto & APPLICATION_PGP && (msg->security & (ENCRYPT | SIGN))) { if (option (OPTPGPAUTOINLINE)) msg->security |= INLINE; if (option (OPTPGPREPLYINLINE) && cur && (cur->security & INLINE)) msg->security |= INLINE; } if (msg->security) { /* * When replying / forwarding, use the original message's * crypto system. According to the documentation, * smime_is_default should be disregarded here. * * Problem: At least with forwarding, this doesn't really * make much sense. Should we have an option to completely * disable individual mechanisms at run-time? */ if (cur) { if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP) && (cur->security & APPLICATION_PGP)) msg->security |= APPLICATION_PGP; else if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME) && (cur->security & APPLICATION_SMIME)) msg->security |= APPLICATION_SMIME; } /* * No crypto mechanism selected? Use availability + smime_is_default * for the decision. */ if (!(msg->security & (APPLICATION_SMIME | APPLICATION_PGP))) { if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME) && option (OPTSMIMEISDEFAULT)) msg->security |= APPLICATION_SMIME; else if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP)) msg->security |= APPLICATION_PGP; else if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME)) msg->security |= APPLICATION_SMIME; } } /* No permissible mechanisms found. Don't sign or encrypt. */ if (!(msg->security & (APPLICATION_SMIME|APPLICATION_PGP))) msg->security = 0; } /* specify a default fcc. if we are in batchmode, only save a copy of * the message if the value of $copy is yes or ask-yes */ if (!fcc[0] && !(flags & (SENDPOSTPONED)) && (!(flags & SENDBATCH) || (quadoption (OPT_COPY) & 0x1))) { /* set the default FCC */ if (!msg->env->from) { msg->env->from = mutt_default_from (); killfrom = 1; /* no need to check $use_from because if the user specified a from address it would have already been set by now */ } mutt_select_fcc (fcc, sizeof (fcc), msg); if (killfrom) { rfc822_free_address (&msg->env->from); killfrom = 0; } } mutt_update_encoding (msg->content); if (! (flags & (SENDMAILX | SENDBATCH))) { main_loop: fcc_error = 0; /* reset value since we may have failed before */ mutt_pretty_mailbox (fcc, sizeof (fcc)); i = mutt_compose_menu (msg, fcc, sizeof (fcc), cur); if (i == -1) { /* abort */ mutt_message _("Mail not sent."); goto cleanup; } else if (i == 1) { /* postpone the message until later. */ if (msg->content->next) msg->content = mutt_make_multipart (msg->content); /* * make sure the message is written to the right part of a maildir * postponed folder. */ msg->read = 0; msg->old = 0; encode_descriptions (msg->content, 1); mutt_prepare_envelope (msg->env, 0); mutt_env_to_idna (msg->env, NULL, NULL); /* Handle bad IDNAs the next time. */ if (!Postponed || mutt_write_fcc (NONULL (Postponed), msg, (cur && (flags & SENDREPLY)) ? cur->env->message_id : NULL, 1, fcc) < 0) { msg->content = mutt_remove_multipart (msg->content); decode_descriptions (msg->content); mutt_unprepare_envelope (msg->env); goto main_loop; } mutt_update_num_postponed (); mutt_message _("Message postponed."); goto cleanup; } } if (!has_recips (msg->env->to) && !has_recips (msg->env->cc) && !has_recips (msg->env->bcc)) { if (! (flags & SENDBATCH)) { mutt_error _("No recipients are specified!"); goto main_loop; } else { puts _("No recipients were specified."); goto cleanup; } } if (mutt_env_to_idna (msg->env, &tag, &err)) { mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err); FREE (&err); if (!(flags & SENDBATCH)) goto main_loop; else goto cleanup; } if (!msg->env->subject && ! (flags & SENDBATCH) && (i = query_quadoption (OPT_SUBJECT, _("No subject, abort sending?"))) != M_NO) { /* if the abort is automatic, print an error message */ if (quadoption (OPT_SUBJECT) == M_YES) mutt_error _("No subject specified."); goto main_loop; } if (msg->content->next) msg->content = mutt_make_multipart (msg->content); /* * Ok, we need to do it this way instead of handling all fcc stuff in * one place in order to avoid going to main_loop with encoded "env" * in case of error. Ugh. */ encode_descriptions (msg->content, 1); /* * Make sure that clear_content and free_clear_content are * properly initialized -- we may visit this particular place in * the code multiple times, including after a failed call to * mutt_protect(). */ clear_content = NULL; free_clear_content = 0; if (WithCrypto) { if (msg->security) { /* save the decrypted attachments */ clear_content = msg->content; if ((crypt_get_keys (msg, &pgpkeylist) == -1) || mutt_protect (msg, pgpkeylist) == -1) { msg->content = mutt_remove_multipart (msg->content); FREE (&pgpkeylist); decode_descriptions (msg->content); goto main_loop; } encode_descriptions (msg->content, 0); } /* * at this point, msg->content is one of the following three things: * - multipart/signed. In this case, clear_content is a child. * - multipart/encrypted. In this case, clear_content exists * independently * - application/pgp. In this case, clear_content exists independently. * - something else. In this case, it's the same as clear_content. */ /* This is ugly -- lack of "reporting back" from mutt_protect(). */ if (clear_content && (msg->content != clear_content) && (msg->content->parts != clear_content)) free_clear_content = 1; } if (!option (OPTNOCURSES) && !(flags & SENDMAILX)) mutt_message _("Sending message..."); mutt_prepare_envelope (msg->env, 1); /* save a copy of the message, if necessary. */ mutt_expand_path (fcc, sizeof (fcc)); /* Don't save a copy when we are in batch-mode, and the FCC * folder is on an IMAP server: This would involve possibly lots * of user interaction, which is not available in batch mode. * * Note: A patch to fix the problems with the use of IMAP servers * from non-curses mode is available from Brendan Cully. However, * I'd like to think a bit more about this before including it. */ #ifdef USE_IMAP if ((flags & SENDBATCH) && fcc[0] && mx_is_imap (fcc)) fcc[0] = '\0'; #endif if (*fcc && mutt_strcmp ("/dev/null", fcc) != 0) { BODY *tmpbody = msg->content; BODY *save_sig = NULL; BODY *save_parts = NULL; if (WithCrypto && msg->security && option (OPTFCCCLEAR)) msg->content = clear_content; /* check to see if the user wants copies of all attachments */ if (query_quadoption (OPT_FCCATTACH, _("Save attachments in Fcc?")) != M_YES && msg->content->type == TYPEMULTIPART) { if (WithCrypto && (mutt_strcmp (msg->content->subtype, "encrypted") == 0 || mutt_strcmp (msg->content->subtype, "signed") == 0)) { if (clear_content->type == TYPEMULTIPART) { if(!(msg->security & ENCRYPT) && (msg->security & SIGN)) { /* save initial signature and attachments */ save_sig = msg->content->parts->next; save_parts = clear_content->parts->next; } /* this means writing only the main part */ msg->content = clear_content->parts; if (mutt_protect (msg, pgpkeylist) == -1) { /* we can't do much about it at this point, so * fallback to saving the whole thing to fcc */ msg->content = tmpbody; save_sig = NULL; goto full_fcc; } save_content = msg->content; } } else msg->content = msg->content->parts; } full_fcc: if (msg->content) { /* update received time so that when storing to a mbox-style folder * the From_ line contains the current time instead of when the * message was first postponed. */ msg->received = time (NULL); if (mutt_write_fcc (fcc, msg, NULL, 0, NULL) == -1) { /* * Error writing FCC, we should abort sending. */ fcc_error = 1; } } msg->content = tmpbody; if (WithCrypto && save_sig) { /* cleanup the second signature structures */ if (save_content->parts) { mutt_free_body (&save_content->parts->next); save_content->parts = NULL; } mutt_free_body (&save_content); /* restore old signature and attachments */ msg->content->parts->next = save_sig; msg->content->parts->parts->next = save_parts; } else if (WithCrypto && save_content) { /* destroy the new encrypted body. */ mutt_free_body (&save_content); } } /* * Don't attempt to send the message if the FCC failed. Just pretend * the send failed as well so we give the user a chance to fix the * error. */ if (fcc_error || (i = send_message (msg)) < 0) { if (!(flags & SENDBATCH)) { if (!WithCrypto) ; else if ((msg->security & ENCRYPT) || ((msg->security & SIGN) && msg->content->type == TYPEAPPLICATION)) { mutt_free_body (&msg->content); /* destroy PGP data */ msg->content = clear_content; /* restore clear text. */ } else if ((msg->security & SIGN) && msg->content->type == TYPEMULTIPART) { mutt_free_body (&msg->content->parts->next); /* destroy sig */ msg->content = mutt_remove_multipart (msg->content); } msg->content = mutt_remove_multipart (msg->content); decode_descriptions (msg->content); mutt_unprepare_envelope (msg->env); goto main_loop; } else { puts _("Could not send the message."); goto cleanup; } } else if (!option (OPTNOCURSES) && ! (flags & SENDMAILX)) mutt_message (i == 0 ? _("Mail sent.") : _("Sending in background.")); if (WithCrypto && (msg->security & ENCRYPT)) FREE (&pgpkeylist); if (WithCrypto && free_clear_content) mutt_free_body (&clear_content); /* set 'replied' flag only if the user didn't change/remove In-Reply-To: and References: headers during edit */ if (flags & SENDREPLY) { if (cur && ctx) mutt_set_flag (ctx, cur, M_REPLIED, is_reply (cur, msg)); else if (!(flags & SENDPOSTPONED) && ctx && ctx->tagged) { for (i = 0; i < ctx->vcount; i++) if (ctx->hdrs[ctx->v2r[i]]->tagged) mutt_set_flag (ctx, ctx->hdrs[ctx->v2r[i]], M_REPLIED, is_reply (ctx->hdrs[ctx->v2r[i]], msg)); } } rv = 0; cleanup: if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED)) { if(signas) { FREE (&PgpSignAs); PgpSignAs = signas; } } safe_fclose (&tempfp); mutt_free_header (&msg); return rv; }
int _mutt_enter_string(char *buf, size_t buflen, int y, int x, int flags, int multiple, char ***files, int *numfiles, struct enter_state *state) { int width = COLS - x - 1; int redraw; int pass =(flags & M_PASS); int first = 1; int ch, w, r; size_t i; wchar_t *tempbuf = 0; size_t templen = 0; history_class_t hclass; wchar_t wc; mbstate_t mbstate; int rv = 0; memset(&mbstate, 0, sizeof(mbstate)); if (state->wbuf) { /* Coming back after return 1 */ redraw = M_REDRAW_LINE; first = 0; } else { /* Initialise wbuf from buf */ state->wbuflen = 0; state->lastchar = my_mbstowcs(&state->wbuf, &state->wbuflen, 0, buf); redraw = M_REDRAW_INIT; } if (flags & M_FILE) hclass = HC_FILE; else if (flags & M_EFILE) hclass = HC_MBOX; else if (flags & M_CMD) hclass = HC_CMD; else if (flags & M_ALIAS) hclass = HC_ALIAS; else if (flags & M_COMMAND) hclass = HC_COMMAND; else if (flags & M_PATTERN) hclass = HC_PATTERN; else hclass = HC_OTHER; for(;;) { if (redraw && !pass) { if (redraw == M_REDRAW_INIT) { /* Go to end of line */ state->curpos = state->lastchar; state->begin = width_ceiling(state->wbuf, state->lastchar, my_wcswidth(state->wbuf, state->lastchar) - width + 1); } if (state->curpos < state->begin || my_wcswidth(state->wbuf + state->begin, state->curpos - state->begin) >= width) state->begin = width_ceiling(state->wbuf, state->lastchar, my_wcswidth(state->wbuf, state->curpos) - width / 2); move(y, x); w = 0; for(i = state->begin; i < state->lastchar; i++) { w += my_wcwidth(state->wbuf[i]); if (w > width) break; my_addwch(state->wbuf[i]); } clrtoeol(); move(y, x + my_wcswidth(state->wbuf + state->begin, state->curpos - state->begin)); } mutt_refresh(); if ((ch = km_dokey(MENU_EDITOR)) == -1) { rv = -1; goto bye; } if (ch != OP_NULL) { first = 0; if (ch != OP_EDITOR_COMPLETE && ch != OP_EDITOR_COMPLETE_QUERY) state->tabs = 0; redraw = M_REDRAW_LINE; switch(ch) { case OP_EDITOR_HISTORY_UP: state->curpos = state->lastchar; if (mutt_history_at_scratch(hclass)) { my_wcstombs(buf, buflen, state->wbuf, state->curpos); mutt_history_save_scratch(hclass, buf); } replace_part(state, 0, mutt_history_prev(hclass)); redraw = M_REDRAW_INIT; break; case OP_EDITOR_HISTORY_DOWN: state->curpos = state->lastchar; if (mutt_history_at_scratch(hclass)) { my_wcstombs(buf, buflen, state->wbuf, state->curpos); mutt_history_save_scratch(hclass, buf); } replace_part(state, 0, mutt_history_next(hclass)); redraw = M_REDRAW_INIT; break; case OP_EDITOR_BACKSPACE: if (state->curpos == 0) BEEP(); else { i = state->curpos; while(i && COMB_CHAR(state->wbuf[i - 1])) --i; if (i) --i; memmove(state->wbuf + i, state->wbuf + state->curpos,(state->lastchar - state->curpos) * sizeof(wchar_t)); state->lastchar -= state->curpos - i; state->curpos = i; } break; case OP_EDITOR_BOL: state->curpos = 0; break; case OP_EDITOR_EOL: redraw= M_REDRAW_INIT; break; case OP_EDITOR_KILL_LINE: state->curpos = state->lastchar = 0; break; case OP_EDITOR_KILL_EOL: state->lastchar = state->curpos; break; case OP_EDITOR_BACKWARD_CHAR: if (state->curpos == 0) BEEP(); else { while(state->curpos && COMB_CHAR(state->wbuf[state->curpos - 1])) state->curpos--; if (state->curpos) state->curpos--; } break; case OP_EDITOR_FORWARD_CHAR: if (state->curpos == state->lastchar) BEEP(); else { ++state->curpos; while(state->curpos < state->lastchar && COMB_CHAR(state->wbuf[state->curpos])) ++state->curpos; } break; case OP_EDITOR_BACKWARD_WORD: if (state->curpos == 0) BEEP(); else { while(state->curpos && iswspace(state->wbuf[state->curpos - 1])) --state->curpos; while(state->curpos && !iswspace(state->wbuf[state->curpos - 1])) --state->curpos; } break; case OP_EDITOR_FORWARD_WORD: if (state->curpos == state->lastchar) BEEP(); else { while(state->curpos < state->lastchar && iswspace(state->wbuf[state->curpos])) ++state->curpos; while(state->curpos < state->lastchar && !iswspace(state->wbuf[state->curpos])) ++state->curpos; } break; case OP_EDITOR_CAPITALIZE_WORD: case OP_EDITOR_UPCASE_WORD: case OP_EDITOR_DOWNCASE_WORD: if (state->curpos == state->lastchar) { BEEP(); break; } while(state->curpos && !iswspace(state->wbuf[state->curpos])) --state->curpos; while(state->curpos < state->lastchar && iswspace(state->wbuf[state->curpos])) ++state->curpos; while(state->curpos < state->lastchar && !iswspace(state->wbuf[state->curpos])) { if (ch == OP_EDITOR_DOWNCASE_WORD) state->wbuf[state->curpos] = towlower(state->wbuf[state->curpos]); else { state->wbuf[state->curpos] = towupper(state->wbuf[state->curpos]); if (ch == OP_EDITOR_CAPITALIZE_WORD) ch = OP_EDITOR_DOWNCASE_WORD; } state->curpos++; } break; case OP_EDITOR_DELETE_CHAR: if (state->curpos == state->lastchar) BEEP(); else { i = state->curpos; while(i < state->lastchar && COMB_CHAR(state->wbuf[i])) ++i; if (i < state->lastchar) ++i; while(i < state->lastchar && COMB_CHAR(state->wbuf[i])) ++i; memmove(state->wbuf + state->curpos, state->wbuf + i,(state->lastchar - i) * sizeof(wchar_t)); state->lastchar -= i - state->curpos; } break; case OP_EDITOR_KILL_WORD: /* delete to beginning of word */ if (state->curpos != 0) { i = state->curpos; while(i && iswspace(state->wbuf[i - 1])) --i; if (i) { if (iswalnum(state->wbuf[i - 1])) { for(--i; i && iswalnum(state->wbuf[i - 1]); i--) ; } else --i; } memmove(state->wbuf + i, state->wbuf + state->curpos, (state->lastchar - state->curpos) * sizeof(wchar_t)); state->lastchar += i - state->curpos; state->curpos = i; } break; case OP_EDITOR_KILL_EOW: /* delete to end of word */ /* first skip over whitespace */ for(i = state->curpos; i < state->lastchar && iswspace(state->wbuf[i]); i++) ; /* if there are any characters left.. */ if (i < state->lastchar) { /* if the current character is alphanumeric.. */ if (iswalnum(state->wbuf[i])) { /* skip over the rest of the word consistent of only alphanumerics */ for(; i < state->lastchar && iswalnum(state->wbuf[i]); i++) ; } else { /* skip over one non-alphanumeric character */ ++i; } } memmove(state->wbuf + state->curpos, state->wbuf + i, (state->lastchar - i) * sizeof(wchar_t)); state->lastchar += state->curpos - i; break; case OP_EDITOR_BUFFY_CYCLE: if (flags & M_EFILE) { first = 1; /* clear input if user types a real key later */ my_wcstombs(buf, buflen, state->wbuf, state->curpos); mutt_buffy(buf, buflen); state->curpos = state->lastchar = my_mbstowcs(&state->wbuf, &state->wbuflen, 0, buf); break; } else if (!(flags & M_FILE)) goto self_insert; /* fall through to completion routine(M_FILE) */ case OP_EDITOR_COMPLETE: case OP_EDITOR_COMPLETE_QUERY: state->tabs++; if (flags & M_CMD) { for(i = state->curpos; i && !is_shell_char(state->wbuf[i-1]); i--) ; my_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i); if (tempbuf && templen == state->lastchar - i && !memcmp(tempbuf, state->wbuf + i,(state->lastchar - i) * sizeof(wchar_t))) { mutt_select_file(buf, buflen,(flags & M_EFILE) ? M_SEL_FOLDER : 0); set_bit(options, OPTNEEDREDRAW); if (*buf) replace_part(state, i, buf); rv = 1; goto bye; } if (!mutt_complete(buf, buflen)) { templen = state->lastchar - i; safe_realloc(&tempbuf, templen * sizeof(wchar_t)); } else BEEP(); replace_part(state, i, buf); } else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE) { /* invoke the alias-menu to get more addresses */ for(i = state->curpos; i && state->wbuf[i-1] != ',' && state->wbuf[i-1] != ':'; i--) ; for(; i < state->lastchar && state->wbuf[i] == ' '; i++) ; my_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i); r = mutt_alias_complete(buf, buflen); replace_part(state, i, buf); if (!r) { rv = 1; goto bye; } break; } else if (flags & M_ALIAS && ch == OP_EDITOR_COMPLETE_QUERY) { /* invoke the query-menu to get more addresses */ if ((i = state->curpos)) { for(; i && state->wbuf[i - 1] != ','; i--) ; for(; i < state->curpos && state->wbuf[i] == ' '; i++) ; } my_wcstombs(buf, buflen, state->wbuf + i, state->curpos - i); mutt_query_complete(buf, buflen); replace_part(state, i, buf); rv = 1; goto bye; } else if (flags & M_COMMAND) { my_wcstombs(buf, buflen, state->wbuf, state->curpos); i = strlen(buf); if (i && buf[i - 1] == '=' && mutt_var_value_complete(buf, buflen, i)) state->tabs = 0; else if (!mutt_command_complete(buf, buflen, i, state->tabs)) BEEP(); replace_part(state, 0, buf); } else if (flags &(M_FILE | M_EFILE)) { my_wcstombs(buf, buflen, state->wbuf, state->curpos); /* see if the path has changed from the last time */ if ((!tempbuf && !state->lastchar) ||(tempbuf && templen == state->lastchar && !memcmp(tempbuf, state->wbuf, state->lastchar * sizeof(wchar_t)))) { _mutt_select_file(buf, buflen, ((flags & M_EFILE) ? M_SEL_FOLDER : 0) |(multiple ? M_SEL_MULTI : 0), files, numfiles); set_bit(options, OPTNEEDREDRAW); if (*buf) { mutt_pretty_mailbox(buf, buflen); if (!pass) mutt_history_add(hclass, buf, 1); rv = 0; goto bye; } /* file selection cancelled */ rv = 1; goto bye; } if (!mutt_complete(buf, buflen)) { templen = state->lastchar; safe_realloc(&tempbuf, templen * sizeof(wchar_t)); memcpy(tempbuf, state->wbuf, templen * sizeof(wchar_t)); } else BEEP(); /* let the user know that nothing matched */ replace_part(state, 0, buf); } else goto self_insert; break; case OP_EDITOR_QUOTE_CHAR: { struct event event; /*ADDCH(LastKey);*/ event = mutt_getch(); if (event.ch >= 0) { LastKey = event.ch; goto self_insert; } } case OP_EDITOR_TRANSPOSE_CHARS: if (state->lastchar < 2) BEEP(); else { wchar_t t; if (state->curpos == 0) state->curpos = 2; else if (state->curpos < state->lastchar) ++state->curpos; t = state->wbuf[state->curpos - 2]; state->wbuf[state->curpos - 2] = state->wbuf[state->curpos - 1]; state->wbuf[state->curpos - 1] = t; } break; default: BEEP(); } } else { self_insert: state->tabs = 0; /* use the raw keypress */ ch = LastKey; #if KEY_ENTER /* treat ENTER the same as RETURN */ if (ch == KEY_ENTER) ch = '\r'; #endif /* quietly ignore all other function keys */ if (ch & ~0xff) continue; /* gather the octets into a wide character */ { char c; size_t k; c = ch; k = mbrtowc(&wc, &c, 1, &mbstate); if (k ==(size_t)(-2)) continue; else if (k && k != 1) { memset(&mbstate, 0, sizeof(mbstate)); continue; } } if (first &&(flags & M_CLEAR)) { first = 0; if (IsWPrint(wc)) /* why? */ state->curpos = state->lastchar = 0; } if (wc == '\r' || wc == '\n') { /* Convert from wide characters */ my_wcstombs(buf, buflen, state->wbuf, state->lastchar); if (!pass) mutt_history_add(hclass, buf, 1); if (multiple) { char **tfiles; *numfiles = 1; tfiles = safe_calloc(*numfiles, sizeof(char *)); mutt_expand_path(buf, buflen); tfiles[0] = safe_strdup(buf); *files = tfiles; } rv = 0; goto bye; } else if (wc &&(wc < ' ' || IsWPrint(wc))) /* why? */ { if (state->lastchar >= state->wbuflen) { state->wbuflen = state->lastchar + 20; safe_realloc(&state->wbuf, state->wbuflen * sizeof(wchar_t)); } memmove(state->wbuf + state->curpos + 1, state->wbuf + state->curpos,(state->lastchar - state->curpos) * sizeof(wchar_t)); state->wbuf[state->curpos++] = wc; state->lastchar++; } else { mutt_flushinp(); BEEP(); } } } bye: mutt_reset_history_state(hclass); safe_free(&tempbuf); return rv; }
/** * 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; }
/** * dump_config - Write all the config to a file * @param cs ConfigSet to dump * @param style Output style, e.g. #CS_DUMP_STYLE_MUTT * @param flags Flags, see #ConfigDumpFlags * @param fp File to write config to */ bool dump_config(struct ConfigSet *cs, enum CsDumpStyle style, ConfigDumpFlags flags, FILE *fp) { if (!cs) return false; struct HashElem *he = NULL; struct HashElem **list = get_elem_list(cs); if (!list) return false; /* LCOV_EXCL_LINE */ bool result = true; struct Buffer *value = mutt_buffer_alloc(256); struct Buffer *initial = mutt_buffer_alloc(256); struct Buffer *tmp = mutt_buffer_alloc(256); for (size_t i = 0; list[i]; i++) { mutt_buffer_reset(value); mutt_buffer_reset(initial); he = list[i]; const int type = DTYPE(he->type); if ((type == DT_SYNONYM) && !(flags & CS_DUMP_SHOW_SYNONYMS)) continue; // if ((type == DT_DISABLED) && !(flags & CS_DUMP_SHOW_DISABLED)) // continue; if (type != DT_SYNONYM) { /* If necessary, get the current value */ if ((flags & CS_DUMP_ONLY_CHANGED) || !(flags & CS_DUMP_HIDE_VALUE) || (flags & CS_DUMP_SHOW_DEFAULTS)) { int rc = cs_he_string_get(cs, he, value); if (CSR_RESULT(rc) != CSR_SUCCESS) { result = false; /* LCOV_EXCL_LINE */ break; /* LCOV_EXCL_LINE */ } const struct ConfigDef *cdef = he->data; if (IS_SENSITIVE(*cdef) && (flags & CS_DUMP_HIDE_SENSITIVE) && !mutt_buffer_is_empty(value)) { mutt_buffer_reset(value); mutt_buffer_addstr(value, "***"); } if ((type == DT_PATH) && (value->data[0] == '/')) mutt_pretty_mailbox(value->data, value->dsize); if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_LONG) && (type != DT_QUAD) && !(flags & CS_DUMP_NO_ESCAPING)) { mutt_buffer_reset(tmp); pretty_var(value->data, tmp); mutt_buffer_strcpy(value, tmp->data); } } /* If necessary, get the default value */ if (flags & (CS_DUMP_ONLY_CHANGED | CS_DUMP_SHOW_DEFAULTS)) { int rc = cs_he_initial_get(cs, he, initial); if (CSR_RESULT(rc) != CSR_SUCCESS) { result = false; /* LCOV_EXCL_LINE */ break; /* LCOV_EXCL_LINE */ } if ((type == DT_PATH) && !(he->type & DT_MAILBOX)) mutt_pretty_mailbox(initial->data, initial->dsize); if ((type != DT_BOOL) && (type != DT_NUMBER) && (type != DT_LONG) && (type != DT_QUAD) && !(flags & CS_DUMP_NO_ESCAPING)) { mutt_buffer_reset(tmp); pretty_var(initial->data, tmp); mutt_buffer_strcpy(initial, tmp->data); } } } if (style == CS_DUMP_STYLE_MUTT) dump_config_mutt(cs, he, value, initial, flags, fp); else dump_config_neo(cs, he, value, initial, flags, fp); } FREE(&list); mutt_buffer_free(&value); mutt_buffer_free(&initial); mutt_buffer_free(&tmp); return result; }
/* 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; }
/* 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); }