2 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
4 * This program is free software; you can redistribute it
5 * and/or modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later
10 * This program is distributed in the hope that it will be
11 * useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13 * PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
27 #include "mutt_curses.h"
28 #include "mutt_menu.h"
36 #include "mutt_idna.h"
38 /* some helper functions to verify that we are exclusively operating
39 * on message/rfc822 attachments
42 static short check_msg (BODY * b, short err)
44 if (!mutt_is_message_type (b->type, b->subtype))
47 mutt_error _("You may only bounce message/rfc822 parts.");
53 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
54 BODY * cur, short err)
58 if (cur && check_msg (cur, err) == -1)
62 for (i = 0; i < idxlen; i++)
64 if (idx[i]->content->tagged)
66 if (check_msg (idx[i]->content, err) == -1)
75 /* can we decode all tagged attachments? */
77 static short check_can_decode (ATTACHPTR ** idx, short idxlen,
83 return mutt_can_decode (cur);
85 for (i = 0; i < idxlen; i++)
86 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
92 static short count_tagged (ATTACHPTR **idx, short idxlen)
97 for (i = 0; i < idxlen; i++)
98 if (idx[i]->content->tagged)
104 /* count the number of tagged children below a multipart or message
108 static short count_tagged_children (ATTACHPTR ** idx,
109 short idxlen, short i)
111 short level = idx[i]->level;
114 while ((++i < idxlen) && (level < idx[i]->level))
115 if (idx[i]->content->tagged)
125 ** The bounce function, from the attachment menu
129 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
130 ATTACHPTR ** idx, short idxlen, BODY * cur)
134 char buf[HUGE_STRING];
140 if (check_all_msg (idx, idxlen, cur, 1) == -1)
143 /* one or more messages? */
144 p = (cur || count_tagged (idx, idxlen) == 1);
147 strfcpy (prompt, _("Bounce message to: "), sizeof (prompt));
149 strfcpy (prompt, _("Bounce tagged messages to: "), sizeof (prompt));
152 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
156 if (!(adr = rfc822_parse_adrlist (adr, buf)))
158 mutt_error _("Error parsing address!");
162 adr = mutt_expand_aliases (adr);
164 if (mutt_addrlist_to_idna (adr, &err) < 0)
166 mutt_error (_("Bad IDN: '%s'"), err);
168 rfc822_free_address (&adr);
173 rfc822_write_address (buf, sizeof (buf), adr, 1);
175 #define extra_space (15+7+2)
179 snprintf (prompt, sizeof (prompt) - 4,
180 (p ? _("Bounce message to %s") : _("Bounce messages to %s")), buf);
182 if (mutt_strwidth (prompt) > COLS - extra_space)
184 mutt_format_string (prompt, sizeof (prompt) - 4,
185 0, COLS-extra_space, FMT_LEFT, 0,
186 prompt, sizeof (prompt), 0);
187 safe_strcat (prompt, sizeof (prompt), "...?");
190 safe_strcat (prompt, sizeof (prompt), "?");
192 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES)
194 rfc822_free_address (&adr);
195 CLEARLINE (LINES - 1);
196 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
200 CLEARLINE (LINES - 1);
203 ret = mutt_bounce_message (fp, cur->hdr, adr);
206 for (i = 0; i < idxlen; i++)
208 if (idx[i]->content->tagged)
209 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
215 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
217 mutt_error (p ? _("Error bouncing message!") : _("Error bouncing messages!"));
224 ** resend-message, from the attachment menu
229 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
230 short idxlen, BODY * cur)
234 if (check_all_msg (idx, idxlen, cur, 1) == -1)
238 mutt_resend_message (fp, Context, cur->hdr);
241 for (i = 0; i < idxlen; i++)
242 if (idx[i]->content->tagged)
243 mutt_resend_message (fp, Context, idx[i]->content->hdr);
250 ** forward-message, from the attachment menu
254 /* try to find a common parent message for the tagged attachments. */
256 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
262 for (i = 0; i < idxlen; i++)
263 if (idx[i]->content->tagged)
268 if (mutt_is_message_type (idx[i]->content->type, idx[i]->content->subtype))
270 nchildren = count_tagged_children (idx, idxlen, i);
271 if (nchildren == nattach)
272 return idx[i]->content->hdr;
280 * check whether attachment #i is a parent of the attachment
283 * Note: This and the calling procedure could be optimized quite a
284 * bit. For now, it's not worth the effort.
287 static int is_parent (short i, ATTACHPTR **idx, short idxlen, BODY *cur)
289 short level = idx[i]->level;
291 while ((++i < idxlen) && idx[i]->level > level)
293 if (idx[i]->content == cur)
300 static HEADER *find_parent (ATTACHPTR **idx, short idxlen, BODY *cur, short nattach)
303 HEADER *parent = NULL;
307 for (i = 0; i < idxlen; i++)
309 if (mutt_is_message_type (idx[i]->content->type, idx[i]->content->subtype)
310 && is_parent (i, idx, idxlen, cur))
311 parent = idx[i]->content->hdr;
312 if (idx[i]->content == cur)
317 parent = find_common_parent (idx, idxlen, nattach);
322 static void include_header (int quote, FILE * ifp,
323 HEADER * hdr, FILE * ofp,
326 int chflags = CH_DECODE;
327 char prefix[SHORT_STRING];
329 if (option (OPTWEED))
330 chflags |= CH_WEED | CH_REORDER;
335 strfcpy (prefix, _prefix, sizeof (prefix));
336 else if (!option (OPTTEXTFLOWED))
337 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
340 strfcpy (prefix, ">", sizeof (prefix));
342 chflags |= CH_PREFIX;
345 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
348 /* Attach all the body parts which can't be decoded.
349 * This code is shared by forwarding and replying. */
351 static BODY ** copy_problematic_attachments (FILE *fp,
359 for (i = 0; i < idxlen; i++)
361 if (idx[i]->content->tagged &&
362 (force || !mutt_can_decode (idx[i]->content)))
364 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
365 return NULL; /* XXXXX - may lead to crashes */
366 last = &((*last)->next);
373 * forward one or several MIME bodies
374 * (non-message types)
377 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
378 ATTACHPTR ** idx, short idxlen,
383 short mime_fwd_all = 0;
384 short mime_fwd_any = 1;
385 HEADER *parent = NULL;
386 HEADER *tmphdr = NULL;
388 char tmpbody[_POSIX_PATH_MAX];
398 * First, find the parent message.
399 * Note: This could be made an option by just
400 * putting the following lines into an if block.
404 parent = find_parent (idx, idxlen, cur, nattach);
410 tmphdr = mutt_new_header ();
411 tmphdr->env = mutt_new_envelope ();
412 mutt_make_forward_subject (tmphdr->env, Context, parent);
414 mutt_mktemp (tmpbody);
415 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL)
417 mutt_error (_("Can't open temporary file %s."), tmpbody);
421 mutt_forward_intro (tmpfp, parent);
423 /* prepare the prefix here since we'll need it later. */
425 if (option (OPTFORWQUOTE))
427 if (!option (OPTTEXTFLOWED))
428 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
431 strfcpy (prefix, ">", sizeof (prefix));
434 include_header (option (OPTFORWQUOTE), fp, parent,
439 * Now, we have prepared the first part of the message body: The
440 * original message's header.
442 * The next part is more interesting: either include the message bodies,
446 if ((!cur || mutt_can_decode (cur)) &&
447 (rc = query_quadoption (OPT_MIMEFWD,
448 _("Forward as attachments?"))) == M_YES)
454 * shortcut MIMEFWDREST when there is only one attachment. Is
458 if (!mime_fwd_all && !cur && (nattach > 1)
459 && !check_can_decode (idx, idxlen, cur))
461 if ((rc = query_quadoption (OPT_MIMEFWDREST,
462 _("Can't decode all tagged attachments. MIME-forward the others?"))) == -1)
468 /* initialize a state structure */
470 memset (&st, 0, sizeof (st));
472 if (option (OPTFORWQUOTE))
474 st.flags = M_CHARCONV;
475 if (option (OPTWEED))
480 /* where do we append new MIME parts? */
481 last = &tmphdr->content;
485 /* single body case */
487 if (!mime_fwd_all && mutt_can_decode (cur))
489 mutt_body_handler (cur, &st);
490 state_putc ('\n', &st);
494 if (mutt_copy_body (fp, last, cur) == -1)
496 last = &((*last)->next);
501 /* multiple body case */
505 for (i = 0; i < idxlen; i++)
507 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content))
509 mutt_body_handler (idx[i]->content, &st);
510 state_putc ('\n', &st);
516 (last = copy_problematic_attachments (fp, last, idx, idxlen, mime_fwd_all)) == NULL)
520 mutt_forward_trailer (tmpfp);
525 /* now that we have the template, send it. */
526 ci_send_message (0, tmphdr, tmpbody, NULL, parent);
534 mutt_unlink (tmpbody);
537 mutt_free_header (&tmphdr);
542 * Forward one or several message-type attachments. This
543 * is different from the previous function
544 * since we want to mimic the index menu's behaviour.
546 * Code reuse from ci_send_message is not possible here -
547 * ci_send_message relies on a context structure to find messages,
548 * while, on the attachment menu, messages are referenced through
549 * the attachment index.
552 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
553 ATTACHPTR ** idx, short idxlen, BODY * cur)
555 HEADER *curhdr = NULL;
561 char tmpbody[_POSIX_PATH_MAX];
565 int chflags = CH_XMIT;
571 for (i = 0; i < idxlen; i++)
572 if (idx[i]->content->tagged)
574 curhdr = idx[i]->content->hdr;
579 tmphdr = mutt_new_header ();
580 tmphdr->env = mutt_new_envelope ();
581 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
586 if ((rc = query_quadoption (OPT_MIMEFWD,
587 _("Forward MIME encapsulated?"))) == M_NO)
590 /* no MIME encapsulation */
592 mutt_mktemp (tmpbody);
593 if (!(tmpfp = safe_fopen (tmpbody, "w")))
595 mutt_error (_("Can't create %s."), tmpbody);
596 mutt_free_header (&tmphdr);
600 if (option (OPTFORWQUOTE))
602 chflags |= CH_PREFIX;
603 cmflags |= M_CM_PREFIX;
606 if (option (OPTFORWDECODE))
608 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
609 if (option (OPTWEED))
611 chflags |= CH_WEED | CH_REORDER;
612 cmflags |= M_CM_WEED;
619 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
620 mutt_forward_intro (tmpfp, cur->hdr);
621 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags, chflags);
622 mutt_forward_trailer (tmpfp);
626 for (i = 0; i < idxlen; i++)
628 if (idx[i]->content->tagged)
630 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
631 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
632 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
633 idx[i]->content->hdr->content, cmflags, chflags);
634 mutt_forward_trailer (tmpfp);
640 else if (rc == M_YES) /* do MIME encapsulation - we don't need to do much here */
642 last = &tmphdr->content;
644 mutt_copy_body (fp, last, cur);
647 for (i = 0; i < idxlen; i++)
648 if (idx[i]->content->tagged)
650 mutt_copy_body (fp, last, idx[i]->content);
651 last = &((*last)->next);
656 mutt_free_header (&tmphdr);
658 ci_send_message (0, tmphdr, *tmpbody ? tmpbody : NULL,
663 void mutt_attach_forward (FILE * fp, HEADER * hdr,
664 ATTACHPTR ** idx, short idxlen, BODY * cur)
669 if (check_all_msg (idx, idxlen, cur, 0) == 0)
670 attach_forward_msgs (fp, hdr, idx, idxlen, cur);
673 nattach = count_tagged (idx, idxlen);
674 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach);
682 ** the various reply functions, from the attachment menu
687 /* Create the envelope defaults for a reply.
689 * This function can be invoked in two ways.
691 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
692 * and the header information is fetched from them.
694 * Or, parent is non-NULL. In this case, cur is the common parent of all the
695 * tagged attachments.
697 * Note that this code is horribly similar to envelope_defaults () from send.c.
701 attach_reply_envelope_defaults (ENVELOPE *env, ATTACHPTR **idx, short idxlen,
702 HEADER *parent, int flags)
704 ENVELOPE *curenv = NULL;
705 HEADER *curhdr = NULL;
710 for (i = 0; i < idxlen; i++)
712 if (idx[i]->content->tagged)
714 curhdr = idx[i]->content->hdr;
715 curenv = curhdr->env;
722 curenv = parent->env;
726 if (curenv == NULL || curhdr == NULL)
728 mutt_error _("Can't find any tagged messages.");
734 if (mutt_fetch_recips (env, curenv, flags) == -1)
739 for (i = 0; i < idxlen; i++)
741 if (idx[i]->content->tagged
742 && mutt_fetch_recips (env, idx[i]->content->hdr->env, flags) == -1)
747 if ((flags & SENDLISTREPLY) && !env->to)
749 mutt_error _("No mailing lists found!");
753 mutt_fix_reply_recipients (env);
754 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
757 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
760 LIST **p = NULL, **q = NULL;
762 for (i = 0; i < idxlen; i++)
764 if (idx[i]->content->tagged)
765 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p, &q);
773 /* This is _very_ similar to send.c's include_reply(). */
775 static void attach_include_reply (FILE *fp, FILE *tmpfp, HEADER *cur, int flags)
777 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
778 int chflags = CH_DECODE;
780 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
782 mutt_make_attribution (Context, cur, tmpfp);
784 if (!option (OPTHEADER))
785 cmflags |= M_CM_NOHEADER;
786 if (option (OPTWEED))
789 cmflags |= M_CM_WEED;
792 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
793 mutt_make_post_indent (Context, cur, tmpfp);
796 void mutt_attach_reply (FILE * fp, HEADER * hdr,
797 ATTACHPTR ** idx, short idxlen, BODY * cur,
800 short mime_reply_any = 0;
803 HEADER *parent = NULL;
804 HEADER *tmphdr = NULL;
808 char tmpbody[_POSIX_PATH_MAX];
811 char prefix[SHORT_STRING];
814 if (check_all_msg (idx, idxlen, cur, 0) == -1)
816 nattach = count_tagged (idx, idxlen);
817 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
821 if (nattach > 1 && !check_can_decode (idx, idxlen, cur))
823 if ((rc = query_quadoption (OPT_MIMEFWDREST,
824 _("Can't decode all tagged attachments. MIME-encapsulate the others?"))) == -1)
826 else if (rc == M_YES)
829 else if (nattach == 1)
832 tmphdr = mutt_new_header ();
833 tmphdr->env = mutt_new_envelope ();
835 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
836 parent ? parent : (cur ? cur->hdr : NULL), flags) == -1)
838 mutt_free_header (&tmphdr);
842 mutt_mktemp (tmpbody);
843 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL)
845 mutt_error (_("Can't create %s."), tmpbody);
846 mutt_free_header (&tmphdr);
853 attach_include_reply (fp, tmpfp, cur->hdr, flags);
856 for (i = 0; i < idxlen; i++)
858 if (idx[i]->content->tagged)
859 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
865 mutt_make_attribution (Context, parent, tmpfp);
867 memset (&st, 0, sizeof (STATE));
871 if (!option (OPTTEXTFLOWED))
872 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
875 strfcpy (prefix, ">", sizeof (prefix));
878 st.flags = M_CHARCONV;
880 if (option (OPTWEED))
883 if (option (OPTHEADER))
884 include_header (1, fp, parent, tmpfp, prefix);
888 if (mutt_can_decode (cur))
890 mutt_body_handler (cur, &st);
891 state_putc ('\n', &st);
894 mutt_copy_body (fp, &tmphdr->content, cur);
898 for (i = 0; i < idxlen; i++)
900 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content))
902 mutt_body_handler (idx[i]->content, &st);
903 state_putc ('\n', &st);
908 mutt_make_post_indent (Context, parent, tmpfp);
910 if (mime_reply_any && !cur &&
911 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen, 0) == NULL)
913 mutt_free_header (&tmphdr);
921 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
922 mutt_set_flag (Context, hdr, M_REPLIED, 1);