int mutt_prepare_template (FILE *fp, CONTEXT *ctx, HEADER *newhdr, HEADER *hdr, short weed) { MESSAGE *msg = NULL; char file[_POSIX_PATH_MAX]; BODY *b; FILE *bfp; int rv = -1; STATE s; memset (&s, 0, sizeof (s)); if (!fp && (msg = mx_open_message (ctx, hdr->msgno)) == NULL) return (-1); if (!fp) fp = msg->fp; bfp = fp; /* parse the message header and MIME structure */ fseeko (fp, hdr->offset, 0); newhdr->offset = hdr->offset; newhdr->env = mutt_read_rfc822_header (fp, newhdr, 1, weed); newhdr->content->length = hdr->content->length; mutt_parse_part (fp, newhdr->content); FREE (&newhdr->env->message_id); FREE (&newhdr->env->mail_followup_to); /* really? */ /* decrypt pgp/mime encoded messages */ if ((WithCrypto & (APPLICATION_PGP|APPLICATION_SMIME) & hdr->security) && mutt_is_multipart_encrypted (newhdr->content)) { int ccap = WithCrypto & (APPLICATION_PGP|APPLICATION_SMIME) & hdr->security; newhdr->security |= ENCRYPT | ccap; if (!crypt_valid_passphrase (ccap)) goto err; mutt_message _("Decrypting message..."); if (((ccap & APPLICATION_PGP) && crypt_pgp_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) || ((ccap & APPLICATION_SMIME) && crypt_smime_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) || b == NULL) { err: mx_close_message (&msg); mutt_free_envelope (&newhdr->env); mutt_free_body (&newhdr->content); mutt_error _("Decryption failed."); return -1; } mutt_free_body (&newhdr->content); newhdr->content = b; mutt_clear_error (); } /* * remove a potential multipart/signed layer - useful when * resending messages */ if (WithCrypto && mutt_is_multipart_signed (newhdr->content)) { newhdr->security |= SIGN; if ((WithCrypto & APPLICATION_PGP) && ascii_strcasecmp (mutt_get_parameter ("protocol", newhdr->content->parameter), "application/pgp-signature") == 0) newhdr->security |= APPLICATION_PGP; else if ((WithCrypto & APPLICATION_SMIME)) newhdr->security |= APPLICATION_SMIME; /* destroy the signature */ mutt_free_body (&newhdr->content->parts->next); newhdr->content = mutt_remove_multipart (newhdr->content); } /* * We don't need no primary multipart. * Note: We _do_ preserve messages! * * XXX - we don't handle multipart/alternative in any * smart way when sending messages. However, one may * consider this a feature. * */ if (newhdr->content->type == TYPEMULTIPART) newhdr->content = mutt_remove_multipart (newhdr->content); s.fpin = bfp; /* create temporary files for all attachments */ for (b = newhdr->content; b; b = b->next) { /* what follows is roughly a receive-mode variant of * mutt_get_tmp_attachment () from muttlib.c */ file[0] = '\0'; if (b->filename) { strfcpy (file, b->filename, sizeof (file)); b->d_filename = safe_strdup (b->filename); } else { /* avoid Content-Disposition: header with temporary filename */ b->use_disp = 0; } /* set up state flags */ s.flags = 0; if (b->type == TYPETEXT) { if (!ascii_strcasecmp ("yes", mutt_get_parameter ("x-mutt-noconv", b->parameter))) b->noconv = 1; else { s.flags |= M_CHARCONV; b->noconv = 0; } mutt_delete_parameter ("x-mutt-noconv", &b->parameter); } mutt_adv_mktemp (file, sizeof(file)); if ((s.fpout = safe_fopen (file, "w")) == NULL) goto bail; if ((WithCrypto & APPLICATION_PGP) && (mutt_is_application_pgp (b) & (ENCRYPT|SIGN))) { mutt_body_handler (b, &s); newhdr->security |= mutt_is_application_pgp (newhdr->content); b->type = TYPETEXT; mutt_str_replace (&b->subtype, "plain"); mutt_delete_parameter ("x-action", &b->parameter); } else mutt_decode_attachment (b, &s); if (safe_fclose (&s.fpout) != 0) goto bail; mutt_str_replace (&b->filename, file); b->unlink = 1; mutt_stamp_attachment (b); mutt_free_body (&b->parts); if (b->hdr) b->hdr->content = NULL; /* avoid dangling pointer */ } /* Fix encryption flags. */ /* No inline if multipart. */ if (WithCrypto && (newhdr->security & INLINE) && newhdr->content->next) newhdr->security &= ~INLINE; /* Do we even support multiple mechanisms? */ newhdr->security &= WithCrypto | ~(APPLICATION_PGP|APPLICATION_SMIME); /* Theoretically, both could be set. Take the one the user wants to set by default. */ if ((newhdr->security & APPLICATION_PGP) && (newhdr->security & APPLICATION_SMIME)) { if (option (OPTSMIMEISDEFAULT)) newhdr->security &= ~APPLICATION_PGP; else newhdr->security &= ~APPLICATION_SMIME; } rv = 0; bail: /* that's it. */ if (bfp != fp) safe_fclose (&bfp); if (msg) mx_close_message (&msg); if (rv == -1) { mutt_free_envelope (&newhdr->env); mutt_free_body (&newhdr->content); } return rv; }
int mutt_parse_crypt_hdr (const char *p, int set_empty_signas, int crypt_app) { char smime_cryptalg[LONG_STRING] = "\0"; char sign_as[LONG_STRING] = "\0", *q; int flags = 0; if (!WithCrypto) return 0; p = skip_email_wsp(p); for (; *p; p++) { switch (*p) { case 'e': case 'E': flags |= ENCRYPT; break; case 'o': case 'O': flags |= OPPENCRYPT; break; case 's': case 'S': flags |= SIGN; q = sign_as; if (*(p+1) == '<') { for (p += 2; *p && *p != '>' && q < sign_as + sizeof (sign_as) - 1; *q++ = *p++) ; if (*p!='>') { mutt_error _("Illegal crypto header"); return 0; } } *q = '\0'; break; /* This used to be the micalg parameter. * * It's no longer needed, so we just skip the parameter in order * to be able to recall old messages. */ case 'm': case 'M': if(*(p+1) == '<') { for (p += 2; *p && *p != '>'; p++) ; if(*p != '>') { mutt_error _("Illegal crypto header"); return 0; } } break; case 'c': case 'C': q = smime_cryptalg; if(*(p+1) == '<') { for(p += 2; *p && *p != '>' && q < smime_cryptalg + sizeof(smime_cryptalg) - 1; *q++ = *p++) ; if(*p != '>') { mutt_error _("Illegal S/MIME header"); return 0; } } *q = '\0'; break; case 'i': case 'I': flags |= INLINE; break; default: mutt_error _("Illegal crypto header"); return 0; } } /* the cryptalg field must not be empty */ if ((WithCrypto & APPLICATION_SMIME) && *smime_cryptalg) mutt_str_replace (&SmimeCryptAlg, smime_cryptalg); /* Set {Smime,Pgp}SignAs, if desired. */ if ((WithCrypto & APPLICATION_PGP) && (crypt_app == APPLICATION_PGP) && (flags & SIGN) && (set_empty_signas || *sign_as)) mutt_str_replace (&PgpSignAs, sign_as); if ((WithCrypto & APPLICATION_SMIME) && (crypt_app == APPLICATION_SMIME) && (flags & SIGN) && (set_empty_signas || *sign_as)) mutt_str_replace (&SmimeDefaultKey, sign_as); return flags; }
static pgp_key_t parse_pub_line (char *buf, int *is_subkey, pgp_key_t k) { pgp_uid_t *uid = NULL; int field = 0, is_uid = 0; int is_pub = 0; char *pend, *p; int trust = 0; int flags = 0; struct pgp_keyinfo tmp; *is_subkey = 0; if (!*buf) return NULL; /* if we're given a key, merge our parsing results, else * start with a fresh one to work with so that we don't * mess up the real key in case we find parsing errors. */ if (k) memcpy (&tmp, k, sizeof (tmp)); else memset (&tmp, 0, sizeof (tmp)); dprint (2, (debugfile, "parse_pub_line: buf = `%s'\n", buf)); for (p = buf; p; p = pend) { if ((pend = strchr (p, ':'))) *pend++ = 0; field++; if (!*p && (field != 1) && (field != 10)) continue; switch (field) { case 1: /* record type */ { dprint (2, (debugfile, "record type: %s\n", p)); if (!mutt_strcmp (p, "pub")) is_pub = 1; else if (!mutt_strcmp (p, "sub")) *is_subkey = 1; else if (!mutt_strcmp (p, "sec")) ; else if (!mutt_strcmp (p, "ssb")) *is_subkey = 1; else if (!mutt_strcmp (p, "uid")) is_uid = 1; else return NULL; if (!(is_uid || (*is_subkey && option (OPTPGPIGNORESUB)))) memset (&tmp, 0, sizeof (tmp)); break; } case 2: /* trust info */ { dprint (2, (debugfile, "trust info: %s\n", p)); switch (*p) { /* look only at the first letter */ case 'e': flags |= KEYFLAG_EXPIRED; break; case 'r': flags |= KEYFLAG_REVOKED; break; case 'd': flags |= KEYFLAG_DISABLED; break; case 'n': trust = 1; break; case 'm': trust = 2; break; case 'f': trust = 3; break; case 'u': trust = 3; break; } if (!is_uid && !(*is_subkey && option (OPTPGPIGNORESUB))) tmp.flags |= flags; break; } case 3: /* key length */ { dprint (2, (debugfile, "key len: %s\n", p)); if (!(*is_subkey && option (OPTPGPIGNORESUB)) && mutt_atos (p, &tmp.keylen) < 0) goto bail; break; } case 4: /* pubkey algo */ { dprint (2, (debugfile, "pubkey algorithm: %s\n", p)); if (!(*is_subkey && option (OPTPGPIGNORESUB))) { int x = 0; if (mutt_atoi (p, &x) < 0) goto bail; tmp.numalg = x; tmp.algorithm = pgp_pkalgbytype (x); } break; } case 5: /* 16 hex digits with the long keyid. */ { dprint (2, (debugfile, "key id: %s\n", p)); if (!(*is_subkey && option (OPTPGPIGNORESUB))) mutt_str_replace (&tmp.keyid, p); break; } case 6: /* timestamp (1998-02-28) */ { char tstr[11]; struct tm time; dprint (2, (debugfile, "time stamp: %s\n", p)); if (!p) break; time.tm_sec = 0; time.tm_min = 0; time.tm_hour = 12; strncpy (tstr, p, 11); tstr[4] = '\0'; tstr[7] = '\0'; if (mutt_atoi (tstr, &time.tm_year) < 0) { p = tstr; goto bail; } time.tm_year -= 1900; if (mutt_atoi (tstr+5, &time.tm_mon) < 0) { p = tstr+5; goto bail; } time.tm_mon -= 1; if (mutt_atoi (tstr+8, &time.tm_mday) < 0) { p = tstr+8; goto bail; } tmp.gen_time = mutt_mktime (&time, 0); break; } case 7: /* valid for n days */ break; case 8: /* Local id */ break; case 9: /* ownertrust */ break; case 10: /* name */ { /* Empty field or no trailing colon. * We allow an empty field for a pub record type because it is * possible for a primary uid record to have an empty User-ID * field. Without any address records, it is not possible to * use the key in mutt. */ if (!(pend && (*p || is_pub))) break; /* ignore user IDs on subkeys */ if (!is_uid && (*is_subkey && option (OPTPGPIGNORESUB))) break; dprint (2, (debugfile, "user ID: %s\n", NONULL (p))); uid = safe_calloc (sizeof (pgp_uid_t), 1); fix_uid (p); uid->addr = safe_strdup (p); uid->trust = trust; uid->flags |= flags; uid->next = tmp.address; tmp.address = uid; if (strstr (p, "ENCR")) tmp.flags |= KEYFLAG_PREFER_ENCRYPTION; if (strstr (p, "SIGN")) tmp.flags |= KEYFLAG_PREFER_SIGNING; break; } case 11: /* signature class */ break; case 12: /* key capabilities */ dprint (2, (debugfile, "capabilities info: %s\n", p)); while(*p) { switch(*p++) { case 'D': flags |= KEYFLAG_DISABLED; break; case 'e': flags |= KEYFLAG_CANENCRYPT; break; case 's': flags |= KEYFLAG_CANSIGN; break; } } if (!is_uid && (!*is_subkey || !option (OPTPGPIGNORESUB) || !((flags & KEYFLAG_DISABLED) || (flags & KEYFLAG_REVOKED) || (flags & KEYFLAG_EXPIRED)))) tmp.flags |= flags; break; default: break; } } /* merge temp key back into real key */ if (!(is_uid || (*is_subkey && option (OPTPGPIGNORESUB)))) k = safe_malloc (sizeof (*k)); memcpy (k, &tmp, sizeof (*k)); /* fixup parentship of uids after mering the temp key into * the real key */ if (tmp.address) { for (uid = k->address; uid; uid = uid->next) uid->parent = k; } return k; bail: dprint(5,(debugfile,"parse_pub_line: invalid number: '%s'\n", p)); return NULL; }
pgp_key_t pgp_get_candidates (pgp_ring_t keyring, LIST * hints) { FILE *fp; pid_t thepid; char buf[LONG_STRING]; pgp_key_t db = NULL, *kend, k = NULL, kk, mainkey = NULL; int is_sub; int devnull; if ((devnull = open ("/dev/null", O_RDWR)) == -1) return NULL; mutt_str_replace (&_chs, Charset); thepid = pgp_invoke_list_keys (NULL, &fp, NULL, -1, -1, devnull, keyring, hints); if (thepid == -1) { close (devnull); return NULL; } kend = &db; k = NULL; while (fgets (buf, sizeof (buf) - 1, fp)) { if (!(kk = parse_pub_line (buf, &is_sub, k))) continue; /* Only append kk to the list if it's new. */ if (kk != k) { if (k) kend = &k->next; *kend = k = kk; if (is_sub) { pgp_uid_t **l; k->flags |= KEYFLAG_SUBKEY; k->parent = mainkey; for (l = &k->address; *l; l = &(*l)->next) ; *l = pgp_copy_uids (mainkey->address, k); } else mainkey = k; } } if (ferror (fp)) mutt_perror ("fgets"); safe_fclose (&fp); mutt_wait_filter (thepid); close (devnull); return db; }
/** * mutt_protect - Encrypt and/or sign a message * @param msg Header of the message * @param keylist List of keys to encrypt to (space-separated) * @retval 0 Success * @retval -1 Error */ int mutt_protect(struct Email *msg, char *keylist) { struct Body *pbody = NULL, *tmp_pbody = NULL; struct Body *tmp_smime_pbody = NULL; struct Body *tmp_pgp_pbody = NULL; int flags = (WithCrypto & APPLICATION_PGP) ? msg->security : 0; if (!WithCrypto) return -1; if (!(msg->security & (SEC_ENCRYPT | SEC_SIGN))) return 0; if ((msg->security & SEC_SIGN) && !crypt_valid_passphrase(msg->security)) return -1; if (((WithCrypto & APPLICATION_PGP) != 0) && ((msg->security & PGP_INLINE) == PGP_INLINE)) { if ((msg->content->type != TYPE_TEXT) || (mutt_str_strcasecmp(msg->content->subtype, "plain") != 0)) { if (query_quadoption(C_PgpMimeAuto, _("Inline PGP can't be used with attachments. " "Revert to PGP/MIME?")) != MUTT_YES) { mutt_error( _("Mail not sent: inline PGP can't be used with attachments")); return -1; } } else if (mutt_str_strcasecmp("flowed", mutt_param_get(&msg->content->parameter, "format")) == 0) { if ((query_quadoption(C_PgpMimeAuto, _("Inline PGP can't be used with format=flowed. " "Revert to PGP/MIME?"))) != MUTT_YES) { mutt_error( _("Mail not sent: inline PGP can't be used with format=flowed")); return -1; } } else { /* they really want to send it inline... go for it */ if (!isendwin()) { mutt_endwin(); puts(_("Invoking PGP...")); } pbody = crypt_pgp_traditional_encryptsign(msg->content, flags, keylist); if (pbody) { msg->content = pbody; return 0; } /* otherwise inline won't work...ask for revert */ if (query_quadoption( C_PgpMimeAuto, _("Message can't be sent inline. Revert to using PGP/MIME?")) != MUTT_YES) { mutt_error(_("Mail not sent")); return -1; } } /* go ahead with PGP/MIME */ } if (!isendwin()) mutt_endwin(); if (WithCrypto & APPLICATION_SMIME) tmp_smime_pbody = msg->content; if (WithCrypto & APPLICATION_PGP) tmp_pgp_pbody = msg->content; if (C_CryptUsePka && (msg->security & SEC_SIGN)) { /* Set sender (necessary for e.g. PKA). */ const char *mailbox = NULL; struct Address *from = msg->env->from; if (!from) from = mutt_default_from(); mailbox = from->mailbox; if (!mailbox && C_EnvelopeFromAddress) mailbox = C_EnvelopeFromAddress->mailbox; if (((WithCrypto & APPLICATION_SMIME) != 0) && (msg->security & APPLICATION_SMIME)) crypt_smime_set_sender(mailbox); else if (((WithCrypto & APPLICATION_PGP) != 0) && (msg->security & APPLICATION_PGP)) crypt_pgp_set_sender(mailbox); if (!msg->env->from) mutt_addr_free(&from); } if (C_CryptProtectedHeadersWrite) { struct Envelope *protected_headers = mutt_env_new(); mutt_str_replace(&protected_headers->subject, msg->env->subject); /* Note: if other headers get added, such as to, cc, then a call to * mutt_env_to_intl() will need to be added here too. */ mutt_prepare_envelope(protected_headers, 0); mutt_env_free(&msg->content->mime_headers); msg->content->mime_headers = protected_headers; } if (msg->security & SEC_SIGN) { if (((WithCrypto & APPLICATION_SMIME) != 0) && (msg->security & APPLICATION_SMIME)) { tmp_pbody = crypt_smime_sign_message(msg->content); if (!tmp_pbody) goto bail; pbody = tmp_pbody; tmp_smime_pbody = tmp_pbody; } if (((WithCrypto & APPLICATION_PGP) != 0) && (msg->security & APPLICATION_PGP) && (!(flags & SEC_ENCRYPT) || C_PgpRetainableSigs)) { tmp_pbody = crypt_pgp_sign_message(msg->content); if (!tmp_pbody) goto bail; flags &= ~SEC_SIGN; pbody = tmp_pbody; tmp_pgp_pbody = tmp_pbody; } if ((WithCrypto != 0) && (msg->security & APPLICATION_SMIME) && (msg->security & APPLICATION_PGP)) { /* here comes the draft ;-) */ } } if (msg->security & SEC_ENCRYPT) { if (((WithCrypto & APPLICATION_SMIME) != 0) && (msg->security & APPLICATION_SMIME)) { tmp_pbody = crypt_smime_build_smime_entity(tmp_smime_pbody, keylist); if (!tmp_pbody) { /* signed ? free it! */ goto bail; } /* free tmp_body if messages was signed AND encrypted ... */ if ((tmp_smime_pbody != msg->content) && (tmp_smime_pbody != tmp_pbody)) { /* detach and don't delete msg->content, * which tmp_smime_pbody->parts after signing. */ tmp_smime_pbody->parts = tmp_smime_pbody->parts->next; msg->content->next = NULL; mutt_body_free(&tmp_smime_pbody); } pbody = tmp_pbody; } if (((WithCrypto & APPLICATION_PGP) != 0) && (msg->security & APPLICATION_PGP)) { pbody = crypt_pgp_encrypt_message(tmp_pgp_pbody, keylist, (flags & SEC_SIGN)); if (!pbody) { /* did we perform a retainable signature? */ if (flags != msg->security) { /* remove the outer multipart layer */ tmp_pgp_pbody = mutt_remove_multipart(tmp_pgp_pbody); /* get rid of the signature */ mutt_body_free(&tmp_pgp_pbody->next); } goto bail; } /* destroy temporary signature envelope when doing retainable * signatures. */ if (flags != msg->security) { tmp_pgp_pbody = mutt_remove_multipart(tmp_pgp_pbody); mutt_body_free(&tmp_pgp_pbody->next); } } } if (pbody) { msg->content = pbody; return 0; } bail: mutt_env_free(&msg->content->mime_headers); return -1; }
/* 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 */ { 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); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_BCC: menu->redraw = edit_address_list (HDR_BCC, &msg->env->bcc); mutt_message_hook (NULL, msg, M_SEND2HOOK); break; case OP_COMPOSE_EDIT_CC: menu->redraw = edit_address_list (HDR_CC, &msg->env->cc); 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); clrtoeol (); if (msg->env->subject) mutt_paddstr (W, msg->env->subject); } 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_idna (msg->env, &tag, &err)) { mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err); FREE (&err); } } 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]); 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_perror (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) { 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) { while (idxlen-- > 0) { /* avoid freeing other attachments */ idx[idxlen]->content->next = NULL; idx[idxlen]->content->parts = NULL; if (idx[idxlen]->unowned) idx[idxlen]->content->unlink = 0; 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 (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "), M_YES) != M_YES) { mutt_clear_error (); break; } msg->security = 0; } 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 (mutt_yesorno (_("PGP already selected. Clear & continue ? "), M_YES) != M_YES) { mutt_clear_error (); break; } msg->security = 0; } 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 formated compose status line */ if (menu->redraw & REDRAW_STATUS) { compose_status_line (buf, sizeof (buf), 0, menu, NONULL(ComposeFormat)); CLEARLINE (option (OPTSTATUSONTOP) ? 0 : LINES-2); SETCOLOR (MT_COLOR_STATUS); BKGDSET (MT_COLOR_STATUS); mutt_paddstr (COLS, buf); SETCOLOR (MT_COLOR_NORMAL); BKGDSET (MT_COLOR_NORMAL); 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]); } } else msg->content = NULL; FREE (&idx); return (r); }
static int mh_sync_message (CONTEXT *ctx, int msgno) { HEADER *h = ctx->hdrs[msgno]; MESSAGE *dest; int rc; short restore = 1; char oldpath[_POSIX_PATH_MAX]; char newpath[_POSIX_PATH_MAX]; char partpath[_POSIX_PATH_MAX]; long old_body_offset = h->content->offset; long old_body_length = h->content->length; long old_hdr_lines = h->lines; if ((dest = mx_open_new_message (ctx, h, 0)) == NULL) return -1; if ((rc = mutt_copy_message (dest->fp, ctx, h, M_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN)) == 0) { snprintf (oldpath, _POSIX_PATH_MAX, "%s/%s", ctx->path, h->path); strfcpy (partpath, h->path, _POSIX_PATH_MAX); if (ctx->magic == M_MAILDIR) rc = maildir_commit_message (ctx, dest, h); else rc = mh_commit_message (ctx, dest, h); mx_close_message (&dest); if (rc == 0) { unlink (oldpath); restore = 0; } /* * Try to move the new message to the old place. * (MH only.) * * This is important when we are just updating flags. * * Note that there is a race condition against programs which * use the first free slot instead of the maximum message * number. Mutt does _not_ behave like this. * * Anyway, if this fails, the message is in the folder, so * all what happens is that a concurrently runnung mutt will * lose flag modifications. */ if (ctx->magic == M_MH && rc == 0) { snprintf (newpath, _POSIX_PATH_MAX, "%s/%s", ctx->path, h->path); if ((rc = safe_rename (newpath, oldpath)) == 0) mutt_str_replace (&h->path, partpath); } } else mx_close_message (&dest); if (rc == -1 && restore) { h->content->offset = old_body_offset; h->content->length = old_body_length; h->lines = old_hdr_lines; } mutt_free_body (&h->content->parts); return rc; }
int mh_check_mailbox(CONTEXT *ctx, int *index_hint) { char buf[_POSIX_PATH_MAX], b1[LONG_STRING], b2[LONG_STRING]; struct stat st, st_cur; short modified = 0, have_new = 0, occult = 0; struct maildir *md, *p; struct maildir **last; HASH *fnames; int i, j, deleted; if(!option (OPTCHECKNEW)) return 0; if(ctx->magic == M_MH) { strfcpy(buf, ctx->path, sizeof(buf)); if(stat(buf, &st) == -1) return -1; } else if(ctx->magic == M_MAILDIR) { snprintf(buf, sizeof(buf), "%s/new", ctx->path); if(stat(buf, &st) == -1) return -1; snprintf(buf, sizeof(buf), "%s/cur", ctx->path); if(stat(buf, &st_cur) == -1) modified = 1; } if(!modified && ctx->magic == M_MAILDIR && st_cur.st_mtime > ctx->mtime_cur) modified = 1; if(!modified && ctx->magic == M_MH && st.st_mtime > ctx->mtime) modified = 1; if(modified || (ctx->magic == M_MAILDIR && st.st_mtime > ctx->mtime)) have_new = 1; if(!modified && !have_new) return 0; if(ctx->magic == M_MAILDIR) ctx->mtime_cur = st_cur.st_mtime; ctx->mtime = st.st_mtime; #if 0 if(Sort != SORT_ORDER) { short old_sort; old_sort = Sort; Sort = SORT_ORDER; mutt_sort_headers(ctx, 1); Sort = old_sort; } #endif md = NULL; last = &md; if(ctx->magic == M_MAILDIR) { if(have_new) maildir_parse_dir(ctx, &last, "new", NULL); if(modified) maildir_parse_dir(ctx, &last, "cur", NULL); } else if(ctx->magic == M_MH) maildir_parse_dir(ctx, &last, NULL, NULL); /* check for modifications and adjust flags */ fnames = hash_create (1031); for(p = md; p; p = p->next) { if(ctx->magic == M_MAILDIR) { maildir_canon_filename(b2, p->h->path, sizeof(b2)); p->canon_fname = safe_strdup(b2); } else p->canon_fname = safe_strdup(p->h->path); hash_insert(fnames, p->canon_fname, p, 0); } deleted = 0; for(i = 0; i < ctx->msgcount; i++) { ctx->hdrs[i]->active = 0; if(ctx->magic == M_MAILDIR) maildir_canon_filename(b1, ctx->hdrs[i]->path, sizeof(b1)); else strfcpy(b1, ctx->hdrs[i]->path, sizeof(b1)); dprint(2, (debugfile, "%s:%d: mh_check_mailbox(): Looking for %s.\n", __FILE__, __LINE__, b1)); if((p = hash_find(fnames, b1)) && p->h && mbox_strict_cmp_headers(ctx->hdrs[i], p->h)) { /* found the right message */ dprint(2, (debugfile, "%s:%d: Found. Flags before: %s%s%s%s%s\n", __FILE__, __LINE__, ctx->hdrs[i]->flagged ? "f" : "", ctx->hdrs[i]->deleted ? "D" : "", ctx->hdrs[i]->replied ? "r" : "", ctx->hdrs[i]->old ? "O" : "", ctx->hdrs[i]->read ? "R" : "")); if(mutt_strcmp(ctx->hdrs[i]->path, p->h->path)) mutt_str_replace (&ctx->hdrs[i]->path, p->h->path); if(modified) { if(!ctx->hdrs[i]->changed) { mutt_set_flag (ctx, ctx->hdrs[i], M_FLAG, p->h->flagged); mutt_set_flag (ctx, ctx->hdrs[i], M_REPLIED, p->h->replied); mutt_set_flag (ctx, ctx->hdrs[i], M_READ, p->h->read); } mutt_set_flag(ctx, ctx->hdrs[i], M_OLD, p->h->old); } ctx->hdrs[i]->active = 1; dprint(2, (debugfile, "%s:%d: Flags after: %s%s%s%s%s\n", __FILE__, __LINE__, ctx->hdrs[i]->flagged ? "f" : "", ctx->hdrs[i]->deleted ? "D" : "", ctx->hdrs[i]->replied ? "r" : "", ctx->hdrs[i]->old ? "O" : "", ctx->hdrs[i]->read ? "R" : "")); mutt_free_header(&p->h); } else if (ctx->magic == M_MAILDIR && !modified && !strncmp("cur/", ctx->hdrs[i]->path, 4)) { /* If the cur/ part wasn't externally modified for a maildir * type folder, assume the message is still active. Actually, * we simply don't know. */ ctx->hdrs[i]->active = 1; } else if (modified || (ctx->magic == M_MAILDIR && !strncmp("new/", ctx->hdrs[i]->path, 4))) { /* Mailbox was modified, or a new message vanished. */ /* Note: This code will _not_ apply for a new message which * is just moved to cur/, as this would modify cur's time * stamp and lead to modified == 1. Thus, we'd have parsed * the complete folder above, and the message would have * been found in the look-up table. */ dprint(2, (debugfile, "%s:%d: Not found. Flags were: %s%s%s%s%s\n", __FILE__, __LINE__, ctx->hdrs[i]->flagged ? "f" : "", ctx->hdrs[i]->deleted ? "D" : "", ctx->hdrs[i]->replied ? "r" : "", ctx->hdrs[i]->old ? "O" : "", ctx->hdrs[i]->read ? "R" : "")); occult = 1; } } /* destroy the file name hash */ hash_destroy(&fnames, NULL); /* If we didn't just get new mail, update the tables. */ if(modified || occult) { short old_sort; int old_count; if (Sort != SORT_ORDER) { old_sort = Sort; Sort = SORT_ORDER; mutt_sort_headers (ctx, 1); Sort = old_sort; } old_count = ctx->msgcount; for (i = 0, j = 0; i < old_count; i++) { if (ctx->hdrs[i]->active && index_hint && *index_hint == i) *index_hint = j; if (ctx->hdrs[i]->active) ctx->hdrs[i]->index = j++; } mx_update_tables(ctx, 0); } /* Incorporate new messages */ maildir_move_to_context(ctx, &md); return (modified || occult) ? M_REOPENED : have_new ? M_NEW_MAIL : 0; }
int mh_commit_message (CONTEXT *ctx, MESSAGE *msg, HEADER *hdr) { DIR *dirp; struct dirent *de; char *cp, *dep; unsigned int n, hi = 0; char path[_POSIX_PATH_MAX]; char tmp[16]; if (safe_fclose (&msg->fp) != 0) return -1; if ((dirp = opendir (ctx->path)) == NULL) { mutt_perror (ctx->path); return (-1); } /* figure out what the next message number is */ while ((de = readdir (dirp)) != NULL) { dep = de->d_name; if (*dep == ',') dep++; cp = dep; while (*cp) { if (!isdigit ((unsigned char) *cp)) break; cp++; } if (!*cp) { n = atoi (dep); if (n > hi) hi = n; } } closedir (dirp); /* * Now try to rename the file to the proper name. * * Note: We may have to try multiple times, until we find a free * slot. */ FOREVER { hi++; snprintf (tmp, sizeof (tmp), "%d", hi); snprintf (path, sizeof (path), "%s/%s", ctx->path, tmp); if (safe_rename (msg->path, path) == 0) { if (hdr) mutt_str_replace (&hdr->path, tmp); FREE (&msg->path); return 0; } else if (errno != EEXIST) { mutt_perror (ctx->path); return -1; } } }
int mutt_prepare_template (FILE *fp, CONTEXT *ctx, HEADER *newhdr, HEADER *hdr, short weed) { MESSAGE *msg = NULL; char file[_POSIX_PATH_MAX]; LIST *p, **q; BODY *b; FILE *bfp; if (!fp && (msg = mx_open_message (ctx, hdr->msgno)) == NULL) return (-1); if (!fp) fp = msg->fp; bfp = fp; /* parse the message header and MIME structure */ fseek (fp, hdr->offset, 0); newhdr->offset = hdr->offset; newhdr->env = mutt_read_rfc822_header (fp, newhdr, 1, weed); newhdr->content->length = hdr->content->length; mutt_parse_part (fp, newhdr->content); /* weed user-agent, x-mailer - we don't want them here */ p = newhdr->env->userhdrs; q = &newhdr->env->userhdrs; while (p) { if (!strncasecmp (p->data, "x-mailer:", 9) || !strncasecmp (p->data, "user-agent:", 11)) { *q = p->next; p->next = NULL; mutt_free_list (&p); } else q = &p->next; p = *q; } safe_free ((void **) &newhdr->env->message_id); safe_free ((void **) &newhdr->env->mail_followup_to); #ifdef HAVE_PGP /* decrypt pgp/mime encoded messages */ if ((hdr->pgp & PGPENCRYPT) && mutt_is_multipart_encrypted (newhdr->content)) { newhdr->pgp |= PGPENCRYPT; if (!pgp_valid_passphrase()) goto err; mutt_message _("Invoking PGP..."); if (pgp_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) { err: mx_close_message (&msg); mutt_free_envelope (&newhdr->env); mutt_free_body (&newhdr->content); return -1; } mutt_free_body (&newhdr->content); newhdr->content = b; mutt_clear_error (); } /* * remove a potential multipart/signed layer - useful when * resending messages */ if (mutt_is_multipart_signed (newhdr->content)) { newhdr->pgp |= PGPSIGN; /* destroy the signature */ mutt_free_body (&newhdr->content->parts->next); newhdr->content = mutt_remove_multipart (newhdr->content); } #endif /* * We don't need no primary multipart. * Note: We _do_ preserve messages! * * XXX - we don't handle multipart/alternative in any * smart way when sending messages. However, one may * consider this a feature. * */ if (newhdr->content->type == TYPEMULTIPART) newhdr->content = mutt_remove_multipart (newhdr->content); /* create temporary files for all attachments */ for (b = newhdr->content; b; b = b->next) { /* what follows is roughly a receive-mode variant of * mutt_get_tmp_attachment () from muttlib.c */ file[0] = '\0'; if (b->filename) { strfcpy (file, b->filename, sizeof (file)); b->d_filename = safe_strdup (b->filename); } else /* avoid Content-Disposition: header with temporary filename */ b->use_disp = 0; mutt_adv_mktemp (file, sizeof(file)); if (mutt_save_attachment (bfp, b, file, 0, NULL) == -1) { mutt_free_envelope (&newhdr->env); mutt_free_body (&newhdr->content); if (bfp != fp) fclose (bfp); if (msg) mx_close_message (&msg); return -1; } mutt_str_replace (&b->filename, file); b->unlink = 1; if (mutt_is_text_type (b->type, b->subtype)) b->noconv = 1; mutt_stamp_attachment (b); mutt_free_body (&b->parts); if (b->hdr) b->hdr->content = NULL; /* avoid dangling pointer */ } /* that's it. */ if (bfp != fp) fclose (bfp); if (msg) mx_close_message (&msg); return 0; }
int mutt_parse_pgp_hdr (char *p, int set_signas) { int pgp = 0; char pgp_sign_as[LONG_STRING] = "\0", *q; char pgp_sign_micalg[LONG_STRING] = "\0"; SKIPWS (p); for (; *p; p++) { switch (*p) { case 'e': case 'E': pgp |= PGPENCRYPT; break; case 's': case 'S': pgp |= PGPSIGN; q = pgp_sign_as; if (*(p+1) == '<') { for (p += 2; *p && *p != '>' && q < pgp_sign_as + sizeof (pgp_sign_as) - 1; *q++ = *p++) ; if (*p!='>') { mutt_error _("Illegal PGP header"); return 0; } } *q = '\0'; break; case 'm': case 'M': q = pgp_sign_micalg; if(*(p+1) == '<') { for(p += 2; *p && *p != '>' && q < pgp_sign_micalg + sizeof(pgp_sign_micalg) - 1; *q++ = *p++) ; if(*p != '>') { mutt_error _("Illegal PGP header"); return 0; } } *q = '\0'; break; default: mutt_error _("Illegal PGP header"); return 0; } } if (set_signas || *pgp_sign_as) mutt_str_replace (&PgpSignAs, pgp_sign_as); /* the micalg field must not be empty */ if (set_signas && *pgp_sign_micalg) mutt_str_replace (&PgpSignMicalg, pgp_sign_micalg); return pgp; }
static void parse_argv(int argc, char *argv[]) { int ret = 0; struct option options[] = { {"help", no_argument, &arg_help, 'h'}, {"version", no_argument, &arg_version, 'v'}, {"dump-config-items", no_argument, &arg_dump_config, 'D'}, {"alias-expand", required_argument, &arg_expand_alias, 'A'}, {"query-config-item", required_argument, NULL, 'Q'}, {"subject", required_argument, NULL, 's'}, {"emulate-mailx", no_argument, &arg_emulate_mailx, 'x'}, {"config-file", required_argument, NULL, 'F'}, {"mailbox", required_argument, NULL, 'm'}, {"attach", required_argument, NULL, 'a'}, {"execute", required_argument, &arg_execute, 'e'}, {"include", required_argument, NULL, 'i'}, {"mailbox_type", required_argument, NULL, 't'}, {"postponed", no_argument, &arg_resume_postponed, 'p'}, {"bcc", required_argument, NULL, 'b'}, {"cc", required_argument, NULL, 'c'}, {"to", required_argument, NULL, 'r'}, {0,0,0,0} }; while ((ret = getopt_long(argc, argv, "hvA:DQ:s:xF:m:a:e:i:t:b:c:r:", options, NULL)) != -1) { switch (ret) { case 'h': mutt_usage(); exit(RETURN_SUCCESS); case 'v': /* we don't exit because we support verbose option */ arg_version++; break; case 's': arg_subject = optarg; break; case 'x': arg_emulate_mailx = 1; break; case 'a': if (optarg == NULL) { mutt_usage(); exit(-1); } attach = mutt_add_list(attach, optarg); arg_attach = 1; break; case 'b': if (!msg) msg = mutt_new_header(); if (!msg->env) msg->env = mutt_new_envelope(); msg->env->bcc = rfc822_parse_adrlist(msg->env->bcc, optarg); break; case 'c': if (!msg) msg = mutt_new_header(); if (!msg->env) msg->env = mutt_new_envelope(); msg->env->cc = rfc822_parse_adrlist(msg->env->cc, optarg); break; case 'e': if (arg_expand_alias || arg_query_conf || optarg == NULL) { mutt_usage(); exit(-1); } commands = mutt_add_list(commands, optarg); arg_execute = 1; break; case 'i': if (optarg == NULL) { mutt_usage(); exit(-1); } arg_include = optarg; break; case 'm': if (optarg == NULL) { mutt_usage(); exit(-1); } arg_mailbox = optarg; break; case 'p': arg_resume_postponed = 1; break; case 'r': addr_to = mutt_add_list(addr_to, optarg); break; case 't': if (optarg == NULL) { mutt_usage(); exit(-1); } arg_mailbox_type = optarg; break; case 'D': if (!is_mutt_init) init(commands); exit(mutt_dump_variables()); case 'A': if (optarg == NULL) { mutt_usage(); exit(-1); } optind--; while (optind < argc) { aliases = mutt_add_list(aliases, argv[optind]); optind++; } arg_expand_alias = 1; break; case 'F': if (optarg == NULL) { mutt_usage(); exit(-1); } mutt_str_replace(&Muttrc, optarg); break; case 'Q': if (optarg == NULL) { mutt_usage(); exit(-1); } optind--; while (optind < argc) { queries = mutt_add_list(queries, argv[optind]); optind++; } arg_query_conf = 1; break; } } }
int main(int argc, char **argv) { char folder[_POSIX_PATH_MAX] = ""; int flags = 0; /* set default locale */ setlocale(LC_ALL, ""); /* initialization of main output routines with default values */ mutt_error = mutt_message = mutt_nocurses_error; /* parse command line options */ parse_argv(argc, argv); if (arg_version == 1) { show_version(); exit(RETURN_SUCCESS); } else if (arg_version == 2) { show_version_verbose(); exit(RETURN_SUCCESS); } if (arg_execute) { init(commands); is_mutt_init = true; } if (arg_expand_alias) { struct list_t *alias; if (!is_mutt_init) init(commands); for(alias = aliases; alias; alias = alias->next) { struct address *a = mutt_lookup_alias(alias->data); if (a) { printf("alias->data %s\n", alias->data); mutt_write_address_list(a, stdout, 0, 0); } } exit(RETURN_SUCCESS); } if (arg_query_conf) { if (!is_mutt_init) init(commands); if (queries) return mutt_query_variables(queries); exit(RETURN_SUCCESS); } if (arg_emulate_mailx) { sendflags |= SENDMAILX; } if (arg_mailbox) strfcpy(folder, arg_mailbox, sizeof(folder)); if (arg_mailbox_type) mx_set_magic(arg_mailbox_type); if (arg_resume_postponed) sendflags |= SENDPOSTPONED; /* no-curses for non-terminal session */ if (!isatty(STDIN_FILENO)) { set_bit(options, OPTNOCURSES); sendflags = SENDBATCH; } else { /* * This must come before mutt_init() because curses needs to be started * before calling the init_pair() function to set the color scheme. */ start_curses(); /* check whether terminal status is supported */ term_status = mutt_ts_capability(); } /* Initialize crypto backends. */ crypt_init(); if (!is_mutt_init) init(commands); if (!bit_val(options, OPTNOCURSES)) { SETCOLOR(MT_COLOR_NORMAL); clear(); mutt_error = mutt_curses_error; mutt_message = mutt_curses_message; } /* Create the Maildir directory if it doesn't exist */ if (!bit_val(options, OPTNOCURSES) && Maildir) { struct stat sb; char fpath[_POSIX_PATH_MAX]; char msg[STRING]; strfcpy(fpath, Maildir, sizeof(fpath)); mutt_expand_path(fpath, sizeof(fpath)); #if USE_IMAP /* we're not connected yet - skip mail folder creation */ if (!mx_is_imap(fpath)) #endif if (stat(fpath, &sb) == -1 && errno == ENOENT) { snprintf(msg, sizeof(msg), ("%s does not exist. Create it?"), Maildir); if (mutt_yesorno(msg, M_YES) == M_YES) if (mkdir(fpath, 0700) == -1 && errno != EEXIST) mutt_error( ("Can't create %s: %s."), Maildir, strerror(errno)); } } if (sendflags & SENDPOSTPONED) { if (!bit_val(options, OPTNOCURSES)) mutt_flushinp(); ci_send_message(SENDPOSTPONED, NULL, NULL, NULL, NULL); mutt_endwin(NULL); } else if (arg_subject || msg || sendflags || arg_include || attach) { FILE *fin = NULL; char buf[LONG_STRING]; char *tempfile = NULL, *infile = NULL; char *bodytext = NULL; int rv = 0; if (!bit_val(options, OPTNOCURSES)) mutt_flushinp(); if (!msg) msg = mutt_new_header(); if (!msg->env) msg->env = mutt_new_envelope(); for(; addr_to; addr_to = addr_to->next) { if (url_check_scheme(addr_to->data) == U_MAILTO) { if (url_parse_mailto(msg->env, &bodytext, addr_to->data) < 0) { if (!bit_val(options, OPTNOCURSES)) mutt_endwin(NULL); fputs(("Failed to parse mailto: link\n"), stderr); exit(RETURN_WRONG_ADDR); } } else msg->env->to = rfc822_parse_adrlist(msg->env->to, addr_to->data); } if (bit_val(options, OPTAUTOEDIT) && !msg->env->to && !msg->env->cc) { if (!bit_val(options, OPTNOCURSES)) mutt_endwin(NULL); fputs(("No recipients specified.\n"), stderr); exit(RETURN_ERR_ARG); } if (arg_subject) msg->env->subject = safe_strdup(arg_subject); if (arg_include) infile = arg_include; if (infile || bodytext) { if (infile) { if (mutt_strcmp("-", infile) == 0) fin = stdin; else { char path[_POSIX_PATH_MAX]; strfcpy(path, infile, sizeof(path)); mutt_expand_path(path, sizeof(path)); if ((fin = fopen(path, "r")) == NULL) { if (!bit_val(options, OPTNOCURSES)) mutt_endwin(NULL); perror(path); exit(RETURN_ERR_ARG); } } } mutt_mktemp(buf, sizeof(buf)); tempfile = safe_strdup(buf); /* TODO: is the following if still needed? */ if (tempfile) { FILE *fout; if ((fout = safe_fopen(tempfile, "w")) == NULL) { if (!bit_val(options, OPTNOCURSES)) mutt_endwin(NULL); perror(tempfile); safe_fclose(&fin); safe_free(&tempfile); exit(RETURN_ERR_ARG); } if (fin) mutt_copy_stream(fin, fout); else if (bodytext) fputs(bodytext, fout); safe_fclose(&fout); } if (fin && fin != stdin) safe_fclose(&fin); } safe_free(&bodytext); if (attach) { struct list_t *t = attach; struct body *a = NULL; while(t) { if (a) { a->next = mutt_make_file_attach(t->data); a = a->next; } else msg->content = a = mutt_make_file_attach(t->data); if (!a) { if (!bit_val(options, OPTNOCURSES)) mutt_endwin(NULL); fprintf(stderr, ("%s: unable to attach file.\n"), t->data); mutt_free_list(&attach); exit(RETURN_ERR_ARG); } t = t->next; } mutt_free_list(&attach); } rv = ci_send_message(sendflags, msg, tempfile, NULL, NULL); if (!bit_val(options, OPTNOCURSES)) mutt_endwin(NULL); if (rv) exit(RETURN_ERR_ARG); } else { if (!folder[0]) strfcpy(folder, NONULL(Spoolfile), sizeof(folder)); mutt_expand_path(folder, sizeof(folder)); mutt_str_replace(&CurrentFolder, folder); mutt_str_replace(&LastFolder, folder); mutt_folder_hook(folder); if ((Context = mx_open_mailbox(folder, flags, NULL)) || !arg_mailbox) { mutt_index_menu(); if (Context) safe_free(&Context); } #if USE_IMAP imap_logout_all(); #endif #if USE_SASL mutt_sasl_done(); #endif mutt_free_opts(); mutt_endwin(Errorbuf); } exit(RETURN_SUCCESS); }