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