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