2 * Copyright notice from original mutt:
3 * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
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.
14 #include <lib-lib/mem.h>
15 #include <lib-lib/str.h>
16 #include <lib-lib/macros.h>
22 #include "recvattach.h"
23 #include "mutt_curses.h"
24 #include "mutt_menu.h"
31 #include "mutt_idna.h"
34 /* some helper functions to verify that we are exclusively operating
35 * on message/rfc822 attachments
38 static short check_msg (BODY * b, short err)
40 if (!mutt_is_message_type (b->type, b->subtype)) {
42 mutt_error _("You may only bounce message/rfc822 parts.");
49 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
50 BODY * cur, short err)
54 if (cur && check_msg (cur, err) == -1)
57 for (i = 0; i < idxlen; i++) {
58 if (idx[i]->content->tagged) {
59 if (check_msg (idx[i]->content, err) == -1)
68 /* can we decode all tagged attachments? */
70 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
75 return mutt_can_decode (cur);
77 for (i = 0; i < idxlen; i++)
78 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
84 static short count_tagged (ATTACHPTR ** idx, short idxlen)
89 for (i = 0; i < idxlen; i++)
90 if (idx[i]->content->tagged)
96 /* count the number of tagged children below a multipart or message
100 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
102 short level = idx[i]->level;
105 while ((++i < idxlen) && (level < idx[i]->level))
106 if (idx[i]->content->tagged)
116 ** The bounce function, from the attachment menu
120 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
121 ATTACHPTR ** idx, short idxlen, BODY * cur)
125 char buf[HUGE_STRING];
131 if (check_all_msg (idx, idxlen, cur, 1) == -1)
134 /* one or more messages? */
135 p = (cur || count_tagged (idx, idxlen) == 1);
138 strfcpy (prompt, _("Bounce message to: "), sizeof (prompt));
140 strfcpy (prompt, _("Bounce tagged messages to: "), sizeof (prompt));
143 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
147 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
148 mutt_error _("Error parsing address!");
153 adr = mutt_expand_aliases (adr);
155 if (mutt_addrlist_to_idna (adr, &err) < 0) {
156 mutt_error (_("Bad IDN: '%s'"), err);
158 rfc822_free_address (&adr);
163 rfc822_write_address (buf, sizeof (buf), adr, 1);
165 #define extra_space (15+7+2)
169 snprintf (prompt, sizeof (prompt) - 4,
170 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
173 if (mutt_strwidth (prompt) > COLS - extra_space) {
174 mutt_format_string (prompt, sizeof (prompt) - 4,
175 0, COLS - extra_space, 0, 0,
176 prompt, sizeof (prompt), 0);
177 str_cat (prompt, sizeof (prompt), "...?");
180 str_cat (prompt, sizeof (prompt), "?");
182 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
183 rfc822_free_address (&adr);
184 CLEARLINE (LINES - 1);
185 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
189 CLEARLINE (LINES - 1);
192 ret = mutt_bounce_message (fp, cur->hdr, adr);
194 for (i = 0; i < idxlen; i++) {
195 if (idx[i]->content->tagged)
196 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
202 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
204 mutt_error (p ? _("Error bouncing message!") :
205 _("Error bouncing messages!"));
212 ** resend-message, from the attachment menu
217 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
218 short idxlen, BODY * cur)
222 if (check_all_msg (idx, idxlen, cur, 1) == -1)
226 mutt_resend_message (fp, Context, cur->hdr);
228 for (i = 0; i < idxlen; i++)
229 if (idx[i]->content->tagged)
230 mutt_resend_message (fp, Context, idx[i]->content->hdr);
237 ** forward-message, from the attachment menu
241 /* try to find a common parent message for the tagged attachments. */
243 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
249 for (i = 0; i < idxlen; i++)
250 if (idx[i]->content->tagged)
254 if (mutt_is_message_type
255 (idx[i]->content->type, idx[i]->content->subtype)) {
256 nchildren = count_tagged_children (idx, idxlen, i);
257 if (nchildren == nattach)
258 return idx[i]->content->hdr;
266 * check whether attachment #i is a parent of the attachment
269 * Note: This and the calling procedure could be optimized quite a
270 * bit. For now, it's not worth the effort.
273 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
275 short level = idx[i]->level;
277 while ((++i < idxlen) && idx[i]->level > level) {
278 if (idx[i]->content == cur)
285 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
289 HEADER *parent = NULL;
292 for (i = 0; i < idxlen; i++) {
293 if (mutt_is_message_type
294 (idx[i]->content->type, idx[i]->content->subtype)
295 && is_parent (i, idx, idxlen, cur))
296 parent = idx[i]->content->hdr;
297 if (idx[i]->content == cur)
302 parent = find_common_parent (idx, idxlen, nattach);
307 static void include_header (int quote, FILE * ifp,
308 HEADER * hdr, FILE * ofp, char *_prefix)
310 int chflags = CH_DECODE;
311 char prefix[SHORT_STRING];
313 if (option (OPTWEED))
314 chflags |= CH_WEED | CH_REORDER;
318 strfcpy (prefix, _prefix, sizeof (prefix));
319 else if (!option (OPTTEXTFLOWED))
320 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
323 strfcpy (prefix, ">", sizeof (prefix));
325 chflags |= CH_PREFIX;
328 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
331 /* Attach all the body parts which can't be decoded.
332 * This code is shared by forwarding and replying. */
334 static BODY **copy_problematic_attachments (FILE * fp,
337 short idxlen, short force)
341 for (i = 0; i < idxlen; i++) {
342 if (idx[i]->content->tagged &&
343 (force || !mutt_can_decode (idx[i]->content))) {
344 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
345 return NULL; /* XXXXX - may lead to crashes */
346 last = &((*last)->next);
353 * forward one or several MIME bodies
354 * (non-message types)
357 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
358 ATTACHPTR ** idx, short idxlen,
359 BODY * cur, short nattach, int flags)
362 short mime_fwd_all = 0;
363 short mime_fwd_any = 1;
364 HEADER *parent = NULL;
365 HEADER *tmphdr = NULL;
367 char tmpbody[_POSIX_PATH_MAX];
377 * First, find the parent message.
378 * Note: This could be made an option by just
379 * putting the following lines into an if block.
383 parent = find_parent (idx, idxlen, cur, nattach);
389 tmphdr = mutt_new_header ();
390 tmphdr->env = mutt_new_envelope ();
391 mutt_make_forward_subject (tmphdr->env, Context, parent);
393 mutt_mktemp (tmpbody);
394 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
395 mutt_error (_("Can't open temporary file %s."), tmpbody);
399 mutt_forward_intro (tmpfp, parent);
401 /* prepare the prefix here since we'll need it later. */
403 if (option (OPTFORWQUOTE)) {
404 if (!option (OPTTEXTFLOWED))
405 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
408 strfcpy (prefix, ">", sizeof (prefix));
411 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
415 * Now, we have prepared the first part of the message body: The
416 * original message's header.
418 * The next part is more interesting: either include the message bodies,
422 if ((!cur || mutt_can_decode (cur)) &&
423 (rc = query_quadoption (OPT_MIMEFWD,
424 _("Forward as attachments?"))) == M_YES)
430 * shortcut MIMEFWDREST when there is only one attachment. Is
434 if (!mime_fwd_all && !cur && (nattach > 1)
435 && !check_can_decode (idx, idxlen, cur)) {
436 if ((rc = query_quadoption (OPT_MIMEFWDREST,
438 ("Can't decode all tagged attachments. MIME-forward the others?")))
445 /* initialize a state structure */
449 if (option (OPTFORWQUOTE))
451 st.flags = M_CHARCONV;
452 if (option (OPTWEED))
457 /* where do we append new MIME parts? */
458 last = &tmphdr->content;
461 /* single body case */
463 if (!mime_fwd_all && mutt_can_decode (cur)) {
464 mutt_body_handler (cur, &st);
465 state_putc ('\n', &st);
468 if (mutt_copy_body (fp, last, cur) == -1)
470 last = &((*last)->next);
474 /* multiple body case */
477 for (i = 0; i < idxlen; i++) {
478 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
479 mutt_body_handler (idx[i]->content, &st);
480 state_putc ('\n', &st);
487 copy_problematic_attachments (fp, last, idx, idxlen,
488 mime_fwd_all)) == NULL)
492 mutt_forward_trailer (tmpfp);
497 /* now that we have the template, send it. */
498 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
505 mutt_unlink (tmpbody);
508 mutt_free_header (&tmphdr);
513 * Forward one or several message-type attachments. This
514 * is different from the previous function
515 * since we want to mimic the index menu's behaviour.
517 * Code reuse from ci_send_message is not possible here -
518 * ci_send_message relies on a context structure to find messages,
519 * while, on the attachment menu, messages are referenced through
520 * the attachment index.
523 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
524 ATTACHPTR ** idx, short idxlen, BODY * cur,
527 HEADER *curhdr = NULL;
533 char tmpbody[_POSIX_PATH_MAX];
537 int chflags = CH_XMIT;
542 for (i = 0; i < idxlen; i++)
543 if (idx[i]->content->tagged) {
544 curhdr = idx[i]->content->hdr;
549 tmphdr = mutt_new_header ();
550 tmphdr->env = mutt_new_envelope ();
551 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
556 if ((rc = query_quadoption (OPT_MIMEFWD,
557 _("Forward MIME encapsulated?"))) == M_NO) {
559 /* no MIME encapsulation */
561 mutt_mktemp (tmpbody);
562 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
563 mutt_error (_("Can't create %s."), tmpbody);
564 mutt_free_header (&tmphdr);
568 if (option (OPTFORWQUOTE)) {
569 chflags |= CH_PREFIX;
570 cmflags |= M_CM_PREFIX;
573 if (option (OPTFORWDECODE)) {
574 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
575 if (option (OPTWEED)) {
576 chflags |= CH_WEED | CH_REORDER;
577 cmflags |= M_CM_WEED;
583 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
584 mutt_forward_intro (tmpfp, cur->hdr);
585 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
587 mutt_forward_trailer (tmpfp);
590 for (i = 0; i < idxlen; i++) {
591 if (idx[i]->content->tagged) {
592 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
593 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
594 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
595 idx[i]->content->hdr->content, cmflags,
597 mutt_forward_trailer (tmpfp);
603 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
604 last = &tmphdr->content;
606 mutt_copy_body (fp, last, cur);
608 for (i = 0; i < idxlen; i++)
609 if (idx[i]->content->tagged) {
610 mutt_copy_body (fp, last, idx[i]->content);
611 last = &((*last)->next);
616 mutt_free_header (&tmphdr);
618 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
622 void mutt_attach_forward (FILE * fp, HEADER * hdr,
623 ATTACHPTR ** idx, short idxlen, BODY * cur,
629 if (check_all_msg (idx, idxlen, cur, 0) == 0)
630 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
632 nattach = count_tagged (idx, idxlen);
633 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
641 ** the various reply functions, from the attachment menu
646 /* Create the envelope defaults for a reply.
648 * This function can be invoked in two ways.
650 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
651 * and the header information is fetched from them.
653 * Or, parent is non-NULL. In this case, cur is the common parent of all the
654 * tagged attachments.
656 * Note that this code is horribly similar to envelope_defaults () from send.c.
660 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
661 short idxlen, HEADER * parent, int flags)
663 ENVELOPE *curenv = NULL;
664 HEADER *curhdr = NULL;
668 for (i = 0; i < idxlen; i++) {
669 if (idx[i]->content->tagged) {
670 curhdr = idx[i]->content->hdr;
671 curenv = curhdr->env;
677 curenv = parent->env;
681 if (curenv == NULL || curhdr == NULL) {
682 mutt_error _("Can't find any tagged messages.");
688 if ((flags & SENDNEWS)) {
689 /* in case followup set Newsgroups: with Followup-To: if it present */
690 if (!env->newsgroups && curenv &&
691 str_casecmp (curenv->followup_to, "poster"))
692 env->newsgroups = m_strdup(curenv->followup_to);
698 if (mutt_fetch_recips (env, curenv, flags) == -1)
702 for (i = 0; i < idxlen; i++) {
703 if (idx[i]->content->tagged
704 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
710 if ((flags & SENDLISTREPLY) && !env->to) {
711 mutt_error _("No mailing lists found!");
716 mutt_fix_reply_recipients (env);
718 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
721 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
723 LIST **p = NULL, **q = NULL;
725 for (i = 0; i < idxlen; i++) {
726 if (idx[i]->content->tagged)
727 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
736 /* This is _very_ similar to send.c's include_reply(). */
738 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
741 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
742 int chflags = CH_DECODE;
744 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
746 mutt_make_attribution (Context, cur, tmpfp);
748 if (!option (OPTHEADER))
749 cmflags |= M_CM_NOHEADER;
750 if (option (OPTWEED)) {
752 cmflags |= M_CM_WEED;
755 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
756 mutt_make_post_indent (Context, cur, tmpfp);
759 void mutt_attach_reply (FILE * fp, HEADER * hdr,
760 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
762 short mime_reply_any = 0;
765 HEADER *parent = NULL;
766 HEADER *tmphdr = NULL;
770 char tmpbody[_POSIX_PATH_MAX];
773 char prefix[SHORT_STRING];
777 if (flags & SENDNEWS)
778 set_option (OPTNEWSSEND);
780 unset_option (OPTNEWSSEND);
783 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
784 nattach = count_tagged (idx, idxlen);
785 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
789 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
790 if ((rc = query_quadoption (OPT_MIMEFWDREST,
792 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
795 else if (rc == M_YES)
798 else if (nattach == 1)
801 tmphdr = mutt_new_header ();
802 tmphdr->env = mutt_new_envelope ();
804 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
805 parent ? parent : (cur ? cur->
808 mutt_free_header (&tmphdr);
812 mutt_mktemp (tmpbody);
813 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
814 mutt_error (_("Can't create %s."), tmpbody);
815 mutt_free_header (&tmphdr);
821 attach_include_reply (fp, tmpfp, cur->hdr, flags);
823 for (i = 0; i < idxlen; i++) {
824 if (idx[i]->content->tagged)
825 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
830 mutt_make_attribution (Context, parent, tmpfp);
836 if (!option (OPTTEXTFLOWED))
837 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
840 strfcpy (prefix, ">", sizeof (prefix));
843 st.flags = M_CHARCONV;
845 if (option (OPTWEED))
848 if (option (OPTHEADER))
849 include_header (1, fp, parent, tmpfp, prefix);
852 if (mutt_can_decode (cur)) {
853 mutt_body_handler (cur, &st);
854 state_putc ('\n', &st);
857 mutt_copy_body (fp, &tmphdr->content, cur);
860 for (i = 0; i < idxlen; i++) {
861 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
862 mutt_body_handler (idx[i]->content, &st);
863 state_putc ('\n', &st);
868 mutt_make_post_indent (Context, parent, tmpfp);
870 if (mime_reply_any && !cur &&
871 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
873 mutt_free_header (&tmphdr);
881 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
882 mutt_set_flag (Context, hdr, M_REPLIED, 1);