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); /* If message_id is set, then we are resending a message and don't want * message_id or mail_followup_to. Otherwise, we are resuming a * postponed message, and want to keep the mail_followup_to. */ if (newhdr->env->message_id != NULL) { FREE (&newhdr->env->message_id); FREE (&newhdr->env->mail_followup_to); } /* 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_copy_message (FILE *fpout, FILE *fpin, HEADER *hdr, BODY *body, int flags, int chflags) { char prefix[SHORT_STRING]; STATE s; LOFF_T new_offset = -1; int rc = 0; if (flags & M_CM_PREFIX) { if (option (OPTTEXTFLOWED)) strfcpy (prefix, ">", sizeof (prefix)); else _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context, hdr, 0); } if ((flags & M_CM_NOHEADER) == 0) { if (flags & M_CM_PREFIX) chflags |= CH_PREFIX; else if (hdr->attach_del && (chflags & CH_UPDATE_LEN)) { int new_lines; LOFF_T new_length = body->length; char date[SHORT_STRING]; mutt_make_date (date, sizeof (date)); date[5] = date[mutt_strlen (date) - 1] = '\"'; /* Count the number of lines and bytes to be deleted */ fseeko (fpin, body->offset, SEEK_SET); new_lines = hdr->lines - count_delete_lines (fpin, body, &new_length, mutt_strlen (date)); /* Copy the headers */ if (mutt_copy_header (fpin, hdr, fpout, chflags | CH_NOLEN | CH_NONEWLINE, NULL)) return -1; fprintf (fpout, "Content-Length: " OFF_T_FMT "\n", new_length); if (new_lines <= 0) new_lines = 0; else fprintf (fpout, "Lines: %d\n", new_lines); putc ('\n', fpout); if (ferror (fpout) || feof (fpout)) return -1; new_offset = ftello (fpout); /* Copy the body */ fseeko (fpin, body->offset, SEEK_SET); if (copy_delete_attach (body, fpin, fpout, date)) return -1; #ifdef DEBUG { LOFF_T fail = ((ftello (fpout) - new_offset) - new_length); if (fail) { mutt_error ("The length calculation was wrong by %ld bytes", fail); new_length += fail; mutt_sleep (1); } } #endif /* Update original message if we are sync'ing a mailfolder */ if (flags & M_CM_UPDATE) { hdr->attach_del = 0; hdr->lines = new_lines; body->offset = new_offset; /* update the total size of the mailbox to reflect this deletion */ Context->size -= body->length - new_length; /* * if the message is visible, update the visible size of the mailbox * as well. */ if (Context->v2r[hdr->msgno] != -1) Context->vsize -= body->length - new_length; body->length = new_length; mutt_free_body (&body->parts); } return 0; } if (mutt_copy_header (fpin, hdr, fpout, chflags, (chflags & CH_PREFIX) ? prefix : NULL) == -1) return -1; new_offset = ftello (fpout); } if (flags & M_CM_DECODE) { /* now make a text/plain version of the message */ memset (&s, 0, sizeof (STATE)); s.fpin = fpin; s.fpout = fpout; if (flags & M_CM_PREFIX) s.prefix = prefix; if (flags & M_CM_DISPLAY) s.flags |= M_DISPLAY; if (flags & M_CM_PRINTING) s.flags |= M_PRINTING; if (flags & M_CM_WEED) s.flags |= M_WEED; if (flags & M_CM_CHARCONV) s.flags |= M_CHARCONV; if (flags & M_CM_REPLYING) s.flags |= M_REPLYING; if (WithCrypto && flags & M_CM_VERIFY) s.flags |= M_VERIFY; rc = mutt_body_handler (body, &s); } else if (WithCrypto && (flags & M_CM_DECODE_CRYPT) && (hdr->security & ENCRYPT)) { BODY *cur = NULL; FILE *fp; if ((WithCrypto & APPLICATION_PGP) && (flags & M_CM_DECODE_PGP) && (hdr->security & APPLICATION_PGP) && hdr->content->type == TYPEMULTIPART) { if (crypt_pgp_decrypt_mime (fpin, &fp, hdr->content, &cur)) return (-1); fputs ("MIME-Version: 1.0\n", fpout); } if ((WithCrypto & APPLICATION_SMIME) && (flags & M_CM_DECODE_SMIME) && (hdr->security & APPLICATION_SMIME) && hdr->content->type == TYPEAPPLICATION) { if (crypt_smime_decrypt_mime (fpin, &fp, hdr->content, &cur)) return (-1); } if (!cur) { mutt_error (_("No decryption engine available for message")); return -1; } mutt_write_mime_header (cur, fpout); fputc ('\n', fpout); fseeko (fp, cur->offset, 0); if (mutt_copy_bytes (fp, fpout, cur->length) == -1) { safe_fclose (&fp); mutt_free_body (&cur); return (-1); } mutt_free_body (&cur); safe_fclose (&fp); } else { fseeko (fpin, body->offset, 0); if (flags & M_CM_PREFIX) { int c; size_t bytes = body->length; fputs(prefix, fpout); while((c = fgetc(fpin)) != EOF && bytes--) { fputc(c, fpout); if(c == '\n') { fputs(prefix, fpout); } } } else if (mutt_copy_bytes (fpin, fpout, body->length) == -1) return -1; } if ((flags & M_CM_UPDATE) && (flags & M_CM_NOHEADER) == 0 && new_offset != -1) { body->offset = new_offset; mutt_free_body (&body->parts); } return rc; }
/* returns 0 on success, -1 on error */ int mutt_decode_save_attachment (FILE *fp, BODY *m, char *path, int displaying, int flags) { STATE s; unsigned int saved_encoding = 0; BODY *saved_parts = NULL; HEADER *saved_hdr = NULL; memset (&s, 0, sizeof (s)); s.flags = displaying; if (flags == M_SAVE_APPEND) s.fpout = fopen (path, "a"); else if (flags == M_SAVE_OVERWRITE) s.fpout = fopen (path, "w"); /* __FOPEN_CHECKED__ */ else s.fpout = safe_fopen (path, "w"); if (s.fpout == NULL) { mutt_perror ("fopen"); return (-1); } if (fp == NULL) { /* When called from the compose menu, the attachment isn't parsed, * so we need to do it here. */ struct stat st; if (stat (m->filename, &st) == -1) { mutt_perror ("stat"); safe_fclose (&s.fpout); return (-1); } if ((s.fpin = fopen (m->filename, "r")) == NULL) { mutt_perror ("fopen"); return (-1); } saved_encoding = m->encoding; if (!is_multipart (m)) m->encoding = ENC8BIT; m->length = st.st_size; m->offset = 0; saved_parts = m->parts; saved_hdr = m->hdr; mutt_parse_part (s.fpin, m); if (m->noconv || is_multipart (m)) s.flags |= M_CHARCONV; } else { s.fpin = fp; s.flags |= M_CHARCONV; } mutt_body_handler (m, &s); safe_fclose (&s.fpout); if (fp == NULL) { m->length = 0; m->encoding = saved_encoding; if (saved_parts) { mutt_free_header (&m->hdr); m->parts = saved_parts; m->hdr = saved_hdr; } safe_fclose (&s.fpin); } return (0); }
/** * mutt_signed_handler - Verify a "multipart/signed" body - Implements ::handler_t */ int mutt_signed_handler(struct Body *a, struct State *s) { bool inconsistent = false; struct Body *b = a; struct Body **signatures = NULL; int sigcnt = 0; int rc = 0; if (!WithCrypto) return -1; a = a->parts; SecurityFlags signed_type = mutt_is_multipart_signed(b); if (signed_type == SEC_NO_FLAGS) { /* A null protocol value is already checked for in mutt_body_handler() */ state_printf(s, _("[-- Error: " "Unknown multipart/signed protocol %s --]\n\n"), mutt_param_get(&b->parameter, "protocol")); return mutt_body_handler(a, s); } if (!(a && a->next)) inconsistent = true; else { switch (signed_type) { case SEC_SIGN: if ((a->next->type != TYPE_MULTIPART) || (mutt_str_strcasecmp(a->next->subtype, "mixed") != 0)) { inconsistent = true; } break; case PGP_SIGN: if ((a->next->type != TYPE_APPLICATION) || (mutt_str_strcasecmp(a->next->subtype, "pgp-signature") != 0)) { inconsistent = true; } break; case SMIME_SIGN: if ((a->next->type != TYPE_APPLICATION) || ((mutt_str_strcasecmp(a->next->subtype, "x-pkcs7-signature") != 0) && (mutt_str_strcasecmp(a->next->subtype, "pkcs7-signature") != 0))) { inconsistent = true; } break; default: inconsistent = true; } } if (inconsistent) { state_attach_puts(_("[-- Error: " "Missing or bad-format multipart/signed signature" " --]\n\n"), s); return mutt_body_handler(a, s); } if (s->flags & MUTT_DISPLAY) { crypt_fetch_signatures(&signatures, a->next, &sigcnt); if (sigcnt) { char tempfile[PATH_MAX]; mutt_mktemp(tempfile, sizeof(tempfile)); bool goodsig = true; if (crypt_write_signed(a, s, tempfile) == 0) { for (int i = 0; i < sigcnt; i++) { if (((WithCrypto & APPLICATION_PGP) != 0) && (signatures[i]->type == TYPE_APPLICATION) && (mutt_str_strcasecmp(signatures[i]->subtype, "pgp-signature") == 0)) { if (crypt_pgp_verify_one(signatures[i], s, tempfile) != 0) goodsig = false; continue; } if (((WithCrypto & APPLICATION_SMIME) != 0) && (signatures[i]->type == TYPE_APPLICATION) && ((mutt_str_strcasecmp(signatures[i]->subtype, "x-pkcs7-signature") == 0) || (mutt_str_strcasecmp(signatures[i]->subtype, "pkcs7-signature") == 0))) { if (crypt_smime_verify_one(signatures[i], s, tempfile) != 0) goodsig = false; continue; } state_printf(s, _("[-- Warning: " "We can't verify %s/%s signatures. --]\n\n"), TYPE(signatures[i]), signatures[i]->subtype); } } mutt_file_unlink(tempfile); b->goodsig = goodsig; b->badsig = !goodsig; /* Now display the signed body */ state_attach_puts(_("[-- The following data is signed --]\n\n"), s); mutt_protected_headers_handler(a, s); FREE(&signatures); } else state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"), s); } rc = mutt_body_handler(a, s); if (s->flags & MUTT_DISPLAY && sigcnt) state_attach_puts(_("\n[-- End of signed data --]\n"), s); return rc; }
void mutt_attach_reply (FILE * fp, struct header *hdr, ATTACHPTR ** idx, short idxlen, struct body * cur, int flags) { short mime_reply_any = 0; short nattach = 0; struct header *parent = NULL; struct header *tmphdr = NULL; short i; STATE st; char tmpbody[_POSIX_PATH_MAX]; FILE *tmpfp; char prefix[SHORT_STRING]; int rc; if (check_all_msg (idx, idxlen, cur, 0) == -1) { nattach = count_tagged (idx, idxlen); if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL) parent = hdr; } if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) { if ((rc = query_quadoption (OPT_MIMEFWDREST, ("Can't decode all tagged attachments. MIME-encapsulate the others?"))) == -1) return; else if (rc == M_YES) mime_reply_any = 1; } else if (nattach == 1) mime_reply_any = 1; tmphdr = mutt_new_header (); tmphdr->env = mutt_new_envelope (); if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen, parent ? parent : (cur ? cur->hdr : NULL), flags) == -1) { mutt_free_header (&tmphdr); return; } mutt_mktemp (tmpbody, sizeof (tmpbody)); if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) { mutt_error (("Can't create %s."), tmpbody); mutt_free_header (&tmphdr); return; } if (!parent) { if (cur) attach_include_reply (fp, tmpfp, cur->hdr, flags); else { for (i = 0; i < idxlen; i++) { if (idx[i]->content->tagged) attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags); } } } else { mutt_make_attribution (Context, parent, tmpfp); memset (&st, 0, sizeof (STATE)); st.fpin = fp; st.fpout = tmpfp; if (!bit_val(options, OPTTEXTFLOWED)) _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context, parent, 0); else strfcpy (prefix, ">", sizeof (prefix)); st.prefix = prefix; st.flags = M_CHARCONV; if (bit_val(options, OPTWEED)) st.flags |= M_WEED; if (bit_val(options, OPTHEADER)) include_header (1, fp, parent, tmpfp, prefix); if (cur) { if (mutt_can_decode (cur)) { mutt_body_handler (cur, &st); state_putc ('\n', &st); } else mutt_copy_body (fp, &tmphdr->content, cur); } else { for (i = 0; i < idxlen; i++) { if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) { mutt_body_handler (idx[i]->content, &st); state_putc ('\n', &st); } } } mutt_make_post_indent (Context, parent, tmpfp); if (mime_reply_any && !cur && copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen, 0) == NULL) { mutt_free_header (&tmphdr); safe_fclose (&tmpfp); return; } } safe_fclose (&tmpfp); if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent ? parent : (cur ? cur->hdr : NULL)) == 0) mutt_set_flag (Context, hdr, M_REPLIED, 1); }
static void attach_forward_bodies (FILE * fp, struct header * hdr, ATTACHPTR ** idx, short idxlen, struct body * cur, short nattach) { short i; short mime_fwd_all = 0; short mime_fwd_any = 1; struct header *parent = NULL; struct header *tmphdr = NULL; struct body **last; char tmpbody[_POSIX_PATH_MAX]; FILE *tmpfp = NULL; char prefix[STRING]; int rc = 0; STATE st; /* * First, find the parent message. * Note: This could be made an option by just * putting the following lines into an if block. */ parent = find_parent (idx, idxlen, cur, nattach); if (parent == NULL) parent = hdr; tmphdr = mutt_new_header (); tmphdr->env = mutt_new_envelope (); mutt_make_forward_subject (tmphdr->env, Context, parent); mutt_mktemp (tmpbody, sizeof (tmpbody)); if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) { mutt_error (("Can't open temporary file %s."), tmpbody); return; } mutt_forward_intro (tmpfp, parent); /* prepare the prefix here since we'll need it later. */ if (bit_val(options, OPTFORWQUOTE)) { if (!bit_val(options, OPTTEXTFLOWED)) _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context, parent, 0); else strfcpy (prefix, ">", sizeof (prefix)); } include_header (bit_val(options, OPTFORWQUOTE), fp, parent, tmpfp, prefix); /* * Now, we have prepared the first part of the message body: The * original message's header. * * The next part is more interesting: either include the message bodies, * or attach them. */ if ((!cur || mutt_can_decode (cur)) && (rc = query_quadoption (OPT_MIMEFWD, ("Forward as attachments?"))) == M_YES) mime_fwd_all = 1; else if (rc == -1) goto bail; /* * shortcut MIMEFWDREST when there is only one attachment. Is * this intuitive? */ if (!mime_fwd_all && !cur && (nattach > 1) && !check_can_decode (idx, idxlen, cur)) { if ((rc = query_quadoption (OPT_MIMEFWDREST, ("Can't decode all tagged attachments. MIME-forward the others?"))) == -1) goto bail; else if (rc == M_NO) mime_fwd_any = 0; } /* initialize a state structure */ memset (&st, 0, sizeof (st)); if (bit_val(options, OPTFORWQUOTE)) st.prefix = prefix; st.flags = M_CHARCONV; if (bit_val(options, OPTWEED)) st.flags |= M_WEED; st.fpin = fp; st.fpout = tmpfp; /* where do we append new MIME parts? */ last = &tmphdr->content; if (cur) { /* single body case */ if (!mime_fwd_all && mutt_can_decode (cur)) { mutt_body_handler (cur, &st); state_putc ('\n', &st); } else { if (mutt_copy_body (fp, last, cur) == -1) goto bail; last = &((*last)->next); } } else { /* multiple body case */ if (!mime_fwd_all) { for (i = 0; i < idxlen; i++) { if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) { mutt_body_handler (idx[i]->content, &st); state_putc ('\n', &st); } } } if (mime_fwd_any && copy_problematic_attachments (fp, last, idx, idxlen, mime_fwd_all) == NULL) goto bail; } mutt_forward_trailer (tmpfp); safe_fclose (&tmpfp); tmpfp = NULL; /* now that we have the template, send it. */ ci_send_message (0, tmphdr, tmpbody, NULL, parent); return; bail: if (tmpfp) { safe_fclose (&tmpfp); mutt_unlink (tmpbody); } mutt_free_header (&tmphdr); }