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>
22 #include <lib-ui/curses.h>
23 #include <lib-ui/enter.h>
24 #include <lib-ui/menu.h>
30 #include "recvattach.h"
34 #include "mutt_idna.h"
37 /* some helper functions to verify that we are exclusively operating
38 * on message/rfc822 attachments
41 static short check_msg (BODY * b, short err)
43 if (!mutt_is_message_type (b->type, b->subtype)) {
45 mutt_error _("You may only bounce message/rfc822 parts.");
52 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
53 BODY * cur, short err)
57 if (cur && check_msg (cur, err) == -1)
60 for (i = 0; i < idxlen; i++) {
61 if (idx[i]->content->tagged) {
62 if (check_msg (idx[i]->content, err) == -1)
71 /* can we decode all tagged attachments? */
73 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
78 return mutt_can_decode (cur);
80 for (i = 0; i < idxlen; i++)
81 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
87 static short count_tagged (ATTACHPTR ** idx, short idxlen)
92 for (i = 0; i < idxlen; i++)
93 if (idx[i]->content->tagged)
99 /* count the number of tagged children below a multipart or message
103 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
105 short level = idx[i]->level;
108 while ((++i < idxlen) && (level < idx[i]->level))
109 if (idx[i]->content->tagged)
119 ** The bounce function, from the attachment menu
123 void mutt_attach_bounce (FILE * fp, HEADER * hdr __attribute__ ((unused)),
124 ATTACHPTR ** idx, short idxlen, BODY * cur)
128 char buf[HUGE_STRING];
129 const char *err = NULL;
130 address_t *adr = NULL;
134 if (check_all_msg (idx, idxlen, cur, 1) == -1)
137 /* one or more messages? */
138 p = (cur || count_tagged (idx, idxlen) == 1);
141 m_strcpy(prompt, sizeof(prompt), _("Bounce message to: "));
143 m_strcpy(prompt, sizeof(prompt), _("Bounce tagged messages to: "));
146 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
150 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
151 mutt_error _("Error parsing address!");
156 adr = mutt_expand_aliases (adr);
158 if (mutt_addrlist_to_idna (adr, &err) < 0) {
159 mutt_error (_("Bad IDN: '%s'"), err);
161 address_list_wipe(&adr);
166 rfc822_write_address (buf, sizeof (buf), adr, 1);
168 #define extra_space (15+7+2)
172 snprintf (prompt, sizeof (prompt) - 4,
173 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
176 if (mutt_strwidth (prompt) > COLS - extra_space) {
177 mutt_format_string (prompt, sizeof (prompt) - 4,
178 0, COLS - extra_space, 0, 0,
179 prompt, sizeof (prompt), 0);
180 m_strcat(prompt, sizeof(prompt), "...?");
182 m_strcat(prompt, sizeof(prompt), "?");
185 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
186 address_list_wipe(&adr);
187 CLEARLINE (LINES - 1);
188 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
192 CLEARLINE (LINES - 1);
195 ret = mutt_bounce_message (fp, cur->hdr, adr);
197 for (i = 0; i < idxlen; i++) {
198 if (idx[i]->content->tagged)
199 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
205 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
207 mutt_error (p ? _("Error bouncing message!") :
208 _("Error bouncing messages!"));
215 ** resend-message, from the attachment menu
220 void mutt_attach_resend (FILE * fp, HEADER * hdr __attribute__ ((unused)), ATTACHPTR ** idx,
221 short idxlen, BODY * cur)
225 if (check_all_msg (idx, idxlen, cur, 1) == -1)
229 mutt_resend_message (fp, Context, cur->hdr);
231 for (i = 0; i < idxlen; i++)
232 if (idx[i]->content->tagged)
233 mutt_resend_message (fp, Context, idx[i]->content->hdr);
240 ** forward-message, from the attachment menu
244 /* try to find a common parent message for the tagged attachments. */
246 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
252 for (i = 0; i < idxlen; i++)
253 if (idx[i]->content->tagged)
257 if (mutt_is_message_type
258 (idx[i]->content->type, idx[i]->content->subtype)) {
259 nchildren = count_tagged_children (idx, idxlen, i);
260 if (nchildren == nattach)
261 return idx[i]->content->hdr;
269 * check whether attachment #i is a parent of the attachment
272 * Note: This and the calling procedure could be optimized quite a
273 * bit. For now, it's not worth the effort.
276 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
278 short level = idx[i]->level;
280 while ((++i < idxlen) && idx[i]->level > level) {
281 if (idx[i]->content == cur)
288 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
292 HEADER *parent = NULL;
295 for (i = 0; i < idxlen; i++) {
296 if (mutt_is_message_type
297 (idx[i]->content->type, idx[i]->content->subtype)
298 && is_parent (i, idx, idxlen, cur))
299 parent = idx[i]->content->hdr;
300 if (idx[i]->content == cur)
305 parent = find_common_parent (idx, idxlen, nattach);
310 static void include_header (int quote, FILE * ifp,
311 HEADER * hdr, FILE * ofp, char *_prefix)
313 int chflags = CH_DECODE;
314 char prefix[SHORT_STRING];
316 if (option (OPTWEED))
317 chflags |= CH_WEED | CH_REORDER;
321 m_strcpy(prefix, sizeof(prefix), _prefix);
322 else if (!option (OPTTEXTFLOWED))
323 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
326 m_strcpy(prefix, sizeof(prefix), ">");
328 chflags |= CH_PREFIX;
331 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
334 /* Attach all the body parts which can't be decoded.
335 * This code is shared by forwarding and replying. */
337 static BODY **copy_problematic_attachments (FILE * fp,
340 short idxlen, short force)
344 for (i = 0; i < idxlen; i++) {
345 if (idx[i]->content->tagged &&
346 (force || !mutt_can_decode (idx[i]->content))) {
347 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
348 return NULL; /* XXXXX - may lead to crashes */
349 last = &((*last)->next);
356 * forward one or several MIME bodies
357 * (non-message types)
360 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
361 ATTACHPTR ** idx, short idxlen,
362 BODY * cur, short nattach, int flags)
365 short mime_fwd_all = 0;
366 short mime_fwd_any = 1;
367 HEADER *parent = NULL;
368 HEADER *tmphdr = NULL;
370 char tmpbody[_POSIX_PATH_MAX];
380 * First, find the parent message.
381 * Note: This could be made an option by just
382 * putting the following lines into an if block.
386 parent = find_parent (idx, idxlen, cur, nattach);
392 tmphdr = header_new();
393 tmphdr->env = envelope_new();
394 mutt_make_forward_subject (tmphdr->env, Context, parent);
396 mutt_mktemp (tmpbody);
397 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
398 mutt_error (_("Can't open temporary file %s."), tmpbody);
402 mutt_forward_intro (tmpfp, parent);
404 /* prepare the prefix here since we'll need it later. */
406 if (option (OPTFORWQUOTE)) {
407 if (!option (OPTTEXTFLOWED))
408 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
411 m_strcpy(prefix, sizeof(prefix), ">");
414 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
418 * Now, we have prepared the first part of the message body: The
419 * original message's header.
421 * The next part is more interesting: either include the message bodies,
425 if ((!cur || mutt_can_decode (cur)) &&
426 (rc = query_quadoption (OPT_MIMEFWD,
427 _("Forward as attachments?"))) == M_YES)
433 * shortcut MIMEFWDREST when there is only one attachment. Is
437 if (!mime_fwd_all && !cur && (nattach > 1)
438 && !check_can_decode (idx, idxlen, cur)) {
439 if ((rc = query_quadoption (OPT_MIMEFWDREST,
441 ("Can't decode all tagged attachments. MIME-forward the others?")))
448 /* initialize a state structure */
452 if (option (OPTFORWQUOTE))
454 st.flags = M_CHARCONV;
455 if (option (OPTWEED))
460 /* where do we append new MIME parts? */
461 last = &tmphdr->content;
464 /* single body case */
466 if (!mime_fwd_all && mutt_can_decode (cur)) {
467 mutt_body_handler (cur, &st);
468 state_putc ('\n', &st);
471 if (mutt_copy_body (fp, last, cur) == -1)
473 last = &((*last)->next);
477 /* multiple body case */
480 for (i = 0; i < idxlen; i++) {
481 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
482 mutt_body_handler (idx[i]->content, &st);
483 state_putc ('\n', &st);
490 copy_problematic_attachments (fp, last, idx, idxlen,
491 mime_fwd_all)) == NULL)
495 mutt_forward_trailer (tmpfp);
500 /* now that we have the template, send it. */
501 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
508 mutt_unlink (tmpbody);
511 header_delete(&tmphdr);
516 * Forward one or several message-type attachments. This
517 * is different from the previous function
518 * since we want to mimic the index menu's behaviour.
520 * Code reuse from ci_send_message is not possible here -
521 * ci_send_message relies on a context structure to find messages,
522 * while, on the attachment menu, messages are referenced through
523 * the attachment index.
526 static void attach_forward_msgs (FILE * fp, HEADER * hdr __attribute__ ((unused)),
527 ATTACHPTR ** idx, short idxlen, BODY * cur,
530 HEADER *curhdr = NULL;
536 char tmpbody[_POSIX_PATH_MAX];
540 int chflags = CH_XMIT;
545 for (i = 0; i < idxlen; i++)
546 if (idx[i]->content->tagged) {
547 curhdr = idx[i]->content->hdr;
552 tmphdr = header_new();
553 tmphdr->env = envelope_new();
554 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
559 if ((rc = query_quadoption (OPT_MIMEFWD,
560 _("Forward MIME encapsulated?"))) == M_NO) {
562 /* no MIME encapsulation */
564 mutt_mktemp (tmpbody);
565 if (!(tmpfp = safe_fopen (tmpbody, "w"))) {
566 mutt_error (_("Can't create %s."), tmpbody);
567 header_delete(&tmphdr);
571 if (option (OPTFORWQUOTE)) {
572 chflags |= CH_PREFIX;
573 cmflags |= M_CM_PREFIX;
576 if (option (OPTFORWDECODE)) {
577 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
578 if (option (OPTWEED)) {
579 chflags |= CH_WEED | CH_REORDER;
580 cmflags |= M_CM_WEED;
586 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
587 mutt_forward_intro (tmpfp, cur->hdr);
588 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
590 mutt_forward_trailer (tmpfp);
593 for (i = 0; i < idxlen; i++) {
594 if (idx[i]->content->tagged) {
595 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
596 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
597 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
598 idx[i]->content->hdr->content, cmflags,
600 mutt_forward_trailer (tmpfp);
606 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
607 last = &tmphdr->content;
609 mutt_copy_body (fp, last, cur);
611 for (i = 0; i < idxlen; i++)
612 if (idx[i]->content->tagged) {
613 mutt_copy_body (fp, last, idx[i]->content);
614 last = &((*last)->next);
619 header_delete(&tmphdr);
621 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
625 void mutt_attach_forward (FILE * fp, HEADER * hdr,
626 ATTACHPTR ** idx, short idxlen, BODY * cur,
632 if (check_all_msg (idx, idxlen, cur, 0) == 0)
633 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
635 nattach = count_tagged (idx, idxlen);
636 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
644 ** the various reply functions, from the attachment menu
649 /* Create the envelope defaults for a reply.
651 * This function can be invoked in two ways.
653 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
654 * and the header information is fetched from them.
656 * Or, parent is non-NULL. In this case, cur is the common parent of all the
657 * tagged attachments.
659 * Note that this code is horribly similar to envelope_defaults () from send.c.
663 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
664 short idxlen, HEADER * parent, int flags)
666 ENVELOPE *curenv = NULL;
667 HEADER *curhdr = NULL;
671 for (i = 0; i < idxlen; i++) {
672 if (idx[i]->content->tagged) {
673 curhdr = idx[i]->content->hdr;
674 curenv = curhdr->env;
680 curenv = parent->env;
684 if (curenv == NULL || curhdr == NULL) {
685 mutt_error _("Can't find any tagged messages.");
691 if ((flags & SENDNEWS)) {
692 /* in case followup set Newsgroups: with Followup-To: if it present */
693 if (!env->newsgroups && curenv &&
694 m_strcasecmp(curenv->followup_to, "poster"))
695 env->newsgroups = m_strdup(curenv->followup_to);
701 if (mutt_fetch_recips (env, curenv, flags) == -1)
705 for (i = 0; i < idxlen; i++) {
706 if (idx[i]->content->tagged
707 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
713 if ((flags & SENDLISTREPLY) && !env->to) {
714 mutt_error _("No mailing lists found!");
719 mutt_fix_reply_recipients (env);
721 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
724 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
726 string_list_t **p = NULL, **q = NULL;
728 for (i = 0; i < idxlen; i++) {
729 if (idx[i]->content->tagged)
730 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
739 /* This is _very_ similar to send.c's include_reply(). */
741 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
742 int flags __attribute__ ((unused)))
744 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
745 int chflags = CH_DECODE;
747 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
749 mutt_make_attribution (Context, cur, tmpfp);
751 if (!option (OPTHEADER))
752 cmflags |= M_CM_NOHEADER;
753 if (option (OPTWEED)) {
755 cmflags |= M_CM_WEED;
758 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
759 mutt_make_post_indent (Context, cur, tmpfp);
762 void mutt_attach_reply (FILE * fp, HEADER * hdr,
763 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
765 short mime_reply_any = 0;
768 HEADER *parent = NULL;
769 HEADER *tmphdr = NULL;
773 char tmpbody[_POSIX_PATH_MAX];
776 char prefix[SHORT_STRING];
780 if (flags & SENDNEWS)
781 set_option (OPTNEWSSEND);
783 unset_option (OPTNEWSSEND);
786 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
787 nattach = count_tagged (idx, idxlen);
788 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
792 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
793 if ((rc = query_quadoption (OPT_MIMEFWDREST,
795 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
798 else if (rc == M_YES)
801 else if (nattach == 1)
804 tmphdr = header_new();
805 tmphdr->env = envelope_new();
807 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
808 parent ? parent : (cur ? cur->
811 header_delete(&tmphdr);
815 mutt_mktemp (tmpbody);
816 if ((tmpfp = safe_fopen (tmpbody, "w")) == NULL) {
817 mutt_error (_("Can't create %s."), tmpbody);
818 header_delete(&tmphdr);
824 attach_include_reply (fp, tmpfp, cur->hdr, flags);
826 for (i = 0; i < idxlen; i++) {
827 if (idx[i]->content->tagged)
828 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
833 mutt_make_attribution (Context, parent, tmpfp);
839 if (!option (OPTTEXTFLOWED))
840 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
843 m_strcpy(prefix, sizeof(prefix), ">");
846 st.flags = M_CHARCONV;
848 if (option (OPTWEED))
851 if (option (OPTHEADER))
852 include_header (1, fp, parent, tmpfp, prefix);
855 if (mutt_can_decode (cur)) {
856 mutt_body_handler (cur, &st);
857 state_putc ('\n', &st);
860 mutt_copy_body (fp, &tmphdr->content, cur);
863 for (i = 0; i < idxlen; i++) {
864 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
865 mutt_body_handler (idx[i]->content, &st);
866 state_putc ('\n', &st);
871 mutt_make_post_indent (Context, parent, tmpfp);
873 if (mime_reply_any && !cur &&
874 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
876 header_delete(&tmphdr);
884 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
885 mutt_set_flag (Context, hdr, M_REPLIED, 1);