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/curses.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
33 static short check_msg (BODY * b, short err)
35 if (!mutt_is_message_type (b->type, b->subtype)) {
37 mutt_error _("You may only bounce message/rfc822 parts.");
44 static short check_all_msg (ATTACHPTR ** idx, short idxlen,
45 BODY * cur, short err)
49 if (cur && check_msg (cur, err) == -1)
52 for (i = 0; i < idxlen; i++) {
53 if (idx[i]->content->tagged) {
54 if (check_msg (idx[i]->content, err) == -1)
63 /* can we decode all tagged attachments? */
65 static short check_can_decode (ATTACHPTR ** idx, short idxlen, BODY * cur)
70 return mutt_can_decode (cur);
72 for (i = 0; i < idxlen; i++)
73 if (idx[i]->content->tagged && !mutt_can_decode (idx[i]->content))
79 static short count_tagged (ATTACHPTR ** idx, short idxlen)
84 for (i = 0; i < idxlen; i++)
85 if (idx[i]->content->tagged)
91 /* count the number of tagged children below a multipart or message
95 static short count_tagged_children (ATTACHPTR ** idx, short idxlen, short i)
97 short level = idx[i]->level;
100 while ((++i < idxlen) && (level < idx[i]->level))
101 if (idx[i]->content->tagged)
110 ** The bounce function, from the attachment menu
113 void mutt_attach_bounce (FILE * fp, HEADER * hdr __attribute__ ((unused)),
114 ATTACHPTR ** idx, short idxlen, BODY * cur)
118 char buf[HUGE_STRING];
120 address_t *adr = NULL;
124 if (check_all_msg (idx, idxlen, cur, 1) == -1)
127 /* one or more messages? */
128 p = (cur || count_tagged (idx, idxlen) == 1);
131 m_strcpy(prompt, sizeof(prompt), _("Bounce message to: "));
133 m_strcpy(prompt, sizeof(prompt), _("Bounce tagged messages to: "));
136 if (mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS)
140 if (!(adr = rfc822_parse_adrlist (adr, buf))) {
141 mutt_error _("Error parsing address!");
146 adr = mutt_expand_aliases (adr);
148 if (mutt_addrlist_to_idna (adr, &err) < 0) {
149 mutt_error (_("Bad IDN: '%s'"), err);
151 address_list_wipe(&adr);
156 rfc822_write_address (buf, sizeof (buf), adr, 1);
158 #define extra_space (15+7+2)
162 snprintf (prompt, sizeof (prompt) - 4,
163 (p ? _("Bounce message to %s") : _("Bounce messages to %s")),
166 if (mutt_strwidth (prompt) > COLS - extra_space) {
167 mutt_format_string (prompt, sizeof (prompt) - 4,
168 0, COLS - extra_space, 0, 0,
169 prompt, sizeof (prompt), 0);
170 m_strcat(prompt, sizeof(prompt), "...?");
172 m_strcat(prompt, sizeof(prompt), "?");
175 if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
176 address_list_wipe(&adr);
177 CLEARLINE (LINES - 1);
178 mutt_message (p ? _("Message not bounced.") : _("Messages not bounced."));
182 CLEARLINE (LINES - 1);
185 ret = mutt_bounce_message (fp, cur->hdr, adr);
187 for (i = 0; i < idxlen; i++) {
188 if (idx[i]->content->tagged)
189 if (mutt_bounce_message (fp, idx[i]->content->hdr, adr))
195 mutt_message (p ? _("Message bounced.") : _("Messages bounced."));
197 mutt_error (p ? _("Error bouncing message!") :
198 _("Error bouncing messages!"));
204 ** resend-message, from the attachment menu
207 void mutt_attach_resend (FILE * fp, HEADER * hdr __attribute__ ((unused)), ATTACHPTR ** idx,
208 short idxlen, BODY * cur)
212 if (check_all_msg (idx, idxlen, cur, 1) == -1)
216 mutt_resend_message (fp, Context, cur->hdr);
218 for (i = 0; i < idxlen; i++)
219 if (idx[i]->content->tagged)
220 mutt_resend_message (fp, Context, idx[i]->content->hdr);
226 ** forward-message, from the attachment menu
229 /* try to find a common parent message for the tagged attachments. */
231 static HEADER *find_common_parent (ATTACHPTR ** idx, short idxlen,
237 for (i = 0; i < idxlen; i++)
238 if (idx[i]->content->tagged)
242 if (mutt_is_message_type
243 (idx[i]->content->type, idx[i]->content->subtype)) {
244 nchildren = count_tagged_children (idx, idxlen, i);
245 if (nchildren == nattach)
246 return idx[i]->content->hdr;
254 * check whether attachment #i is a parent of the attachment
257 * Note: This and the calling procedure could be optimized quite a
258 * bit. For now, it's not worth the effort.
261 static int is_parent (short i, ATTACHPTR ** idx, short idxlen, BODY * cur)
263 short level = idx[i]->level;
265 while ((++i < idxlen) && idx[i]->level > level) {
266 if (idx[i]->content == cur)
273 static HEADER *find_parent (ATTACHPTR ** idx, short idxlen, BODY * cur,
277 HEADER *parent = NULL;
280 for (i = 0; i < idxlen; i++) {
281 if (mutt_is_message_type
282 (idx[i]->content->type, idx[i]->content->subtype)
283 && is_parent (i, idx, idxlen, cur))
284 parent = idx[i]->content->hdr;
285 if (idx[i]->content == cur)
290 parent = find_common_parent (idx, idxlen, nattach);
295 static void include_header (int quote, FILE * ifp,
296 HEADER * hdr, FILE * ofp, char *_prefix)
298 int chflags = CH_DECODE;
299 char prefix[SHORT_STRING];
301 if (option (OPTWEED))
302 chflags |= CH_WEED | CH_REORDER;
306 m_strcpy(prefix, sizeof(prefix), _prefix);
307 else if (!option (OPTTEXTFLOWED))
308 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
311 m_strcpy(prefix, sizeof(prefix), ">");
313 chflags |= CH_PREFIX;
316 mutt_copy_header (ifp, hdr, ofp, chflags, quote ? prefix : NULL);
319 /* create a send-mode duplicate from a receive-mode body */
320 static int mutt_copy_body (FILE * fp, BODY ** tgt, BODY * src)
322 char tmp[_POSIX_PATH_MAX];
325 parameter_t *par, **ppar;
331 m_strcpy(tmp, sizeof(tmp), src->filename);
338 mutt_adv_mktemp (NULL, tmp, sizeof (tmp));
339 if (mutt_save_attachment (fp, src, tmp, 0, NULL) == -1)
345 memcpy (b, src, sizeof (BODY));
349 b->filename = m_strdup(tmp);
350 b->use_disp = use_disp;
353 if (mutt_is_text_part (b))
356 b->xtype = m_strdup(b->xtype);
357 b->subtype = m_strdup(b->subtype);
358 b->form_name = m_strdup(b->form_name);
359 b->filename = m_strdup(b->filename);
360 b->d_filename = m_strdup(b->d_filename);
361 b->description = m_strdup(b->description);
364 * we don't seem to need the HEADER structure currently.
365 * XXX - this may change in the future
371 /* copy parameters */
372 for (par = b->parameter, ppar = &b->parameter; par;
373 ppar = &(*ppar)->next, par = par->next) {
374 *ppar = parameter_new();
375 (*ppar)->attribute = m_strdup(par->attribute);
376 (*ppar)->value = m_strdup(par->value);
379 mutt_stamp_attachment (b);
384 /* Attach all the body parts which can't be decoded.
385 * This code is shared by forwarding and replying. */
387 static BODY **copy_problematic_attachments (FILE * fp,
390 short idxlen, short force)
394 for (i = 0; i < idxlen; i++) {
395 if (idx[i]->content->tagged &&
396 (force || !mutt_can_decode (idx[i]->content))) {
397 if (mutt_copy_body (fp, last, idx[i]->content) == -1)
398 return NULL; /* XXXXX - may lead to crashes */
399 last = &((*last)->next);
406 * forward one or several MIME bodies
407 * (non-message types)
410 static void attach_forward_bodies (FILE * fp, HEADER * hdr,
411 ATTACHPTR ** idx, short idxlen,
412 BODY * cur, short nattach, int flags)
415 short mime_fwd_all = 0;
416 short mime_fwd_any = 1;
417 HEADER *parent = NULL;
418 HEADER *tmphdr = NULL;
420 char tmpbody[_POSIX_PATH_MAX];
430 * First, find the parent message.
431 * Note: This could be made an option by just
432 * putting the following lines into an if block.
436 parent = find_parent (idx, idxlen, cur, nattach);
442 tmphdr = header_new();
443 tmphdr->env = envelope_new();
444 mutt_make_forward_subject (tmphdr->env, Context, parent);
446 tmpfp = m_tempfile(tmpbody, sizeof(tmpbody), NONULL(Tempdir), NULL);
448 mutt_error(_("Could not create temporary file"));
452 mutt_forward_intro (tmpfp, parent);
454 /* prepare the prefix here since we'll need it later. */
456 if (option (OPTFORWQUOTE)) {
457 if (!option (OPTTEXTFLOWED))
458 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
461 m_strcpy(prefix, sizeof(prefix), ">");
464 include_header (option (OPTFORWQUOTE), fp, parent, tmpfp, prefix);
468 * Now, we have prepared the first part of the message body: The
469 * original message's header.
471 * The next part is more interesting: either include the message bodies,
475 if ((!cur || mutt_can_decode (cur)) &&
476 (rc = query_quadoption (OPT_MIMEFWD,
477 _("Forward as attachments?"))) == M_YES)
483 * shortcut MIMEFWDREST when there is only one attachment. Is
487 if (!mime_fwd_all && !cur && (nattach > 1)
488 && !check_can_decode (idx, idxlen, cur)) {
489 if ((rc = query_quadoption (OPT_MIMEFWDREST,
491 ("Can't decode all tagged attachments. MIME-forward the others?")))
498 /* initialize a state structure */
502 if (option (OPTFORWQUOTE))
504 st.flags = M_CHARCONV;
505 if (option (OPTWEED))
510 /* where do we append new MIME parts? */
511 last = &tmphdr->content;
514 /* single body case */
516 if (!mime_fwd_all && mutt_can_decode (cur)) {
517 mutt_body_handler (cur, &st);
518 state_putc ('\n', &st);
521 if (mutt_copy_body (fp, last, cur) == -1)
523 last = &((*last)->next);
527 /* multiple body case */
530 for (i = 0; i < idxlen; i++) {
531 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
532 mutt_body_handler (idx[i]->content, &st);
533 state_putc ('\n', &st);
540 copy_problematic_attachments (fp, last, idx, idxlen,
541 mime_fwd_all)) == NULL)
545 mutt_forward_trailer (tmpfp);
548 /* now that we have the template, send it. */
549 ci_send_message (flags, tmphdr, tmpbody, NULL, parent);
554 mutt_unlink(tmpbody);
555 header_delete(&tmphdr);
560 * Forward one or several message-type attachments. This
561 * is different from the previous function
562 * since we want to mimic the index menu's behaviour.
564 * Code reuse from ci_send_message is not possible here -
565 * ci_send_message relies on a context structure to find messages,
566 * while, on the attachment menu, messages are referenced through
567 * the attachment index.
570 static void attach_forward_msgs (FILE * fp, HEADER * hdr __attribute__ ((unused)),
571 ATTACHPTR ** idx, short idxlen, BODY * cur,
574 HEADER *curhdr = NULL;
580 char tmpbody[_POSIX_PATH_MAX];
584 int chflags = CH_XMIT;
589 for (i = 0; i < idxlen; i++)
590 if (idx[i]->content->tagged) {
591 curhdr = idx[i]->content->hdr;
596 tmphdr = header_new();
597 tmphdr->env = envelope_new();
598 mutt_make_forward_subject (tmphdr->env, Context, curhdr);
603 if ((rc = query_quadoption (OPT_MIMEFWD,
604 _("Forward MIME encapsulated?"))) == M_NO) {
606 /* no MIME encapsulation */
608 tmpfp = m_tempfile(tmpbody, sizeof(tmpbody), NONULL(Tempdir), NULL);
610 mutt_error(_("Could not create temporary file"));
611 header_delete(&tmphdr);
615 if (option (OPTFORWQUOTE)) {
616 chflags |= CH_PREFIX;
617 cmflags |= M_CM_PREFIX;
620 if (option (OPTFORWDECODE)) {
621 cmflags |= M_CM_DECODE | M_CM_CHARCONV;
622 if (option (OPTWEED)) {
623 chflags |= CH_WEED | CH_REORDER;
624 cmflags |= M_CM_WEED;
630 /* mutt_message_hook (cur->hdr, M_MESSAGEHOOK); */
631 mutt_forward_intro (tmpfp, cur->hdr);
632 _mutt_copy_message (tmpfp, fp, cur->hdr, cur->hdr->content, cmflags,
634 mutt_forward_trailer (tmpfp);
637 for (i = 0; i < idxlen; i++) {
638 if (idx[i]->content->tagged) {
639 /* mutt_message_hook (idx[i]->content->hdr, M_MESSAGEHOOK); */
640 mutt_forward_intro (tmpfp, idx[i]->content->hdr);
641 _mutt_copy_message (tmpfp, fp, idx[i]->content->hdr,
642 idx[i]->content->hdr->content, cmflags,
644 mutt_forward_trailer (tmpfp);
650 else if (rc == M_YES) { /* do MIME encapsulation - we don't need to do much here */
651 last = &tmphdr->content;
653 mutt_copy_body (fp, last, cur);
655 for (i = 0; i < idxlen; i++)
656 if (idx[i]->content->tagged) {
657 mutt_copy_body (fp, last, idx[i]->content);
658 last = &((*last)->next);
663 header_delete(&tmphdr);
665 ci_send_message (flags, tmphdr, *tmpbody ? tmpbody : NULL, NULL, curhdr);
669 void mutt_attach_forward (FILE * fp, HEADER * hdr,
670 ATTACHPTR ** idx, short idxlen, BODY * cur,
676 if (check_all_msg (idx, idxlen, cur, 0) == 0)
677 attach_forward_msgs (fp, hdr, idx, idxlen, cur, flags);
679 nattach = count_tagged (idx, idxlen);
680 attach_forward_bodies (fp, hdr, idx, idxlen, cur, nattach, flags);
687 ** the various reply functions, from the attachment menu
690 /* Create the envelope defaults for a reply.
692 * This function can be invoked in two ways.
694 * Either, parent is NULL. In this case, all tagged bodies are of a message type,
695 * and the header information is fetched from them.
697 * Or, parent is non-NULL. In this case, cur is the common parent of all the
698 * tagged attachments.
700 * Note that this code is horribly similar to envelope_defaults () from send.c.
704 attach_reply_envelope_defaults (ENVELOPE * env, ATTACHPTR ** idx,
705 short idxlen, HEADER * parent, int flags)
707 ENVELOPE *curenv = NULL;
708 HEADER *curhdr = NULL;
712 for (i = 0; i < idxlen; i++) {
713 if (idx[i]->content->tagged) {
714 curhdr = idx[i]->content->hdr;
715 curenv = curhdr->env;
721 curenv = parent->env;
725 if (curenv == NULL || curhdr == NULL) {
726 mutt_error _("Can't find any tagged messages.");
732 if ((flags & SENDNEWS)) {
733 /* in case followup set Newsgroups: with Followup-To: if it present */
734 if (!env->newsgroups && curenv &&
735 m_strcasecmp(curenv->followup_to, "poster"))
736 env->newsgroups = m_strdup(curenv->followup_to);
742 if (mutt_fetch_recips (env, curenv, flags) == -1)
746 for (i = 0; i < idxlen; i++) {
747 if (idx[i]->content->tagged
748 && mutt_fetch_recips (env, idx[i]->content->hdr->env,
754 if ((flags & SENDLISTREPLY) && !env->to) {
755 mutt_error _("No mailing lists found!");
760 mutt_fix_reply_recipients (env);
762 mutt_make_misc_reply_headers (env, Context, curhdr, curenv);
765 mutt_add_to_reference_headers (env, curenv, NULL, NULL);
767 string_list_t **p = NULL, **q = NULL;
769 for (i = 0; i < idxlen; i++) {
770 if (idx[i]->content->tagged)
771 mutt_add_to_reference_headers (env, idx[i]->content->hdr->env, &p,
780 /* This is _very_ similar to send.c's include_reply(). */
782 static void attach_include_reply (FILE * fp, FILE * tmpfp, HEADER * cur,
783 int flags __attribute__ ((unused)))
785 int cmflags = M_CM_PREFIX | M_CM_DECODE | M_CM_CHARCONV;
786 int chflags = CH_DECODE;
788 /* mutt_message_hook (cur, M_MESSAGEHOOK); */
790 mutt_make_attribution (Context, cur, tmpfp);
792 if (!option (OPTHEADER))
793 cmflags |= M_CM_NOHEADER;
794 if (option (OPTWEED)) {
796 cmflags |= M_CM_WEED;
799 _mutt_copy_message (tmpfp, fp, cur, cur->content, cmflags, chflags);
800 mutt_make_post_indent (Context, cur, tmpfp);
803 void mutt_attach_reply (FILE * fp, HEADER * hdr,
804 ATTACHPTR ** idx, short idxlen, BODY * cur, int flags)
806 short mime_reply_any = 0;
809 HEADER *parent = NULL;
810 HEADER *tmphdr = NULL;
814 char tmpbody[_POSIX_PATH_MAX];
817 char prefix[SHORT_STRING];
821 if (flags & SENDNEWS)
822 set_option (OPTNEWSSEND);
824 unset_option (OPTNEWSSEND);
827 if (check_all_msg (idx, idxlen, cur, 0) == -1) {
828 nattach = count_tagged (idx, idxlen);
829 if ((parent = find_parent (idx, idxlen, cur, nattach)) == NULL)
833 if (nattach > 1 && !check_can_decode (idx, idxlen, cur)) {
834 if ((rc = query_quadoption (OPT_MIMEFWDREST,
836 ("Can't decode all tagged attachments. MIME-encapsulate the others?")))
839 else if (rc == M_YES)
842 else if (nattach == 1)
845 tmphdr = header_new();
846 tmphdr->env = envelope_new();
848 if (attach_reply_envelope_defaults (tmphdr->env, idx, idxlen,
849 parent ? parent : (cur ? cur->
852 header_delete(&tmphdr);
856 tmpfp = m_tempfile(tmpbody, sizeof(tmpbody), NONULL(Tempdir), NULL);
858 mutt_error(_("Could not create temporary file"));
859 header_delete(&tmphdr);
865 attach_include_reply (fp, tmpfp, cur->hdr, flags);
867 for (i = 0; i < idxlen; i++) {
868 if (idx[i]->content->tagged)
869 attach_include_reply (fp, tmpfp, idx[i]->content->hdr, flags);
874 mutt_make_attribution (Context, parent, tmpfp);
880 if (!option (OPTTEXTFLOWED))
881 _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix),
884 m_strcpy(prefix, sizeof(prefix), ">");
887 st.flags = M_CHARCONV;
889 if (option (OPTWEED))
892 if (option (OPTHEADER))
893 include_header (1, fp, parent, tmpfp, prefix);
896 if (mutt_can_decode (cur)) {
897 mutt_body_handler (cur, &st);
898 state_putc ('\n', &st);
901 mutt_copy_body (fp, &tmphdr->content, cur);
904 for (i = 0; i < idxlen; i++) {
905 if (idx[i]->content->tagged && mutt_can_decode (idx[i]->content)) {
906 mutt_body_handler (idx[i]->content, &st);
907 state_putc ('\n', &st);
912 mutt_make_post_indent (Context, parent, tmpfp);
914 if (mime_reply_any && !cur &&
915 copy_problematic_attachments (fp, &tmphdr->content, idx, idxlen,
917 header_delete(&tmphdr);
925 if (ci_send_message (flags, tmphdr, tmpbody, NULL, parent) == 0)
926 mutt_set_flag (Context, hdr, M_REPLIED, 1);