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/lib-lib.h>
16 #include <lib-mime/mime.h>
18 #include <lib-ui/curses.h>
19 #include <lib-ui/enter.h>
20 #include <lib-ui/menu.h>
26 #include "recvattach.h"
30 #include "mutt_idna.h"
33 /* some helper functions to verify that we are exclusively operating
34 * on message/rfc822 attachments
37 static short check_msg (BODY * b, short err)
39 if (!mutt_is_message_type (b->type, b->subtype)) {
41 mutt_error _("You may only bounce message/rfc822 parts.");
48 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
49 BODY * cur, short err)
53 if (cur && check_msg (cur, err) == -1)
56 for (i = 0; i < idxlen; i++) {
57 if (idx[i]->content->tagged) {
58 if (check_msg (idx[i]->content, err) == -1)
67 /* can we decode all tagged attachments? */
69 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
74 return mutt_can_decode (cur);
76 for (i = 0; i < idxlen; i++)
77 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
83 static short count_tagged (ATTACHPTR ** idx, short idxlen)
88 for (i = 0; i < idxlen; i++)
89 if (idx[i]->content->tagged)
95 /* count the number of tagged children below a multipart or message
99 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
101 short level = idx[i]->level;
104 while ((++i < idxlen) && (level < idx[i]->level))
105 if (idx[i]->content->tagged)
114 ** The bounce function, from the attachment menu
117 void mutt_attach_bounce (FILE * fp, HEADER * hdr __attribute__ ((unused)),
118 ATTACHPTR ** idx, short idxlen, BODY * cur)
122 char buf[HUGE_STRING];
124 address_t *adr = NULL;
128 if (check_all_msg (idx, idxlen, cur, 1) == -1)
131 /* one or more messages? */
132 p = (cur || count_tagged (idx, idxlen) == 1);
135 m_strcpy(prompt, sizeof(prompt), _("Bounce message to: "));
137 m_strcpy(prompt, sizeof(prompt), _("Bounce tagged messages to: "));
140 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
144 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
145 mutt_error _("Error parsing address!");
150 adr = mutt_expand_aliases (adr);
152 if (mutt_addrlist_to_idna (adr, &err) < 0) {
153 mutt_error (_("Bad IDN: '%s'"), err);
155 address_list_wipe(&adr);
160 rfc822_write_address (buf, sizeof (buf), adr, 1);
162 #define extra_space (15+7+2)
166 snprintf (prompt, sizeof (prompt) - 4,
167 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
170 if (mutt_strwidth (prompt) > COLS - extra_space) {
171 mutt_format_string (prompt, sizeof (prompt) - 4,
172 0, COLS - extra_space, 0, 0,
173 prompt, sizeof (prompt), 0);
174 m_strcat(prompt, sizeof(prompt), "...?");
176 m_strcat(prompt, sizeof(prompt), "?");
179 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
180 address_list_wipe(&adr);
181 CLEARLINE (LINES - 1);
182 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
186 CLEARLINE (LINES - 1);
189 ret = mutt_bounce_message (fp, cur->hdr, adr);
191 for (i = 0; i < idxlen; i++) {
192 if (idx[i]->content->tagged)
193 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
199 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
201 mutt_error (p ? _("Error bouncing message!") :
202 _("Error bouncing messages!"));
208 ** resend-message, from the attachment menu
211 void mutt_attach_resend (FILE * fp, HEADER * hdr __attribute__ ((unused)), ATTACHPTR ** idx,
212 short idxlen, BODY * cur)
216 if (check_all_msg (idx, idxlen, cur, 1) == -1)
220 mutt_resend_message (fp, Context, cur->hdr);
222 for (i = 0; i < idxlen; i++)
223 if (idx[i]->content->tagged)
224 mutt_resend_message (fp, Context, idx[i]->content->hdr);
230 ** forward-message, from the attachment menu
233 /* try to find a common parent message for the tagged attachments. */
235 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
241 for (i = 0; i < idxlen; i++)
242 if (idx[i]->content->tagged)
246 if (mutt_is_message_type
247 (idx[i]->content->type, idx[i]->content->subtype)) {
248 nchildren = count_tagged_children (idx, idxlen, i);
249 if (nchildren == nattach)
250 return idx[i]->content->hdr;
258 * check whether attachment #i is a parent of the attachment
261 * Note: This and the calling procedure could be optimized quite a
262 * bit. For now, it's not worth the effort.
265 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
267 short level = idx[i]->level;
269 while ((++i < idxlen) && idx[i]->level > level) {
270 if (idx[i]->content == cur)
277 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
281 HEADER *parent = NULL;
284 for (i = 0; i < idxlen; i++) {
285 if (mutt_is_message_type
286 (idx[i]->content->type, idx[i]->content->subtype)
287 && is_parent (i, idx, idxlen, cur))
288 parent = idx[i]->content->hdr;
289 if (idx[i]->content == cur)
294 parent = find_common_parent (idx, idxlen, nattach);
299 static void include_header (int quote, FILE * ifp,
300 HEADER * hdr, FILE * ofp, char *_prefix)
302 int chflags = CH_DECODE;
303 char prefix[SHORT_STRING];
305 if (option (OPTWEED))
306 chflags |= CH_WEED | CH_REORDER;
310 m_strcpy(prefix, sizeof(prefix), _prefix);
311 else if (!option (OPTTEXTFLOWED))
312 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
315 m_strcpy(prefix, sizeof(prefix), ">");
317 chflags |= CH_PREFIX;
320 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
323 /* Attach all the body parts which can't be decoded.
324 * This code is shared by forwarding and replying. */
326 static BODY **copy_problematic_attachments (FILE * fp,
329 short idxlen, short force)
333 for (i = 0; i < idxlen; i++) {
334 if (idx[i]->content->tagged &&
335 (force || !mutt_can_decode (idx[i]->content))) {
336 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
337 return NULL; /* XXXXX - may lead to crashes */
338 last = &((*last)->next);
345 * forward one or several MIME bodies
346 * (non-message types)
349 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
350 ATTACHPTR ** idx, short idxlen,
351 BODY * cur, short nattach, int flags)
354 short mime_fwd_all = 0;
355 short mime_fwd_any = 1;
356 HEADER *parent = NULL;
357 HEADER *tmphdr = NULL;
359 char tmpbody[_POSIX_PATH_MAX];
369 * First, find the parent message.
370 * Note: This could be made an option by just
371 * putting the following lines into an if block.
375 parent = find_parent (idx, idxlen, cur, nattach);
381 tmphdr = header_new();
382 tmphdr->env = envelope_new();
383 mutt_make_forward_subject (tmphdr->env, Context, parent);
385 mutt_mktemp (tmpbody);
386 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
387 mutt_error (_("Can't open temporary file %s."), tmpbody);
391 mutt_forward_intro (tmpfp, parent);
393 /* prepare the prefix here since we'll need it later. */
395 if (option (OPTFORWQUOTE)) {
396 if (!option (OPTTEXTFLOWED))
397 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
400 m_strcpy(prefix, sizeof(prefix), ">");
403 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
407 * Now, we have prepared the first part of the message body: The
408 * original message's header.
410 * The next part is more interesting: either include the message bodies,
414 if ((!cur || mutt_can_decode (cur)) &&
415 (rc = query_quadoption (OPT_MIMEFWD,
416 _("Forward as attachments?"))) == M_YES)
422 * shortcut MIMEFWDREST when there is only one attachment. Is
426 if (!mime_fwd_all && !cur && (nattach > 1)
427 && !check_can_decode (idx, idxlen, cur)) {
428 if ((rc = query_quadoption (OPT_MIMEFWDREST,
430 ("Can't decode all tagged attachments. MIME-forward the others?")))
437 /* initialize a state structure */
441 if (option (OPTFORWQUOTE))
443 st.flags = M_CHARCONV;
444 if (option (OPTWEED))
449 /* where do we append new MIME parts? */
450 last = &tmphdr->content;
453 /* single body case */
455 if (!mime_fwd_all && mutt_can_decode (cur)) {
456 mutt_body_handler (cur, &st);
457 state_putc ('\n', &st);
460 if (mutt_copy_body (fp, last, cur) == -1)
462 last = &((*last)->next);
466 /* multiple body case */
469 for (i = 0; i < idxlen; i++) {
470 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
471 mutt_body_handler (idx[i]->content, &st);
472 state_putc ('\n', &st);
479 copy_problematic_attachments (fp, last, idx, idxlen,
480 mime_fwd_all)) == NULL)
484 mutt_forward_trailer (tmpfp);
489 /* now that we have the template, send it. */
490 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
497 mutt_unlink (tmpbody);
500 header_delete(&tmphdr);
505 * Forward one or several message-type attachments. This
506 * is different from the previous function
507 * since we want to mimic the index menu's behaviour.
509 * Code reuse from ci_send_message is not possible here -
510 * ci_send_message relies on a context structure to find messages,
511 * while, on the attachment menu, messages are referenced through
512 * the attachment index.
515 static void attach_forward_msgs (FILE * fp, HEADER * hdr __attribute__ ((unused)),
516 ATTACHPTR ** idx, short idxlen, BODY * cur,
519 HEADER *curhdr = NULL;
525 char tmpbody[_POSIX_PATH_MAX];
529 int chflags = CH_XMIT;
534 for (i = 0; i < idxlen; i++)
535 if (idx[i]->content->tagged) {
536 curhdr = idx[i]->content->hdr;
541 tmphdr = header_new();
542 tmphdr->env = envelope_new();
543 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
548 if ((rc = query_quadoption (OPT_MIMEFWD,
549 _("Forward MIME encapsulated?"))) == M_NO) {
551 /* no MIME encapsulation */
553 mutt_mktemp (tmpbody);
554 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
555 mutt_error (_("Can't create %s."), tmpbody);
556 header_delete(&tmphdr);
560 if (option (OPTFORWQUOTE)) {
561 chflags |= CH_PREFIX;
562 cmflags |= M_CM_PREFIX;
565 if (option (OPTFORWDECODE)) {
566 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
567 if (option (OPTWEED)) {
568 chflags |= CH_WEED | CH_REORDER;
569 cmflags |= M_CM_WEED;
575 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
576 mutt_forward_intro (tmpfp, cur->hdr);
577 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
579 mutt_forward_trailer (tmpfp);
582 for (i = 0; i < idxlen; i++) {
583 if (idx[i]->content->tagged) {
584 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
585 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
586 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
587 idx[i]->content->hdr->content, cmflags,
589 mutt_forward_trailer (tmpfp);
595 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
596 last = &tmphdr->content;
598 mutt_copy_body (fp, last, cur);
600 for (i = 0; i < idxlen; i++)
601 if (idx[i]->content->tagged) {
602 mutt_copy_body (fp, last, idx[i]->content);
603 last = &((*last)->next);
608 header_delete(&tmphdr);
610 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
614 void mutt_attach_forward (FILE * fp, HEADER * hdr,
615 ATTACHPTR ** idx, short idxlen, BODY * cur,
621 if (check_all_msg (idx, idxlen, cur, 0) == 0)
622 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
624 nattach = count_tagged (idx, idxlen);
625 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
632 ** the various reply functions, from the attachment menu
635 /* Create the envelope defaults for a reply.
637 * This function can be invoked in two ways.
639 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
640 * and the header information is fetched from them.
642 * Or, parent is non-NULL. In this case, cur is the common parent of all the
643 * tagged attachments.
645 * Note that this code is horribly similar to envelope_defaults () from send.c.
649 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
650 short idxlen, HEADER * parent, int flags)
652 ENVELOPE *curenv = NULL;
653 HEADER *curhdr = NULL;
657 for (i = 0; i < idxlen; i++) {
658 if (idx[i]->content->tagged) {
659 curhdr = idx[i]->content->hdr;
660 curenv = curhdr->env;
666 curenv = parent->env;
670 if (curenv == NULL || curhdr == NULL) {
671 mutt_error _("Can't find any tagged messages.");
677 if ((flags & SENDNEWS)) {
678 /* in case followup set Newsgroups: with Followup-To: if it present */
679 if (!env->newsgroups && curenv &&
680 m_strcasecmp(curenv->followup_to, "poster"))
681 env->newsgroups = m_strdup(curenv->followup_to);
687 if (mutt_fetch_recips (env, curenv, flags) == -1)
691 for (i = 0; i < idxlen; i++) {
692 if (idx[i]->content->tagged
693 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
699 if ((flags & SENDLISTREPLY) && !env->to) {
700 mutt_error _("No mailing lists found!");
705 mutt_fix_reply_recipients (env);
707 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
710 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
712 string_list_t **p = NULL, **q = NULL;
714 for (i = 0; i < idxlen; i++) {
715 if (idx[i]->content->tagged)
716 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
725 /* This is _very_ similar to send.c's include_reply(). */
727 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
728 int flags __attribute__ ((unused)))
730 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
731 int chflags = CH_DECODE;
733 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
735 mutt_make_attribution (Context, cur, tmpfp);
737 if (!option (OPTHEADER))
738 cmflags |= M_CM_NOHEADER;
739 if (option (OPTWEED)) {
741 cmflags |= M_CM_WEED;
744 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
745 mutt_make_post_indent (Context, cur, tmpfp);
748 void mutt_attach_reply (FILE * fp, HEADER * hdr,
749 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
751 short mime_reply_any = 0;
754 HEADER *parent = NULL;
755 HEADER *tmphdr = NULL;
759 char tmpbody[_POSIX_PATH_MAX];
762 char prefix[SHORT_STRING];
766 if (flags & SENDNEWS)
767 set_option (OPTNEWSSEND);
769 unset_option (OPTNEWSSEND);
772 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
773 nattach = count_tagged (idx, idxlen);
774 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
778 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
779 if ((rc = query_quadoption (OPT_MIMEFWDREST,
781 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
784 else if (rc == M_YES)
787 else if (nattach == 1)
790 tmphdr = header_new();
791 tmphdr->env = envelope_new();
793 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
794 parent ? parent : (cur ? cur->
797 header_delete(&tmphdr);
801 mutt_mktemp (tmpbody);
802 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
803 mutt_error (_("Can't create %s."), tmpbody);
804 header_delete(&tmphdr);
810 attach_include_reply (fp, tmpfp, cur->hdr, flags);
812 for (i = 0; i < idxlen; i++) {
813 if (idx[i]->content->tagged)
814 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
819 mutt_make_attribution (Context, parent, tmpfp);
825 if (!option (OPTTEXTFLOWED))
826 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
829 m_strcpy(prefix, sizeof(prefix), ">");
832 st.flags = M_CHARCONV;
834 if (option (OPTWEED))
837 if (option (OPTHEADER))
838 include_header (1, fp, parent, tmpfp, prefix);
841 if (mutt_can_decode (cur)) {
842 mutt_body_handler (cur, &st);
843 state_putc ('\n', &st);
846 mutt_copy_body (fp, &tmphdr->content, cur);
849 for (i = 0; i < idxlen; i++) {
850 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
851 mutt_body_handler (idx[i]->content, &st);
852 state_putc ('\n', &st);
857 mutt_make_post_indent (Context, parent, tmpfp);
859 if (mime_reply_any && !cur &&
860 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
862 header_delete(&tmphdr);
870 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
871 mutt_set_flag (Context, hdr, M_REPLIED, 1);