Rocco Rutte:
[apps/madmutt.git] / send.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  *
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.
8  */
9
10 #if HAVE_CONFIG_H
11 # include "config.h"
12 #endif
13
14 #include "mutt.h"
15 #include "enter.h"
16 #include "ascii.h"
17 #include "mutt_curses.h"
18 #include "rfc2047.h"
19 #include "keymap.h"
20 #include "mime.h"
21 #include "copy.h"
22 #include "mx.h"
23 #include "mutt_crypt.h"
24 #include "mutt_idna.h"
25 #include "url.h"
26 #include "attach.h"
27
28 #include "lib/mem.h"
29 #include "lib/intl.h"
30 #include "lib/str.h"
31 #include "lib/debug.h"
32
33 #include <ctype.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <dirent.h>
41 #include <time.h>
42 #include <sys/types.h>
43 #include <utime.h>
44
45 #ifdef USE_NNTP
46 #include "nntp.h"
47 #endif
48
49 #ifdef MIXMASTER
50 #include "remailer.h"
51 #endif
52
53
54 static void append_signature (FILE * f)
55 {
56   FILE *tmpfp;
57   pid_t thepid;
58
59   if (SignOffString) {
60     fprintf (f, "\n%s", SignOffString);
61   }
62
63   if (Signature && (tmpfp = mutt_open_read (Signature, &thepid))) {
64     if (option (OPTSIGDASHES))
65       fputs ("\n-- \n", f);
66     else if (SignOffString)
67       fputs ("\n", f);
68     mutt_copy_stream (tmpfp, f);
69     fclose (tmpfp);
70     if (thepid != -1)
71       mutt_wait_filter (thepid);
72   }
73 }
74
75 /* compare two e-mail addresses and return 1 if they are equivalent */
76 static int mutt_addrcmp (ADDRESS * a, ADDRESS * b)
77 {
78   if (!a->mailbox || !b->mailbox)
79     return 0;
80   if (ascii_strcasecmp (a->mailbox, b->mailbox))
81     return 0;
82   return 1;
83 }
84
85 /* search an e-mail address in a list */
86 static int mutt_addrsrc (ADDRESS * a, ADDRESS * lst)
87 {
88   for (; lst; lst = lst->next) {
89     if (mutt_addrcmp (a, lst))
90       return (1);
91   }
92   return (0);
93 }
94
95 /* removes addresses from "b" which are contained in "a" */
96 static ADDRESS *mutt_remove_xrefs (ADDRESS * a, ADDRESS * b)
97 {
98   ADDRESS *top, *p, *prev = NULL;
99
100   top = b;
101   while (b) {
102     for (p = a; p; p = p->next) {
103       if (mutt_addrcmp (p, b))
104         break;
105     }
106     if (p) {
107       if (prev) {
108         prev->next = b->next;
109         b->next = NULL;
110         rfc822_free_address (&b);
111         b = prev;
112       }
113       else {
114         top = top->next;
115         b->next = NULL;
116         rfc822_free_address (&b);
117         b = top;
118       }
119     }
120     else {
121       prev = b;
122       b = b->next;
123     }
124   }
125   return top;
126 }
127
128 /* remove any address which matches the current user.  if `leave_only' is
129  * nonzero, don't remove the user's address if it is the only one in the list
130  */
131 static ADDRESS *remove_user (ADDRESS * a, int leave_only)
132 {
133   ADDRESS *top = NULL, *last = NULL;
134
135   while (a) {
136     if (!mutt_addr_is_user (a)) {
137       if (top) {
138         last->next = a;
139         last = last->next;
140       }
141       else
142         last = top = a;
143       a = a->next;
144       last->next = NULL;
145     }
146     else {
147       ADDRESS *tmp = a;
148
149       a = a->next;
150       if (!leave_only || a || last) {
151         tmp->next = NULL;
152         rfc822_free_address (&tmp);
153       }
154       else
155         last = top = tmp;
156     }
157   }
158   return top;
159 }
160
161 static ADDRESS *find_mailing_lists (ADDRESS * t, ADDRESS * c)
162 {
163   ADDRESS *top = NULL, *ptr = NULL;
164
165   for (; t || c; t = c, c = NULL) {
166     for (; t; t = t->next) {
167       if (mutt_is_mail_list (t) && !t->group) {
168         if (top) {
169           ptr->next = rfc822_cpy_adr_real (t);
170           ptr = ptr->next;
171         }
172         else
173           ptr = top = rfc822_cpy_adr_real (t);
174       }
175     }
176   }
177   return top;
178 }
179
180 static int edit_address (ADDRESS ** a, /* const */ char *field)
181 {
182   char buf[HUGE_STRING];
183   char *err = NULL;
184   int idna_ok = 0;
185
186   do {
187     buf[0] = 0;
188     mutt_addrlist_to_local (*a);
189     rfc822_write_address (buf, sizeof (buf), *a, 0);
190     if (mutt_get_field (field, buf, sizeof (buf), M_ALIAS) != 0)
191       return (-1);
192     rfc822_free_address (a);
193     *a = mutt_expand_aliases (mutt_parse_adrlist (NULL, buf));
194     if ((idna_ok = mutt_addrlist_to_idna (*a, &err)) != 0) {
195       mutt_error (_("Error: '%s' is a bad IDN."), err);
196       mutt_refresh ();
197       mutt_sleep (2);
198       mem_free (&err);
199     }
200   }
201   while (idna_ok != 0);
202   return 0;
203 }
204
205 static int edit_envelope (ENVELOPE * en, int flags)
206 {
207   char buf[HUGE_STRING];
208   LIST *uh = UserHeader;
209   regmatch_t pat_match[1];
210
211 #ifdef USE_NNTP
212   if (option (OPTNEWSSEND)) {
213     if (en->newsgroups)
214       strfcpy (buf, en->newsgroups, sizeof (buf));
215     else
216       buf[0] = 0;
217     if (mutt_get_field ("Newsgroups: ", buf, sizeof (buf), 0) != 0)
218       return (-1);
219     mem_free (&en->newsgroups);
220     en->newsgroups = str_dup (buf);
221
222     if (en->followup_to)
223       strfcpy (buf, en->followup_to, sizeof (buf));
224     else
225       buf[0] = 0;
226     if (option (OPTASKFOLLOWUP)
227         && mutt_get_field ("Followup-To: ", buf, sizeof (buf), 0) != 0)
228       return (-1);
229     mem_free (&en->followup_to);
230     en->followup_to = str_dup (buf);
231
232     if (en->x_comment_to)
233       strfcpy (buf, en->x_comment_to, sizeof (buf));
234     else
235       buf[0] = 0;
236     if (option (OPTXCOMMENTTO) && option (OPTASKXCOMMENTTO)
237         && mutt_get_field ("X-Comment-To: ", buf, sizeof (buf), 0) != 0)
238       return (-1);
239     mem_free (&en->x_comment_to);
240     en->x_comment_to = str_dup (buf);
241   }
242   else
243 #endif
244   {
245     if (edit_address (&en->to, "To: ") == -1 || en->to == NULL)
246       return (-1);
247     if (option (OPTASKCC) && edit_address (&en->cc, "Cc: ") == -1)
248       return (-1);
249     if (option (OPTASKBCC) && edit_address (&en->bcc, "Bcc: ") == -1)
250       return (-1);
251   }
252
253   if (en->subject) {
254     if (option (OPTFASTREPLY))
255       return (0);
256     else
257       strfcpy (buf, en->subject, sizeof (buf));
258   }
259   else {
260     char *p;
261
262     buf[0] = 0;
263     for (; uh; uh = uh->next) {
264       if (ascii_strncasecmp ("subject:", uh->data, 8) == 0) {
265         p = uh->data + 8;
266         SKIPWS (p);
267         strncpy (buf, p, sizeof (buf));
268       }
269     }
270   }
271
272   if ((flags & (SENDREPLY)) && option (OPTSTRIPWAS) && StripWasRegexp.rx &&
273       regexec (StripWasRegexp.rx, buf, 1, pat_match, 0) == 0) {
274     unsigned int pos = pat_match->rm_so;
275
276     if (ascii_strncasecmp (buf, "re: ", pos) != 0) {
277       buf[pos] = '\0';          /* kill match */
278       while (pos-- && buf[pos] == ' ')
279         buf[pos] = '\0';        /* remove trailing spaces */
280     }
281     else {
282       mutt_error (_("Ignoring $strip_was: Subject would be empty."));
283       sleep (2);
284     }
285   }
286   if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) != 0 || (!buf[0]
287                                                                   &&
288                                                                   query_quadoption
289                                                                   (OPT_SUBJECT,
290                                                                    _
291                                                                    ("No subject, abort?"))
292                                                                   != M_NO)) {
293     mutt_message _("No subject, aborting.");
294
295     return (-1);
296   }
297   str_replace (&en->subject, buf);
298
299   return 0;
300 }
301
302 #ifdef USE_NNTP
303 char *nntp_get_header (const char *s)
304 {
305   SKIPWS (s);
306   return str_dup (s);
307 }
308 #endif
309
310 static void process_user_recips (ENVELOPE * env)
311 {
312   LIST *uh = UserHeader;
313
314   for (; uh; uh = uh->next) {
315     if (ascii_strncasecmp ("to:", uh->data, 3) == 0)
316       env->to = rfc822_parse_adrlist (env->to, uh->data + 3);
317     else if (ascii_strncasecmp ("cc:", uh->data, 3) == 0)
318       env->cc = rfc822_parse_adrlist (env->cc, uh->data + 3);
319     else if (ascii_strncasecmp ("bcc:", uh->data, 4) == 0)
320       env->bcc = rfc822_parse_adrlist (env->bcc, uh->data + 4);
321 #ifdef USE_NNTP
322     else if (ascii_strncasecmp ("newsgroups:", uh->data, 11) == 0)
323       env->newsgroups = nntp_get_header (uh->data + 11);
324     else if (ascii_strncasecmp ("followup-to:", uh->data, 12) == 0)
325       env->followup_to = nntp_get_header (uh->data + 12);
326     else if (ascii_strncasecmp ("x-comment-to:", uh->data, 13) == 0)
327       env->x_comment_to = nntp_get_header (uh->data + 13);
328 #endif
329   }
330 }
331
332 static void process_user_header (ENVELOPE * env)
333 {
334   LIST *uh = UserHeader;
335   LIST *last = env->userhdrs;
336
337   if (last)
338     while (last->next)
339       last = last->next;
340
341   for (; uh; uh = uh->next) {
342     if (ascii_strncasecmp ("from:", uh->data, 5) == 0) {
343       /* User has specified a default From: address.  Remove default address */
344       rfc822_free_address (&env->from);
345       env->from = rfc822_parse_adrlist (env->from, uh->data + 5);
346     }
347     else if (ascii_strncasecmp ("reply-to:", uh->data, 9) == 0) {
348       rfc822_free_address (&env->reply_to);
349       env->reply_to = rfc822_parse_adrlist (env->reply_to, uh->data + 9);
350     }
351     else if (ascii_strncasecmp ("message-id:", uh->data, 11) == 0)
352       str_replace (&env->message_id, uh->data + 11);
353     else if (ascii_strncasecmp ("to:", uh->data, 3) != 0 &&
354              ascii_strncasecmp ("cc:", uh->data, 3) != 0 &&
355              ascii_strncasecmp ("bcc:", uh->data, 4) != 0 &&
356 #ifdef USE_NNTP
357              ascii_strncasecmp ("newsgroups:", uh->data, 11) != 0 &&
358              ascii_strncasecmp ("followup-to:", uh->data, 12) != 0 &&
359              ascii_strncasecmp ("x-comment-to:", uh->data, 13) != 0 &&
360 #endif
361              ascii_strncasecmp ("supersedes:", uh->data, 11) != 0 &&
362              ascii_strncasecmp ("subject:", uh->data, 8) != 0) {
363       if (last) {
364         last->next = mutt_new_list ();
365         last = last->next;
366       }
367       else
368         last = env->userhdrs = mutt_new_list ();
369       last->data = str_dup (uh->data);
370     }
371   }
372 }
373
374 void mutt_forward_intro (FILE * fp, HEADER * cur)
375 {
376   char buffer[STRING];
377
378   fputs ("----- Forwarded message from ", fp);
379   buffer[0] = 0;
380   rfc822_write_address (buffer, sizeof (buffer), cur->env->from, 1);
381   fputs (buffer, fp);
382   fputs (" -----\n\n", fp);
383 }
384
385 void mutt_forward_trailer (FILE * fp)
386 {
387   fputs ("\n----- End forwarded message -----\n", fp);
388 }
389
390
391 static int include_forward (CONTEXT * ctx, HEADER * cur, FILE * out)
392 {
393   int chflags = CH_DECODE, cmflags = 0;
394
395   mutt_parse_mime_message (ctx, cur);
396   mutt_message_hook (ctx, cur, M_MESSAGEHOOK);
397
398   if (WithCrypto && (cur->security & ENCRYPT) && option (OPTFORWDECODE)) {
399     /* make sure we have the user's passphrase before proceeding... */
400     crypt_valid_passphrase (cur->security);
401   }
402
403   mutt_forward_intro (out, cur);
404
405   if (option (OPTFORWDECODE)) {
406     cmflags |= M_CM_DECODE | M_CM_CHARCONV;
407     if (option (OPTWEED)) {
408       chflags |= CH_WEED | CH_REORDER;
409       cmflags |= M_CM_WEED;
410     }
411   }
412   if (option (OPTFORWQUOTE))
413     cmflags |= M_CM_PREFIX;
414
415   mutt_copy_message (out, ctx, cur, cmflags, chflags);
416   mutt_forward_trailer (out);
417   return 0;
418 }
419
420 void mutt_make_attribution (CONTEXT * ctx, HEADER * cur, FILE * out)
421 {
422   char buffer[STRING];
423
424   if (Attribution) {
425     mutt_make_string (buffer, sizeof (buffer), Attribution, ctx, cur);
426     fputs (buffer, out);
427     fputc ('\n', out);
428   }
429 }
430
431 void mutt_make_post_indent (CONTEXT * ctx, HEADER * cur, FILE * out)
432 {
433   char buffer[STRING];
434
435   if (PostIndentString) {
436     mutt_make_string (buffer, sizeof (buffer), PostIndentString, ctx, cur);
437     fputs (buffer, out);
438     fputc ('\n', out);
439   }
440 }
441
442 static int include_reply (CONTEXT * ctx, HEADER * cur, FILE * out)
443 {
444   int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV | M_CM_REPLYING;
445   int chflags = CH_DECODE;
446
447   if (WithCrypto && (cur->security & ENCRYPT)) {
448     /* make sure we have the user's passphrase before proceeding... */
449     crypt_valid_passphrase (cur->security);
450   }
451
452   mutt_parse_mime_message (ctx, cur);
453   mutt_message_hook (ctx, cur, M_MESSAGEHOOK);
454
455   mutt_make_attribution (ctx, cur, out);
456
457   if (!option (OPTHEADER))
458     cmflags |= M_CM_NOHEADER;
459   if (option (OPTWEED)) {
460     chflags |= CH_WEED | CH_REORDER;
461     cmflags |= M_CM_WEED;
462   }
463
464   mutt_copy_message (out, ctx, cur, cmflags, chflags);
465
466   mutt_make_post_indent (ctx, cur, out);
467
468   return 0;
469 }
470
471 static int default_to (ADDRESS ** to, ENVELOPE * env, int flags, int hmfupto)
472 {
473   char prompt[STRING];
474   ADDRESS *tmp;
475
476   if (flags && env->mail_followup_to && hmfupto == M_YES) {
477     rfc822_append (to, env->mail_followup_to);
478     return 0;
479   }
480
481   /* Exit now if we're setting up the default Cc list for list-reply
482    * (only set if Mail-Followup-To is present and honoured).
483    */
484   if (flags & SENDLISTREPLY)
485     return 0;
486
487   /* If this message came from a mailing list, ask the user if he really
488    * intended to reply to the author only.
489    */
490   if (!(flags & SENDGROUPREPLY) && mutt_is_list_cc (0, env->to, env->cc)) {
491     switch (query_quadoption (OPT_LISTREPLY,
492                               _
493                               ("Message came from a mailing list. Reply to author only?")))
494     {
495     case M_NO:
496       tmp = find_mailing_lists (env->to, env->cc);
497       rfc822_append (to, tmp);
498       rfc822_free_address (&tmp);
499       return 0;
500     case -1:
501       return -1;                /* abort */
502     }
503   }
504
505   if (!option (OPTREPLYSELF) && mutt_addr_is_user (env->from)) {
506     /* mail is from the user, assume replying to recipients */
507     rfc822_append (to, env->to);
508   }
509   else if (env->reply_to) {
510     if ((mutt_addrcmp (env->from, env->reply_to) && !env->reply_to->next) ||
511         (option (OPTIGNORELISTREPLYTO) &&
512          mutt_is_mail_list (env->reply_to) &&
513          (mutt_addrsrc (env->reply_to, env->to) ||
514           mutt_addrsrc (env->reply_to, env->cc)))) {
515       /* If the Reply-To: address is a mailing list, assume that it was
516        * put there by the mailing list, and use the From: address
517        * 
518        * We also take the from header if our correspondant has a reply-to
519        * header which is identical to the electronic mail address given
520        * in his From header.
521        * 
522        */
523       rfc822_append (to, env->from);
524     }
525     else if (!(mutt_addrcmp (env->from, env->reply_to) &&
526                !env->reply_to->next) && quadoption (OPT_REPLYTO) != M_YES) {
527       /* There are quite a few mailing lists which set the Reply-To:
528        * header field to the list address, which makes it quite impossible
529        * to send a message to only the sender of the message.  This
530        * provides a way to do that.
531        */
532       snprintf (prompt, sizeof (prompt), _("Reply to %s%s?"),
533                 env->reply_to->mailbox, env->reply_to->next ? ",..." : "");
534       switch (query_quadoption (OPT_REPLYTO, prompt)) {
535       case M_YES:
536         rfc822_append (to, env->reply_to);
537         break;
538
539       case M_NO:
540         rfc822_append (to, env->from);
541         break;
542
543       default:
544         return (-1);            /* abort */
545       }
546     }
547     else
548       rfc822_append (to, env->reply_to);
549   }
550   else
551     rfc822_append (to, env->from);
552
553   return (0);
554 }
555
556 int mutt_fetch_recips (ENVELOPE * out, ENVELOPE * in, int flags)
557 {
558   char prompt[STRING];
559   ADDRESS *tmp;
560   int hmfupto = -1;
561
562   if ((flags & (SENDLISTREPLY | SENDGROUPREPLY)) && in->mail_followup_to) {
563     snprintf (prompt, sizeof (prompt), _("Follow-up to %s%s?"),
564               in->mail_followup_to->mailbox,
565               in->mail_followup_to->next ? ",..." : "");
566
567     if ((hmfupto = query_quadoption (OPT_MFUPTO, prompt)) == -1)
568       return -1;
569   }
570
571   if (flags & SENDLISTREPLY) {
572     tmp = find_mailing_lists (in->to, in->cc);
573     rfc822_append (&out->to, tmp);
574     rfc822_free_address (&tmp);
575
576     if (in->mail_followup_to && hmfupto == M_YES &&
577         default_to (&out->cc, in, flags & SENDLISTREPLY, hmfupto) == -1)
578       return (-1);              /* abort */
579   }
580   else {
581     if (default_to (&out->to, in, flags & SENDGROUPREPLY, hmfupto) == -1)
582       return (-1);              /* abort */
583
584     if ((flags & SENDGROUPREPLY)
585         && (!in->mail_followup_to || hmfupto != M_YES)) {
586       /* if(!mutt_addr_is_user(in->to)) */
587       rfc822_append (&out->cc, in->to);
588       rfc822_append (&out->cc, in->cc);
589     }
590   }
591   return 0;
592 }
593
594 LIST *mutt_make_references (ENVELOPE * e)
595 {
596   LIST *t = NULL, *l = NULL;
597
598   if (e->references)
599     l = mutt_copy_list (e->references);
600   else
601     l = mutt_copy_list (e->in_reply_to);
602
603   if (e->message_id) {
604     t = mutt_new_list ();
605     t->data = str_dup (e->message_id);
606     t->next = l;
607     l = t;
608   }
609
610   return l;
611 }
612
613 void mutt_fix_reply_recipients (ENVELOPE * env)
614 {
615   mutt_expand_aliases_env (env);
616
617   if (!option (OPTMETOO)) {
618     /* the order is important here.  do the CC: first so that if the
619      * the user is the only recipient, it ends up on the TO: field
620      */
621     env->cc = remove_user (env->cc, (env->to == NULL));
622     env->to = remove_user (env->to, (env->cc == NULL));
623   }
624
625   /* the CC field can get cluttered, especially with lists */
626   env->to = mutt_remove_duplicates (env->to);
627   env->cc = mutt_remove_duplicates (env->cc);
628   env->cc = mutt_remove_xrefs (env->to, env->cc);
629 }
630
631 void mutt_make_forward_subject (ENVELOPE * env, CONTEXT * ctx, HEADER * cur)
632 {
633   char buffer[STRING];
634
635   /* set the default subject for the message. */
636   mutt_make_string (buffer, sizeof (buffer), NONULL (ForwFmt), ctx, cur);
637   str_replace (&env->subject, buffer);
638 }
639
640 void mutt_make_misc_reply_headers (ENVELOPE * env, CONTEXT * ctx,
641                                    HEADER * cur, ENVELOPE * curenv)
642 {
643   /* This takes precedence over a subject that might have
644    * been taken from a List-Post header.  Is that correct?
645    */
646   if (curenv->real_subj) {
647     mem_free (&env->subject);
648     env->subject = mem_malloc (str_len (curenv->real_subj) + 5);
649     sprintf (env->subject, "Re: %s", curenv->real_subj);        /* __SPRINTF_CHECKED__ */
650   }
651   else if (!env->subject)
652     env->subject = str_dup ("Re: your mail");
653
654 #ifdef USE_NNTP
655   if (option (OPTNEWSSEND) && option (OPTXCOMMENTTO) && curenv->from)
656     env->x_comment_to = str_dup (mutt_get_name (curenv->from));
657 #endif
658 }
659
660 void mutt_add_to_reference_headers (ENVELOPE * env, ENVELOPE * curenv,
661                                     LIST *** pp, LIST *** qq)
662 {
663   LIST **p = NULL, **q = NULL;
664
665   if (pp)
666     p = *pp;
667   if (qq)
668     q = *qq;
669
670   if (!p)
671     p = &env->references;
672   if (!q)
673     q = &env->in_reply_to;
674
675   while (*p)
676     p = &(*p)->next;
677   while (*q)
678     q = &(*q)->next;
679
680   *p = mutt_make_references (curenv);
681
682   if (curenv->message_id) {
683     *q = mutt_new_list ();
684     (*q)->data = str_dup (curenv->message_id);
685   }
686
687   if (pp)
688     *pp = p;
689   if (qq)
690     *qq = q;
691
692 }
693
694 static void
695 mutt_make_reference_headers (ENVELOPE * curenv, ENVELOPE * env, CONTEXT * ctx)
696 {
697   env->references = NULL;
698   env->in_reply_to = NULL;
699
700   if (!curenv) {
701     HEADER *h;
702     LIST **p = NULL, **q = NULL;
703     int i;
704
705     for (i = 0; i < ctx->vcount; i++) {
706       h = ctx->hdrs[ctx->v2r[i]];
707       if (h->tagged)
708         mutt_add_to_reference_headers (env, h->env, &p, &q);
709     }
710   }
711   else
712     mutt_add_to_reference_headers (env, curenv, NULL, NULL);
713 }
714
715 static int
716 envelope_defaults (ENVELOPE * env, CONTEXT * ctx, HEADER * cur, int flags)
717 {
718   ENVELOPE *curenv = NULL;
719   int i = 0, tag = 0;
720
721   if (!cur) {
722     tag = 1;
723     for (i = 0; i < ctx->vcount; i++)
724       if (ctx->hdrs[ctx->v2r[i]]->tagged) {
725         cur = ctx->hdrs[ctx->v2r[i]];
726         curenv = cur->env;
727         break;
728       }
729
730     if (!cur) {
731       /* This could happen if the user tagged some messages and then did
732        * a limit such that none of the tagged message are visible.
733        */
734       mutt_error _("No tagged messages are visible!");
735
736       return (-1);
737     }
738   }
739   else
740     curenv = cur->env;
741
742   if (flags & SENDREPLY) {
743 #ifdef USE_NNTP
744     if ((flags & SENDNEWS)) {
745       /* in case followup set Newsgroups: with Followup-To: if it present */
746       if (!env->newsgroups && curenv &&
747           str_casecmp (curenv->followup_to, "poster"))
748         env->newsgroups = str_dup (curenv->followup_to);
749     }
750     else
751 #endif
752     if (tag) {
753       HEADER *h;
754
755       for (i = 0; i < ctx->vcount; i++) {
756         h = ctx->hdrs[ctx->v2r[i]];
757         if (h->tagged && mutt_fetch_recips (env, h->env, flags) == -1)
758           return -1;
759       }
760     }
761     else if (mutt_fetch_recips (env, curenv, flags) == -1)
762       return -1;
763
764     if ((flags & SENDLISTREPLY) && !env->to) {
765       mutt_error _("No mailing lists found!");
766
767       return (-1);
768     }
769
770     mutt_make_misc_reply_headers (env, ctx, cur, curenv);
771     mutt_make_reference_headers (tag ? NULL : curenv, env, ctx);
772   }
773   else if (flags & SENDFORWARD)
774     mutt_make_forward_subject (env, ctx, cur);
775
776   return (0);
777 }
778
779 static int generate_body (FILE * tempfp,        /* stream for outgoing message */
780                           HEADER * msg, /* header for outgoing message */
781                           int flags,    /* compose mode */
782                           CONTEXT * ctx,        /* current mailbox */
783                           HEADER * cur)
784 {                               /* current message */
785   int i;
786   HEADER *h;
787   BODY *tmp;
788
789   if (flags & SENDREPLY) {
790     if ((i =
791          query_quadoption (OPT_INCLUDE,
792                            _("Include message in reply?"))) == -1)
793       return (-1);
794
795     if (i == M_YES) {
796       mutt_message _("Including quoted message...");
797
798       if (!cur) {
799         for (i = 0; i < ctx->vcount; i++) {
800           h = ctx->hdrs[ctx->v2r[i]];
801           if (h->tagged) {
802             if (include_reply (ctx, h, tempfp) == -1) {
803               mutt_error _("Could not include all requested messages!");
804
805               return (-1);
806             }
807             fputc ('\n', tempfp);
808           }
809         }
810       }
811       else
812         include_reply (ctx, cur, tempfp);
813
814     }
815   }
816   else if (flags & SENDFORWARD) {
817     if ((i =
818          query_quadoption (OPT_MIMEFWD,
819                            _("Forward as attachment?"))) == M_YES) {
820       BODY *last = msg->content;
821
822       mutt_message _("Preparing forwarded message...");
823
824       while (last && last->next)
825         last = last->next;
826
827       if (cur) {
828         tmp = mutt_make_message_attach (ctx, cur, 0);
829         if (last)
830           last->next = tmp;
831         else
832           msg->content = tmp;
833       }
834       else {
835         for (i = 0; i < ctx->vcount; i++) {
836           if (ctx->hdrs[ctx->v2r[i]]->tagged) {
837             tmp = mutt_make_message_attach (ctx, ctx->hdrs[ctx->v2r[i]], 0);
838             if (last) {
839               last->next = tmp;
840               last = tmp;
841             }
842             else
843               last = msg->content = tmp;
844           }
845         }
846       }
847     }
848     else if (i != -1) {
849       if (cur)
850         include_forward (ctx, cur, tempfp);
851       else
852         for (i = 0; i < ctx->vcount; i++)
853           if (ctx->hdrs[ctx->v2r[i]]->tagged)
854             include_forward (ctx, ctx->hdrs[ctx->v2r[i]], tempfp);
855     }
856     else if (i == -1)
857       return -1;
858   }
859   /* if (WithCrypto && (flags & SENDKEY)) */
860   else if ((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY)) {
861     BODY *tmp;
862
863     if ((WithCrypto & APPLICATION_PGP)
864         && (tmp = crypt_pgp_make_key_attachment (NULL)) == NULL)
865       return -1;
866
867     tmp->next = msg->content;
868     msg->content = tmp;
869   }
870
871   mutt_clear_error ();
872
873   return (0);
874 }
875
876 void mutt_set_followup_to (ENVELOPE * e)
877 {
878   ADDRESS *t = NULL;
879   ADDRESS *from;
880
881   /* 
882    * Only generate the Mail-Followup-To if the user has requested it, and
883    * it hasn't already been set
884    */
885
886   if (!option (OPTFOLLOWUPTO))
887     return;
888 #ifdef USE_NNTP
889   if (option (OPTNEWSSEND)) {
890     if (!e->followup_to && e->newsgroups && (strrchr (e->newsgroups, ',')))
891       e->followup_to = str_dup (e->newsgroups);
892     return;
893   }
894 #endif
895
896   if (!e->mail_followup_to) {
897     if (mutt_is_list_cc (0, e->to, e->cc)) {
898       /* 
899        * this message goes to known mailing lists, so create a proper
900        * mail-followup-to header
901        */
902
903       t = rfc822_append (&e->mail_followup_to, e->to);
904       rfc822_append (&t, e->cc);
905     }
906
907     /* remove ourselves from the mail-followup-to header */
908     e->mail_followup_to = remove_user (e->mail_followup_to, 0);
909
910     /*
911      * If we are not subscribed to any of the lists in question,
912      * re-add ourselves to the mail-followup-to header.  The 
913      * mail-followup-to header generated is a no-op with group-reply,
914      * but makes sure list-reply has the desired effect.
915      */
916
917     if (e->mail_followup_to && !mutt_is_list_recipient (0, e->to, e->cc)) {
918       if (e->reply_to)
919         from = rfc822_cpy_adr (e->reply_to);
920       else if (e->from)
921         from = rfc822_cpy_adr (e->from);
922       else
923         from = mutt_default_from ();
924
925       if (from) {
926         /* Normally, this loop will not even be entered. */
927         for (t = from; t && t->next; t = t->next);
928
929         t->next = e->mail_followup_to;  /* t cannot be NULL at this point. */
930         e->mail_followup_to = from;
931       }
932     }
933
934     e->mail_followup_to = mutt_remove_duplicates (e->mail_followup_to);
935
936   }
937 }
938
939
940 /* look through the recipients of the message we are replying to, and if
941    we find an address that matches $alternates, we use that as the default
942    from field */
943 static ADDRESS *set_reverse_name (ENVELOPE * env)
944 {
945   ADDRESS *tmp;
946
947   for (tmp = env->to; tmp; tmp = tmp->next) {
948     if (mutt_addr_is_user (tmp))
949       break;
950   }
951   if (!tmp) {
952     for (tmp = env->cc; tmp; tmp = tmp->next) {
953       if (mutt_addr_is_user (tmp))
954         break;
955     }
956   }
957   if (!tmp && mutt_addr_is_user (env->from))
958     tmp = env->from;
959   if (tmp) {
960     tmp = rfc822_cpy_adr_real (tmp);
961     if (!option (OPTREVREAL))
962       mem_free (&tmp->personal);
963     if (!tmp->personal)
964       tmp->personal = str_dup (Realname);
965   }
966   return (tmp);
967 }
968
969 ADDRESS *mutt_default_from (void)
970 {
971   ADDRESS *adr;
972   const char *fqdn = mutt_fqdn (1);
973
974   /* 
975    * Note: We let $from override $realname here.  Is this the right
976    * thing to do? 
977    */
978
979   if (From)
980     adr = rfc822_cpy_adr_real (From);
981   else if (option (OPTUSEDOMAIN)) {
982     adr = rfc822_new_address ();
983     adr->mailbox =
984       mem_malloc (str_len (Username) + str_len (fqdn) + 2);
985     sprintf (adr->mailbox, "%s@%s", NONULL (Username), NONULL (fqdn));  /* __SPRINTF_CHECKED__ */
986   }
987   else {
988     adr = rfc822_new_address ();
989     adr->mailbox = str_dup (NONULL (Username));
990   }
991
992   return (adr);
993 }
994
995 static int send_message (HEADER * msg)
996 {
997   char tempfile[_POSIX_PATH_MAX];
998   FILE *tempfp;
999   int i;
1000
1001   /* Write out the message in MIME form. */
1002   mutt_mktemp (tempfile);
1003   if ((tempfp = safe_fopen (tempfile, "w")) == NULL)
1004     return (-1);
1005
1006 #ifdef MIXMASTER
1007   mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0,
1008                             msg->chain ? 1 : 0);
1009 #endif
1010 #ifndef MIXMASTER
1011   mutt_write_rfc822_header (tempfp, msg->env, msg->content, 0, 0);
1012 #endif
1013
1014   fputc ('\n', tempfp);         /* tie off the header. */
1015
1016   if ((mutt_write_mime_body (msg->content, tempfp) == -1)) {
1017     fclose (tempfp);
1018     unlink (tempfile);
1019     return (-1);
1020   }
1021
1022   if (fclose (tempfp) != 0) {
1023     mutt_perror (tempfile);
1024     unlink (tempfile);
1025     return (-1);
1026   }
1027
1028 #ifdef MIXMASTER
1029   if (msg->chain)
1030     return mix_send_message (msg->chain, tempfile);
1031 #endif
1032
1033   i = mutt_invoke_mta (msg->env->from, msg->env->to, msg->env->cc,
1034                        msg->env->bcc, tempfile,
1035                        (msg->content->encoding == ENC8BIT));
1036   return (i);
1037 }
1038
1039 /* rfc2047 encode the content-descriptions */
1040 static void encode_descriptions (BODY * b, short recurse)
1041 {
1042   BODY *t;
1043
1044   for (t = b; t; t = t->next) {
1045     if (t->description) {
1046       rfc2047_encode_string (&t->description);
1047     }
1048     if (recurse && t->parts)
1049       encode_descriptions (t->parts, recurse);
1050   }
1051 }
1052
1053 /* rfc2047 decode them in case of an error */
1054 static void decode_descriptions (BODY * b)
1055 {
1056   BODY *t;
1057
1058   for (t = b; t; t = t->next) {
1059     if (t->description) {
1060       rfc2047_decode (&t->description);
1061     }
1062     if (t->parts)
1063       decode_descriptions (t->parts);
1064   }
1065 }
1066
1067 int mutt_resend_message (FILE * fp, CONTEXT * ctx, HEADER * cur)
1068 {
1069   HEADER *msg = mutt_new_header ();
1070
1071   if (mutt_prepare_template (fp, ctx, msg, cur, 1) < 0)
1072     return -1;
1073
1074   return ci_send_message (SENDRESEND, msg, NULL, ctx, cur);
1075 }
1076
1077 int ci_send_message (int flags, /* send mode */
1078                      HEADER * msg,      /* template to use for new message */
1079                      char *tempfile,    /* file specified by -i or -H */
1080                      CONTEXT * ctx,     /* current mailbox */
1081                      HEADER * cur)
1082 {                               /* current message */
1083   char buffer[LONG_STRING];
1084   char fcc[_POSIX_PATH_MAX] = "";       /* where to copy this message */
1085   FILE *tempfp = NULL;
1086   BODY *pbody;
1087   int i, killfrom = 0;
1088   int fcc_error = 0;
1089   int free_clear_content = 0;
1090
1091   BODY *save_content = NULL;
1092   BODY *clear_content = NULL;
1093   char *pgpkeylist = NULL;
1094
1095   /* save current value of "pgp_sign_as" */
1096   char *signas = NULL;
1097   char *tag = NULL, *err = NULL;
1098   char *ctype;
1099
1100   int rv = -1;
1101
1102 #ifdef USE_NNTP
1103   if (flags & SENDNEWS)
1104     set_option (OPTNEWSSEND);
1105   else
1106     unset_option (OPTNEWSSEND);
1107 #endif
1108
1109   if (!flags && !msg && quadoption (OPT_RECALL) != M_NO &&
1110       mutt_num_postponed (1)) {
1111     /* If the user is composing a new message, check to see if there
1112      * are any postponed messages first.
1113      */
1114     if ((i =
1115          query_quadoption (OPT_RECALL, _("Recall postponed message?"))) == -1)
1116       return rv;
1117
1118     if (i == M_YES)
1119       flags |= SENDPOSTPONED;
1120   }
1121
1122
1123   if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED))
1124     signas = str_dup (PgpSignAs);
1125
1126   /* Delay expansion of aliases until absolutely necessary--shouldn't
1127    * be necessary unless we are prompting the user or about to execute a
1128    * send-hook.
1129    */
1130
1131   if (!msg) {
1132     msg = mutt_new_header ();
1133
1134     if (flags == SENDPOSTPONED) {
1135       if ((flags =
1136            mutt_get_postponed (ctx, msg, &cur, fcc, sizeof (fcc))) < 0)
1137         goto cleanup;
1138 #ifdef USE_NNTP
1139       /*
1140        * If postponed message is a news article, it have
1141        * a "Newsgroups:" header line, then set appropriate flag.
1142        */
1143       if (msg->env->newsgroups) {
1144         flags |= SENDNEWS;
1145         set_option (OPTNEWSSEND);
1146       }
1147       else {
1148         flags &= ~SENDNEWS;
1149         unset_option (OPTNEWSSEND);
1150       }
1151 #endif
1152     }
1153
1154     if (flags & (SENDPOSTPONED | SENDRESEND)) {
1155       if ((tempfp = safe_fopen (msg->content->filename, "a+")) == NULL) {
1156         mutt_perror (msg->content->filename);
1157         goto cleanup;
1158       }
1159     }
1160
1161     if (!msg->env)
1162       msg->env = mutt_new_envelope ();
1163   }
1164
1165   /* Parse and use an eventual list-post header */
1166   if ((flags & SENDLISTREPLY)
1167       && cur && cur->env && cur->env->list_post) {
1168     /* Use any list-post header as a template */
1169     url_parse_mailto (msg->env, NULL, cur->env->list_post);
1170     /* We don't let them set the sender's address. */
1171     rfc822_free_address (&msg->env->from);
1172   }
1173
1174   if (!(flags & (SENDKEY | SENDPOSTPONED | SENDRESEND))) {
1175     pbody = mutt_new_body ();
1176     pbody->next = msg->content; /* don't kill command-line attachments */
1177     msg->content = pbody;
1178
1179     ctype = str_dup (ContentType);
1180     mutt_parse_content_type (ctype, msg->content);
1181     mem_free (&ctype);
1182
1183     msg->content->unlink = 1;
1184     msg->content->use_disp = 0;
1185     msg->content->disposition = DISPINLINE;
1186     if (option (OPTTEXTFLOWED) && msg->content->type == TYPETEXT
1187         && !ascii_strcasecmp (msg->content->subtype, "plain")) {
1188       mutt_set_parameter ("format", "flowed", &msg->content->parameter);
1189       if (option (OPTDELSP))
1190         mutt_set_parameter ("delsp", "yes", &msg->content->parameter);
1191     }
1192
1193     if (!tempfile) {
1194       mutt_mktemp (buffer);
1195       tempfp = safe_fopen (buffer, "w+");
1196       msg->content->filename = str_dup (buffer);
1197     }
1198     else {
1199       tempfp = safe_fopen (tempfile, "a+");
1200       msg->content->filename = str_dup (tempfile);
1201     }
1202
1203     if (!tempfp) {
1204       debug_print (1, ("can't create tempfile %s (errno=%d)\n", 
1205                   msg->content->filename, errno));
1206       mutt_perror (msg->content->filename);
1207       goto cleanup;
1208     }
1209   }
1210
1211   /* this is handled here so that the user can match ~f in send-hook */
1212   if (cur && option (OPTREVNAME) && !(flags & (SENDPOSTPONED | SENDRESEND))) {
1213     /* we shouldn't have to worry about freeing `msg->env->from' before
1214      * setting it here since this code will only execute when doing some
1215      * sort of reply.  the pointer will only be set when using the -H command
1216      * line option.
1217      *
1218      * We shouldn't have to worry about alias expansion here since we are
1219      * either replying to a real or postponed message, therefore no aliases
1220      * should exist since the user has not had the opportunity to add
1221      * addresses to the list.  We just have to ensure the postponed messages
1222      * have their aliases expanded.
1223      */
1224
1225     msg->env->from = set_reverse_name (cur->env);
1226   }
1227
1228   if (!msg->env->from && option (OPTUSEFROM)
1229       && !(flags & (SENDPOSTPONED | SENDRESEND)))
1230     msg->env->from = mutt_default_from ();
1231
1232   if (flags & SENDBATCH) {
1233     mutt_copy_stream (stdin, tempfp);
1234     if (option (OPTHDRS)) {
1235       process_user_recips (msg->env);
1236       process_user_header (msg->env);
1237     }
1238     mutt_expand_aliases_env (msg->env);
1239   }
1240   else if (!(flags & (SENDPOSTPONED | SENDRESEND))) {
1241     if ((flags & (SENDREPLY | SENDFORWARD)) && ctx &&
1242         envelope_defaults (msg->env, ctx, cur, flags) == -1)
1243       goto cleanup;
1244
1245     if (option (OPTHDRS))
1246       process_user_recips (msg->env);
1247
1248     /* Expand aliases and remove duplicates/crossrefs */
1249     mutt_fix_reply_recipients (msg->env);
1250
1251 #ifdef USE_NNTP
1252     if ((flags & SENDNEWS) && ctx && ctx->magic == M_NNTP
1253         && !msg->env->newsgroups)
1254       msg->env->newsgroups = str_dup (((NNTP_DATA *) ctx->data)->group);
1255 #endif
1256
1257     if (!(flags & SENDMAILX) &&
1258         !(option (OPTAUTOEDIT) && option (OPTEDITHDRS)) &&
1259         !((flags & SENDREPLY) && option (OPTFASTREPLY))) {
1260       if (edit_envelope (msg->env, flags) == -1)
1261         goto cleanup;
1262     }
1263
1264     /* the from address must be set here regardless of whether or not
1265      * $use_from is set so that the `~P' (from you) operator in send-hook
1266      * patterns will work.  if $use_from is unset, the from address is killed
1267      * after send-hooks are evaulated */
1268
1269     if (!msg->env->from) {
1270       msg->env->from = mutt_default_from ();
1271       killfrom = 1;
1272     }
1273
1274     if ((flags & SENDREPLY) && cur) {
1275       /* change setting based upon message we are replying to */
1276       mutt_message_hook (ctx, cur, M_REPLYHOOK);
1277
1278       /*
1279        * set the replied flag for the message we are generating so that the
1280        * user can use ~Q in a send-hook to know when reply-hook's are also
1281        * being used.
1282        */
1283       msg->replied = 1;
1284     }
1285
1286     /* change settings based upon recipients */
1287
1288     mutt_message_hook (NULL, msg, M_SENDHOOK);
1289
1290     /*
1291      * Unset the replied flag from the message we are composing since it is
1292      * no longer required.  This is done here because the FCC'd copy of
1293      * this message was erroneously get the 'R'eplied flag when stored in
1294      * a maildir-style mailbox.
1295      */
1296     msg->replied = 0;
1297
1298     if (killfrom) {
1299       rfc822_free_address (&msg->env->from);
1300       killfrom = 0;
1301     }
1302
1303     if (option (OPTHDRS))
1304       process_user_header (msg->env);
1305
1306
1307     if (option (OPTSIGONTOP)
1308         && (!(flags & (SENDMAILX | SENDKEY)) && Editor
1309             && str_cmp (Editor, "builtin") != 0))
1310       append_signature (tempfp);
1311
1312     /* include replies/forwarded messages, unless we are given a template */
1313     if (!tempfile && (ctx || !(flags & (SENDREPLY | SENDFORWARD)))
1314         && generate_body (tempfp, msg, flags, ctx, cur) == -1)
1315       goto cleanup;
1316
1317     if (!option (OPTSIGONTOP)
1318         && (!(flags & (SENDMAILX | SENDKEY)) && Editor
1319             && str_cmp (Editor, "builtin") != 0))
1320       append_signature (tempfp);
1321
1322     /* 
1323      * this wants to be done _after_ generate_body, so message-hooks
1324      * can take effect.
1325      */
1326
1327     if (WithCrypto && !(flags & SENDMAILX)) {
1328       if (option (OPTCRYPTAUTOSIGN))
1329         msg->security |= SIGN;
1330       if (option (OPTCRYPTAUTOENCRYPT))
1331         msg->security |= ENCRYPT;
1332       if (option (OPTCRYPTREPLYENCRYPT) && cur && (cur->security & ENCRYPT))
1333         msg->security |= ENCRYPT;
1334       if (option (OPTCRYPTREPLYSIGN) && cur && (cur->security & SIGN))
1335         msg->security |= SIGN;
1336       if (option (OPTCRYPTREPLYSIGNENCRYPTED) && cur
1337           && (cur->security & ENCRYPT))
1338         msg->security |= SIGN;
1339       if (WithCrypto & APPLICATION_PGP && (msg->security & (ENCRYPT | SIGN))) {
1340         if (option (OPTPGPAUTOINLINE))
1341           msg->security |= INLINE;
1342         if (option (OPTPGPREPLYINLINE) && cur && (cur->security & INLINE))
1343           msg->security |= INLINE;
1344       }
1345     }
1346
1347     if (WithCrypto && msg->security) {
1348       /* 
1349        * When reypling / forwarding, use the original message's
1350        * crypto system.  According to the documentation,
1351        * smime_is_default should be disregarded here.
1352        * 
1353        * Problem: At least with forwarding, this doesn't really
1354        * make much sense. Should we have an option to completely
1355        * disable individual mechanisms at run-time?
1356        */
1357       if (cur) {
1358         if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP)
1359             && (cur->security & APPLICATION_PGP))
1360           msg->security |= APPLICATION_PGP;
1361         else if ((WithCrypto & APPLICATION_SMIME)
1362                  && option (OPTCRYPTAUTOSMIME)
1363                  && (cur->security & APPLICATION_SMIME))
1364           msg->security |= APPLICATION_SMIME;
1365       }
1366
1367       /*
1368        * No crypto mechanism selected? Use availability + smime_is_default
1369        * for the decision. 
1370        */
1371       if (!(msg->security & (APPLICATION_SMIME | APPLICATION_PGP))) {
1372         if ((WithCrypto & APPLICATION_SMIME) && option (OPTCRYPTAUTOSMIME)
1373             && option (OPTSMIMEISDEFAULT))
1374           msg->security |= APPLICATION_SMIME;
1375         else if ((WithCrypto & APPLICATION_PGP) && option (OPTCRYPTAUTOPGP))
1376           msg->security |= APPLICATION_PGP;
1377         else if ((WithCrypto & APPLICATION_SMIME)
1378                  && option (OPTCRYPTAUTOSMIME))
1379           msg->security |= APPLICATION_SMIME;
1380       }
1381     }
1382
1383     /* No permissible mechanisms found.  Don't sign or encrypt. */
1384     if (!(msg->security & (APPLICATION_SMIME | APPLICATION_PGP)))
1385       msg->security = 0;
1386   }
1387
1388   /* 
1389    * This hook is even called for postponed messages, and can, e.g., be
1390    * used for setting the editor, the sendmail path, or the
1391    * envelope sender.
1392    */
1393   mutt_message_hook (NULL, msg, M_SEND2HOOK);
1394
1395   /* wait until now to set the real name portion of our return address so
1396      that $realname can be set in a send-hook */
1397   if (msg->env->from && !msg->env->from->personal
1398       && !(flags & (SENDRESEND | SENDPOSTPONED)))
1399     msg->env->from->personal = str_dup (Realname);
1400
1401   if (!((WithCrypto & APPLICATION_PGP) && (flags & SENDKEY)))
1402     safe_fclose (&tempfp);
1403
1404   if (flags & SENDMAILX) {
1405     if (mutt_builtin_editor (msg->content->filename, msg, cur) == -1)
1406       goto cleanup;
1407   }
1408   else if (!(flags & SENDBATCH)) {
1409     struct stat st;
1410     time_t mtime = mutt_decrease_mtime (msg->content->filename, NULL);
1411
1412     mutt_update_encoding (msg->content);
1413
1414     /*
1415      * Select whether or not the user's editor should be called now.  We
1416      * don't want to do this when:
1417      * 1) we are sending a key/cert
1418      * 2) we are forwarding a message and the user doesn't want to edit it.
1419      *    This is controled by the quadoption $forward_edit.  However, if
1420      *    both $edit_headers and $autoedit are set, we want to ignore the
1421      *    setting of $forward_edit because the user probably needs to add the
1422      *    recipients.
1423      */
1424     if (!(flags & SENDKEY) &&
1425         ((flags & SENDFORWARD) == 0 ||
1426          (option (OPTEDITHDRS) && option (OPTAUTOEDIT)) ||
1427          query_quadoption (OPT_FORWEDIT,
1428                            _("Edit forwarded message?")) == M_YES)) {
1429       /* If the this isn't a text message, look for a mailcap edit command */
1430       if (mutt_needs_mailcap (msg->content))
1431         mutt_edit_attachment (msg->content);
1432       else if (!Editor || str_cmp ("builtin", Editor) == 0)
1433         mutt_builtin_editor (msg->content->filename, msg, cur);
1434       else if (option (OPTEDITHDRS)) {
1435         mutt_env_to_local (msg->env);
1436         mutt_edit_headers (Editor, msg->content->filename, msg, fcc,
1437                            sizeof (fcc));
1438         mutt_env_to_idna (msg->env, NULL, NULL);
1439       }
1440       else
1441         mutt_edit_file (Editor, msg->content->filename);
1442
1443       mutt_message_hook (NULL, msg, M_SEND2HOOK);
1444     }
1445
1446     if (!(flags & (SENDPOSTPONED | SENDFORWARD | SENDKEY | SENDRESEND))) {
1447       if (stat (msg->content->filename, &st) == 0) {
1448         /* if the file was not modified, bail out now */
1449         if (mtime == st.st_mtime && !msg->content->next &&
1450             query_quadoption (OPT_ABORT,
1451                               _("Abort unmodified message?")) == M_YES) {
1452           mutt_message _("Aborted unmodified message.");
1453
1454           goto cleanup;
1455         }
1456       }
1457       else
1458         mutt_perror (msg->content->filename);
1459     }
1460   }
1461
1462   /* specify a default fcc.  if we are in batchmode, only save a copy of
1463    * the message if the value of $copy is yes or ask-yes */
1464
1465   if (!fcc[0] && !(flags & (SENDPOSTPONED))
1466       && (!(flags & SENDBATCH) || (quadoption (OPT_COPY) & 0x1))) {
1467     /* set the default FCC */
1468     if (!msg->env->from) {
1469       msg->env->from = mutt_default_from ();
1470       killfrom = 1;             /* no need to check $use_from because if the user specified
1471                                    a from address it would have already been set by now */
1472     }
1473     mutt_select_fcc (fcc, sizeof (fcc), msg);
1474     if (killfrom) {
1475       rfc822_free_address (&msg->env->from);
1476       killfrom = 0;
1477     }
1478   }
1479
1480
1481   mutt_update_encoding (msg->content);
1482
1483   if (!(flags & (SENDMAILX | SENDBATCH))) {
1484   main_loop:
1485
1486     fcc_error = 0;              /* reset value since we may have failed before */
1487     mutt_pretty_mailbox (fcc);
1488     i = mutt_compose_menu (msg, fcc, sizeof (fcc), cur);
1489     if (i == -1) {
1490       /* abort */
1491 #ifdef USE_NNTP
1492       if (flags & SENDNEWS)
1493         mutt_message (_("Article not posted."));
1494
1495       else
1496 #endif
1497         mutt_message _("Mail not sent.");
1498       goto cleanup;
1499     }
1500     else if (i == 1) {
1501       /* postpone the message until later. */
1502       if (msg->content->next)
1503         msg->content = mutt_make_multipart (msg->content);
1504
1505       /*
1506        * make sure the message is written to the right part of a maildir 
1507        * postponed folder.
1508        */
1509       msg->read = 0;
1510       msg->old = 0;
1511
1512       encode_descriptions (msg->content, 1);
1513       mutt_prepare_envelope (msg->env, 0);
1514       mutt_env_to_idna (msg->env, NULL, NULL);  /* Handle bad IDNAs the next time. */
1515
1516       if (!Postponed
1517           || mutt_write_fcc (NONULL (Postponed), msg,
1518                              (cur
1519                               && (flags & SENDREPLY)) ? cur->env->
1520                              message_id : NULL, 1, fcc) < 0) {
1521         msg->content = mutt_remove_multipart (msg->content);
1522         decode_descriptions (msg->content);
1523         mutt_unprepare_envelope (msg->env);
1524         goto main_loop;
1525       }
1526       mutt_update_num_postponed ();
1527       mutt_message _("Message postponed.");
1528
1529       goto cleanup;
1530     }
1531   }
1532
1533 #ifdef USE_NNTP
1534   if (!(flags & SENDNEWS))
1535 #endif
1536     if (!msg->env->to && !msg->env->cc && !msg->env->bcc) {
1537       if (!(flags & SENDBATCH)) {
1538         mutt_error _("No recipients are specified!");
1539
1540         goto main_loop;
1541       }
1542       else {
1543         puts _("No recipients were specified.");
1544
1545         goto cleanup;
1546       }
1547     }
1548
1549   if (mutt_env_to_idna (msg->env, &tag, &err)) {
1550     mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err);
1551     mem_free (&err);
1552     if (!(flags & SENDBATCH))
1553       goto main_loop;
1554     else
1555       goto cleanup;
1556   }
1557
1558   if (!msg->env->subject && !(flags & SENDBATCH) &&
1559       (i =
1560        query_quadoption (OPT_SUBJECT,
1561                          _("No subject, abort sending?"))) != M_NO) {
1562     /* if the abort is automatic, print an error message */
1563     if (quadoption (OPT_SUBJECT) == M_YES)
1564       mutt_error _("No subject specified.");
1565
1566     goto main_loop;
1567   }
1568 #ifdef USE_NNTP
1569   if ((flags & SENDNEWS) && !msg->env->subject) {
1570     mutt_error _("No subject specified.");
1571
1572     goto main_loop;
1573   }
1574
1575   if ((flags & SENDNEWS) && !msg->env->newsgroups) {
1576     mutt_error _("No newsgroup specified.");
1577
1578     goto main_loop;
1579   }
1580 #endif
1581
1582   if (msg->content->next)
1583     msg->content = mutt_make_multipart (msg->content);
1584
1585   if (mutt_attach_check (msg) &&
1586       !msg->content->next &&
1587       query_quadoption (OPT_ATTACH,
1588                         _("No attachments made but indicator found in text. "
1589                           "Cancel sending?")) == M_YES) {
1590     if (quadoption (OPT_ATTACH) == M_YES) {
1591       mutt_message _("No attachments made but indicator found in text. "
1592                      "Abort sending.");
1593       sleep (2);
1594     }
1595     mutt_message (_("Mail not sent."));
1596     goto main_loop;
1597   }
1598
1599   /* 
1600    * Ok, we need to do it this way instead of handling all fcc stuff in
1601    * one place in order to avoid going to main_loop with encoded "env"
1602    * in case of error.  Ugh.
1603    */
1604
1605   encode_descriptions (msg->content, 1);
1606
1607   /*
1608    * Make sure that clear_content and free_clear_content are
1609    * properly initialized -- we may visit this particular place in
1610    * the code multiple times, including after a failed call to
1611    * mutt_protect().
1612    */
1613
1614   clear_content = NULL;
1615   free_clear_content = 0;
1616
1617   if (WithCrypto) {
1618     if (msg->security) {
1619       /* save the decrypted attachments */
1620       clear_content = msg->content;
1621
1622       if ((crypt_get_keys (msg, &pgpkeylist) == -1) ||
1623           mutt_protect (msg, pgpkeylist) == -1) {
1624         msg->content = mutt_remove_multipart (msg->content);
1625
1626         mem_free (&pgpkeylist);
1627
1628         decode_descriptions (msg->content);
1629         goto main_loop;
1630       }
1631       encode_descriptions (msg->content, 0);
1632     }
1633
1634     /* 
1635      * at this point, msg->content is one of the following three things:
1636      * - multipart/signed.  In this case, clear_content is a child.
1637      * - multipart/encrypted.  In this case, clear_content exists
1638      *   independently
1639      * - application/pgp.  In this case, clear_content exists independently.
1640      * - something else.  In this case, it's the same as clear_content.
1641      */
1642
1643     /* This is ugly -- lack of "reporting back" from mutt_protect(). */
1644
1645     if (clear_content && (msg->content != clear_content)
1646         && (msg->content->parts != clear_content))
1647       free_clear_content = 1;
1648   }
1649
1650   if (!option (OPTNOCURSES) && !(flags & SENDMAILX))
1651     mutt_message _("Sending message...");
1652
1653   mutt_prepare_envelope (msg->env, 1);
1654
1655   /* save a copy of the message, if necessary. */
1656
1657   mutt_expand_path (fcc, sizeof (fcc));
1658
1659
1660   /* Don't save a copy when we are in batch-mode, and the FCC
1661    * folder is on an IMAP server: This would involve possibly lots
1662    * of user interaction, which is not available in batch mode. 
1663    * 
1664    * Note: A patch to fix the problems with the use of IMAP servers
1665    * from non-curses mode is available from Brendan Cully.  However, 
1666    * I'd like to think a bit more about this before including it.
1667    */
1668
1669 #ifdef USE_IMAP
1670   if ((flags & SENDBATCH) && fcc[0] && mx_get_magic (fcc) == M_IMAP)
1671     fcc[0] = '\0';
1672 #endif
1673
1674   if (*fcc && str_cmp ("/dev/null", fcc) != 0) {
1675     BODY *tmpbody = msg->content;
1676     BODY *save_sig = NULL;
1677     BODY *save_parts = NULL;
1678
1679     if (WithCrypto && msg->security && option (OPTFCCCLEAR))
1680       msg->content = clear_content;
1681
1682     /* check to see if the user wants copies of all attachments */
1683     if (!option (OPTFCCATTACH) && msg->content->type == TYPEMULTIPART) {
1684       if (WithCrypto
1685           && (str_cmp (msg->content->subtype, "encrypted") == 0 ||
1686               str_cmp (msg->content->subtype, "signed") == 0)) {
1687         if (clear_content->type == TYPEMULTIPART) {
1688           if (!(msg->security & ENCRYPT) && (msg->security & SIGN)) {
1689             /* save initial signature and attachments */
1690             save_sig = msg->content->parts->next;
1691             save_parts = clear_content->parts->next;
1692           }
1693
1694           /* this means writing only the main part */
1695           msg->content = clear_content->parts;
1696
1697           if (mutt_protect (msg, pgpkeylist) == -1) {
1698             /* we can't do much about it at this point, so
1699              * fallback to saving the whole thing to fcc
1700              */
1701             msg->content = tmpbody;
1702             save_sig = NULL;
1703             goto full_fcc;
1704           }
1705
1706           save_content = msg->content;
1707         }
1708       }
1709       else
1710         msg->content = msg->content->parts;
1711     }
1712
1713   full_fcc:
1714     if (msg->content) {
1715       /* update received time so that when storing to a mbox-style folder
1716        * the From_ line contains the current time instead of when the
1717        * message was first postponed.
1718        */
1719       msg->received = time (NULL);
1720       if (mutt_write_fcc (fcc, msg, NULL, 0, NULL) == -1) {
1721         /*
1722          * Error writing FCC, we should abort sending.
1723          */
1724         fcc_error = 1;
1725       }
1726     }
1727
1728     msg->content = tmpbody;
1729
1730     if (WithCrypto && save_sig) {
1731       /* cleanup the second signature structures */
1732       if (save_content->parts) {
1733         mutt_free_body (&save_content->parts->next);
1734         save_content->parts = NULL;
1735       }
1736       mutt_free_body (&save_content);
1737
1738       /* restore old signature and attachments */
1739       msg->content->parts->next = save_sig;
1740       msg->content->parts->parts->next = save_parts;
1741     }
1742     else if (WithCrypto && save_content) {
1743       /* destroy the new encrypted body. */
1744       mutt_free_body (&save_content);
1745     }
1746
1747   }
1748
1749
1750   /*
1751    * Don't attempt to send the message if the FCC failed.  Just pretend
1752    * the send failed as well so we give the user a chance to fix the
1753    * error.
1754    */
1755   if (fcc_error || (i = send_message (msg)) == -1) {
1756     if (!(flags & SENDBATCH)) {
1757       if (!WithCrypto);
1758       else if ((msg->security & ENCRYPT) || ((msg->security & SIGN)
1759                                              && msg->content->type ==
1760                                              TYPEAPPLICATION)) {
1761         mutt_free_body (&msg->content); /* destroy PGP data */
1762         msg->content = clear_content;   /* restore clear text. */
1763       }
1764       else if ((msg->security & SIGN) && msg->content->type == TYPEMULTIPART) {
1765         mutt_free_body (&msg->content->parts->next);    /* destroy sig */
1766         msg->content = mutt_remove_multipart (msg->content);
1767       }
1768
1769       msg->content = mutt_remove_multipart (msg->content);
1770       decode_descriptions (msg->content);
1771       mutt_unprepare_envelope (msg->env);
1772       goto main_loop;
1773     }
1774     else {
1775       puts _("Could not send the message.");
1776
1777       goto cleanup;
1778     }
1779   }
1780   else if (!option (OPTNOCURSES) && !(flags & SENDMAILX))
1781     mutt_message (i != 0 ? _("Sending in background.") :
1782 #ifdef USE_NNTP
1783                   (flags & SENDNEWS) ? _("Article posted.") :
1784                   _("Mail sent."));
1785 #else
1786                   _("Mail sent."));
1787 #endif
1788
1789   if (WithCrypto && (msg->security & ENCRYPT))
1790     mem_free (&pgpkeylist);
1791
1792   if (WithCrypto && free_clear_content)
1793     mutt_free_body (&clear_content);
1794
1795   if (flags & SENDREPLY) {
1796     if (cur && ctx)
1797       mutt_set_flag (ctx, cur, M_REPLIED, 1);
1798     else if (!(flags & SENDPOSTPONED) && ctx && ctx->tagged) {
1799       for (i = 0; i < ctx->vcount; i++)
1800         if (ctx->hdrs[ctx->v2r[i]]->tagged)
1801           mutt_set_flag (ctx, ctx->hdrs[ctx->v2r[i]], M_REPLIED, 1);
1802     }
1803   }
1804
1805
1806   rv = 0;
1807
1808 cleanup:
1809
1810   if ((WithCrypto & APPLICATION_PGP) && (flags & SENDPOSTPONED)) {
1811     if (signas) {
1812       mem_free (&PgpSignAs);
1813       PgpSignAs = signas;
1814     }
1815   }
1816
1817   safe_fclose (&tempfp);
1818   mutt_free_header (&msg);
1819
1820   return rv;
1821 }
1822
1823 /* vim: set sw=2: */