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.
10 #include <lib-lib/lib-lib.h>
12 #include <lib-mime/mime.h>
14 #include <lib-ui/lib-ui.h>
15 #include <lib-ui/enter.h>
16 #include <lib-ui/menu.h>
17 #include <lib-mx/mx.h>
23 #include "recvattach.h"
26 #include "mutt_idna.h"
29 /* some helper functions to verify that we are exclusively operating
30 * on message/rfc822 attachments
32 static int check_all_msg(ATTACHPTR **idx, int idxlen, BODY *cur, bool err)
35 if (!mutt_is_message_type(cur))
38 for (int i = 0; i < idxlen; i++) {
39 BODY *b = idx[i]->content;
40 if (b->tagged && !mutt_is_message_type(b))
48 mutt_error(_("You may only bounce message/rfc822 parts."));
53 /* can we decode all tagged attachments? */
54 static int check_can_decode(ATTACHPTR ** idx, int idxlen, BODY * cur)
57 return mutt_can_decode(cur);
59 for (int i = 0; i < idxlen; i++) {
60 if (idx[i]->content->tagged && !mutt_can_decode(idx[i]->content))
67 static int count_tagged(ATTACHPTR **idx, int idxlen)
71 for (int i = 0; i < idxlen; i++) {
72 if (idx[i]->content->tagged)
79 /* count the number of tagged children below a multipart or message
82 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
84 short level = idx[i]->level;
87 while ((++i < idxlen) && (level < idx[i]->level))
88 if (idx[i]->content->tagged)
96 ** The bounce function, from the attachment menu
99 void mutt_attach_bounce (FILE * fp, HEADER * hdr __attribute__ ((unused)),
100 ATTACHPTR ** idx, short idxlen, BODY * cur)
104 char buf[HUGE_STRING];
106 address_t *adr = NULL;
110 if (check_all_msg (idx, idxlen, cur, 1) == -1)
113 /* one or more messages? */
114 p = (cur || count_tagged (idx, idxlen) == 1);
117 m_strcpy(prompt, sizeof(prompt), _("Bounce message to: "));
119 m_strcpy(prompt, sizeof(prompt), _("Bounce tagged messages to: "));
122 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
126 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
127 mutt_error _("Error parsing address!");
132 adr = mutt_expand_aliases (adr);
134 if (mutt_addrlist_to_idna (adr, &err) < 0) {
135 mutt_error (_("Bad IDN: '%s'"), err);
137 address_list_wipe(&adr);
142 rfc822_addrcat(buf, sizeof(buf), adr, 1);
147 snprintf (prompt, sizeof (prompt) - 4,
148 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
151 #define extra_space (15+7+2)
152 if (m_strwidth(prompt) > getmaxx(main_w) - extra_space) {
153 mutt_format_string (prompt, sizeof (prompt) - 4, 0,
154 getmaxx(main_w) - extra_space, 0, 0,
155 prompt, sizeof (prompt), 0);
156 m_strcat(prompt, sizeof(prompt), "...?");
158 m_strcat(prompt, sizeof(prompt), "?");
162 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
163 address_list_wipe(&adr);
164 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
169 ret = mutt_bounce_message (fp, cur->hdr, adr);
171 for (i = 0; i < idxlen; i++) {
172 if (idx[i]->content->tagged)
173 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
179 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
181 mutt_error (p ? _("Error bouncing message!") :
182 _("Error bouncing messages!"));
187 ** resend-message, from the attachment menu
190 void mutt_attach_resend (FILE * fp, HEADER * hdr __attribute__ ((unused)), ATTACHPTR ** idx,
191 short idxlen, BODY * cur)
195 if (check_all_msg (idx, idxlen, cur, 1) == -1)
199 mutt_resend_message (fp, Context, cur->hdr);
201 for (i = 0; i < idxlen; i++)
202 if (idx[i]->content->tagged)
203 mutt_resend_message (fp, Context, idx[i]->content->hdr);
208 ** forward-message, from the attachment menu
211 /* try to find a common parent message for the tagged attachments. */
213 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
219 for (i = 0; i < idxlen; i++)
220 if (idx[i]->content->tagged)
224 if (mutt_is_message_type(idx[i]->content)) {
225 nchildren = count_tagged_children (idx, idxlen, i);
226 if (nchildren == nattach)
227 return idx[i]->content->hdr;
235 * check whether attachment #i is a parent of the attachment
238 * Note: This and the calling procedure could be optimized quite a
239 * bit. For now, it's not worth the effort.
242 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
244 short level = idx[i]->level;
246 while ((++i < idxlen) && idx[i]->level > level) {
247 if (idx[i]->content == cur)
254 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
258 HEADER *parent = NULL;
261 for (i = 0; i < idxlen; i++) {
262 if (mutt_is_message_type(idx[i]->content)
263 && is_parent (i, idx, idxlen, cur))
264 parent = idx[i]->content->hdr;
265 if (idx[i]->content == cur)
270 parent = find_common_parent (idx, idxlen, nattach);
275 static void include_header (int quote, FILE * ifp,
276 HEADER * hdr, FILE * ofp, char *_prefix)
278 int chflags = CH_DECODE;
281 if (option (OPTWEED))
282 chflags |= CH_WEED | CH_REORDER;
286 m_strcpy(prefix, sizeof(prefix), _prefix);
287 else if (!option (OPTTEXTFLOWED))
288 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
291 m_strcpy(prefix, sizeof(prefix), ">");
293 chflags |= CH_PREFIX;
296 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
299 /* create a send-mode duplicate from a receive-mode body */
300 static int mutt_copy_body (FILE * fp, BODY ** tgt, BODY * src)
302 char tmp[_POSIX_PATH_MAX];
305 parameter_t *par, **ppar;
311 m_strcpy(tmp, sizeof(tmp), src->filename);
317 mutt_adv_mktemp (NULL, tmp, sizeof (tmp));
318 if (mutt_save_attachment (fp, src, tmp, 0, NULL) == -1)
324 memcpy (b, src, sizeof (BODY));
328 b->filename = m_strdup(tmp);
329 b->use_disp = use_disp;
332 if (mutt_is_text_part (b))
335 b->xtype = m_strdup(b->xtype);
336 b->subtype = m_strdup(b->subtype);
337 b->form_name = m_strdup(b->form_name);
338 b->filename = m_strdup(b->filename);
339 b->d_filename = m_strdup(b->d_filename);
340 b->description = m_strdup(b->description);
343 * we don't seem to need the HEADER structure currently.
344 * XXX - this may change in the future
350 /* copy parameters */
351 for (par = b->parameter, ppar = &b->parameter; par;
352 ppar = &(*ppar)->next, par = par->next) {
353 *ppar = parameter_new();
354 (*ppar)->attribute = m_strdup(par->attribute);
355 (*ppar)->value = m_strdup(par->value);
358 mutt_stamp_attachment (b);
363 /* Attach all the body parts which can't be decoded.
364 * This code is shared by forwarding and replying. */
366 static BODY **copy_problematic_attachments (FILE * fp,
369 short idxlen, short force)
373 for (i = 0; i < idxlen; i++) {
374 if (idx[i]->content->tagged &&
375 (force || !mutt_can_decode (idx[i]->content))) {
376 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
377 return NULL; /* XXXXX - may lead to crashes */
378 last = &((*last)->next);
385 * forward one or several MIME bodies
386 * (non-message types)
389 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
390 ATTACHPTR ** idx, short idxlen,
391 BODY * cur, short nattach, int flags)
394 short mime_fwd_all = 0;
395 short mime_fwd_any = 1;
396 HEADER *parent = NULL;
397 HEADER *tmphdr = NULL;
399 char tmpbody[_POSIX_PATH_MAX];
409 * First, find the parent message.
410 * Note: This could be made an option by just
411 * putting the following lines into an if block.
415 parent = find_parent (idx, idxlen, cur, nattach);
421 tmphdr = header_new();
422 tmphdr->env = envelope_new();
423 mutt_make_forward_subject (tmphdr->env, Context, parent);
425 tmpfp = m_tempfile(tmpbody, sizeof(tmpbody), NONULL(mod_core.tmpdir), NULL);
427 mutt_error(_("Could not create temporary file"));
431 mutt_forward_intro (tmpfp, parent);
433 /* prepare the prefix here since we'll need it later. */
435 if (option (OPTFORWQUOTE)) {
436 if (!option (OPTTEXTFLOWED))
437 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
440 m_strcpy(prefix, sizeof(prefix), ">");
443 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
447 * Now, we have prepared the first part of the message body: The
448 * original message's header.
450 * The next part is more interesting: either include the message bodies,
454 if ((!cur || mutt_can_decode (cur)) &&
455 (rc = query_quadoption (OPT_MIMEFWD,
456 _("Forward as attachments?"))) == M_YES)
462 * shortcut MIMEFWDREST when there is only one attachment. Is
466 if (!mime_fwd_all && !cur && (nattach > 1)
467 && !check_can_decode (idx, idxlen, cur)) {
468 if ((rc = query_quadoption (OPT_MIMEFWDREST,
470 ("Can't decode all tagged attachments. MIME-forward the others?")))
477 /* initialize a state structure */
481 if (option (OPTFORWQUOTE))
483 st.flags = M_CHARCONV;
484 if (option (OPTWEED))
489 /* where do we append new MIME parts? */
490 last = &tmphdr->content;
493 /* single body case */
495 if (!mime_fwd_all && mutt_can_decode (cur)) {
496 mutt_body_handler (cur, &st);
497 state_putc ('\n', &st);
500 if (mutt_copy_body (fp, last, cur) == -1)
502 last = &((*last)->next);
506 /* multiple body case */
509 for (i = 0; i < idxlen; i++) {
510 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
511 mutt_body_handler (idx[i]->content, &st);
512 state_putc ('\n', &st);
519 copy_problematic_attachments (fp, last, idx, idxlen,
520 mime_fwd_all)) == NULL)
524 mutt_forward_trailer (tmpfp);
527 /* now that we have the template, send it. */
528 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
533 mutt_unlink(tmpbody);
534 header_delete(&tmphdr);
539 * Forward one or several message-type attachments. This
540 * is different from the previous function
541 * since we want to mimic the index menu's behaviour.
543 * Code reuse from ci_send_message is not possible here -
544 * ci_send_message relies on a context structure to find messages,
545 * while, on the attachment menu, messages are referenced through
546 * the attachment index.
549 static void attach_forward_msgs (FILE * fp, HEADER * hdr __attribute__ ((unused)),
550 ATTACHPTR ** idx, short idxlen, BODY * cur,
553 HEADER *curhdr = NULL;
559 char tmpbody[_POSIX_PATH_MAX];
563 int chflags = CH_XMIT;
568 for (i = 0; i < idxlen; i++)
569 if (idx[i]->content->tagged) {
570 curhdr = idx[i]->content->hdr;
575 tmphdr = header_new();
576 tmphdr->env = envelope_new();
577 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
582 if ((rc = query_quadoption (OPT_MIMEFWD,
583 _("Forward MIME encapsulated?"))) == M_NO) {
585 /* no MIME encapsulation */
587 tmpfp = m_tempfile(tmpbody, sizeof(tmpbody), NONULL(mod_core.tmpdir), NULL);
589 mutt_error(_("Could not create temporary file"));
590 header_delete(&tmphdr);
594 if (option (OPTFORWQUOTE)) {
595 chflags |= CH_PREFIX;
596 cmflags |= M_CM_PREFIX;
599 if (option (OPTFORWDECODE)) {
600 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
601 if (option (OPTWEED)) {
602 chflags |= CH_WEED | CH_REORDER;
603 cmflags |= M_CM_WEED;
609 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
610 mutt_forward_intro (tmpfp, cur->hdr);
611 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
613 mutt_forward_trailer (tmpfp);
616 for (i = 0; i < idxlen; i++) {
617 if (idx[i]->content->tagged) {
618 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
619 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
620 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
621 idx[i]->content->hdr->content, cmflags,
623 mutt_forward_trailer (tmpfp);
629 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
630 last = &tmphdr->content;
632 mutt_copy_body (fp, last, cur);
634 for (i = 0; i < idxlen; i++)
635 if (idx[i]->content->tagged) {
636 mutt_copy_body (fp, last, idx[i]->content);
637 last = &((*last)->next);
642 header_delete(&tmphdr);
644 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
648 void mutt_attach_forward (FILE * fp, HEADER * hdr,
649 ATTACHPTR ** idx, short idxlen, BODY * cur,
655 if (check_all_msg (idx, idxlen, cur, 0) == 0) {
656 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
658 nattach = count_tagged (idx, idxlen);
659 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
665 ** the various reply functions, from the attachment menu
668 /* Create the envelope defaults for a reply.
670 * This function can be invoked in two ways.
672 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
673 * and the header information is fetched from them.
675 * Or, parent is non-NULL. In this case, cur is the common parent of all the
676 * tagged attachments.
678 * Note that this code is horribly similar to envelope_defaults () from send.c.
682 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
683 short idxlen, HEADER * parent, int flags)
685 ENVELOPE *curenv = NULL;
686 HEADER *curhdr = NULL;
690 for (i = 0; i < idxlen; i++) {
691 if (idx[i]->content->tagged) {
692 curhdr = idx[i]->content->hdr;
693 curenv = curhdr->env;
699 curenv = parent->env;
703 if (curenv == NULL || curhdr == NULL) {
704 mutt_error _("Can't find any tagged messages.");
710 if (mutt_fetch_recips (env, curenv, flags) == -1)
713 for (i = 0; i < idxlen; i++) {
714 if (idx[i]->content->tagged
715 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
721 if ((flags & SENDLISTREPLY) && !env->to) {
722 mutt_error _("No mailing lists found!");
727 mutt_fix_reply_recipients (env);
728 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
731 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
733 string_list_t **p = NULL, **q = NULL;
735 for (i = 0; i < idxlen; i++) {
736 if (idx[i]->content->tagged)
737 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
746 /* This is _very_ similar to send.c's include_reply(). */
748 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
749 int flags __attribute__ ((unused)))
751 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
752 int chflags = CH_DECODE;
754 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
756 mutt_make_attribution (Context, cur, tmpfp);
758 if (!option (OPTHEADER))
759 cmflags |= M_CM_NOHEADER;
760 if (option (OPTWEED)) {
762 cmflags |= M_CM_WEED;
765 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
768 void mutt_attach_reply (FILE * fp, HEADER * hdr,
769 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
771 short mime_reply_any = 0;
774 HEADER *parent = NULL;
775 HEADER *tmphdr = NULL;
779 char tmpbody[_POSIX_PATH_MAX];
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 tmpfp = m_tempfile(tmpbody, sizeof(tmpbody), NONULL(mod_core.tmpdir), NULL);
816 mutt_error(_("Could not create temporary file"));
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 if (mime_reply_any && !cur &&
871 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
873 header_delete(&tmphdr);
881 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
882 mutt_set_flag (Context, hdr, M_REPLIED, 1);