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>
17 #include <lib-lib/file.h>
18 #include <lib-lib/mapping.h>
20 #include <lib-mime/mime.h>
26 #include "recvattach.h"
27 #include "mutt_curses.h"
28 #include "mutt_menu.h"
33 #include "mutt_idna.h"
36 /* some helper functions to verify that we are exclusively operating
37 * on message/rfc822 attachments
40 static short check_msg (BODY * b, short err)
42 if (!mutt_is_message_type (b->type, b->subtype)) {
44 mutt_error _("You may only bounce message/rfc822 parts.");
51 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
52 BODY * cur, short err)
56 if (cur && check_msg (cur, err) == -1)
59 for (i = 0; i < idxlen; i++) {
60 if (idx[i]->content->tagged) {
61 if (check_msg (idx[i]->content, err) == -1)
70 /* can we decode all tagged attachments? */
72 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
77 return mutt_can_decode (cur);
79 for (i = 0; i < idxlen; i++)
80 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
86 static short count_tagged (ATTACHPTR ** idx, short idxlen)
91 for (i = 0; i < idxlen; i++)
92 if (idx[i]->content->tagged)
98 /* count the number of tagged children below a multipart or message
102 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
104 short level = idx[i]->level;
107 while ((++i < idxlen) && (level < idx[i]->level))
108 if (idx[i]->content->tagged)
118 ** The bounce function, from the attachment menu
122 void mutt_attach_bounce (FILE * fp, HEADER * hdr,
123 ATTACHPTR ** idx, short idxlen, BODY * cur)
127 char buf[HUGE_STRING];
129 address_t *adr = NULL;
133 if (check_all_msg (idx, idxlen, cur, 1) == -1)
136 /* one or more messages? */
137 p = (cur || count_tagged (idx, idxlen) == 1);
140 m_strcpy(prompt, sizeof(prompt), _("Bounce message to: "));
142 m_strcpy(prompt, sizeof(prompt), _("Bounce tagged messages to: "));
145 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
149 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
150 mutt_error _("Error parsing address!");
155 adr = mutt_expand_aliases (adr);
157 if (mutt_addrlist_to_idna (adr, &err) < 0) {
158 mutt_error (_("Bad IDN: '%s'"), err);
160 address_delete (&adr);
165 rfc822_write_address (buf, sizeof (buf), adr, 1);
167 #define extra_space (15+7+2)
171 snprintf (prompt, sizeof (prompt) - 4,
172 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
175 if (mutt_strwidth (prompt) > COLS - extra_space) {
176 mutt_format_string (prompt, sizeof (prompt) - 4,
177 0, COLS - extra_space, 0, 0,
178 prompt, sizeof (prompt), 0);
179 m_strcat(prompt, sizeof(prompt), "...?");
181 m_strcat(prompt, sizeof(prompt), "?");
184 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
185 address_delete (&adr);
186 CLEARLINE (LINES - 1);
187 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
191 CLEARLINE (LINES - 1);
194 ret = mutt_bounce_message (fp, cur->hdr, adr);
196 for (i = 0; i < idxlen; i++) {
197 if (idx[i]->content->tagged)
198 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
204 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
206 mutt_error (p ? _("Error bouncing message!") :
207 _("Error bouncing messages!"));
214 ** resend-message, from the attachment menu
219 void mutt_attach_resend (FILE * fp, HEADER * hdr, ATTACHPTR ** idx,
220 short idxlen, BODY * cur)
224 if (check_all_msg (idx, idxlen, cur, 1) == -1)
228 mutt_resend_message (fp, Context, cur->hdr);
230 for (i = 0; i < idxlen; i++)
231 if (idx[i]->content->tagged)
232 mutt_resend_message (fp, Context, idx[i]->content->hdr);
239 ** forward-message, from the attachment menu
243 /* try to find a common parent message for the tagged attachments. */
245 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
251 for (i = 0; i < idxlen; i++)
252 if (idx[i]->content->tagged)
256 if (mutt_is_message_type
257 (idx[i]->content->type, idx[i]->content->subtype)) {
258 nchildren = count_tagged_children (idx, idxlen, i);
259 if (nchildren == nattach)
260 return idx[i]->content->hdr;
268 * check whether attachment #i is a parent of the attachment
271 * Note: This and the calling procedure could be optimized quite a
272 * bit. For now, it's not worth the effort.
275 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
277 short level = idx[i]->level;
279 while ((++i < idxlen) && idx[i]->level > level) {
280 if (idx[i]->content == cur)
287 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
291 HEADER *parent = NULL;
294 for (i = 0; i < idxlen; i++) {
295 if (mutt_is_message_type
296 (idx[i]->content->type, idx[i]->content->subtype)
297 && is_parent (i, idx, idxlen, cur))
298 parent = idx[i]->content->hdr;
299 if (idx[i]->content == cur)
304 parent = find_common_parent (idx, idxlen, nattach);
309 static void include_header (int quote, FILE * ifp,
310 HEADER * hdr, FILE * ofp, char *_prefix)
312 int chflags = CH_DECODE;
313 char prefix[SHORT_STRING];
315 if (option (OPTWEED))
316 chflags |= CH_WEED | CH_REORDER;
320 m_strcpy(prefix, sizeof(prefix), _prefix);
321 else if (!option (OPTTEXTFLOWED))
322 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
325 m_strcpy(prefix, sizeof(prefix), ">");
327 chflags |= CH_PREFIX;
330 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
333 /* Attach all the body parts which can't be decoded.
334 * This code is shared by forwarding and replying. */
336 static BODY **copy_problematic_attachments (FILE * fp,
339 short idxlen, short force)
343 for (i = 0; i < idxlen; i++) {
344 if (idx[i]->content->tagged &&
345 (force || !mutt_can_decode (idx[i]->content))) {
346 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
347 return NULL; /* XXXXX - may lead to crashes */
348 last = &((*last)->next);
355 * forward one or several MIME bodies
356 * (non-message types)
359 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
360 ATTACHPTR ** idx, short idxlen,
361 BODY * cur, short nattach, int flags)
364 short mime_fwd_all = 0;
365 short mime_fwd_any = 1;
366 HEADER *parent = NULL;
367 HEADER *tmphdr = NULL;
369 char tmpbody[_POSIX_PATH_MAX];
379 * First, find the parent message.
380 * Note: This could be made an option by just
381 * putting the following lines into an if block.
385 parent = find_parent (idx, idxlen, cur, nattach);
391 tmphdr = header_new();
392 tmphdr->env = envelope_new();
393 mutt_make_forward_subject (tmphdr->env, Context, parent);
395 mutt_mktemp (tmpbody);
396 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
397 mutt_error (_("Can't open temporary file %s."), tmpbody);
401 mutt_forward_intro (tmpfp, parent);
403 /* prepare the prefix here since we'll need it later. */
405 if (option (OPTFORWQUOTE)) {
406 if (!option (OPTTEXTFLOWED))
407 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
410 m_strcpy(prefix, sizeof(prefix), ">");
413 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
417 * Now, we have prepared the first part of the message body: The
418 * original message's header.
420 * The next part is more interesting: either include the message bodies,
424 if ((!cur || mutt_can_decode (cur)) &&
425 (rc = query_quadoption (OPT_MIMEFWD,
426 _("Forward as attachments?"))) == M_YES)
432 * shortcut MIMEFWDREST when there is only one attachment. Is
436 if (!mime_fwd_all && !cur && (nattach > 1)
437 && !check_can_decode (idx, idxlen, cur)) {
438 if ((rc = query_quadoption (OPT_MIMEFWDREST,
440 ("Can't decode all tagged attachments. MIME-forward the others?")))
447 /* initialize a state structure */
451 if (option (OPTFORWQUOTE))
453 st.flags = M_CHARCONV;
454 if (option (OPTWEED))
459 /* where do we append new MIME parts? */
460 last = &tmphdr->content;
463 /* single body case */
465 if (!mime_fwd_all && mutt_can_decode (cur)) {
466 mutt_body_handler (cur, &st);
467 state_putc ('\n', &st);
470 if (mutt_copy_body (fp, last, cur) == -1)
472 last = &((*last)->next);
476 /* multiple body case */
479 for (i = 0; i < idxlen; i++) {
480 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
481 mutt_body_handler (idx[i]->content, &st);
482 state_putc ('\n', &st);
489 copy_problematic_attachments (fp, last, idx, idxlen,
490 mime_fwd_all)) == NULL)
494 mutt_forward_trailer (tmpfp);
499 /* now that we have the template, send it. */
500 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
507 mutt_unlink (tmpbody);
510 header_delete(&tmphdr);
515 * Forward one or several message-type attachments. This
516 * is different from the previous function
517 * since we want to mimic the index menu's behaviour.
519 * Code reuse from ci_send_message is not possible here -
520 * ci_send_message relies on a context structure to find messages,
521 * while, on the attachment menu, messages are referenced through
522 * the attachment index.
525 static void attach_forward_msgs (FILE * fp, HEADER * hdr,
526 ATTACHPTR ** idx, short idxlen, BODY * cur,
529 HEADER *curhdr = NULL;
535 char tmpbody[_POSIX_PATH_MAX];
539 int chflags = CH_XMIT;
544 for (i = 0; i < idxlen; i++)
545 if (idx[i]->content->tagged) {
546 curhdr = idx[i]->content->hdr;
551 tmphdr = header_new();
552 tmphdr->env = envelope_new();
553 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
558 if ((rc = query_quadoption (OPT_MIMEFWD,
559 _("Forward MIME encapsulated?"))) == M_NO) {
561 /* no MIME encapsulation */
563 mutt_mktemp (tmpbody);
564 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
565 mutt_error (_("Can't create %s."), tmpbody);
566 header_delete(&tmphdr);
570 if (option (OPTFORWQUOTE)) {
571 chflags |= CH_PREFIX;
572 cmflags |= M_CM_PREFIX;
575 if (option (OPTFORWDECODE)) {
576 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
577 if (option (OPTWEED)) {
578 chflags |= CH_WEED | CH_REORDER;
579 cmflags |= M_CM_WEED;
585 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
586 mutt_forward_intro (tmpfp, cur->hdr);
587 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
589 mutt_forward_trailer (tmpfp);
592 for (i = 0; i < idxlen; i++) {
593 if (idx[i]->content->tagged) {
594 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
595 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
596 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
597 idx[i]->content->hdr->content, cmflags,
599 mutt_forward_trailer (tmpfp);
605 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
606 last = &tmphdr->content;
608 mutt_copy_body (fp, last, cur);
610 for (i = 0; i < idxlen; i++)
611 if (idx[i]->content->tagged) {
612 mutt_copy_body (fp, last, idx[i]->content);
613 last = &((*last)->next);
618 header_delete(&tmphdr);
620 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
624 void mutt_attach_forward (FILE * fp, HEADER * hdr,
625 ATTACHPTR ** idx, short idxlen, BODY * cur,
631 if (check_all_msg (idx, idxlen, cur, 0) == 0)
632 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
634 nattach = count_tagged (idx, idxlen);
635 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
643 ** the various reply functions, from the attachment menu
648 /* Create the envelope defaults for a reply.
650 * This function can be invoked in two ways.
652 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
653 * and the header information is fetched from them.
655 * Or, parent is non-NULL. In this case, cur is the common parent of all the
656 * tagged attachments.
658 * Note that this code is horribly similar to envelope_defaults () from send.c.
662 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
663 short idxlen, HEADER * parent, int flags)
665 ENVELOPE *curenv = NULL;
666 HEADER *curhdr = NULL;
670 for (i = 0; i < idxlen; i++) {
671 if (idx[i]->content->tagged) {
672 curhdr = idx[i]->content->hdr;
673 curenv = curhdr->env;
679 curenv = parent->env;
683 if (curenv == NULL || curhdr == NULL) {
684 mutt_error _("Can't find any tagged messages.");
690 if ((flags & SENDNEWS)) {
691 /* in case followup set Newsgroups: with Followup-To: if it present */
692 if (!env->newsgroups && curenv &&
693 m_strcasecmp(curenv->followup_to, "poster"))
694 env->newsgroups = m_strdup(curenv->followup_to);
700 if (mutt_fetch_recips (env, curenv, flags) == -1)
704 for (i = 0; i < idxlen; i++) {
705 if (idx[i]->content->tagged
706 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
712 if ((flags & SENDLISTREPLY) && !env->to) {
713 mutt_error _("No mailing lists found!");
718 mutt_fix_reply_recipients (env);
720 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
723 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
725 LIST **p = NULL, **q = NULL;
727 for (i = 0; i < idxlen; i++) {
728 if (idx[i]->content->tagged)
729 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
738 /* This is _very_ similar to send.c's include_reply(). */
740 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
743 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
744 int chflags = CH_DECODE;
746 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
748 mutt_make_attribution (Context, cur, tmpfp);
750 if (!option (OPTHEADER))
751 cmflags |= M_CM_NOHEADER;
752 if (option (OPTWEED)) {
754 cmflags |= M_CM_WEED;
757 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
758 mutt_make_post_indent (Context, cur, tmpfp);
761 void mutt_attach_reply (FILE * fp, HEADER * hdr,
762 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
764 short mime_reply_any = 0;
767 HEADER *parent = NULL;
768 HEADER *tmphdr = NULL;
772 char tmpbody[_POSIX_PATH_MAX];
775 char prefix[SHORT_STRING];
779 if (flags & SENDNEWS)
780 set_option (OPTNEWSSEND);
782 unset_option (OPTNEWSSEND);
785 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
786 nattach = count_tagged (idx, idxlen);
787 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
791 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
792 if ((rc = query_quadoption (OPT_MIMEFWDREST,
794 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
797 else if (rc == M_YES)
800 else if (nattach == 1)
803 tmphdr = header_new();
804 tmphdr->env = envelope_new();
806 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
807 parent ? parent : (cur ? cur->
810 header_delete(&tmphdr);
814 mutt_mktemp (tmpbody);
815 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
816 mutt_error (_("Can't create %s."), tmpbody);
817 header_delete(&tmphdr);
823 attach_include_reply (fp, tmpfp, cur->hdr, flags);
825 for (i = 0; i < idxlen; i++) {
826 if (idx[i]->content->tagged)
827 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
832 mutt_make_attribution (Context, parent, tmpfp);
838 if (!option (OPTTEXTFLOWED))
839 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
842 m_strcpy(prefix, sizeof(prefix), ">");
845 st.flags = M_CHARCONV;
847 if (option (OPTWEED))
850 if (option (OPTHEADER))
851 include_header (1, fp, parent, tmpfp, prefix);
854 if (mutt_can_decode (cur)) {
855 mutt_body_handler (cur, &st);
856 state_putc ('\n', &st);
859 mutt_copy_body (fp, &tmphdr->content, cur);
862 for (i = 0; i < idxlen; i++) {
863 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
864 mutt_body_handler (idx[i]->content, &st);
865 state_putc ('\n', &st);
870 mutt_make_post_indent (Context, parent, tmpfp);
872 if (mime_reply_any && !cur &&
873 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
875 header_delete(&tmphdr);
883 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
884 mutt_set_flag (Context, hdr, M_REPLIED, 1);