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