2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
17 #include "mutt_curses.h"
23 #include "mutt_crypt.h"
24 #include "mutt_idna.h"
31 #include "lib/debug.h"
42 #include <sys/types.h>
54 static void append_signature (FILE * f)
59 if (Signature && (tmpfp = mutt_open_read (Signature, &thepid))) {
60 if (option (OPTSIGDASHES))
62 mutt_copy_stream (tmpfp, f);
65 mutt_wait_filter (thepid);
69 /* compare two e-mail addresses and return 1 if they are equivalent */
70 static int mutt_addrcmp (ADDRESS * a, ADDRESS * b)
72 if (!a->mailbox || !b->mailbox)
74 if (ascii_strcasecmp (a->mailbox, b->mailbox))
79 /* search an e-mail address in a list */
80 static int mutt_addrsrc (ADDRESS * a, ADDRESS * lst)
82 for (; lst; lst = lst->next) {
83 if (mutt_addrcmp (a, lst))
89 /* removes addresses from "b" which are contained in "a" */
90 static ADDRESS *mutt_remove_xrefs (ADDRESS * a, ADDRESS * b)
92 ADDRESS *top, *p, *prev = NULL;
96 for (p = a; p; p = p->next) {
97 if (mutt_addrcmp (p, b))
102 prev->next = b->next;
104 rfc822_free_address (&b);
110 rfc822_free_address (&b);
122 /* remove any address which matches the current user. if `leave_only' is
123 * nonzero, don't remove the user's address if it is the only one in the list
125 static ADDRESS *remove_user (ADDRESS * a, int leave_only)
127 ADDRESS *top = NULL, *last = NULL;
130 if (!mutt_addr_is_user (a)) {
144 if (!leave_only || a || last) {
146 rfc822_free_address (&tmp);
155 static ADDRESS *find_mailing_lists (ADDRESS * t, ADDRESS * c)
157 ADDRESS *top = NULL, *ptr = NULL;
159 for (; t || c; t = c, c = NULL) {
160 for (; t; t = t->next) {
161 if (mutt_is_mail_list (t) && !t->group) {
163 ptr->next = rfc822_cpy_adr_real (t);
167 ptr = top = rfc822_cpy_adr_real (t);
174 static int edit_address (ADDRESS ** a, /* const */ char *field)
176 char buf[HUGE_STRING];
182 mutt_addrlist_to_local (*a);
183 rfc822_write_address (buf, sizeof (buf), *a, 0);
184 if (mutt_get_field (field, buf, sizeof (buf), M_ALIAS) != 0)
186 rfc822_free_address (a);
187 *a = mutt_expand_aliases (mutt_parse_adrlist (NULL, buf));
188 if ((idna_ok = mutt_addrlist_to_idna (*a, &err)) != 0) {
189 mutt_error (_("Error: '%s' is a bad IDN."), err);
195 while (idna_ok != 0);
199 static int edit_envelope (ENVELOPE * en, int flags)
201 char buf[HUGE_STRING];
202 LIST *uh = UserHeader;
203 regmatch_t pat_match[1];
206 if (option (OPTNEWSSEND)) {
208 strfcpy (buf, en->newsgroups, sizeof (buf));
211 if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) != 0)
213 mem_free (&en->newsgroups);
214 en->newsgroups = str_dup (buf);
217 strfcpy (buf, en->followup_to, sizeof (buf));
220 if (option (OPTASKFOLLOWUP)
221 && mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) != 0)
223 mem_free (&en->followup_to);
224 en->followup_to = str_dup (buf);
226 if (en->x_comment_to)
227 strfcpy (buf, en->x_comment_to, sizeof (buf));
230 if (option (OPTXCOMMENTTO) && option (OPTASKXCOMMENTTO)
231 && mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) != 0)
233 mem_free (&en->x_comment_to);
234 en->x_comment_to = str_dup (buf);
239 if (edit_address (&en->to, "To: ") == -1 || en->to == NULL)
241 if (option (OPTASKCC) && edit_address (&en->cc, "Cc: ") == -1)
243 if (option (OPTASKBCC) && edit_address (&en->bcc, "Bcc: ") == -1)
248 if (option (OPTFASTREPLY))
251 strfcpy (buf, en->subject, sizeof (buf));
257 for (; uh; uh = uh->next) {
258 if (ascii_strncasecmp ("subject:", uh->data, 8) == 0) {
261 strncpy (buf, p, sizeof (buf));
266 if ((flags & (SENDREPLY)) && option (OPTSTRIPWAS) && StripWasRegexp.rx &&
267 regexec (StripWasRegexp.rx, buf, 1, pat_match, 0) == 0) {
268 unsigned int pos = pat_match->rm_so;
270 if (ascii_strncasecmp (buf, "re: ", pos) != 0) {
271 buf[pos] = '\0'; /* kill match */
272 while (pos-- && buf[pos] == ' ')
273 buf[pos] = '\0'; /* remove trailing spaces */
276 mutt_error (_("Ignoring $strip_was: Subject would be empty."));
280 if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) != 0 || (!buf[0]
285 ("No subject, abort?"))
287 mutt_message _("No subject, aborting.");
291 str_replace (&en->subject, buf);
297 char *nntp_get_header (const char *s)
304 static void process_user_recips (ENVELOPE * env)
306 LIST *uh = UserHeader;
308 for (; uh; uh = uh->next) {
309 if (ascii_strncasecmp ("to:", uh->data, 3) == 0)
310 env->to = rfc822_parse_adrlist (env->to, uh->data + 3);
311 else if (ascii_strncasecmp ("cc:", uh->data, 3) == 0)
312 env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3);
313 else if (ascii_strncasecmp ("bcc:", uh->data, 4) == 0)
314 env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4);
316 else if (ascii_strncasecmp ("newsgroups:", uh->data, 11) == 0)
317 env->newsgroups = nntp_get_header (uh->data + 11);
318 else if (ascii_strncasecmp ("followup-to:", uh->data, 12) == 0)
319 env->followup_to = nntp_get_header (uh->data + 12);
320 else if (ascii_strncasecmp ("x-comment-to:", uh->data, 13) == 0)
321 env->x_comment_to = nntp_get_header (uh->data + 13);
326 static void process_user_header (ENVELOPE * env)
328 LIST *uh = UserHeader;
329 LIST *last = env->userhdrs;
335 for (; uh; uh = uh->next) {
336 if (ascii_strncasecmp ("from:", uh->data, 5) == 0) {
337 /* User has specified a default From: address. Remove default address */
338 rfc822_free_address (&env->from);
339 env->from = rfc822_parse_adrlist (env->from, uh->data + 5);
341 else if (ascii_strncasecmp ("reply-to:", uh->data, 9) == 0) {
342 rfc822_free_address (&env->reply_to);
343 env->reply_to = rfc822_parse_adrlist (env->reply_to, uh->data + 9);
345 else if (ascii_strncasecmp ("message-id:", uh->data, 11) == 0)
346 str_replace (&env->message_id, uh->data + 11);
347 else if (ascii_strncasecmp ("to:", uh->data, 3) != 0 &&
348 ascii_strncasecmp ("cc:", uh->data, 3) != 0 &&
349 ascii_strncasecmp ("bcc:", uh->data, 4) != 0 &&
351 ascii_strncasecmp ("newsgroups:", uh->data, 11) != 0 &&
352 ascii_strncasecmp ("followup-to:", uh->data, 12) != 0 &&
353 ascii_strncasecmp ("x-comment-to:", uh->data, 13) != 0 &&
355 ascii_strncasecmp ("supersedes:", uh->data, 11) != 0 &&
356 ascii_strncasecmp ("subject:", uh->data, 8) != 0) {
358 last->next = mutt_new_list ();
362 last = env->userhdrs = mutt_new_list ();
363 last->data = str_dup (uh->data);
368 void mutt_forward_intro (FILE * fp, HEADER * cur)
372 fputs ("----- Forwarded message from ", fp);
374 rfc822_write_address (buffer, sizeof (buffer), cur->env->from, 1);
376 fputs (" -----\n\n", fp);
379 void mutt_forward_trailer (FILE * fp)
381 fputs ("\n----- End forwarded message -----\n", fp);
385 static int include_forward (CONTEXT * ctx, HEADER * cur, FILE * out)
387 int chflags = CH_DECODE, cmflags = 0;
389 mutt_parse_mime_message (ctx, cur);
390 mutt_message_hook (ctx, cur, M_MESSAGEHOOK);
392 if (WithCrypto && (cur->security & ENCRYPT) && option (OPTFORWDECODE)) {
393 /* make sure we have the user's passphrase before proceeding... */
394 crypt_valid_passphrase (cur->security);
397 mutt_forward_intro (out, cur);
399 if (option (OPTFORWDECODE)) {
400 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
401 if (option (OPTWEED)) {
402 chflags |= CH_WEED | CH_REORDER;
403 cmflags |= M_CM_WEED;
406 if (option (OPTFORWQUOTE))
407 cmflags |= M_CM_PREFIX;
409 mutt_copy_message (out, ctx, cur, cmflags, chflags);
410 mutt_forward_trailer (out);
414 void mutt_make_attribution (CONTEXT * ctx, HEADER * cur, FILE * out)
419 mutt_make_string (buffer, sizeof (buffer), Attribution, ctx, cur);
425 void mutt_make_post_indent (CONTEXT * ctx, HEADER * cur, FILE * out)
429 if (PostIndentString) {
430 mutt_make_string (buffer, sizeof (buffer), PostIndentString, ctx, cur);
435 static int include_reply (CONTEXT * ctx, HEADER * cur, FILE * out)
437 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV | M_CM_REPLYING;
438 int chflags = CH_DECODE;
440 if (WithCrypto && (cur->security & ENCRYPT)) {
441 /* make sure we have the user's passphrase before proceeding... */
442 crypt_valid_passphrase (cur->security);
445 mutt_parse_mime_message (ctx, cur);
446 mutt_message_hook (ctx, cur, M_MESSAGEHOOK);
448 mutt_make_attribution (ctx, cur, out);
450 if (!option (OPTHEADER))
451 cmflags |= M_CM_NOHEADER;
452 if (option (OPTWEED)) {
453 chflags |= CH_WEED | CH_REORDER;
454 cmflags |= M_CM_WEED;
457 mutt_copy_message (out, ctx, cur, cmflags, chflags);
459 mutt_make_post_indent (ctx, cur, out);
464 static int default_to (ADDRESS ** to, ENVELOPE * env, int flags, int hmfupto)
469 if (flags && env->mail_followup_to && hmfupto == M_YES) {
470 rfc822_append (to, env->mail_followup_to);
474 /* Exit now if we're setting up the default Cc list for list-reply
475 * (only set if Mail-Followup-To is present and honoured).
477 if (flags & SENDLISTREPLY)
480 /* If this message came from a mailing list, ask the user if he really
481 * intended to reply to the author only.
483 if (!(flags & SENDGROUPREPLY) && mutt_is_list_cc (0, env->to, env->cc)) {
484 switch (query_quadoption (OPT_LISTREPLY,
486 ("Message came from a mailing list. Reply to author only?")))
489 tmp = find_mailing_lists (env->to, env->cc);
490 rfc822_append (to, tmp);
491 rfc822_free_address (&tmp);
494 return -1; /* abort */
498 if (!option (OPTREPLYSELF) && mutt_addr_is_user (env->from)) {
499 /* mail is from the user, assume replying to recipients */
500 rfc822_append (to, env->to);
502 else if (env->reply_to) {
503 if ((mutt_addrcmp (env->from, env->reply_to) && !env->reply_to->next) ||
504 (option (OPTIGNORELISTREPLYTO) &&
505 mutt_is_mail_list (env->reply_to) &&
506 (mutt_addrsrc (env->reply_to, env->to) ||
507 mutt_addrsrc (env->reply_to, env->cc)))) {
508 /* If the Reply-To: address is a mailing list, assume that it was
509 * put there by the mailing list, and use the From: address
511 * We also take the from header if our correspondant has a reply-to
512 * header which is identical to the electronic mail address given
513 * in his From header.
516 rfc822_append (to, env->from);
518 else if (!(mutt_addrcmp (env->from, env->reply_to) &&
519 !env->reply_to->next) && quadoption (OPT_REPLYTO) != M_YES) {
520 /* There are quite a few mailing lists which set the Reply-To:
521 * header field to the list address, which makes it quite impossible
522 * to send a message to only the sender of the message. This
523 * provides a way to do that.
525 snprintf (prompt, sizeof (prompt), _("Reply to %s%s?"),
526 env->reply_to->mailbox, env->reply_to->next ? ",..." : "");
527 switch (query_quadoption (OPT_REPLYTO, prompt)) {
529 rfc822_append (to, env->reply_to);
533 rfc822_append (to, env->from);
537 return (-1); /* abort */
541 rfc822_append (to, env->reply_to);
544 rfc822_append (to, env->from);
549 int mutt_fetch_recips (ENVELOPE * out, ENVELOPE * in, int flags)
555 if ((flags & (SENDLISTREPLY | SENDGROUPREPLY)) && in->mail_followup_to) {
556 snprintf (prompt, sizeof (prompt), _("Follow-up to %s%s?"),
557 in->mail_followup_to->mailbox,
558 in->mail_followup_to->next ? ",..." : "");
560 if ((hmfupto = query_quadoption (OPT_MFUPTO, prompt)) == -1)
564 if (flags & SENDLISTREPLY) {
565 tmp = find_mailing_lists (in->to, in->cc);
566 rfc822_append (&out->to, tmp);
567 rfc822_free_address (&tmp);
569 if (in->mail_followup_to && hmfupto == M_YES &&
570 default_to (&out->cc, in, flags & SENDLISTREPLY, hmfupto) == -1)
571 return (-1); /* abort */
574 if (default_to (&out->to, in, flags & SENDGROUPREPLY, hmfupto) == -1)
575 return (-1); /* abort */
577 if ((flags & SENDGROUPREPLY)
578 && (!in->mail_followup_to || hmfupto != M_YES)) {
579 /* if(!mutt_addr_is_user(in->to)) */
580 rfc822_append (&out->cc, in->to);
581 rfc822_append (&out->cc, in->cc);
587 LIST *mutt_make_references (ENVELOPE * e)
589 LIST *t = NULL, *l = NULL;
592 l = mutt_copy_list (e->references);
594 l = mutt_copy_list (e->in_reply_to);
597 t = mutt_new_list ();
598 t->data = str_dup (e->message_id);
606 void mutt_fix_reply_recipients (ENVELOPE * env)
608 mutt_expand_aliases_env (env);
610 if (!option (OPTMETOO)) {
611 /* the order is important here. do the CC: first so that if the
612 * the user is the only recipient, it ends up on the TO: field
614 env->cc = remove_user (env->cc, (env->to == NULL));
615 env->to = remove_user (env->to, (env->cc == NULL));
618 /* the CC field can get cluttered, especially with lists */
619 env->to = mutt_remove_duplicates (env->to);
620 env->cc = mutt_remove_duplicates (env->cc);
621 env->cc = mutt_remove_xrefs (env->to, env->cc);
624 void mutt_make_forward_subject (ENVELOPE * env, CONTEXT * ctx, HEADER * cur)
628 /* set the default subject for the message. */
629 mutt_make_string (buffer, sizeof (buffer), NONULL (ForwFmt), ctx, cur);
630 str_replace (&env->subject, buffer);
633 void mutt_make_misc_reply_headers (ENVELOPE * env, CONTEXT * ctx,
634 HEADER * cur, ENVELOPE * curenv)
636 /* This takes precedence over a subject that might have
637 * been taken from a List-Post header. Is that correct?
639 if (curenv->real_subj) {
640 mem_free (&env->subject);
641 env->subject = mem_malloc (str_len (curenv->real_subj) + 5);
642 sprintf (env->subject, "Re: %s", curenv->real_subj); /* __SPRINTF_CHECKED__ */
644 else if (!env->subject)
645 env->subject = str_dup ("Re: your mail");
648 if (option (OPTNEWSSEND) && option (OPTXCOMMENTTO) && curenv->from)
649 env->x_comment_to = str_dup (mutt_get_name (curenv->from));
653 void mutt_add_to_reference_headers (ENVELOPE * env, ENVELOPE * curenv,
654 LIST *** pp, LIST *** qq)
656 LIST **p = NULL, **q = NULL;
664 p = &env->references;
666 q = &env->in_reply_to;
673 *p = mutt_make_references (curenv);
675 if (curenv->message_id) {
676 *q = mutt_new_list ();
677 (*q)->data = str_dup (curenv->message_id);
688 mutt_make_reference_headers (ENVELOPE * curenv, ENVELOPE * env, CONTEXT * ctx)
690 env->references = NULL;
691 env->in_reply_to = NULL;
695 LIST **p = NULL, **q = NULL;
698 for (i = 0; i < ctx->vcount; i++) {
699 h = ctx->hdrs[ctx->v2r[i]];
701 mutt_add_to_reference_headers (env, h->env, &p, &q);
705 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
709 envelope_defaults (ENVELOPE * env, CONTEXT * ctx, HEADER * cur, int flags)
711 ENVELOPE *curenv = NULL;
716 for (i = 0; i < ctx->vcount; i++)
717 if (ctx->hdrs[ctx->v2r[i]]->tagged) {
718 cur = ctx->hdrs[ctx->v2r[i]];
724 /* This could happen if the user tagged some messages and then did
725 * a limit such that none of the tagged message are visible.
727 mutt_error _("No tagged messages are visible!");
735 if (flags & SENDREPLY) {
737 if ((flags & SENDNEWS)) {
738 /* in case followup set Newsgroups: with Followup-To: if it present */
739 if (!env->newsgroups && curenv &&
740 str_casecmp (curenv->followup_to, "poster"))
741 env->newsgroups = str_dup (curenv->followup_to);
748 for (i = 0; i < ctx->vcount; i++) {
749 h = ctx->hdrs[ctx->v2r[i]];
750 if (h->tagged && mutt_fetch_recips (env, h->env, flags) == -1)
754 else if (mutt_fetch_recips (env, curenv, flags) == -1)
757 if ((flags & SENDLISTREPLY) && !env->to) {
758 mutt_error _("No mailing lists found!");
763 mutt_make_misc_reply_headers (env, ctx, cur, curenv);
764 mutt_make_reference_headers (tag ? NULL : curenv, env, ctx);
766 else if (flags & SENDFORWARD)
767 mutt_make_forward_subject (env, ctx, cur);
772 static int generate_body (FILE * tempfp, /* stream for outgoing message */
773 HEADER * msg, /* header for outgoing message */
774 int flags, /* compose mode */
775 CONTEXT * ctx, /* current mailbox */
777 { /* current message */
782 if (flags & SENDREPLY) {
784 query_quadoption (OPT_INCLUDE,
785 _("Include message in reply?"))) == -1)
789 mutt_message _("Including quoted message...");
792 for (i = 0; i < ctx->vcount; i++) {
793 h = ctx->hdrs[ctx->v2r[i]];
795 if (include_reply (ctx, h, tempfp) == -1) {
796 mutt_error _("Could not include all requested messages!");
800 fputc ('\n', tempfp);
805 include_reply (ctx, cur, tempfp);
809 else if (flags & SENDFORWARD) {
811 query_quadoption (OPT_MIMEFWD,
812 _("Forward as attachment?"))) == M_YES) {
813 BODY *last = msg->content;
815 mutt_message _("Preparing forwarded message...");
817 while (last && last->next)
821 tmp = mutt_make_message_attach (ctx, cur, 0);
828 for (i = 0; i < ctx->vcount; i++) {
829 if (ctx->hdrs[ctx->v2r[i]]->tagged) {
830 tmp = mutt_make_message_attach (ctx, ctx->hdrs[ctx->v2r[i]], 0);
836 last = msg->content = tmp;
843 include_forward (ctx, cur, tempfp);
845 for (i = 0; i < ctx->vcount; i++)
846 if (ctx->hdrs[ctx->v2r[i]]->tagged)
847 include_forward (ctx, ctx->hdrs[ctx->v2r[i]], tempfp);
852 /* if (WithCrypto && (flags & SENDKEY)) */
853 else if ((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY)) {
856 if ((WithCrypto & APPLICATION_PGP)
857 && (tmp = crypt_pgp_make_key_attachment (NULL)) == NULL)
860 tmp->next = msg->content;
869 void mutt_set_followup_to (ENVELOPE * e)
875 * Only generate the Mail-Followup-To if the user has requested it, and
876 * it hasn't already been set
879 if (!option (OPTFOLLOWUPTO))
882 if (option (OPTNEWSSEND)) {
883 if (!e->followup_to && e->newsgroups && (strrchr (e->newsgroups, ',')))
884 e->followup_to = str_dup (e->newsgroups);
889 if (!e->mail_followup_to) {
890 if (mutt_is_list_cc (0, e->to, e->cc)) {
892 * this message goes to known mailing lists, so create a proper
893 * mail-followup-to header
896 t = rfc822_append (&e->mail_followup_to, e->to);
897 rfc822_append (&t, e->cc);
900 /* remove ourselves from the mail-followup-to header */
901 e->mail_followup_to = remove_user (e->mail_followup_to, 0);
904 * If we are not subscribed to any of the lists in question,
905 * re-add ourselves to the mail-followup-to header. The
906 * mail-followup-to header generated is a no-op with group-reply,
907 * but makes sure list-reply has the desired effect.
910 if (e->mail_followup_to && !mutt_is_list_recipient (0, e->to, e->cc)) {
912 from = rfc822_cpy_adr (e->reply_to);
914 from = rfc822_cpy_adr (e->from);
916 from = mutt_default_from ();
919 /* Normally, this loop will not even be entered. */
920 for (t = from; t && t->next; t = t->next);
922 t->next = e->mail_followup_to; /* t cannot be NULL at this point. */
923 e->mail_followup_to = from;
927 e->mail_followup_to = mutt_remove_duplicates (e->mail_followup_to);
933 /* look through the recipients of the message we are replying to, and if
934 we find an address that matches $alternates, we use that as the default
936 static ADDRESS *set_reverse_name (ENVELOPE * env)
940 for (tmp = env->to; tmp; tmp = tmp->next) {
941 if (mutt_addr_is_user (tmp))
945 for (tmp = env->cc; tmp; tmp = tmp->next) {
946 if (mutt_addr_is_user (tmp))
950 if (!tmp && mutt_addr_is_user (env->from))
953 tmp = rfc822_cpy_adr_real (tmp);
954 if (!option (OPTREVREAL))
955 mem_free (&tmp->personal);
957 tmp->personal = str_dup (Realname);
962 ADDRESS *mutt_default_from (void)
965 const char *fqdn = mutt_fqdn (1);
968 * Note: We let $from override $realname here. Is this the right
973 adr = rfc822_cpy_adr_real (From);
974 else if (option (OPTUSEDOMAIN)) {
975 adr = rfc822_new_address ();
977 mem_malloc (str_len (Username) + str_len (fqdn) + 2);
978 sprintf (adr->mailbox, "%s@%s", NONULL (Username), NONULL (fqdn)); /* __SPRINTF_CHECKED__ */
981 adr = rfc822_new_address ();
982 adr->mailbox = str_dup (NONULL (Username));
988 static int send_message (HEADER * msg)
990 char tempfile[_POSIX_PATH_MAX];
994 /* Write out the message in MIME form. */
995 mutt_mktemp (tempfile);
996 if ((tempfp = safe_fopen (tempfile, "w")) == NULL)
1000 mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0,
1001 msg->chain ? 1 : 0);
1004 mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0, 0);
1007 fputc ('\n', tempfp); /* tie off the header. */
1009 if ((mutt_write_mime_body (msg->content, tempfp) == -1)) {
1015 if (fclose (tempfp) != 0) {
1016 mutt_perror (tempfile);
1023 return mix_send_message (msg->chain, tempfile);
1026 i = mutt_invoke_mta (msg->env->from, msg->env->to, msg->env->cc,
1027 msg->env->bcc, tempfile,
1028 (msg->content->encoding == ENC8BIT));
1032 /* rfc2047 encode the content-descriptions */
1033 static void encode_descriptions (BODY * b, short recurse)
1037 for (t = b; t; t = t->next) {
1038 if (t->description) {
1039 rfc2047_encode_string (&t->description);
1041 if (recurse && t->parts)
1042 encode_descriptions (t->parts, recurse);
1046 /* rfc2047 decode them in case of an error */
1047 static void decode_descriptions (BODY * b)
1051 for (t = b; t; t = t->next) {
1052 if (t->description) {
1053 rfc2047_decode (&t->description);
1056 decode_descriptions (t->parts);
1060 int mutt_resend_message (FILE * fp, CONTEXT * ctx, HEADER * cur)
1062 HEADER *msg = mutt_new_header ();
1064 if (mutt_prepare_template (fp, ctx, msg, cur, 1) < 0)
1067 return ci_send_message (SENDRESEND, msg, NULL, ctx, cur);
1070 int ci_send_message (int flags, /* send mode */
1071 HEADER * msg, /* template to use for new message */
1072 char *tempfile, /* file specified by -i or -H */
1073 CONTEXT * ctx, /* current mailbox */
1075 { /* current message */
1076 char buffer[LONG_STRING];
1077 char fcc[_POSIX_PATH_MAX] = ""; /* where to copy this message */
1078 FILE *tempfp = NULL;
1080 int i, killfrom = 0;
1082 int free_clear_content = 0;
1084 BODY *save_content = NULL;
1085 BODY *clear_content = NULL;
1086 char *pgpkeylist = NULL;
1088 /* save current value of "pgp_sign_as" */
1089 char *signas = NULL;
1090 char *tag = NULL, *err = NULL;
1096 if (flags & SENDNEWS)
1097 set_option (OPTNEWSSEND);
1099 unset_option (OPTNEWSSEND);
1102 if (!flags && !msg && quadoption (OPT_RECALL) != M_NO &&
1103 mutt_num_postponed (1)) {
1104 /* If the user is composing a new message, check to see if there
1105 * are any postponed messages first.
1108 query_quadoption (OPT_RECALL, _("Recall postponed message?"))) == -1)
1112 flags |= SENDPOSTPONED;
1116 if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED))
1117 signas = str_dup (PgpSignAs);
1119 /* Delay expansion of aliases until absolutely necessary--shouldn't
1120 * be necessary unless we are prompting the user or about to execute a
1125 msg = mutt_new_header ();
1127 if (flags == SENDPOSTPONED) {
1129 mutt_get_postponed (ctx, msg, &cur, fcc, sizeof (fcc))) < 0)
1133 * If postponed message is a news article, it have
1134 * a "Newsgroups:" header line, then set appropriate flag.
1136 if (msg->env->newsgroups) {
1138 set_option (OPTNEWSSEND);
1142 unset_option (OPTNEWSSEND);
1147 if (flags & (SENDPOSTPONED | SENDRESEND)) {
1148 if ((tempfp = safe_fopen (msg->content->filename, "a+")) == NULL) {
1149 mutt_perror (msg->content->filename);
1155 msg->env = mutt_new_envelope ();
1158 /* Parse and use an eventual list-post header */
1159 if ((flags & SENDLISTREPLY)
1160 && cur && cur->env && cur->env->list_post) {
1161 /* Use any list-post header as a template */
1162 url_parse_mailto (msg->env, NULL, cur->env->list_post);
1163 /* We don't let them set the sender's address. */
1164 rfc822_free_address (&msg->env->from);
1167 if (!(flags & (SENDKEY | SENDPOSTPONED | SENDRESEND))) {
1168 pbody = mutt_new_body ();
1169 pbody->next = msg->content; /* don't kill command-line attachments */
1170 msg->content = pbody;
1172 ctype = str_dup (ContentType);
1173 mutt_parse_content_type (ctype, msg->content);
1176 msg->content->unlink = 1;
1177 msg->content->use_disp = 0;
1178 msg->content->disposition = DISPINLINE;
1179 if (option (OPTTEXTFLOWED) && msg->content->type == TYPETEXT
1180 && !ascii_strcasecmp (msg->content->subtype, "plain"))
1181 mutt_set_parameter ("format", "flowed", &msg->content->parameter);
1184 mutt_mktemp (buffer);
1185 tempfp = safe_fopen (buffer, "w+");
1186 msg->content->filename = str_dup (buffer);
1189 tempfp = safe_fopen (tempfile, "a+");
1190 msg->content->filename = str_dup (tempfile);
1194 debug_print (1, ("can't create tempfile %s (errno=%d)\n",
1195 msg->content->filename, errno));
1196 mutt_perror (msg->content->filename);
1201 /* this is handled here so that the user can match ~f in send-hook */
1202 if (cur && option (OPTREVNAME) && !(flags & (SENDPOSTPONED | SENDRESEND))) {
1203 /* we shouldn't have to worry about freeing `msg->env->from' before
1204 * setting it here since this code will only execute when doing some
1205 * sort of reply. the pointer will only be set when using the -H command
1208 * We shouldn't have to worry about alias expansion here since we are
1209 * either replying to a real or postponed message, therefore no aliases
1210 * should exist since the user has not had the opportunity to add
1211 * addresses to the list. We just have to ensure the postponed messages
1212 * have their aliases expanded.
1215 msg->env->from = set_reverse_name (cur->env);
1218 if (!msg->env->from && option (OPTUSEFROM)
1219 && !(flags & (SENDPOSTPONED | SENDRESEND)))
1220 msg->env->from = mutt_default_from ();
1222 if (flags & SENDBATCH) {
1223 mutt_copy_stream (stdin, tempfp);
1224 if (option (OPTHDRS)) {
1225 process_user_recips (msg->env);
1226 process_user_header (msg->env);
1228 mutt_expand_aliases_env (msg->env);
1230 else if (!(flags & (SENDPOSTPONED | SENDRESEND))) {
1231 if ((flags & (SENDREPLY | SENDFORWARD)) && ctx &&
1232 envelope_defaults (msg->env, ctx, cur, flags) == -1)
1235 if (option (OPTHDRS))
1236 process_user_recips (msg->env);
1238 /* Expand aliases and remove duplicates/crossrefs */
1239 mutt_fix_reply_recipients (msg->env);
1242 if ((flags & SENDNEWS) && ctx && ctx->magic == M_NNTP
1243 && !msg->env->newsgroups)
1244 msg->env->newsgroups = str_dup (((NNTP_DATA *) ctx->data)->group);
1247 if (!(flags & SENDMAILX) &&
1248 !(option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
1249 !((flags & SENDREPLY) && option (OPTFASTREPLY))) {
1250 if (edit_envelope (msg->env, flags) == -1)
1254 /* the from address must be set here regardless of whether or not
1255 * $use_from is set so that the `~P' (from you) operator in send-hook
1256 * patterns will work. if $use_from is unset, the from address is killed
1257 * after send-hooks are evaulated */
1259 if (!msg->env->from) {
1260 msg->env->from = mutt_default_from ();
1264 if ((flags & SENDREPLY) && cur) {
1265 /* change setting based upon message we are replying to */
1266 mutt_message_hook (ctx, cur, M_REPLYHOOK);
1269 * set the replied flag for the message we are generating so that the
1270 * user can use ~Q in a send-hook to know when reply-hook's are also
1276 /* change settings based upon recipients */
1278 mutt_message_hook (NULL, msg, M_SENDHOOK);
1281 * Unset the replied flag from the message we are composing since it is
1282 * no longer required. This is done here because the FCC'd copy of
1283 * this message was erroneously get the 'R'eplied flag when stored in
1284 * a maildir-style mailbox.
1289 rfc822_free_address (&msg->env->from);
1293 if (option (OPTHDRS))
1294 process_user_header (msg->env);
1297 if (option (OPTSIGONTOP)
1298 && (!(flags & (SENDMAILX | SENDKEY)) && Editor
1299 && str_cmp (Editor, "builtin") != 0))
1300 append_signature (tempfp);
1302 /* include replies/forwarded messages, unless we are given a template */
1303 if (!tempfile && (ctx || !(flags & (SENDREPLY | SENDFORWARD)))
1304 && generate_body (tempfp, msg, flags, ctx, cur) == -1)
1307 if (!option (OPTSIGONTOP)
1308 && (!(flags & (SENDMAILX | SENDKEY)) && Editor
1309 && str_cmp (Editor, "builtin") != 0))
1310 append_signature (tempfp);
1313 * this wants to be done _after_ generate_body, so message-hooks
1317 if (WithCrypto && !(flags & SENDMAILX)) {
1318 if (option (OPTCRYPTAUTOSIGN))
1319 msg->security |= SIGN;
1320 if (option (OPTCRYPTAUTOENCRYPT))
1321 msg->security |= ENCRYPT;
1322 if (option (OPTCRYPTREPLYENCRYPT) && cur && (cur->security & ENCRYPT))
1323 msg->security |= ENCRYPT;
1324 if (option (OPTCRYPTREPLYSIGN) && cur && (cur->security & SIGN))
1325 msg->security |= SIGN;
1326 if (option (OPTCRYPTREPLYSIGNENCRYPTED) && cur
1327 && (cur->security & ENCRYPT))
1328 msg->security |= SIGN;
1329 if (WithCrypto & APPLICATION_PGP && (msg->security & (ENCRYPT | SIGN))) {
1330 if (option (OPTPGPAUTOINLINE))
1331 msg->security |= INLINE;
1332 if (option (OPTPGPREPLYINLINE) && cur && (cur->security & INLINE))
1333 msg->security |= INLINE;
1337 if (WithCrypto && msg->security) {
1339 * When reypling / forwarding, use the original message's
1340 * crypto system. According to the documentation,
1341 * smime_is_default should be disregarded here.
1343 * Problem: At least with forwarding, this doesn't really
1344 * make much sense. Should we have an option to completely
1345 * disable individual mechanisms at run-time?
1348 if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP)
1349 && (cur->security & APPLICATION_PGP))
1350 msg->security |= APPLICATION_PGP;
1351 else if ((WithCrypto & APPLICATION_SMIME)
1352 && option (OPTCRYPTAUTOSMIME)
1353 && (cur->security & APPLICATION_SMIME))
1354 msg->security |= APPLICATION_SMIME;
1358 * No crypto mechanism selected? Use availability + smime_is_default
1361 if (!(msg->security & (APPLICATION_SMIME | APPLICATION_PGP))) {
1362 if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME)
1363 && option (OPTSMIMEISDEFAULT))
1364 msg->security |= APPLICATION_SMIME;
1365 else if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP))
1366 msg->security |= APPLICATION_PGP;
1367 else if ((WithCrypto & APPLICATION_SMIME)
1368 && option (OPTCRYPTAUTOSMIME))
1369 msg->security |= APPLICATION_SMIME;
1373 /* No permissible mechanisms found. Don't sign or encrypt. */
1374 if (!(msg->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1379 * This hook is even called for postponed messages, and can, e.g., be
1380 * used for setting the editor, the sendmail path, or the
1383 mutt_message_hook (NULL, msg, M_SEND2HOOK);
1385 /* wait until now to set the real name portion of our return address so
1386 that $realname can be set in a send-hook */
1387 if (msg->env->from && !msg->env->from->personal
1388 && !(flags & (SENDRESEND | SENDPOSTPONED)))
1389 msg->env->from->personal = str_dup (Realname);
1391 if (!((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY)))
1392 safe_fclose (&tempfp);
1394 if (flags & SENDMAILX) {
1395 if (mutt_builtin_editor (msg->content->filename, msg, cur) == -1)
1398 else if (!(flags & SENDBATCH)) {
1400 time_t mtime = mutt_decrease_mtime (msg->content->filename, NULL);
1402 mutt_update_encoding (msg->content);
1405 * Select whether or not the user's editor should be called now. We
1406 * don't want to do this when:
1407 * 1) we are sending a key/cert
1408 * 2) we are forwarding a message and the user doesn't want to edit it.
1409 * This is controled by the quadoption $forward_edit. However, if
1410 * both $edit_headers and $autoedit are set, we want to ignore the
1411 * setting of $forward_edit because the user probably needs to add the
1414 if (!(flags & SENDKEY) &&
1415 ((flags & SENDFORWARD) == 0 ||
1416 (option (OPTEDITHDRS) && option (OPTAUTOEDIT)) ||
1417 query_quadoption (OPT_FORWEDIT,
1418 _("Edit forwarded message?")) == M_YES)) {
1419 /* If the this isn't a text message, look for a mailcap edit command */
1420 if (mutt_needs_mailcap (msg->content))
1421 mutt_edit_attachment (msg->content);
1422 else if (!Editor || str_cmp ("builtin", Editor) == 0)
1423 mutt_builtin_editor (msg->content->filename, msg, cur);
1424 else if (option (OPTEDITHDRS)) {
1425 mutt_env_to_local (msg->env);
1426 mutt_edit_headers (Editor, msg->content->filename, msg, fcc,
1428 mutt_env_to_idna (msg->env, NULL, NULL);
1431 mutt_edit_file (Editor, msg->content->filename);
1433 mutt_message_hook (NULL, msg, M_SEND2HOOK);
1436 if (!(flags & (SENDPOSTPONED | SENDFORWARD | SENDKEY | SENDRESEND))) {
1437 if (stat (msg->content->filename, &st) == 0) {
1438 /* if the file was not modified, bail out now */
1439 if (mtime == st.st_mtime && !msg->content->next &&
1440 query_quadoption (OPT_ABORT,
1441 _("Abort unmodified message?")) == M_YES) {
1442 mutt_message _("Aborted unmodified message.");
1448 mutt_perror (msg->content->filename);
1452 /* specify a default fcc. if we are in batchmode, only save a copy of
1453 * the message if the value of $copy is yes or ask-yes */
1455 if (!fcc[0] && !(flags & (SENDPOSTPONED))
1456 && (!(flags & SENDBATCH) || (quadoption (OPT_COPY) & 0x1))) {
1457 /* set the default FCC */
1458 if (!msg->env->from) {
1459 msg->env->from = mutt_default_from ();
1460 killfrom = 1; /* no need to check $use_from because if the user specified
1461 a from address it would have already been set by now */
1463 mutt_select_fcc (fcc, sizeof (fcc), msg);
1465 rfc822_free_address (&msg->env->from);
1471 mutt_update_encoding (msg->content);
1473 if (!(flags & (SENDMAILX | SENDBATCH))) {
1476 fcc_error = 0; /* reset value since we may have failed before */
1477 mutt_pretty_mailbox (fcc);
1478 i = mutt_compose_menu (msg, fcc, sizeof (fcc), cur);
1482 if (flags & SENDNEWS)
1483 mutt_message (_("Article not posted."));
1487 mutt_message _("Mail not sent.");
1491 /* postpone the message until later. */
1492 if (msg->content->next)
1493 msg->content = mutt_make_multipart (msg->content);
1496 * make sure the message is written to the right part of a maildir
1502 encode_descriptions (msg->content, 1);
1503 mutt_prepare_envelope (msg->env, 0);
1504 mutt_env_to_idna (msg->env, NULL, NULL); /* Handle bad IDNAs the next time. */
1507 || mutt_write_fcc (NONULL (Postponed), msg,
1509 && (flags & SENDREPLY)) ? cur->env->
1510 message_id : NULL, 1, fcc) < 0) {
1511 msg->content = mutt_remove_multipart (msg->content);
1512 decode_descriptions (msg->content);
1513 mutt_unprepare_envelope (msg->env);
1516 mutt_update_num_postponed ();
1517 mutt_message _("Message postponed.");
1524 if (!(flags & SENDNEWS))
1526 if (!msg->env->to && !msg->env->cc && !msg->env->bcc) {
1527 if (!(flags & SENDBATCH)) {
1528 mutt_error _("No recipients are specified!");
1533 puts _("No recipients were specified.");
1539 if (mutt_env_to_idna (msg->env, &tag, &err)) {
1540 mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
1542 if (!(flags & SENDBATCH))
1548 if (!msg->env->subject && !(flags & SENDBATCH) &&
1550 query_quadoption (OPT_SUBJECT,
1551 _("No subject, abort sending?"))) != M_NO) {
1552 /* if the abort is automatic, print an error message */
1553 if (quadoption (OPT_SUBJECT) == M_YES)
1554 mutt_error _("No subject specified.");
1559 if ((flags & SENDNEWS) && !msg->env->subject) {
1560 mutt_error _("No subject specified.");
1565 if ((flags & SENDNEWS) && !msg->env->newsgroups) {
1566 mutt_error _("No newsgroup specified.");
1572 if (msg->content->next)
1573 msg->content = mutt_make_multipart (msg->content);
1576 * Ok, we need to do it this way instead of handling all fcc stuff in
1577 * one place in order to avoid going to main_loop with encoded "env"
1578 * in case of error. Ugh.
1581 encode_descriptions (msg->content, 1);
1584 * Make sure that clear_content and free_clear_content are
1585 * properly initialized -- we may visit this particular place in
1586 * the code multiple times, including after a failed call to
1590 clear_content = NULL;
1591 free_clear_content = 0;
1594 if (msg->security) {
1595 /* save the decrypted attachments */
1596 clear_content = msg->content;
1598 if ((crypt_get_keys (msg, &pgpkeylist) == -1) ||
1599 mutt_protect (msg, pgpkeylist) == -1) {
1600 msg->content = mutt_remove_multipart (msg->content);
1602 mem_free (&pgpkeylist);
1604 decode_descriptions (msg->content);
1607 encode_descriptions (msg->content, 0);
1611 * at this point, msg->content is one of the following three things:
1612 * - multipart/signed. In this case, clear_content is a child.
1613 * - multipart/encrypted. In this case, clear_content exists
1615 * - application/pgp. In this case, clear_content exists independently.
1616 * - something else. In this case, it's the same as clear_content.
1619 /* This is ugly -- lack of "reporting back" from mutt_protect(). */
1621 if (clear_content && (msg->content != clear_content)
1622 && (msg->content->parts != clear_content))
1623 free_clear_content = 1;
1626 if (!option (OPTNOCURSES) && !(flags & SENDMAILX))
1627 mutt_message _("Sending message...");
1629 mutt_prepare_envelope (msg->env, 1);
1631 /* save a copy of the message, if necessary. */
1633 mutt_expand_path (fcc, sizeof (fcc));
1636 /* Don't save a copy when we are in batch-mode, and the FCC
1637 * folder is on an IMAP server: This would involve possibly lots
1638 * of user interaction, which is not available in batch mode.
1640 * Note: A patch to fix the problems with the use of IMAP servers
1641 * from non-curses mode is available from Brendan Cully. However,
1642 * I'd like to think a bit more about this before including it.
1646 if ((flags & SENDBATCH) && fcc[0] && mx_get_magic (fcc) == M_IMAP)
1650 if (*fcc && str_cmp ("/dev/null", fcc) != 0) {
1651 BODY *tmpbody = msg->content;
1652 BODY *save_sig = NULL;
1653 BODY *save_parts = NULL;
1655 if (WithCrypto && msg->security && option (OPTFCCCLEAR))
1656 msg->content = clear_content;
1658 /* check to see if the user wants copies of all attachments */
1659 if (!option (OPTFCCATTACH) && msg->content->type == TYPEMULTIPART) {
1661 && (str_cmp (msg->content->subtype, "encrypted") == 0 ||
1662 str_cmp (msg->content->subtype, "signed") == 0)) {
1663 if (clear_content->type == TYPEMULTIPART) {
1664 if (!(msg->security & ENCRYPT) && (msg->security & SIGN)) {
1665 /* save initial signature and attachments */
1666 save_sig = msg->content->parts->next;
1667 save_parts = clear_content->parts->next;
1670 /* this means writing only the main part */
1671 msg->content = clear_content->parts;
1673 if (mutt_protect (msg, pgpkeylist) == -1) {
1674 /* we can't do much about it at this point, so
1675 * fallback to saving the whole thing to fcc
1677 msg->content = tmpbody;
1682 save_content = msg->content;
1686 msg->content = msg->content->parts;
1691 /* update received time so that when storing to a mbox-style folder
1692 * the From_ line contains the current time instead of when the
1693 * message was first postponed.
1695 msg->received = time (NULL);
1696 if (mutt_write_fcc (fcc, msg, NULL, 0, NULL) == -1) {
1698 * Error writing FCC, we should abort sending.
1704 msg->content = tmpbody;
1706 if (WithCrypto && save_sig) {
1707 /* cleanup the second signature structures */
1708 if (save_content->parts) {
1709 mutt_free_body (&save_content->parts->next);
1710 save_content->parts = NULL;
1712 mutt_free_body (&save_content);
1714 /* restore old signature and attachments */
1715 msg->content->parts->next = save_sig;
1716 msg->content->parts->parts->next = save_parts;
1718 else if (WithCrypto && save_content) {
1719 /* destroy the new encrypted body. */
1720 mutt_free_body (&save_content);
1727 * Don't attempt to send the message if the FCC failed. Just pretend
1728 * the send failed as well so we give the user a chance to fix the
1731 if (fcc_error || (i = send_message (msg)) == -1) {
1732 if (!(flags & SENDBATCH)) {
1734 else if ((msg->security & ENCRYPT) || ((msg->security & SIGN)
1735 && msg->content->type ==
1737 mutt_free_body (&msg->content); /* destroy PGP data */
1738 msg->content = clear_content; /* restore clear text. */
1740 else if ((msg->security & SIGN) && msg->content->type == TYPEMULTIPART) {
1741 mutt_free_body (&msg->content->parts->next); /* destroy sig */
1742 msg->content = mutt_remove_multipart (msg->content);
1745 msg->content = mutt_remove_multipart (msg->content);
1746 decode_descriptions (msg->content);
1747 mutt_unprepare_envelope (msg->env);
1751 puts _("Could not send the message.");
1756 else if (!option (OPTNOCURSES) && !(flags & SENDMAILX))
1757 mutt_message (i != 0 ? _("Sending in background.") :
1759 (flags & SENDNEWS) ? _("Article posted.") :
1765 if (WithCrypto && (msg->security & ENCRYPT))
1766 mem_free (&pgpkeylist);
1768 if (WithCrypto && free_clear_content)
1769 mutt_free_body (&clear_content);
1771 if (flags & SENDREPLY) {
1773 mutt_set_flag (ctx, cur, M_REPLIED, 1);
1774 else if (!(flags & SENDPOSTPONED) && ctx && ctx->tagged) {
1775 for (i = 0; i < ctx->vcount; i++)
1776 if (ctx->hdrs[ctx->v2r[i]]->tagged)
1777 mutt_set_flag (ctx, ctx->hdrs[ctx->v2r[i]], M_REPLIED, 1);
1786 if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED)) {
1788 mem_free (&PgpSignAs);
1793 safe_fclose (&tempfp);
1794 mutt_free_header (&msg);
1799 /* vim: set sw=2: */