Add *{smime,ssl}-ca-flags*..
[s-mailx.git] / cmd_resend.c
blob99239e9d38c98b2a8b5e1c55e1af9ee8a8f194e8
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ All sorts of `reply', `resend', `forward', and similar user commands.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE cmd_resend
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 /* Modify subject we reply to to begin with Re: if it does not already */
43 static char *a_crese_reedit(char const *subj);
45 static void make_ref_and_cs(struct message *mp, struct header *head);
47 /* `reply' and `Lreply' workhorse */
48 static int _list_reply(int *msgvec, enum header_flags hf);
50 /* Get PTF to implementation of command c (i.e., take care for *flipr*) */
51 static int (* _reply_or_Reply(char c))(int *, bool_t);
53 /* Reply to a single message. Extract each name from the message header and
54 * send them off to mail1() */
55 static int _reply(int *msgvec, bool_t recipient_record);
57 /* Reply to a series of messages by simply mailing to the senders and not
58 * messing around with the To: and Cc: lists as in normal reply */
59 static int _Reply(int *msgvec, bool_t recipient_record);
61 /* Forward a message to a new recipient, in the sense of RFC 2822 */
62 static int _fwd(char *str, int recipient_record);
64 /* Modify the subject we are replying to to begin with Fwd: */
65 static char * __fwdedit(char *subj);
67 /* Do the real work of resending */
68 static int _resend1(void *v, bool_t add_resent);
70 static char *
71 a_crese_reedit(char const *subj){
72 char *newsubj;
73 NYD_ENTER;
75 newsubj = NULL;
77 if(subj != NULL && *subj != '\0'){
78 struct str in, out;
79 size_t i;
80 char const *cp;
82 in.l = strlen(in.s = n_UNCONST(subj));
83 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
85 i = strlen(cp = subject_re_trim(out.s)) +1;
86 /* RFC mandates english "Re: " */
87 newsubj = salloc(sizeof("Re: ") -1 + i);
88 memcpy(newsubj, "Re: ", sizeof("Re: ") -1);
89 memcpy(&newsubj[sizeof("Re: ") -1], cp, i);
91 free(out.s);
93 NYD_LEAVE;
94 return newsubj;
97 static void
98 make_ref_and_cs(struct message *mp, struct header *head) /* TODO rewrite FAST */
100 char *oldref, *oldmsgid, *newref, *cp;
101 size_t oldreflen = 0, oldmsgidlen = 0, reflen;
102 unsigned i;
103 struct name *n;
104 NYD_ENTER;
106 oldref = hfield1("references", mp);
107 oldmsgid = hfield1("message-id", mp);
108 if (oldmsgid == NULL || *oldmsgid == '\0') {
109 head->h_ref = NULL;
110 goto jleave;
113 reflen = 1;
114 if (oldref) {
115 oldreflen = strlen(oldref);
116 reflen += oldreflen + 2;
118 if (oldmsgid) {
119 oldmsgidlen = strlen(oldmsgid);
120 reflen += oldmsgidlen;
123 newref = smalloc(reflen);
124 if (oldref != NULL) {
125 memcpy(newref, oldref, oldreflen +1);
126 if (oldmsgid != NULL) {
127 newref[oldreflen++] = ',';
128 newref[oldreflen++] = ' ';
129 memcpy(newref + oldreflen, oldmsgid, oldmsgidlen +1);
131 } else if (oldmsgid)
132 memcpy(newref, oldmsgid, oldmsgidlen +1);
133 n = extract(newref, GREF);
134 free(newref);
136 /* Limit number of references TODO better on parser side */
137 while (n->n_flink != NULL)
138 n = n->n_flink;
139 for (i = 1; i <= REFERENCES_MAX; ++i) {
140 if (n->n_blink != NULL)
141 n = n->n_blink;
142 else
143 break;
145 n->n_blink = NULL;
146 head->h_ref = n;
147 if (ok_blook(reply_in_same_charset) &&
148 (cp = hfield1("content-type", mp)) != NULL){
149 if((head->h_charset = cp = mime_param_get("charset", cp)) != NULL){
150 char *cpo, c;
152 for(cpo = cp; (c = *cpo) != '\0'; ++cpo)
153 *cpo = lowerconv(c);
156 jleave:
157 NYD_LEAVE;
160 static int
161 _list_reply(int *msgvec, enum header_flags hf)
163 struct header head;
164 struct message *mp;
165 char const *reply_to, *rcv, *cp;
166 enum gfield gf;
167 struct name *rt, *mft, *np;
168 int *save_msgvec;
169 NYD_ENTER;
171 /* TODO Since we may recur and do stuff with message lists we need to save
172 * TODO away the argument vector as long as that isn't done by machinery */
174 size_t i;
175 for (i = 0; msgvec[i] != 0; ++i)
177 ++i;
178 save_msgvec = ac_alloc(sizeof(*save_msgvec) * i);
179 while (i-- > 0)
180 save_msgvec[i] = msgvec[i];
181 msgvec = save_msgvec;
184 jnext_msg:
185 mp = message + *msgvec - 1;
186 touch(mp);
187 setdot(mp);
189 memset(&head, 0, sizeof head);
190 head.h_flags = hf;
192 head.h_subject = a_crese_reedit(hfield1("subject", mp));
193 gf = ok_blook(fullnames) ? GFULL : GSKIN;
194 rt = mft = NULL;
196 rcv = NULL;
197 if ((reply_to = hfield1("reply-to", mp)) != NULL &&
198 (cp = ok_vlook(reply_to_honour)) != NULL &&
199 (rt = checkaddrs(lextract(reply_to, GTO | gf), EACM_STRICT, NULL)
200 ) != NULL) {
201 char const *tr = _("Reply-To %s%s");
202 size_t l = strlen(tr) + strlen(rt->n_name) + 3 +1;
203 char *sp = salloc(l);
205 snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..." : n_empty));
206 if (quadify(cp, UIZ_MAX, sp, TRU1) > FAL0)
207 rcv = reply_to;
210 if (rcv == NULL && (rcv = hfield1("from", mp)) == NULL)
211 rcv = nameof(mp, 1);
213 /* Cc: */
214 np = NULL;
215 if (ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
216 np = lextract(cp, GCC | gf);
217 if ((cp = hfield1("cc", mp)) != NULL)
218 np = cat(np, lextract(cp, GCC | gf));
219 if (np != NULL)
220 head.h_cc = delete_alternates(np);
222 /* To: */
223 np = NULL;
224 if (rcv != NULL)
225 np = (rcv == reply_to) ? namelist_dup(rt, GTO | gf)
226 : lextract(rcv, GTO | gf);
227 if (!ok_blook(recipients_in_cc) && (cp = hfield1("to", mp)) != NULL)
228 np = cat(np, lextract(cp, GTO | gf));
229 /* Delete my name from reply list, and with it, all my alternate names */
230 np = delete_alternates(np);
231 if (count(np) == 0)
232 np = lextract(rcv, GTO | gf);
233 head.h_to = np;
235 /* The user may have send this to himself, don't ignore that */
236 namelist_vaporise_head(&head, EACM_NORMAL, FAL0, NULL);
237 if (head.h_to == NULL)
238 head.h_to = np;
240 /* Mail-Followup-To: */
241 mft = NULL;
242 if (ok_vlook(followup_to_honour) != NULL &&
243 (cp = hfield1("mail-followup-to", mp)) != NULL &&
244 (mft = np = checkaddrs(lextract(cp, GTO | gf), EACM_STRICT, NULL)
245 ) != NULL) {
246 char const *tr = _("Followup-To %s%s");
247 size_t l = strlen(tr) + strlen(np->n_name) + 3 +1;
248 char *sp = salloc(l);
250 snprintf(sp, l, tr, np->n_name, (np->n_flink != NULL ? "..." : n_empty));
251 if (quadify(ok_vlook(followup_to_honour), UIZ_MAX, sp, TRU1) > FAL0) {
252 head.h_cc = NULL;
253 head.h_to = np;
254 head.h_mft =
255 mft = namelist_vaporise_head(&head, EACM_STRICT, FAL0, NULL);
256 } else
257 mft = NULL;
260 /* Special massage for list (follow-up) messages */
261 if (mft != NULL || (hf & HF_LIST_REPLY) || ok_blook(followup_to)) {
262 /* Learn about a possibly sending mailing list; use do for break; */
263 if ((cp = hfield1("list-post", mp)) != NULL) do {
264 struct name *x;
266 if ((x = lextract(cp, GEXTRA | GSKIN)) == NULL || x->n_flink != NULL ||
267 (cp = url_mailto_to_address(x->n_name)) == NULL ||
268 /* XXX terribly wasteful to create a new name, and can't we find
269 * XXX a way to mitigate that?? */
270 is_addr_invalid(x = nalloc(cp, GEXTRA | GSKIN), EACM_STRICT)) {
271 if(n_poption & n_PO_D_V)
272 n_err(_("Message contains invalid List-Post: header\n"));
273 cp = NULL;
274 break;
276 cp = x->n_name;
278 /* A special case has been seen on e.g. ietf-announce@ietf.org:
279 * these usually post to multiple groups, with ietf-announce@
280 * in List-Post:, but with Reply-To: set to ietf@ietf.org (since
281 * -announce@ is only used for announcements, say).
282 * So our desire is to honour this request and actively overwrite
283 * List-Post: for our purpose; but only if its a single address.
284 * However, to avoid ambiguities with users that place themselve in
285 * Reply-To: and mailing lists which don't overwrite this (or only
286 * extend this, shall such exist), only do so if reply_to exists of
287 * a single address which points to the same domain as List-Post: */
288 if (reply_to != NULL && rt->n_flink == NULL &&
289 name_is_same_domain(x, rt))
290 cp = rt->n_name; /* rt is EACM_STRICT tested */
292 /* "Automatically `mlist'" the List-Post: address temporarily */
293 if (is_mlist(cp, FAL0) == MLIST_OTHER)
294 head.h_list_post = cp;
295 else
296 cp = NULL;
297 } while (0);
299 /* In case of list replies we actively sort out any non-list recipient,
300 * but _only_ if we did not honour a MFT:, assuming that members of MFT
301 * were there for a reason; cp is still List-Post:/eqivalent */
302 if ((hf & HF_LIST_REPLY) && mft == NULL) {
303 struct name *nhp = head.h_to;
304 head.h_to = NULL;
305 j_lt_redo:
306 while (nhp != NULL) {
307 np = nhp;
308 nhp = nhp->n_flink;
310 if ((cp != NULL && !asccasecmp(cp, np->n_name)) ||
311 is_mlist(np->n_name, FAL0) != MLIST_OTHER) {
312 np->n_type = (np->n_type & ~GMASK) | GTO;
313 np->n_flink = head.h_to;
314 head.h_to = np;
317 if ((nhp = head.h_cc) != NULL) {
318 head.h_cc = NULL;
319 goto j_lt_redo;
324 make_ref_and_cs(mp, &head);
326 if (ok_blook(quote_as_attachment)) {
327 head.h_attach = csalloc(1, sizeof *head.h_attach);
328 head.h_attach->a_msgno = *msgvec;
329 head.h_attach->a_content_description = _("Original message content");
332 if (mail1(&head, 1, mp, NULL, !!(hf & HF_RECIPIENT_RECORD), 0) == OKAY &&
333 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
334 mp->m_flag |= MANSWER | MANSWERED;
336 if (*++msgvec != 0) {
337 /* TODO message (error) ring.., less sleep */
338 fprintf(n_stdout,
339 _("Waiting a second before proceeding to the next message..\n"));
340 fflush(n_stdout);
341 n_msleep(1000, FAL0);
342 goto jnext_msg;
345 ac_free(save_msgvec);
346 NYD_LEAVE;
347 return 0;
350 static int
351 (*_reply_or_Reply(char c))(int *, bool_t)
353 int (*rv)(int*, bool_t);
354 NYD_ENTER;
356 rv = (ok_blook(flipr) ^ (c == 'R')) ? &_Reply : &_reply;
357 NYD_LEAVE;
358 return rv;
361 static int
362 _reply(int *msgvec, bool_t recipient_record)
364 int rv;
365 NYD_ENTER;
367 rv = _list_reply(msgvec, recipient_record ? HF_RECIPIENT_RECORD : HF_NONE);
368 NYD_LEAVE;
369 return rv;
372 static int
373 _Reply(int *msgvec, bool_t recipient_record)
375 struct header head;
376 struct message *mp;
377 int *ap;
378 char *cp;
379 enum gfield gf;
380 NYD_ENTER;
382 memset(&head, 0, sizeof head);
383 gf = ok_blook(fullnames) ? GFULL : GSKIN;
385 for (ap = msgvec; *ap != 0; ++ap) {
386 char const *rp;
387 struct name *rt;
389 mp = message + *ap - 1;
390 touch(mp);
391 setdot(mp);
393 if ((rp = hfield1("reply-to", mp)) != NULL &&
394 (cp = ok_vlook(reply_to_honour)) != NULL &&
395 (rt = checkaddrs(lextract(rp, GTO | gf), EACM_STRICT, NULL)
396 ) != NULL) {
397 char const *tr = _("Reply-To %s%s");
398 size_t l = strlen(tr) + strlen(rt->n_name) + 3 +1;
399 char *sp = salloc(l);
401 snprintf(sp, l, tr, rt->n_name, (rt->n_flink != NULL ? "..."
402 : n_empty));
403 if (quadify(cp, UIZ_MAX, sp, TRU1) > FAL0) {
404 head.h_to = cat(head.h_to, rt);
405 continue;
409 if ((cp = hfield1("from", mp)) == NULL)
410 cp = nameof(mp, 2);
411 head.h_to = cat(head.h_to, lextract(cp, GTO | gf));
413 if (head.h_to == NULL)
414 goto jleave;
416 mp = message + msgvec[0] - 1;
417 head.h_subject = hfield1("subject", mp);
418 head.h_subject = a_crese_reedit(head.h_subject);
419 make_ref_and_cs(mp, &head);
421 if (ok_blook(quote_as_attachment)) {
422 head.h_attach = csalloc(1, sizeof *head.h_attach);
423 head.h_attach->a_msgno = *msgvec;
424 head.h_attach->a_content_description = _("Original message content");
427 if (mail1(&head, 1, mp, NULL, recipient_record, 0) == OKAY &&
428 ok_blook(markanswered) && !(mp->m_flag & MANSWERED))
429 mp->m_flag |= MANSWER | MANSWERED;
430 jleave:
431 NYD_LEAVE;
432 return 0;
435 static int
436 _fwd(char *str, int recipient_record)
438 struct header head;
439 int *msgvec, rv = 1;
440 char *recipient;
441 struct message *mp;
442 bool_t f, forward_as_attachment;
443 NYD_ENTER;
445 if ((recipient = laststring(str, &f, TRU1)) == NULL) {
446 fputs(_("No recipient specified.\n"), n_stdout);
447 goto jleave;
450 forward_as_attachment = ok_blook(forward_as_attachment);
451 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
453 if (!f) {
454 *msgvec = first(0, MMNORM);
455 if (*msgvec == 0) {
456 if (n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT)) {
457 rv = 0;
458 goto jleave;
460 fprintf(n_stdout, _("No messages to forward.\n"));
461 goto jleave;
463 msgvec[1] = 0;
464 } else if (getmsglist(str, msgvec, 0) < 0)
465 goto jleave;
467 if (*msgvec == 0) {
468 if (n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT)) {
469 rv = 0;
470 goto jleave;
472 fprintf(n_stdout, _("No applicable messages.\n"));
473 goto jleave;
475 if (msgvec[1] != 0) {
476 fprintf(n_stdout, _("Cannot forward multiple messages at once\n"));
477 goto jleave;
480 memset(&head, 0, sizeof head);
481 if ((head.h_to = lextract(recipient,
482 (GTO | (ok_blook(fullnames) ? GFULL : GSKIN)))) == NULL)
483 goto jleave;
485 mp = message + *msgvec - 1;
487 if (forward_as_attachment) {
488 head.h_attach = csalloc(1, sizeof *head.h_attach);
489 head.h_attach->a_msgno = *msgvec;
490 head.h_attach->a_content_description = _("Forwarded message");
491 } else {
492 touch(mp);
493 setdot(mp);
495 head.h_subject = hfield1("subject", mp);
496 head.h_subject = __fwdedit(head.h_subject);
497 mail1(&head, 1, (forward_as_attachment ? NULL : mp), NULL, recipient_record,
499 rv = 0;
500 jleave:
501 NYD_LEAVE;
502 return rv;
505 static char *
506 __fwdedit(char *subj)
508 struct str in, out;
509 char *newsubj = NULL;
510 NYD_ENTER;
512 if (subj == NULL || *subj == '\0')
513 goto jleave;
515 in.s = subj;
516 in.l = strlen(subj);
517 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
519 newsubj = salloc(out.l + 6);
520 memcpy(newsubj, "Fwd: ", 5); /* XXX localizable */
521 memcpy(newsubj + 5, out.s, out.l +1);
522 free(out.s);
523 jleave:
524 NYD_LEAVE;
525 return newsubj;
528 static int
529 _resend1(void *v, bool_t add_resent)
531 char *name, *str;
532 struct name *to, *sn;
533 int *ip, *msgvec;
534 bool_t f = TRU1;
535 NYD_ENTER;
537 str = v;
538 msgvec = salloc((msgCount + 2) * sizeof *msgvec);
539 name = laststring(str, &f, TRU1);
540 if (name == NULL) {
541 fputs(_("No recipient specified.\n"), n_stdout);
542 goto jleave;
545 if (!f) {
546 *msgvec = first(0, MMNORM);
547 if (*msgvec == 0) {
548 if (n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT)) {
549 f = FAL0;
550 goto jleave;
552 fputs(_("No applicable messages.\n"), n_stdout);
553 goto jleave;
555 msgvec[1] = 0;
556 } else if (getmsglist(str, msgvec, 0) < 0)
557 goto jleave;
559 if (*msgvec == 0) {
560 if (n_pstate & (n_PS_HOOK_MASK | n_PS_ROBOT)) {
561 f = FAL0;
562 goto jleave;
564 fprintf(n_stdout, "No applicable messages.\n");
565 goto jleave;
568 sn = nalloc(name, GTO | GSKIN);
569 to = usermap(sn, FAL0);
570 for (ip = msgvec; *ip != 0 && UICMP(z, PTR2SIZE(ip - msgvec), <, msgCount);
571 ++ip)
572 if (resend_msg(message + *ip - 1, to, add_resent) != OKAY)
573 goto jleave;
574 f = FAL0;
575 jleave:
576 NYD_LEAVE;
577 return (f != FAL0);
580 FL int
581 c_reply(void *v)
583 int rv;
584 NYD_ENTER;
586 rv = (*_reply_or_Reply('r'))(v, FAL0);
587 NYD_LEAVE;
588 return rv;
591 FL int
592 c_replyall(void *v)
594 int rv;
595 NYD_ENTER;
597 rv = _reply(v, FAL0);
598 NYD_LEAVE;
599 return rv;
602 FL int
603 c_replysender(void *v)
605 int rv;
606 NYD_ENTER;
608 rv = _Reply(v, FAL0);
609 NYD_LEAVE;
610 return rv;
613 FL int
614 c_Reply(void *v)
616 int rv;
617 NYD_ENTER;
619 rv = (*_reply_or_Reply('R'))(v, FAL0);
620 NYD_LEAVE;
621 return rv;
624 FL int
625 c_Lreply(void *v)
627 int rv;
628 NYD_ENTER;
630 rv = _list_reply(v, HF_LIST_REPLY);
631 NYD_LEAVE;
632 return rv;
635 FL int
636 c_followup(void *v)
638 int rv;
639 NYD_ENTER;
641 rv = (*_reply_or_Reply('r'))(v, TRU1);
642 NYD_LEAVE;
643 return rv;
646 FL int
647 c_followupall(void *v)
649 int rv;
650 NYD_ENTER;
652 rv = _reply(v, TRU1);
653 NYD_LEAVE;
654 return rv;
657 FL int
658 c_followupsender(void *v)
660 int rv;
661 NYD_ENTER;
663 rv = _Reply(v, TRU1);
664 NYD_LEAVE;
665 return rv;
668 FL int
669 c_Followup(void *v)
671 int rv;
672 NYD_ENTER;
674 rv = (*_reply_or_Reply('R'))(v, TRU1);
675 NYD_LEAVE;
676 return rv;
679 FL int
680 c_forward(void *v)
682 int rv;
683 NYD_ENTER;
685 rv = _fwd(v, 0);
686 NYD_LEAVE;
687 return rv;
690 FL int
691 c_Forward(void *v)
693 int rv;
694 NYD_ENTER;
696 rv = _fwd(v, 1);
697 NYD_LEAVE;
698 return rv;
701 FL int
702 c_resend(void *v)
704 int rv;
705 NYD_ENTER;
707 rv = _resend1(v, TRU1);
708 NYD_LEAVE;
709 return rv;
712 FL int
713 c_Resend(void *v)
715 int rv;
716 NYD_ENTER;
718 rv = _resend1(v, FAL0);
719 NYD_LEAVE;
720 return rv;
723 /* s-it-mode */