* Addition of a link to the Apache License 2.0. This is available from
[alpine.git] / pith / reply.c
blobab4e97c247ee40cf797b338992f89733a61ff7df
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2020 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "../pith/headers.h"
20 #include "../pith/reply.h"
21 #include "../pith/send.h"
22 #include "../pith/init.h"
23 #include "../pith/state.h"
24 #include "../pith/conf.h"
25 #include "../pith/remote.h"
26 #include "../pith/status.h"
27 #include "../pith/mailview.h"
28 #include "../pith/filter.h"
29 #include "../pith/newmail.h"
30 #include "../pith/bldaddr.h"
31 #include "../pith/mailindx.h"
32 #include "../pith/mimedesc.h"
33 #include "../pith/detach.h"
34 #include "../pith/help.h"
35 #include "../pith/pipe.h"
36 #include "../pith/addrstring.h"
37 #include "../pith/news.h"
38 #include "../pith/util.h"
39 #include "../pith/pattern.h"
40 #include "../pith/detoken.h"
41 #include "../pith/stream.h"
42 #include "../pith/busy.h"
43 #include "../pith/readfile.h"
44 #include "../pith/text.h"
45 #include "../pith/list.h"
46 #include "../pith/ablookup.h"
47 #include "../pith/mailcmd.h"
48 #include "../pith/margin.h"
49 #include "../pith/smime.h"
53 * Internal prototypes
55 void bounce_mask_header(char **, char *);
58 int (*pith_opt_replyto_prompt)(void);
59 int (*pith_opt_reply_to_all_prompt)(int *);
63 * standard type of storage object used for body parts...
65 #define PART_SO_TYPE CharStar
68 char *(*pith_opt_user_agent_prefix)(void);
70 /* compare two subjects and return if they are the same.
71 We compare stripped subjects, that is, those that do
72 not have re/fwd. Before we compare the subjects, we
73 decode them in case they were encoded.
74 Return value: 0 - not the same subject, 1 - same subject.
77 int
78 same_subject(char *s, char *t)
80 int rv = 0;
81 int i, j, len;
82 char *s1, *s2; /* holds decoded subjects from s and t */
83 char *u, *v;
85 if (s == NULL || t == NULL)
86 return s == t ? 1 : 0;
88 i = strlen(s); j = strlen(t);
89 len = i < j ? j : i;
90 u = fs_get(6*len+1);
91 v = fs_get(6*len+1);
92 s1 = (char *) rfc1522_decode_to_utf8((unsigned char *) u, 6*len + 1, s);
93 s2 = (char *) rfc1522_decode_to_utf8((unsigned char *) v, 6*len + 1, t);
94 mail_strip_subject(s1, &u);
95 mail_strip_subject(s2, &v);
97 rv = !strucmp(u, v);
99 fs_give((void **) &u);
100 fs_give((void **) &v);
101 return rv;
105 * reply_harvest -
107 * Returns: 1 if addresses successfully copied
108 * 0 on user cancel or error
110 * Input flags:
111 * RSF_FORCE_REPLY_TO
112 * RSF_QUERY_REPLY_ALL
113 * RSF_FORCE_REPLY_ALL
115 * Output flags:
116 * RSF_FORCE_REPLY_ALL
120 reply_harvest(struct pine *ps, long int msgno, char *section, ENVELOPE *env,
121 struct mail_address **saved_from, struct mail_address **saved_to,
122 struct mail_address **saved_cc, struct mail_address **saved_resent,
123 int *flags)
125 ADDRESS *ap, *ap2, *rep_address;
126 int ret = 0, sniff_resent = 0;
127 char *rep_field;
130 * If Reply-To is same as From just treat it like it was From.
131 * Otherwise, always use the reply-to if we're replying to more
132 * than one msg or say ok to using it, even if it's us.
133 * If there's no reply-to or it's the same as the from, assume
134 * that the user doesn't want to reply to himself, unless there's
135 * nobody else.
137 if(env->reply_to && !addr_lists_same(env->reply_to, env->from)
138 && (F_ON(F_AUTO_REPLY_TO, ps)
139 || ((*flags) & RSF_FORCE_REPLY_TO)
140 || (pith_opt_replyto_prompt && (*pith_opt_replyto_prompt)() == 'y'))){
141 rep_field = "reply-to";
142 rep_address = env->reply_to;
144 else{
145 rep_field = "From";
146 rep_address = env->from;
149 ap = reply_cp_addr(ps, msgno, section, rep_field, *saved_from,
150 (ADDRESS *) NULL, rep_address, RCA_NOT_US);
152 if(ret == 'x') {
153 cmd_cancelled("Reply");
154 return(0);
157 reply_append_addr(saved_from, ap);
159 /*--------- check for other recipients ---------*/
160 if(((*flags) & (RSF_FORCE_REPLY_ALL | RSF_QUERY_REPLY_ALL))){
162 if((ap = reply_cp_addr(ps, msgno, section, "To", *saved_to,
163 *saved_from, env->to, RCA_NOT_US)) != NULL)
164 reply_append_addr(saved_to, ap);
166 if((ap = reply_cp_addr(ps, msgno, section, "Cc", *saved_cc,
167 *saved_from, env->cc, RCA_NOT_US)) != NULL)
168 reply_append_addr(saved_cc, ap);
171 * In these cases, we either need to look at the resent headers
172 * to include in the reply-to-all, or to decide whether or not
173 * we need to ask the reply-to-all question.
175 if(((*flags) & RSF_FORCE_REPLY_ALL)
176 || (((*flags) & RSF_QUERY_REPLY_ALL)
177 && ((!(*saved_from) && !(*saved_cc))
178 || (*saved_from && !(*saved_to) && !(*saved_cc))))){
180 sniff_resent++;
181 if((ap2 = reply_resent(ps, msgno, section)) != NULL){
183 * look for bogus addr entries and replace
185 if((ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent,
186 *saved_from, ap2, RCA_NOT_US)) != NULL)
188 reply_append_addr(saved_resent, ap);
190 mail_free_address(&ap2);
195 * It makes sense to ask reply-to-all now.
197 if(((*flags) & RSF_QUERY_REPLY_ALL)
198 && ((*saved_from && (*saved_to || *saved_cc || *saved_resent))
199 || (*saved_cc || *saved_resent))){
200 *flags &= ~RSF_QUERY_REPLY_ALL;
201 if(pith_opt_reply_to_all_prompt
202 && (*pith_opt_reply_to_all_prompt)(flags) < 0){
203 cmd_cancelled("Reply");
204 return(0);
209 * If we just answered yes to the reply-to-all question and
210 * we still haven't collected the resent headers, do so now.
212 if(((*flags) & RSF_FORCE_REPLY_ALL) && !sniff_resent
213 && (ap2 = reply_resent(ps, msgno, section))){
215 * look for bogus addr entries and replace
217 if((ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent,
218 *saved_from, ap2, RCA_NOT_US)) != NULL)
219 reply_append_addr(saved_resent, ap);
221 mail_free_address(&ap2);
225 return(1);
229 /*----------------------------------------------------------------------
230 Return a pointer to a copy of the given address list
231 filtering out those already in the "mask" lists and ourself.
233 Args: mask1 -- Don't copy if in this list
234 mask2 -- or if in this list
235 source -- List to be copied
236 us_too -- Don't filter out ourself.
237 flags -- RCA_NOT_US copy all addrs except for our own
238 RCA_ALL copy all addrs, including our own
239 RCA_ONLY_US copy only addrs that are our own
241 ---*/
242 ADDRESS *
243 reply_cp_addr(struct pine *ps, long int msgno, char *section, char *field,
244 struct mail_address *mask1, struct mail_address *mask2,
245 struct mail_address *source, int flags)
247 ADDRESS *tmp1, *tmp2, *ret = NULL, **ret_tail;
249 /* can only choose one of these flags values */
250 assert(!((flags & RCA_ALL && flags & RCA_ONLY_US)
251 || (flags & RCA_ALL && flags & RCA_NOT_US)
252 || (flags & RCA_ONLY_US && flags & RCA_NOT_US)));
254 for(tmp1 = source; msgno && tmp1; tmp1 = tmp1->next)
255 if(tmp1->host && tmp1->host[0] == '.'){
256 char *h, *fields[2];
258 fields[0] = field;
259 fields[1] = NULL;
260 if((h = pine_fetchheader_lines(ps ? ps->mail_stream : NULL,
261 msgno, section, fields)) != NULL){
262 char *p, fname[32];
263 int q;
265 q = strlen(h);
267 strncpy(fname, field, sizeof(fname)-2);
268 fname[sizeof(fname)-2] = '\0';
269 strncat(fname, ":", sizeof(fname)-strlen(fname)-1);
270 fname[sizeof(fname)-1] = '\0';
272 for(p = h; (p = strstr(p, fname)) != NULL; )
273 rplstr(p, q-(p-h), strlen(fname), ""); /* strip field strings */
275 sqznewlines(h); /* blat out CR's & LF's */
276 for(p = h; (p = strchr(p, TAB)) != NULL; )
277 *p++ = ' '; /* turn TABs to whitespace */
279 if(*h){
280 long l;
281 size_t ll;
283 ret = (ADDRESS *) fs_get(sizeof(ADDRESS));
284 memset(ret, 0, sizeof(ADDRESS));
286 /* get rid of leading white space */
287 for(p = h; *p == SPACE; p++)
290 if(p != h){
291 memmove(h, p, l = strlen(p));
292 h[l] = '\0';
295 /* base64 armor plate the gunk to protect against
296 * c-client quoting in output.
298 p = (char *) rfc822_binary(h, strlen(h),
299 (unsigned long *) &l);
300 sqznewlines(p);
301 fs_give((void **) &h);
303 * Seems like the 4 ought to be a 2, but I'll leave it
304 * to be safe, in case something else adds 2 chars later.
306 ll = strlen(p) + 4;
307 ret->mailbox = (char *) fs_get(ll * sizeof(char));
308 snprintf(ret->mailbox, ll, "&%s", p);
309 ret->mailbox[ll-1] = '\0';
310 fs_give((void **) &p);
311 ret->host = cpystr(RAWFIELD);
315 return(ret);
318 ret_tail = &ret;
319 for(source = first_addr(source); source; source = source->next){
320 for(tmp1 = first_addr(mask1); tmp1; tmp1 = tmp1->next)
321 if(address_is_same(source, tmp1)) /* it is in mask1, skip it */
322 break;
324 for(tmp2 = first_addr(mask2); !tmp1 && tmp2; tmp2 = tmp2->next)
325 if(address_is_same(source, tmp2)) /* it is in mask2, skip it */
326 break;
329 * If there's no match in masks and this address satisfies the
330 * flags requirement, copy it.
332 if(!tmp1 && !tmp2 /* no mask match */
333 && ((flags & RCA_ALL) /* including everybody */
334 || (flags & RCA_ONLY_US && address_is_us(source, ps))
335 || (flags & RCA_NOT_US && !address_is_us(source, ps)))){
336 tmp1 = source->next;
337 source->next = NULL; /* only copy one addr! */
338 *ret_tail = rfc822_cpy_adr(source);
339 ret_tail = &(*ret_tail)->next;
340 source->next = tmp1; /* restore rest of list */
344 return(ret);
348 ACTION_S *
349 set_role_from_msg(struct pine *ps, long int rflags, long int msgno, char *section)
351 ACTION_S *role = NULL;
352 PAT_S *pat = NULL;
353 SEARCHSET *ss = NULL;
354 PAT_STATE pstate;
356 if(!nonempty_patterns(rflags, &pstate))
357 return(role);
359 if(msgno > 0L){
360 ss = mail_newsearchset();
361 ss->first = ss->last = (unsigned long)msgno;
364 /* Go through the possible roles one at a time until we get a match. */
365 pat = first_pattern(&pstate);
367 /* calculate this message's score if needed */
368 if(ss && pat && scores_are_used(SCOREUSE_GET) & SCOREUSE_ROLES &&
369 get_msg_score(ps->mail_stream, msgno) == SCORE_UNDEF)
370 (void)calculate_some_scores(ps->mail_stream, ss, 0);
372 while(!role && pat){
373 if(match_pattern(pat->patgrp, ps->mail_stream, ss, section,
374 get_msg_score, SE_NOSERVER|SE_NOPREFETCH)){
375 if(!pat->action || pat->action->bogus)
376 break;
378 role = pat->action;
380 else
381 pat = next_pattern(&pstate);
384 if(ss)
385 mail_free_searchset(&ss);
387 return(role);
392 * reply_seed - fill in reply header
395 void
396 reply_seed(struct pine *ps, ENVELOPE *outgoing, ENVELOPE *env,
397 struct mail_address *saved_from, struct mail_address *saved_to,
398 struct mail_address *saved_cc, struct mail_address *saved_resent,
399 char **fcc, int replytoall, char **errmsg)
401 ADDRESS **to_tail, **cc_tail;
403 to_tail = &outgoing->to;
404 cc_tail = &outgoing->cc;
406 if(saved_from){
407 /* Put Reply-To or From in To. */
408 *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
409 (ADDRESS *) NULL, saved_from, RCA_ALL);
410 if(replytoall){
411 if(ps->reply.preserve_fields){
412 while(*to_tail)
413 to_tail = &(*to_tail)->next;
415 *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
416 (ADDRESS *) NULL, saved_to, RCA_ALL);
418 while(*to_tail)
419 to_tail = &(*to_tail)->next;
421 *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
422 outgoing->to, saved_resent, RCA_ALL);
424 *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
425 outgoing->to, saved_cc, RCA_ALL);
427 else{ /* and the rest in cc */
428 *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
429 outgoing->to, saved_to, RCA_ALL);
430 while(*cc_tail) /* stay on last address */
431 cc_tail = &(*cc_tail)->next;
433 *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
434 outgoing->to, saved_cc, RCA_ALL);
435 while(*cc_tail)
436 cc_tail = &(*cc_tail)->next;
438 *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
439 outgoing->to, saved_resent, RCA_ALL);
443 else if(saved_to){
444 /* No From (maybe from us), put To in To. */
445 *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
446 (ADDRESS *)NULL, saved_to, RCA_ALL);
447 /* and the rest in cc */
448 if(replytoall){
449 *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
450 outgoing->to, saved_cc, RCA_ALL);
451 while(*cc_tail)
452 cc_tail = &(*cc_tail)->next;
454 *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
455 outgoing->to, saved_resent, RCA_ALL);
458 else{
459 /* No From or To, put everything else in To if replytoall, */
460 if(replytoall){
461 *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
462 (ADDRESS *) NULL, saved_cc, RCA_ALL);
463 while(*to_tail)
464 to_tail = &(*to_tail)->next;
466 *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
467 (ADDRESS *) NULL, saved_resent, RCA_ALL);
469 /* else, reply to original From which must be us */
470 else{
472 * Put self in To if in original From.
474 if(!outgoing->newsgroups)
475 *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
476 (ADDRESS *) NULL, env->from, RCA_ALL);
480 /* add any missing personal data */
481 reply_fish_personal(outgoing, env);
483 /* get fcc */
484 if(fcc && outgoing->to && outgoing->to->host[0] != '.'){
485 *fcc = get_fcc_based_on_to(outgoing->to);
487 else if(fcc && outgoing->newsgroups){
488 char *newsgroups_returned = NULL;
489 int rv;
491 rv = news_grouper(outgoing->newsgroups, &newsgroups_returned, errmsg, fcc, NULL);
492 if(rv != -1 &&
493 strcmp(outgoing->newsgroups, newsgroups_returned)){
494 fs_give((void **)&outgoing->newsgroups);
495 outgoing->newsgroups = newsgroups_returned;
497 else
498 fs_give((void **) &newsgroups_returned);
503 /*----------------------------------------------------------------------
504 Test the given address lists for equivalence
506 Args: x -- First address list for comparison
507 y -- Second address for comparison
509 ---*/
511 addr_lists_same(struct mail_address *x, struct mail_address *y)
513 for(x = first_addr(x), y = first_addr(y);
514 x && y;
515 x = first_addr(x->next), y = first_addr(y->next)){
516 if(!address_is_same(x, y))
517 return(0);
520 return(!x && !y); /* true if ran off both lists */
524 /*----------------------------------------------------------------------
525 Test the given address against those in the given envelope's to, cc
527 Args: addr -- address for comparison
528 env -- envelope to compare against
530 ---*/
532 addr_in_env(struct mail_address *addr, ENVELOPE *env)
534 ADDRESS *ap;
536 for(ap = env ? env->to : NULL; ap; ap = ap->next)
537 if(address_is_same(addr, ap))
538 return(1);
540 for(ap = env ? env->cc : NULL; ap; ap = ap->next)
541 if(address_is_same(addr, ap))
542 return(1);
544 return(0); /* not found! */
548 /*----------------------------------------------------------------------
549 Add missing personal info dest from src envelope
551 Args: dest -- envelope to add personal info to
552 src -- envelope to get personal info from
554 NOTE: This is just kind of a courtesy function. It's really not adding
555 anything needed to get the mail thru, but it is nice for the user
556 under some odd circumstances.
557 ---*/
558 void
559 reply_fish_personal(ENVELOPE *dest, ENVELOPE *src)
561 ADDRESS *da, *sa;
563 for(da = dest ? dest->to : NULL; da; da = da->next){
564 if(da->personal && !da->personal[0])
565 fs_give((void **)&da->personal);
567 for(sa = src ? src->to : NULL; sa && !da->personal ; sa = sa->next)
568 if(address_is_same(da, sa) && sa->personal && sa->personal[0])
569 da->personal = cpystr(sa->personal);
571 for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
572 if(address_is_same(da, sa) && sa->personal && sa->personal[0])
573 da->personal = cpystr(sa->personal);
576 for(da = dest ? dest->cc : NULL; da; da = da->next){
577 if(da->personal && !da->personal[0])
578 fs_give((void **)&da->personal);
580 for(sa = src ? src->to : NULL; sa && !da->personal; sa = sa->next)
581 if(address_is_same(da, sa) && sa->personal && sa->personal[0])
582 da->personal = cpystr(sa->personal);
584 for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
585 if(address_is_same(da, sa) && sa->personal && sa->personal[0])
586 da->personal = cpystr(sa->personal);
591 /*----------------------------------------------------------------------
592 Given a header field and envelope, build "References: " header data
594 Args:
596 Returns:
597 ---*/
598 char *
599 reply_build_refs(ENVELOPE *env)
601 int len, id_len, first_ref_len = 0, foldslop;
602 char *p, *refs = NULL, *h = env->references;
603 char *first_ref = NULL, *tail_refs = NULL;
606 if(!(env->message_id && (id_len = strlen(env->message_id))))
607 return(NULL);
609 if(h){
611 * The length we have to work with doesn't seem to appear in any
612 * standards. Steve Jones says that in comp.news discussions he
613 * has seen 1024 as the longest length of a header value.
614 * In the inn news source we find MAXHEADERSIZE = 1024. It appears
615 * that is the maximum length of the header value, including
616 * newlines for folded lines (that is, the newlines are counted).
617 * We'll be conservative and figure every reference will take up a
618 * line of its own when we fold. We'll also count 2 for CRLF instead
619 * of just one for LF just to be safe. hubert 2001-jan
620 * J.B. Moreno <planb@newsreaders.com> says "The server limit is
621 * more commonly encountered at 999/1000 bytes [...]". So we'll
622 * back off to 999 instead of 1024.
624 #define MAXHEADERSIZE (999)
626 /* count the total number of potential folds, max of 2 bytes each */
627 for(foldslop = 2, p = h; (p = strstr(p+1, "> <")); )
628 foldslop += 2;
630 if((len=strlen(h)) + 1+id_len + foldslop >= MAXHEADERSIZE
631 && (p = strstr(h, "> <"))){
633 * If the references line is so long that we are going to have
634 * to delete some of the references, delete the 2nd, 3rd, ...
635 * We don't want to delete the first message in the thread.
637 p[1] = '\0';
638 first_ref = cpystr(h);
639 first_ref_len = strlen(first_ref)+1; /* len includes space */
640 p[1] = ' ';
641 tail_refs = p+2;
642 /* get rid of 2nd, 3rd, ... until it fits */
643 while((len=strlen(tail_refs)) + first_ref_len + 1+id_len +
644 foldslop >= MAXHEADERSIZE
645 && (p = strstr(tail_refs, "> <"))){
646 tail_refs = p + 2;
647 foldslop -= 2;
651 * If the individual references are seriously long, somebody
652 * is messing with us and we don't care if it works right.
654 if((len=strlen(tail_refs)) + first_ref_len + 1+id_len +
655 foldslop >= MAXHEADERSIZE){
656 first_ref_len = len = 0;
657 tail_refs = NULL;
658 if(first_ref)
659 fs_give((void **)&first_ref);
662 else
663 tail_refs = h;
665 refs = (char *)fs_get((first_ref_len + 1+id_len + len + 1) *
666 sizeof(char));
667 snprintf(refs, first_ref_len + 1+id_len + len + 1, "%s%s%s%s%s",
668 first_ref ? first_ref : "",
669 first_ref ? " " : "",
670 tail_refs ? tail_refs : "",
671 tail_refs ? " " : "",
672 env->message_id);
673 refs[first_ref_len + 1+id_len + len] = '\0';
676 if(!refs && id_len)
677 refs = cpystr(env->message_id);
679 if(first_ref)
680 fs_give((void **)&first_ref);
682 return(refs);
687 /*----------------------------------------------------------------------
688 Snoop for any Resent-* headers, and return an ADDRESS list
690 Args: stream --
691 msgno --
693 Returns: either NULL if no Resent-* or parsed ADDRESS struct list
694 ---*/
695 ADDRESS *
696 reply_resent(struct pine *pine_state, long int msgno, char *section)
698 #define RESENTFROM 0
699 #define RESENTTO 1
700 #define RESENTCC 2
701 ADDRESS *rlist = NULL, **a, **b;
702 char *hdrs, *values[RESENTCC+1];
703 int i;
704 static char *fields[] = {"Resent-From", "Resent-To", "Resent-Cc", NULL};
705 static char *fakedomain = "@";
707 if((hdrs = pine_fetchheader_lines(pine_state->mail_stream,
708 msgno, section, fields)) != NULL){
709 memset(values, 0, (RESENTCC+1) * sizeof(char *));
710 simple_header_parse(hdrs, fields, values);
711 for(i = RESENTFROM; i <= RESENTCC; i++)
712 rfc822_parse_adrlist(&rlist, values[i],
713 (F_ON(F_COMPOSE_REJECTS_UNQUAL, pine_state))
714 ? fakedomain : pine_state->maildomain);
716 /* pare dup's ... */
717 for(a = &rlist; *a; a = &(*a)->next) /* compare every address */
718 for(b = &(*a)->next; *b; ) /* to the others */
719 if(address_is_same(*a, *b)){
720 ADDRESS *t = *b;
722 if(!(*a)->personal){ /* preserve personal name */
723 (*a)->personal = (*b)->personal;
724 (*b)->personal = NULL;
727 *b = t->next;
728 t->next = NULL;
729 mail_free_address(&t);
731 else
732 b = &(*b)->next;
735 if(hdrs)
736 fs_give((void **) &hdrs);
738 return(rlist);
742 /*----------------------------------------------------------------------
743 Format and return subject suitable for the reply command
745 Args: subject -- subject to build reply subject for
746 buf -- buffer to use for writing. If non supplied, alloc one.
747 buflen -- length of buf if supplied, else ignored
749 Returns: with either "Re:" prepended or not, if already there.
750 returned string is allocated.
751 ---*/
752 char *
753 reply_subject(char *subject, char *buf, size_t buflen)
755 size_t l = (subject && *subject) ? 4*strlen(subject) : 10;
756 char *tmp = fs_get(l + 1), *decoded, *p;
758 if(!buf){
759 buflen = l + 5;
760 buf = fs_get(buflen);
763 /* decode any 8bit into tmp buffer */
764 decoded = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp, l+1, subject);
766 buf[0] = '\0';
767 if(decoded /* already "re:" ? */
768 && (decoded[0] == 'R' || decoded[0] == 'r')
769 && (decoded[1] == 'E' || decoded[1] == 'e')){
771 if(decoded[2] == ':')
772 snprintf(buf, buflen, "%.*s", (int)(buflen-1), subject);
773 else if((decoded[2] == '[') && (p = strchr(decoded, ']'))){
774 p++;
775 while(*p && isspace((unsigned char)*p)) p++;
776 if(p[0] == ':')
777 snprintf(buf, buflen, "%.*s", (int)(buflen-1), subject);
781 if(!buf[0])
782 snprintf(buf, buflen, "Re: %.*s", (int)(buflen-1),
783 (subject && *subject) ? subject : "your mail");
785 buf[buflen-1] = '\0';
787 fs_give((void **) &tmp);
788 return(buf);
792 /*----------------------------------------------------------------------
793 return initials for the given personal name
795 Args: name -- Personal name to extract initials from
797 Returns: pointer to name overwritten with initials
798 ---*/
799 char *
800 reply_quote_initials(char *name)
802 char *s = name,
803 *w = name;
804 int i, j;
805 CBUF_S cbuf;
806 UCS ucs;
808 cbuf.cbuf[i = 0] = '\0';
809 cbuf.cbufp = cbuf.cbuf;
810 cbuf.cbufend = cbuf.cbuf;
812 /* while there are still characters to look at */
813 while(s && *s){
814 /* skip to next initial */
815 while(*s && (unsigned char) *s == ' ')
816 s++;
818 if(!utf8_to_ucs4_oneatatime((unsigned char) *s++ & 0xff, &cbuf, &ucs, NULL)){
819 i++;
820 continue;
823 /* skip over non-alpha stuff */
824 if(i == 0 && !isalnum((unsigned char) cbuf.cbuf[0]))
825 goto reset_cbuf;
827 /* copy cbuf */
828 for(j = 0; j <= i; j++) *w++ = cbuf.cbuf[j];
830 /* skip to end of this piece of name */
831 while(*s && (unsigned char) *s != ' ')
832 s++;
834 reset_cbuf:
835 cbuf.cbuf[i = 0] = '\0';
836 cbuf.cbufp = cbuf.cbuf;
837 cbuf.cbufend = cbuf.cbuf;
840 if(w)
841 *w = '\0';
843 return(name);
847 * There is an assumption that MAX_SUBSTITUTION is <= the size of the
848 * tokens being substituted for (only in the size of buf below).
850 #define MAX_SUBSTITUTION 6
851 #define MAX_PREFIX 63
852 static char *from_token = "_FROM_";
853 static char *nick_token = "_NICK_";
854 static char *init_token = "_INIT_";
856 /*----------------------------------------------------------------------
857 return a quoting string, "> " by default, for replied text
859 Args: env -- envelope of message being replied to
861 Returns: malloc'd array containing quoting string, freed by caller
862 ---*/
863 char *
864 reply_quote_str(ENVELOPE *env)
866 char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1];
868 strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1);
869 buf[sizeof(buf)-1] = '\0';
871 /* set up the prefix to quote included text */
872 if((p = strstr(buf, from_token)) != NULL){
873 repl = (env && env->from && env->from->mailbox) ? env->from->mailbox
874 : "";
875 strncpy(pbf, repl, sizeof(pbf)-1);
876 pbf[sizeof(pbf)-1] = '\0';;
877 rplstr(p, sizeof(buf)-(p-buf), strlen(from_token), pbf);
880 if((p = strstr(buf, nick_token)) != NULL){
881 repl = (env &&
882 env->from &&
883 env->from &&
884 get_nickname_from_addr(env->from, tmp_20k_buf, 1000))
885 ? tmp_20k_buf : "";
886 strncpy(pbf, repl, sizeof(pbf)-1);
887 pbf[sizeof(pbf)-1] = '\0';;
888 rplstr(p, sizeof(buf)-(p-buf), strlen(nick_token), pbf);
891 if((p = strstr(buf, init_token)) != NULL){
892 char *q = NULL;
893 char buftmp[MAILTMPLEN];
895 snprintf(buftmp, sizeof(buftmp), "%.200s",
896 (env && env->from && env->from->personal) ? env->from->personal : "");
897 buftmp[sizeof(buftmp)-1] = '\0';
899 repl = (env && env->from && env->from->personal)
900 ? reply_quote_initials(q = cpystr((char *)rfc1522_decode_to_utf8(
901 (unsigned char *)tmp_20k_buf,
902 SIZEOF_20KBUF, buftmp)))
903 : "";
905 istrncpy(pbf, repl, sizeof(pbf)-1);
906 pbf[sizeof(pbf)-1] = '\0';;
907 rplstr(p, sizeof(buf)-(p-buf), strlen(init_token), pbf);
908 if(q)
909 fs_give((void **)&q);
912 prefix = removing_quotes(cpystr(buf));
914 return(prefix);
918 reply_quote_str_contains_tokens(void)
920 return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] &&
921 (strstr(ps_global->VAR_REPLY_STRING, from_token) ||
922 strstr(ps_global->VAR_REPLY_STRING, nick_token) ||
923 strstr(ps_global->VAR_REPLY_STRING, init_token)));
927 /*----------------------------------------------------------------------
928 Build the body for the message number/part being replied to
930 Args:
932 Result: BODY structure suitable for sending
934 ----------------------------------------------------------------------*/
935 BODY *
936 reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
937 long int msgno, char *sect_prefix, void *msgtext, char *prefix,
938 int plustext, ACTION_S *role, int toplevel, REDRAFT_POS_S **redraft_pos)
940 #define SECTBUFLEN 256
941 char *p, *sig = NULL, *section, sect_buf[SECTBUFLEN];
942 BODY *body = NULL, *tmp_body = NULL;
943 PART *part;
944 gf_io_t pc;
945 int impl, template_len = 0, leave_cursor_at_top = 0, reply_raw_body = 0;
947 if(sect_prefix) /* SECTBUFLEN = sizeof(sect_buf) */
948 snprintf(section = sect_buf, sizeof(sect_buf), "%.*s.1", SECTBUFLEN-1, sect_prefix);
949 else
950 section = "1";
952 sect_buf[sizeof(sect_buf)-1] = '\0';
954 if(ps_global->full_header == 2
955 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
956 reply_raw_body = 1;
958 gf_set_so_writec(&pc, (STORE_S *) msgtext);
960 if(toplevel){
961 char *filtered;
963 impl = 0;
964 filtered = detoken(role, env, 0,
965 ps_global->reply.signature_bottom,
966 0, redraft_pos, &impl);
967 if(filtered){
968 if(*filtered){
969 so_puts((STORE_S *)msgtext, filtered);
970 if(impl == 1)
971 template_len = strlen(filtered);
972 else if(impl == 2)
973 leave_cursor_at_top++;
976 fs_give((void **)&filtered);
978 else
979 impl = 1;
981 else
982 impl = 1;
984 if(toplevel &&
985 (sig = reply_signature(role, env, redraft_pos, &impl)) &&
986 !ps_global->reply.signature_bottom){
989 * If CURSORPOS was set explicitly in sig_file, and there was a
990 * template file before that, we need to adjust the offset by the
991 * length of the template file. However, if the template had
992 * a set CURSORPOS in it then impl was 2 before getting to the
993 * signature, so offset wouldn't have been reset by the signature
994 * CURSORPOS and offset would already be correct. That case will
995 * be ok here because template_len will be 0 and adding it does
996 * nothing. If template
997 * didn't have CURSORPOS in it, then impl was 1 and got set to 2
998 * by the CURSORPOS in the sig. In that case we have to adjust the
999 * offset. That's what the next line does. It adjusts it if
1000 * template_len is nonzero and if CURSORPOS was set in sig_file.
1002 if(impl == 2)
1003 (*redraft_pos)->offset += template_len;
1005 if(*sig)
1006 so_puts((STORE_S *)msgtext, sig);
1009 * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
1010 * is set, we won't have used it yet and want it to be non-NULL.
1012 fs_give((void **)&sig);
1016 * Only put cursor in sig if there is a cursorpos there but not
1017 * one in the template, and sig-at-bottom.
1019 if(!(sig && impl == 2 && !leave_cursor_at_top))
1020 leave_cursor_at_top++;
1022 if(plustext){
1023 if(!orig_body
1024 || orig_body->type == TYPETEXT
1025 || reply_raw_body
1026 || !ps_global->reply.keep_attach){
1027 char *charset = NULL;
1029 /*------ Simple text-only message ----*/
1030 body = mail_newbody();
1031 body->type = TYPETEXT;
1032 body->contents.text.data = msgtext;
1033 reply_delimiter(env, role, pc);
1034 if(ps_global->reply.include_header)
1035 reply_forward_header(stream, msgno, sect_prefix,
1036 env, pc, prefix);
1038 if(!orig_body || reply_raw_body || reply_body_text(orig_body, &tmp_body)){
1039 BODY *bodyp = NULL;
1041 bodyp = reply_raw_body ? NULL : tmp_body;
1044 * We set the charset in the outgoing message to the same
1045 * as the one in the message we're replying to unless it
1046 * is the unknown charset. We do that in order to attempt
1047 * to preserve the same charset in the reply if possible.
1048 * It may be safer to just set it to whatever we want instead
1049 * but then the receiver may not be able to read it.
1051 if(bodyp
1052 && (charset = parameter_val(bodyp->parameter, "charset"))
1053 && strucmp(charset, UNKNOWN_CHARSET))
1054 set_parameter(&body->parameter, "charset", charset);
1056 if(charset)
1057 fs_give((void **) &charset);
1059 get_body_part_text(stream, bodyp, msgno,
1060 bodyp ? (p = body_partno(stream, msgno, bodyp))
1061 : sect_prefix,
1062 0L, pc, prefix, NULL, GBPT_NONE);
1063 if(bodyp && p)
1064 fs_give((void **) &p);
1066 else{
1067 gf_puts(NEWLINE, pc);
1068 gf_puts(" [NON-Text Body part not included]", pc);
1069 gf_puts(NEWLINE, pc);
1072 else if(orig_body->type == TYPEMULTIPART){
1073 /*------ Message is Multipart ------*/
1074 if(orig_body->subtype
1075 && (!strucmp(orig_body->subtype, "signed")
1076 #ifdef SMIME
1077 || !strucmp(orig_body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)
1078 #endif /* SMIME */
1080 && orig_body->nested.part){
1081 /* operate only on the signed part */
1082 body = reply_body(stream, env,
1083 &orig_body->nested.part->body,
1084 msgno, section, msgtext, prefix,
1085 plustext, role, 0, redraft_pos);
1087 else if(orig_body->subtype
1088 && !strucmp(orig_body->subtype, "mixed")
1089 && orig_body->nested.part
1090 && orig_body->nested.part->body.type == TYPEMULTIPART
1091 && orig_body->nested.part->body.subtype
1092 && (!strucmp(orig_body->nested.part->body.subtype, "signed")
1093 #ifdef SMIME
1094 || !strucmp(orig_body->nested.part->body.subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)
1095 #endif /* SMIME */
1097 /* we can call reply_body as in the call above with section
1098 * equal to "1", but that adds the multipart text to the
1099 * list of attachments. We do not want that, so we redo this
1100 * manually.
1102 body = copy_body(NULL, orig_body);
1105 * whatever subtype it is, demote it
1106 * to plain old MIXED.
1108 if(body->subtype)
1109 fs_give((void **) &body->subtype);
1111 body->subtype = cpystr("Mixed");
1113 if(reply_body_text(&orig_body->nested.part->body.nested.part->body,
1114 &tmp_body)){
1115 int partnum;
1117 reply_delimiter(env, role, pc);
1118 if(ps_global->reply.include_header)
1119 reply_forward_header(stream, msgno, sect_prefix,
1120 env, pc, prefix);
1121 /* SECTBUFLEN = sizeof(sect_buf) */
1122 snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%.*s",
1123 SECTBUFLEN/2-2,
1124 sect_prefix ? sect_prefix : "",
1125 sect_prefix ? "." : "",
1126 SECTBUFLEN/2-2,
1127 p = partno(orig_body, tmp_body));
1128 sect_buf[sizeof(sect_buf)-1] = '\0';
1129 fs_give((void **) &p);
1130 get_body_part_text(stream, tmp_body, msgno,
1131 sect_buf, 0L, pc, prefix,
1132 NULL, GBPT_NONE);
1134 part = body->nested.part->next;
1135 body->nested.part->next = NULL;
1136 mail_free_body_part(&body->nested.part);
1137 body->nested.part = mail_newbody_part();
1138 body->nested.part->body.type = TYPETEXT;
1139 body->nested.part->body.subtype = cpystr("Plain");
1140 body->nested.part->body.contents.text.data = msgtext;
1141 body->nested.part->next = part;
1142 /* SECTBUFLEN = sizeof(sect_buf) */
1143 for(partnum = 2; part != NULL; part = part->next){
1144 snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%d",
1145 SECTBUFLEN/2,
1146 sect_prefix ? sect_prefix : "",
1147 sect_prefix ? "." : "", partnum++);
1148 sect_buf[sizeof(sect_buf)-1] = '\0';
1150 if(!fetch_contents(stream, msgno,
1151 sect_buf, &part->body)){
1152 break;
1156 else {
1157 /*--- Fetch the original pieces ---*/
1158 if(!fetch_contents(stream, msgno, sect_prefix, body))
1159 q_status_message(SM_ORDER | SM_DING, 3, 4,
1160 _("Error including all message parts"));
1162 /*--- No text part, create a blank one ---*/
1163 part = mail_newbody_part();
1164 part->next = body->nested.part;
1165 body->nested.part = part;
1166 part->body.contents.text.data = msgtext;
1169 else if(orig_body->subtype
1170 && !strucmp(orig_body->subtype, "alternative")
1171 && orig_body->nested.part
1172 && orig_body->nested.part->next
1173 && orig_body->nested.part->next->body.type == TYPEMULTIPART
1174 && orig_body->nested.part->next->body.subtype
1175 && !strucmp(orig_body->nested.part->next->body.subtype, "MIXED"))
1176 body = reply_body(stream, env,
1177 &orig_body->nested.part->next->body,
1178 msgno, "2", msgtext, prefix,
1179 plustext, role, 0, redraft_pos);
1180 else if(orig_body->subtype
1181 && !strucmp(orig_body->subtype, "alternative")){
1182 /* Set up the simple text reply */
1183 body = mail_newbody();
1184 body->type = TYPETEXT;
1185 body->contents.text.data = msgtext;
1187 if(reply_body_text(orig_body, &tmp_body)){
1188 reply_delimiter(env, role, pc);
1189 if(ps_global->reply.include_header)
1190 reply_forward_header(stream, msgno, sect_prefix,
1191 env, pc, prefix);
1193 get_body_part_text(stream, tmp_body, msgno,
1194 p = body_partno(stream,msgno,tmp_body),
1195 0L, pc, prefix, NULL, GBPT_NONE);
1196 if(p)
1197 fs_give((void **) &p);
1199 else
1200 q_status_message(SM_ORDER | SM_DING, 3, 3,
1201 "No suitable multipart text found for inclusion!");
1203 else{
1204 body = copy_body(NULL, orig_body);
1207 * whatever subtype it is, demote it
1208 * to plain old MIXED.
1210 if(body->subtype)
1211 fs_give((void **) &body->subtype);
1213 body->subtype = cpystr("Mixed");
1215 if(body->nested.part &&
1216 body->nested.part->body.type == TYPETEXT) {
1217 char *new_charset = NULL;
1219 /*---- First part of the message is text -----*/
1220 body->nested.part->body.contents.text.data = msgtext;
1221 if(body->nested.part->body.subtype &&
1222 strucmp(body->nested.part->body.subtype, "Plain")){
1223 fs_give((void **)&body->nested.part->body.subtype);
1224 body->nested.part->body.subtype = cpystr("Plain");
1226 reply_delimiter(env, role, pc);
1227 if(ps_global->reply.include_header)
1228 reply_forward_header(stream, msgno, sect_prefix,
1229 env, pc, prefix);
1231 if(!(get_body_part_text(stream,
1232 &orig_body->nested.part->body,
1233 msgno, section, 0L, pc, prefix,
1234 &new_charset, GBPT_NONE)
1235 && fetch_contents(stream, msgno, sect_prefix, body)))
1236 q_status_message(SM_ORDER | SM_DING, 3, 4,
1237 _("Error including all message parts"));
1238 else if(new_charset)
1239 set_parameter(&body->nested.part->body.parameter, "charset", new_charset);
1241 else if(orig_body->nested.part->body.type == TYPEMULTIPART
1242 && orig_body->nested.part->body.subtype
1243 && !strucmp(orig_body->nested.part->body.subtype,
1244 "alternative")
1245 && reply_body_text(&orig_body->nested.part->body,
1246 &tmp_body)){
1247 int partnum;
1249 reply_delimiter(env, role, pc);
1250 if(ps_global->reply.include_header)
1251 reply_forward_header(stream, msgno, sect_prefix,
1252 env, pc, prefix);
1253 /* SECTBUFLEN = sizeof(sect_buf) */
1254 snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%.*s",
1255 SECTBUFLEN/2-2,
1256 sect_prefix ? sect_prefix : "",
1257 sect_prefix ? "." : "",
1258 SECTBUFLEN/2-2,
1259 p = partno(orig_body, tmp_body));
1260 sect_buf[sizeof(sect_buf)-1] = '\0';
1261 fs_give((void **) &p);
1262 get_body_part_text(stream, tmp_body, msgno,
1263 sect_buf, 0L, pc, prefix,
1264 NULL, GBPT_NONE);
1266 part = body->nested.part->next;
1267 body->nested.part->next = NULL;
1268 mail_free_body_part(&body->nested.part);
1269 body->nested.part = mail_newbody_part();
1270 body->nested.part->body.type = TYPETEXT;
1271 body->nested.part->body.subtype = cpystr("Plain");
1272 body->nested.part->body.contents.text.data = msgtext;
1273 body->nested.part->next = part;
1274 /* SECTBUFLEN = sizeof(sect_buf) */
1275 for(partnum = 2; part != NULL; part = part->next){
1276 snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%d",
1277 SECTBUFLEN/2,
1278 sect_prefix ? sect_prefix : "",
1279 sect_prefix ? "." : "", partnum++);
1280 sect_buf[sizeof(sect_buf)-1] = '\0';
1282 if(!fetch_contents(stream, msgno,
1283 sect_buf, &part->body)){
1284 break;
1288 else {
1289 /*--- Fetch the original pieces ---*/
1290 if(!fetch_contents(stream, msgno, sect_prefix, body))
1291 q_status_message(SM_ORDER | SM_DING, 3, 4,
1292 _("Error including all message parts"));
1294 /*--- No text part, create a blank one ---*/
1295 part = mail_newbody_part();
1296 part->next = body->nested.part;
1297 body->nested.part = part;
1298 part->body.contents.text.data = msgtext;
1302 else{
1303 /*---- Single non-text message of some sort ----*/
1304 body = mail_newbody();
1305 body->type = TYPEMULTIPART;
1306 part = mail_newbody_part();
1307 body->nested.part = part;
1309 /*--- The first part, a blank text part to be edited ---*/
1310 part->body.type = TYPETEXT;
1311 part->body.contents.text.data = msgtext;
1313 /*--- The second part, what ever it is ---*/
1314 part->next = mail_newbody_part();
1315 part = part->next;
1316 part->body.id = generate_message_id();
1317 copy_body(&(part->body), orig_body);
1320 * the idea here is to fetch part into storage object
1322 if((part->body.contents.text.data = (void *) so_get(PART_SO_TYPE,
1323 NULL,EDIT_ACCESS)) != NULL){
1324 if((p = pine_mail_fetch_body(stream, msgno, section,
1325 &part->body.size.bytes, NIL)) != NULL){
1326 so_nputs((STORE_S *)part->body.contents.text.data,
1327 p, part->body.size.bytes);
1329 else
1330 mail_free_body(&body);
1332 else
1333 mail_free_body(&body);
1336 else{
1337 /*--------- No text included --------*/
1338 body = mail_newbody();
1339 body->type = TYPETEXT;
1340 body->contents.text.data = msgtext;
1343 if(!leave_cursor_at_top){
1344 long cnt = 0L;
1345 unsigned char c;
1347 /* rewind and count chars to start of sig file */
1348 so_seek((STORE_S *)msgtext, 0L, 0);
1349 while(so_readc(&c, (STORE_S *)msgtext))
1350 cnt++;
1352 if(!*redraft_pos){
1353 *redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(**redraft_pos));
1354 memset((void *)*redraft_pos, 0,sizeof(**redraft_pos));
1355 (*redraft_pos)->hdrname = cpystr(":");
1359 * If explicit cursor positioning in sig file,
1360 * add offset to start of sig file plus offset into sig file.
1361 * Else, just offset to start of sig file.
1363 (*redraft_pos)->offset += cnt;
1366 if(sig){
1367 if(*sig)
1368 so_puts((STORE_S *)msgtext, sig);
1370 fs_give((void **)&sig);
1373 gf_clear_so_writec((STORE_S *) msgtext);
1375 return(body);
1380 * reply_part - first replyable multipart of a multipart.
1383 reply_body_text(struct mail_bodystruct *body, struct mail_bodystruct **new_body)
1385 if(body){
1386 switch(body->type){
1387 case TYPETEXT :
1388 *new_body = body;
1389 return(1);
1391 case TYPEMULTIPART :
1392 if(body->subtype && !strucmp(body->subtype, "alternative")){
1393 PART *part;
1394 int got_one = 0;
1396 if((part = body->nested.part) != NULL
1397 && part->body.type == TYPEMULTIPART)
1398 return reply_body_text(&body->nested.part->body, new_body);
1400 if(ps_global->force_prefer_plain
1401 || (!ps_global->force_no_prefer_plain
1402 && F_ON(F_PREFER_PLAIN_TEXT, ps_global))){
1403 for(part = body->nested.part; part; part = part->next)
1404 if((!part->body.type || part->body.type == TYPETEXT)
1405 && (!part->body.subtype
1406 || !strucmp(part->body.subtype, "plain"))){
1407 *new_body = &part->body;
1408 return(1);
1413 * Else choose last alternative among plain or html parts.
1414 * Perhaps we should really be using mime_show() to make this
1415 * potentially more general than just plain or html.
1417 for(part = body->nested.part; part; part = part->next){
1418 if((!part->body.type || part->body.type == TYPETEXT)
1419 && ((!part->body.subtype || !strucmp(part->body.subtype, "plain"))
1421 (part->body.subtype && !strucmp(part->body.subtype, "html")))){
1422 got_one++;
1423 *new_body = &part->body;
1427 if(got_one)
1428 return(1);
1430 else if(body->nested.part)
1431 /* NOTE: we're only interested in "first" part of mixed */
1432 return(reply_body_text(&body->nested.part->body, new_body));
1434 break;
1436 default:
1437 break;
1441 return(0);
1445 char *
1446 reply_signature(ACTION_S *role, ENVELOPE *env, REDRAFT_POS_S **redraft_pos, int *impl)
1448 char *sig;
1449 size_t l;
1451 sig = detoken(role, env,
1452 2, ps_global->reply.signature_bottom ? 0 : 1, 1,
1453 redraft_pos, impl);
1455 if(!ps_global->reply.signature_bottom && (!sig || !*sig)){
1456 if(sig)
1457 fs_give((void **)&sig);
1459 l = 2 * strlen(NEWLINE);
1460 sig = (char *)fs_get((l+1) * sizeof(char));
1461 strncpy(sig, NEWLINE, l);
1462 sig[l] = '\0';
1463 strncat(sig, NEWLINE, l+1-1-strlen(sig));
1464 sig[l] = '\0';
1465 return(sig);
1468 return(sig);
1473 * Buf is at least size maxlen+1
1475 void
1476 get_addr_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
1478 ADDRESS *addr = NULL;
1479 ADDRESS *last_to = NULL;
1480 ADDRESS *first_addr = NULL, *second_addr = NULL;
1481 ADDRESS *third_addr = NULL, *fourth_addr = NULL;
1482 int cntaddr, l;
1483 size_t orig_maxlen;
1484 char *p;
1486 buf[0] = '\0';
1488 switch(type){
1489 case iFrom:
1490 addr = env ? env->from : NULL;
1491 break;
1493 case iTo:
1494 addr = env ? env->to : NULL;
1495 break;
1497 case iCc:
1498 addr = env ? env->cc : NULL;
1499 break;
1501 case iSender:
1502 addr = env ? env->sender : NULL;
1503 break;
1506 * Recips is To and Cc togeter. We hook the two adrlists together
1507 * temporarily.
1509 case iRecips:
1510 addr = env ? env->to : NULL;
1511 /* Find end of To list */
1512 for(last_to = addr; last_to && last_to->next; last_to = last_to->next)
1515 /* Make the end of To list point to cc list */
1516 if(last_to)
1517 last_to->next = (env ? env->cc : NULL);
1519 break;
1522 * Initials.
1524 case iInit:
1525 if(env && env->from && env->from->personal){
1526 char *name, *initials = NULL;
1528 name = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
1529 SIZEOF_20KBUF, env->from->personal);
1530 if(name == env->from->personal){
1531 strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1);
1532 tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
1533 name = tmp_20k_buf;
1536 if(name && *name){
1537 initials = reply_quote_initials(name);
1538 iutf8ncpy(buf, initials, maxlen);
1539 buf[maxlen] = '\0';
1543 return;
1545 default:
1546 break;
1549 orig_maxlen = maxlen;
1551 first_addr = addr;
1552 /* skip over rest of c-client group addr */
1553 if(first_addr && first_addr->mailbox && !first_addr->host){
1554 for(second_addr = first_addr->next;
1555 second_addr && second_addr->host;
1556 second_addr = second_addr->next)
1559 if(second_addr && !second_addr->host)
1560 second_addr = second_addr->next;
1562 else if(!(first_addr && first_addr->host && first_addr->host[0] == '.'))
1563 second_addr = first_addr ? first_addr->next : NULL;
1565 if(second_addr && second_addr->mailbox && !second_addr->host){
1566 for(third_addr = second_addr->next;
1567 third_addr && third_addr->host;
1568 third_addr = third_addr->next)
1571 if(third_addr && !third_addr->host)
1572 third_addr = third_addr->next;
1574 else if(!(second_addr && second_addr->host && second_addr->host[0] == '.'))
1575 third_addr = second_addr ? second_addr->next : NULL;
1577 if(third_addr && third_addr->mailbox && !third_addr->host){
1578 for(fourth_addr = third_addr->next;
1579 fourth_addr && fourth_addr->host;
1580 fourth_addr = fourth_addr->next)
1583 if(fourth_addr && !fourth_addr->host)
1584 fourth_addr = fourth_addr->next;
1586 else if(!(third_addr && third_addr->host && third_addr->host[0] == '.'))
1587 fourth_addr = third_addr ? third_addr->next : NULL;
1589 /* Just attempting to make a nice display */
1590 if(first_addr && ((first_addr->personal && first_addr->personal[0]) ||
1591 (first_addr->mailbox && first_addr->mailbox[0]))){
1592 if(second_addr){
1593 if((second_addr->personal && second_addr->personal[0]) ||
1594 (second_addr->mailbox && second_addr->mailbox[0])){
1595 if(third_addr){
1596 if((third_addr->personal && third_addr->personal[0]) ||
1597 (third_addr->mailbox && third_addr->mailbox[0])){
1598 if(fourth_addr)
1599 cntaddr = 4;
1600 else
1601 cntaddr = 3;
1603 else
1604 cntaddr = -1;
1606 else
1607 cntaddr = 2;
1609 else
1610 cntaddr = -1;
1612 else
1613 cntaddr = 1;
1615 else
1616 cntaddr = -1;
1618 p = buf;
1619 if(cntaddr == 1)
1620 a_little_addr_string(first_addr, p, maxlen);
1621 else if(cntaddr == 2){
1622 a_little_addr_string(first_addr, p, maxlen);
1623 maxlen -= (l=strlen(p));
1624 p += l;
1625 if(maxlen > 7){
1626 strncpy(p, " and ", maxlen);
1627 maxlen -= 5;
1628 p += 5;
1629 a_little_addr_string(second_addr, p, maxlen);
1632 else if(cntaddr == 3){
1633 a_little_addr_string(first_addr, p, maxlen);
1634 maxlen -= (l=strlen(p));
1635 p += l;
1636 if(maxlen > 7){
1637 strncpy(p, ", ", maxlen);
1638 maxlen -= 2;
1639 p += 2;
1640 a_little_addr_string(second_addr, p, maxlen);
1641 maxlen -= (l=strlen(p));
1642 p += l;
1643 if(maxlen > 7){
1644 strncpy(p, ", and ", maxlen);
1645 maxlen -= 6;
1646 p += 6;
1647 a_little_addr_string(third_addr, p, maxlen);
1651 else if(cntaddr > 3){
1652 a_little_addr_string(first_addr, p, maxlen);
1653 maxlen -= (l=strlen(p));
1654 p += l;
1655 if(maxlen > 7){
1656 strncpy(p, ", ", maxlen);
1657 maxlen -= 2;
1658 p += 2;
1659 a_little_addr_string(second_addr, p, maxlen);
1660 maxlen -= (l=strlen(p));
1661 p += l;
1662 if(maxlen > 7){
1663 strncpy(p, ", ", maxlen);
1664 maxlen -= 2;
1665 p += 2;
1666 a_little_addr_string(third_addr, p, maxlen);
1667 maxlen -= (l=strlen(p));
1668 p += l;
1669 if(maxlen >= 12)
1670 strncpy(p, ", and others", maxlen);
1671 else if(maxlen >= 3)
1672 strncpy(p, "...", maxlen);
1676 else if(addr){
1677 char *a_string;
1679 a_string = addr_list_string(addr, NULL, 0);
1680 iutf8ncpy(buf, a_string, maxlen);
1682 fs_give((void **)&a_string);
1685 if(last_to)
1686 last_to->next = NULL;
1688 buf[orig_maxlen] = '\0';
1693 * Buf is at least size maxlen+1
1695 void
1696 get_news_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
1698 int cntnews = 0, orig_maxlen;
1699 char *news = NULL, *p, *q;
1701 switch(type){
1702 case iNews:
1703 case iNewsAndTo:
1704 case iToAndNews:
1705 case iNewsAndRecips:
1706 case iRecipsAndNews:
1707 news = env ? env->newsgroups : NULL;
1708 break;
1710 case iCurNews:
1711 if(ps_global->mail_stream && IS_NEWS(ps_global->mail_stream))
1712 news = ps_global->cur_folder;
1714 break;
1716 default:
1717 break;
1720 orig_maxlen = maxlen;
1722 if(news){
1723 q = news;
1724 while(isspace((unsigned char)*q))
1725 q++;
1727 if(*q)
1728 cntnews++;
1730 while((q = strindex(q, ',')) != NULL){
1731 q++;
1732 while(isspace((unsigned char)*q))
1733 q++;
1735 if(*q)
1736 cntnews++;
1737 else
1738 break;
1742 if(cntnews == 1){
1743 istrncpy(buf, news, maxlen);
1744 buf[maxlen] = '\0';
1745 removing_leading_and_trailing_white_space(buf);
1747 else if(cntnews == 2){
1748 p = buf;
1749 q = news;
1750 while(isspace((unsigned char)*q))
1751 q++;
1753 while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
1754 *p++ = *q++;
1755 maxlen--;
1758 if(maxlen > 7){
1759 strncpy(p, " and ", maxlen);
1760 p += 5;
1761 maxlen -= 5;
1764 while(isspace((unsigned char)*q) || *q == ',')
1765 q++;
1767 while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
1768 *p++ = *q++;
1769 maxlen--;
1772 *p = '\0';
1774 istrncpy(tmp_20k_buf, buf, 10000);
1775 strncpy(buf, tmp_20k_buf, orig_maxlen);
1777 else if(cntnews > 2){
1778 char b[100];
1780 p = buf;
1781 q = news;
1782 while(isspace((unsigned char)*q))
1783 q++;
1785 while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
1786 *p++ = *q++;
1787 maxlen--;
1790 *p = '\0';
1791 snprintf(b, sizeof(b), " and %d other newsgroups", cntnews-1);
1792 b[sizeof(b)-1] = '\0';
1793 if(maxlen >= strlen(b))
1794 strncpy(p, b, maxlen);
1795 else if(maxlen >= 3)
1796 strncpy(p, "...", maxlen);
1798 buf[orig_maxlen] = '\0';
1800 istrncpy(tmp_20k_buf, buf, 10000);
1801 tmp_20k_buf[10000-1] = '\0';
1802 strncpy(buf, tmp_20k_buf, orig_maxlen);
1805 buf[orig_maxlen] = '\0';
1808 void
1809 shorten_subject(char *origsubj)
1811 char *s, *t, *u;
1812 int endlist = 0;
1814 if(origsubj == NULL || *origsubj == '\0')
1815 return;
1817 for(t=s=origsubj; *s ; s++){
1818 switch(*s){
1819 /* this transforms "A [B [C] D" into "A D" should this be
1820 * "A [B D"?
1822 case '[' : if((u = strchr(s+1,']')) != NULL){
1823 s = u;
1824 endlist = 1;
1826 else
1827 *t++ = *s;
1828 break;
1829 case ' ' : if(endlist == 0) *t++ = *s; break;
1830 default : endlist = 0; *t++ = *s; break;
1833 *t = '\0';
1837 * Buf is at least size maxlen+1
1839 char *
1840 get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size_t maxlen)
1842 char *space = NULL;
1843 IndexColType addrtype;
1845 buf[0] = '\0';
1847 switch(type){
1848 case iRDate: case iSDate: case iSTime: case iSTime24:
1849 case iS1Date: case iS2Date: case iS3Date: case iS4Date:
1850 case iSDateIso: case iSDateIsoS:
1851 case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
1852 case iSDateTime:
1853 case iSDateTimeIso: case iSDateTimeIsoS:
1854 case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
1855 case iSDateTime24:
1856 case iSDateTimeIso24: case iSDateTimeIsoS24:
1857 case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
1858 case iDateIso: case iDateIsoS: case iTime24: case iTime12:
1859 case iDay: case iDayOrdinal: case iDay2Digit:
1860 case iMonAbb: case iMonLong: case iMon: case iMon2Digit:
1861 case iYear: case iYear2Digit:
1862 case iDate: case iLDate:
1863 case iTimezone: case iDayOfWeekAbb: case iDayOfWeek:
1864 case iPrefDate: case iPrefTime: case iPrefDateTime:
1865 if(env && env->date && env->date[0] && maxlen >= 20)
1866 date_str((char *) env->date, type, 1, buf, maxlen+1, 0);
1868 break;
1870 case iCurDate:
1871 case iCurDateIso:
1872 case iCurDateIsoS:
1873 case iCurTime24:
1874 case iCurTime12:
1875 case iCurDay:
1876 case iCurDay2Digit:
1877 case iCurDayOfWeek:
1878 case iCurDayOfWeekAbb:
1879 case iCurMon:
1880 case iCurMon2Digit:
1881 case iCurMonLong:
1882 case iCurMonAbb:
1883 case iCurYear:
1884 case iCurYear2Digit:
1885 case iCurPrefDate:
1886 case iCurPrefDateTime:
1887 case iCurPrefTime:
1888 case iLstMon:
1889 case iLstMon2Digit:
1890 case iLstMonLong:
1891 case iLstMonAbb:
1892 case iLstMonYear:
1893 case iLstMonYear2Digit:
1894 case iLstYear:
1895 case iLstYear2Digit:
1896 if(maxlen >= 20)
1897 date_str(NULL, type, 1, buf, maxlen+1, 0);
1899 break;
1901 case iFrom:
1902 case iTo:
1903 case iCc:
1904 case iSender:
1905 case iRecips:
1906 case iInit:
1907 get_addr_data(env, type, buf, maxlen);
1908 break;
1910 case iRoleNick:
1911 if(role && role->nick){
1912 strncpy(buf, role->nick, maxlen);
1913 buf[maxlen] = '\0';
1915 break;
1917 case iNewLine:
1918 if(maxlen >= strlen(NEWLINE)){
1919 strncpy(buf, NEWLINE, maxlen);
1920 buf[maxlen] = '\0';
1922 break;
1924 case iAddress:
1925 case iMailbox:
1926 if(env && env->from && env->from->mailbox && env->from->mailbox[0] &&
1927 strlen(env->from->mailbox) <= maxlen){
1928 strncpy(buf, env->from->mailbox, maxlen);
1929 buf[maxlen] = '\0';
1930 if(type == iAddress &&
1931 env->from->host &&
1932 env->from->host[0] &&
1933 env->from->host[0] != '.' &&
1934 strlen(buf) + strlen(env->from->host) + 1 <= maxlen){
1935 strncat(buf, "@", maxlen+1-1-strlen(buf));
1936 buf[maxlen] = '\0';
1937 strncat(buf, env->from->host, maxlen+1-1-strlen(buf));
1938 buf[maxlen] = '\0';
1942 break;
1944 case iNews:
1945 case iCurNews:
1946 get_news_data(env, type, buf, maxlen);
1947 break;
1949 case iToAndNews:
1950 case iNewsAndTo:
1951 case iRecipsAndNews:
1952 case iNewsAndRecips:
1953 if(type == iToAndNews || type == iNewsAndTo)
1954 addrtype = iTo;
1955 else
1956 addrtype = iRecips;
1958 if(env && env->newsgroups){
1959 space = (char *)fs_get((maxlen+1) * sizeof(char));
1960 get_news_data(env, type, space, maxlen);
1963 get_addr_data(env, addrtype, buf, maxlen);
1965 if(space && *space && *buf){
1966 if(strlen(space) + strlen(buf) + 5 > maxlen){
1967 if(strlen(space) > maxlen/2)
1968 get_news_data(env, type, space, maxlen - strlen(buf) - 5);
1969 else
1970 get_addr_data(env, addrtype, buf, maxlen - strlen(space) - 5);
1973 if(type == iToAndNews || type == iRecipsAndNews)
1974 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", buf, space);
1975 else
1976 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", space, buf);
1978 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1980 strncpy(buf, tmp_20k_buf, maxlen);
1981 buf[maxlen] = '\0';
1983 else if(space && *space){
1984 strncpy(buf, space, maxlen);
1985 buf[maxlen] = '\0';
1988 if(space)
1989 fs_give((void **)&space);
1991 break;
1993 case iSubject:
1994 case iShortSubject:
1995 if(env && env->subject){
1996 size_t n, len;
1997 unsigned char *p, *tmp = NULL;
1999 if((n = 4*strlen(env->subject)) > SIZEOF_20KBUF-1){
2000 len = n+1;
2001 p = tmp = (unsigned char *)fs_get(len * sizeof(char));
2003 else{
2004 len = SIZEOF_20KBUF;
2005 p = (unsigned char *)tmp_20k_buf;
2008 istrncpy(buf, (char *)rfc1522_decode_to_utf8(p, len, env->subject), maxlen);
2010 buf[maxlen] = '\0';
2012 if(tmp)
2013 fs_give((void **)&tmp);
2015 if(type == iShortSubject)
2016 shorten_subject(buf);
2019 break;
2021 case iMsgID:
2022 if(env && env->message_id){
2023 strncpy(buf, env->message_id, maxlen);
2024 buf[maxlen] = '\0';
2027 break;
2029 default:
2030 break;
2033 buf[maxlen] = '\0';
2034 return(buf);
2039 * reply_delimiter - output formatted reply delimiter for given envelope
2040 * with supplied character writing function.
2042 void
2043 reply_delimiter(ENVELOPE *env, ACTION_S *role, gf_io_t pc)
2045 #define MAX_DELIM 2000
2046 char buf[MAX_DELIM+1];
2047 char *p;
2048 char *filtered = NULL;
2049 int contains_newline_token = 0;
2052 if(!env)
2053 return;
2055 strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM);
2056 buf[MAX_DELIM] = '\0';
2057 /* preserve exact default behavior from before */
2058 if(!strcmp(buf, DEFAULT_REPLY_INTRO)){
2059 struct date d;
2060 int include_date;
2062 parse_date((char *) env->date, &d);
2063 include_date = !(d.day == -1 || d.month == -1 || d.year == -1);
2064 if(include_date){
2065 gf_puts("On ", pc); /* All delims have... */
2066 if(d.wkday != -1){ /* "On day, date month year" */
2067 gf_puts(day_abbrev(d.wkday), pc); /* in common */
2068 gf_puts(", ", pc);
2071 gf_puts(int2string(d.day), pc);
2072 (*pc)(' ');
2073 gf_puts(month_abbrev(d.month), pc);
2074 (*pc)(' ');
2075 gf_puts(int2string(d.year), pc);
2078 if(env->from
2079 && ((env->from->personal && env->from->personal[0])
2080 || (env->from->mailbox && env->from->mailbox[0]))){
2081 char buftmp[MAILTMPLEN];
2083 a_little_addr_string(env->from, buftmp, sizeof(buftmp)-1);
2084 if(include_date)
2085 gf_puts(", ", pc);
2087 gf_puts(buftmp, pc);
2088 gf_puts(" wrote:", pc);
2090 else{
2091 if(include_date)
2092 gf_puts(", it was written", pc);
2093 else
2094 gf_puts("It was written", pc);
2098 else{
2100 * This is here for backwards compatibility. There didn't used
2101 * to be a _NEWLINE_ token. The user would enter text that should
2102 * all fit on one line and then that was followed by two newlines.
2103 * Also, truncation occurs if it is long.
2104 * Now, if _NEWLINE_ is not in the text, same thing still works
2105 * the same. However, if _NEWLINE_ is in there, then all bets are
2106 * off and the user is on his or her own. No automatic newlines
2107 * are added, only those that come from the tokens. No truncation
2108 * is done, the user is trusted to get it right. Newlines may be
2109 * embedded so that the leadin is multi-line.
2111 contains_newline_token = (strstr(buf, "_NEWLINE_") != NULL);
2112 filtered = detoken_src(buf, FOR_REPLY_INTRO, env, role,
2113 NULL, NULL);
2115 /* try to truncate if too long */
2116 if(!contains_newline_token && filtered && utf8_width(filtered) > 80){
2117 int ended_with_colon = 0;
2118 int ended_with_quote = 0;
2119 int ended_with_quote_colon = 0;
2120 int l;
2122 l = strlen(filtered);
2124 if(filtered[l-1] == ':'){
2125 ended_with_colon = ':';
2126 if(filtered[l-2] == QUOTE || filtered[l-2] == '\'')
2127 ended_with_quote_colon = filtered[l-2];
2129 else if(filtered[l-1] == QUOTE || filtered[l-1] == '\'')
2130 ended_with_quote = filtered[l-1];
2132 /* try to find space to break at */
2133 for(p = &filtered[75]; p > &filtered[60] &&
2134 !isspace((unsigned char)*p); p--)
2137 if(!isspace((unsigned char)*p))
2138 p = &filtered[75];
2140 *p++ = '.';
2141 *p++ = '.';
2142 *p++ = '.';
2143 if(ended_with_quote_colon){
2144 *p++ = ended_with_quote_colon;
2145 *p++ = ':';
2147 else if(ended_with_colon)
2148 *p++ = ended_with_colon;
2149 else if(ended_with_quote)
2150 *p++ = ended_with_quote;
2152 *p = '\0';
2155 if(filtered && *filtered)
2156 gf_puts(filtered, pc);
2159 /* and end with two newlines unless no leadin at all */
2160 if(!contains_newline_token){
2161 if(!strcmp(buf, DEFAULT_REPLY_INTRO) || (filtered && *filtered)){
2162 gf_puts(NEWLINE, pc);
2163 gf_puts(NEWLINE, pc);
2167 if(filtered)
2168 fs_give((void **)&filtered);
2172 void
2173 free_redraft_pos(REDRAFT_POS_S **redraft_pos)
2175 if(redraft_pos && *redraft_pos){
2176 if((*redraft_pos)->hdrname)
2177 fs_give((void **)&(*redraft_pos)->hdrname);
2179 fs_give((void **)redraft_pos);
2184 /*----------------------------------------------------------------------
2185 Build the body for the message number/part being forwarded as ATTACHMENT
2187 Args:
2189 Result: PARTS suitably attached to body
2191 ----------------------------------------------------------------------*/
2193 forward_mime_msg(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env, struct mail_body_part **partp, void *msgtext)
2195 char *tmp_text;
2196 unsigned long len;
2197 BODY *b;
2199 *partp = mail_newbody_part();
2200 b = &(*partp)->body;
2201 b->type = TYPEMESSAGE;
2202 b->id = generate_message_id();
2203 b->description = cpystr("Forwarded Message");
2204 b->nested.msg = mail_newmsg();
2205 b->disposition.type = cpystr("inline");
2207 /*---- Package each message in a storage object ----*/
2208 if((b->contents.text.data = (void *) so_get(PART_SO_TYPE,NULL,EDIT_ACCESS))
2209 && (tmp_text = mail_fetch_header(stream,msgno,section,NIL,NIL,FT_PEEK))
2210 && *tmp_text){
2211 so_puts((STORE_S *) b->contents.text.data, tmp_text);
2213 b->size.bytes = strlen(tmp_text);
2214 so_puts((STORE_S *) b->contents.text.data, "\015\012");
2215 if((tmp_text = pine_mail_fetch_text (stream,msgno,section,&len,NIL)) != NULL){
2216 so_nputs((STORE_S *)b->contents.text.data,tmp_text,(long) len);
2217 b->size.bytes += len;
2218 return(1);
2222 return(0);
2227 * forward_delimiter - return delimiter for forwarded text
2229 void
2230 forward_delimiter(gf_io_t pc)
2232 gf_puts(NEWLINE, pc);
2233 /* TRANSLATORS: When a message is forwarded by the user this is the
2234 text that shows where the forwarded part of the message begins. */
2235 gf_puts(_("---------- Forwarded message ----------"), pc);
2236 gf_puts(NEWLINE, pc);
2240 /*----------------------------------------------------------------------
2241 Wrapper for header formatting tool
2243 Args: stream --
2244 msgno --
2245 env --
2246 pc --
2247 prefix --
2249 Result: header suitable for reply/forward text written using "pc"
2251 ----------------------------------------------------------------------*/
2252 void
2253 reply_forward_header(MAILSTREAM *stream, long int msgno, char *part, ENVELOPE *env,
2254 gf_io_t pc, char *prefix)
2256 int rv;
2257 HEADER_S h;
2258 char **list, **new_list = NULL;
2260 list = ps_global->VAR_VIEW_HEADERS;
2263 * If VIEW_HEADERS is set, we should remove BCC from the list so that
2264 * the user doesn't inadvertently forward the BCC header.
2266 if(list && list[0]){
2267 int i, cnt = 0;
2268 char **p;
2270 while(list[cnt++])
2273 p = new_list = (char **) fs_get((cnt+1) * sizeof(char *));
2275 for(i=0; list[i]; i++)
2276 if(strucmp(list[i], "bcc"))
2277 *p++ = cpystr(list[i]);
2279 *p = NULL;
2281 if(new_list && new_list[0])
2282 list = new_list;
2286 HD_INIT(&h, list, ps_global->view_all_except, FE_DEFAULT & ~FE_BCC);
2287 if((rv = format_header(stream, msgno, part, env, &h,
2288 prefix, NULL, FM_NOINDENT, NULL, pc)) != 0){
2289 if(rv == 1)
2290 gf_puts(" [Error fetching message header data]", pc);
2292 else
2293 gf_puts(NEWLINE, pc); /* write header delimiter */
2295 if(new_list)
2296 free_list_array(&new_list);
2300 /*----------------------------------------------------------------------
2301 Build the subject for the message number being forwarded
2303 Args: pine_state -- The usual pine structure
2304 msgno -- The message number to build subject for
2306 Result: malloc'd string containing new subject or NULL on error
2308 ----------------------------------------------------------------------*/
2309 char *
2310 forward_subject(ENVELOPE *env, int flags)
2312 size_t l;
2313 char *p, buftmp[MAILTMPLEN];
2315 if(!env)
2316 return(NULL);
2318 dprint((9, "checking subject: \"%s\"\n",
2319 env->subject ? env->subject : "NULL"));
2321 if(env->subject && env->subject[0]){ /* add (fwd)? */
2322 snprintf(buftmp, sizeof(buftmp), "%s", env->subject);
2323 buftmp[sizeof(buftmp)-1] = '\0';
2324 /* decode any 8bit (copy to the temp buffer if decoding doesn't) */
2325 if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2326 SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp)
2327 strncpy(tmp_20k_buf, buftmp, SIZEOF_20KBUF);
2329 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2331 removing_trailing_white_space(tmp_20k_buf);
2332 if((l = strlen(tmp_20k_buf)) < 1000 &&
2333 (l < 5 || strcmp(tmp_20k_buf+l-5,"(fwd)"))){
2334 snprintf(tmp_20k_buf+2000, SIZEOF_20KBUF-2000, "%s (fwd)", tmp_20k_buf);
2335 tmp_20k_buf[SIZEOF_20KBUF-2000-1] = '\0';
2336 memmove(tmp_20k_buf, tmp_20k_buf+2000, strlen(tmp_20k_buf+2000));
2337 tmp_20k_buf[strlen(tmp_20k_buf+2000)] = '\0';
2341 * HACK: composer can't handle embedded double quotes in attachment
2342 * comments so we substitute two single quotes.
2344 if(flags & FS_CONVERT_QUOTES)
2345 while((p = strchr(tmp_20k_buf, QUOTE)) != NULL)
2346 (void)rplstr(p, SIZEOF_20KBUF-(p-tmp_20k_buf), 1, "''");
2348 return(cpystr(tmp_20k_buf));
2352 return(cpystr("Forwarded mail...."));
2356 /*----------------------------------------------------------------------
2357 Build the body for the message number/part being forwarded
2359 Args:
2361 Result: BODY structure suitable for sending
2363 ----------------------------------------------------------------------*/
2364 BODY *
2365 forward_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
2366 long int msgno, char *sect_prefix, void *msgtext, int flags)
2368 BODY *body = NULL, *text_body, *tmp_body;
2369 PART *part;
2370 gf_io_t pc;
2371 char *tmp_text, *section, sect_buf[256];
2372 int forward_raw_body = 0;
2375 * Check to see if messages got expunged out from underneath us. This
2376 * could have happened during the prompt to the user asking whether to
2377 * include the message as an attachment. Either the message is gone or
2378 * it might be at a different sequence number. We'd better bail.
2380 if(ps_global->full_header == 2
2381 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
2382 forward_raw_body = 1;
2383 if(sp_expunge_count(stream))
2384 return(NULL);
2386 if(sect_prefix && forward_raw_body == 0)
2387 snprintf(section = sect_buf, sizeof(sect_buf), "%s.1", sect_prefix);
2388 else if(sect_prefix && forward_raw_body)
2389 section = sect_prefix;
2390 else if(!sect_prefix && forward_raw_body)
2391 section = NULL;
2392 else
2393 section = "1";
2395 sect_buf[sizeof(sect_buf)-1] = '\0';
2397 gf_set_so_writec(&pc, (STORE_S *) msgtext);
2398 if(!orig_body || orig_body->type == TYPETEXT || forward_raw_body) {
2399 char *charset = NULL;
2401 /*---- Message has a single text part -----*/
2402 body = mail_newbody();
2403 body->type = TYPETEXT;
2404 body->contents.text.data = msgtext;
2405 if(orig_body
2406 && (charset = parameter_val(orig_body->parameter, "charset")))
2407 set_parameter(&body->parameter, "charset", charset);
2409 if(charset)
2410 fs_give((void **) &charset);
2412 if(!(flags & FWD_ANON)){
2413 forward_delimiter(pc);
2414 reply_forward_header(stream, msgno, sect_prefix, env, pc, "");
2417 if(!get_body_part_text(stream, forward_raw_body ? NULL : orig_body,
2418 msgno, section, 0L, pc, NULL, NULL, GBPT_NONE)){
2419 mail_free_body(&body);
2420 return(NULL);
2423 else if(orig_body->type == TYPEMULTIPART) {
2424 if(orig_body->subtype
2425 && ((!strucmp(orig_body->subtype, "signed") && orig_body->nested.part)
2426 #ifdef SMIME
2427 || (!strucmp(orig_body->subtype, "mixed")
2428 && orig_body->nested.part
2429 && orig_body->nested.part->body.type == TYPEMULTIPART
2430 && orig_body->nested.part->body.subtype
2431 && (!strucmp(orig_body->nested.part->body.subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)
2432 || !strucmp(orig_body->nested.part->body.subtype, "signed")))
2433 || !strucmp(orig_body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)
2434 #endif /* SMIME */
2436 /* only operate on the signed data (not the signature) */
2437 body = forward_body(stream, env, &orig_body->nested.part->body,
2438 msgno,
2439 orig_body->nested.part->body.type == TYPEMULTIPART
2440 ? section : sect_prefix, msgtext, flags);
2442 /*---- Message is multipart ----*/
2443 else if(!(orig_body->subtype && !strucmp(orig_body->subtype,
2444 "alternative")
2445 && (body = forward_multi_alt(stream, env, orig_body, msgno,
2446 sect_prefix, msgtext,
2447 pc, flags)))){
2448 /*--- Copy the body and entire structure ---*/
2449 body = copy_body(NULL, orig_body);
2452 * whatever subtype it is, demote it
2453 * to plain old MIXED.
2455 if(body->subtype)
2456 fs_give((void **) &body->subtype);
2458 body->subtype = cpystr("Mixed");
2460 /*--- The text part of the message ---*/
2461 if(!body->nested.part){
2462 q_status_message(SM_ORDER | SM_DING, 3, 6,
2463 "Error referencing body part 1");
2464 mail_free_body(&body);
2466 else if(body->nested.part->body.type == TYPETEXT) {
2467 char *new_charset = NULL;
2469 /*--- The first part is text ----*/
2470 text_body = &body->nested.part->body;
2471 text_body->contents.text.data = msgtext;
2472 if(text_body->subtype && strucmp(text_body->subtype, "Plain")){
2473 /* this text is going to the composer, it should be Plain */
2474 fs_give((void **)&text_body->subtype);
2475 text_body->subtype = cpystr("PLAIN");
2477 if(!(flags & FWD_ANON)){
2478 forward_delimiter(pc);
2479 reply_forward_header(stream, msgno,
2480 sect_prefix, env, pc, "");
2483 if(!(get_body_part_text(stream, &orig_body->nested.part->body,
2484 msgno, section, 0L, pc,
2485 NULL, &new_charset, GBPT_NONE)
2486 && fetch_contents(stream, msgno, sect_prefix, body)))
2487 mail_free_body(&body);
2488 else if(new_charset)
2489 set_parameter(&text_body->parameter, "charset", new_charset);
2491 /* BUG: ? matter that we're not setting body.size.bytes */
2493 else if(body->nested.part->body.type == TYPEMULTIPART
2494 && body->nested.part->body.subtype
2495 && !strucmp(body->nested.part->body.subtype, "alternative")
2496 && (tmp_body = forward_multi_alt(stream, env,
2497 &body->nested.part->body,
2498 msgno, sect_prefix,
2499 msgtext, pc,
2500 flags | FWD_NESTED))){
2501 /* for the forward_multi_alt call above, we want to pass
2502 * sect_prefix instead of section so we can obtain the header.
2503 * Set the FWD_NESTED flag so we fetch the right body_part.
2505 int partnum;
2507 part = body->nested.part->next;
2508 body->nested.part->next = NULL;
2509 mail_free_body_part(&body->nested.part);
2510 body->nested.part = mail_newbody_part();
2511 body->nested.part->body = *tmp_body;
2512 body->nested.part->next = part;
2514 for(partnum = 2; part != NULL; part = part->next){
2515 snprintf(sect_buf, sizeof(sect_buf), "%s%s%d",
2516 sect_prefix ? sect_prefix : "",
2517 sect_prefix ? "." : "", partnum++);
2518 sect_buf[sizeof(sect_buf)-1] = '\0';
2520 if(!fetch_contents(stream, msgno, sect_buf, &part->body)){
2521 mail_free_body(&body);
2522 break;
2526 else {
2527 if(fetch_contents(stream, msgno, sect_prefix, body)){
2528 /*--- Create a new blank text part ---*/
2529 part = mail_newbody_part();
2530 part->next = body->nested.part;
2531 body->nested.part = part;
2532 part->body.contents.text.data = msgtext;
2534 else
2535 mail_free_body(&body);
2539 else {
2540 /*---- A single part message, not of type text ----*/
2541 body = mail_newbody();
2542 body->type = TYPEMULTIPART;
2543 part = mail_newbody_part();
2544 body->nested.part = part;
2546 /*--- The first part, a blank text part to be edited ---*/
2547 part->body.type = TYPETEXT;
2548 part->body.contents.text.data = msgtext;
2550 /*--- The second part, what ever it is ---*/
2551 part->next = mail_newbody_part();
2552 part = part->next;
2553 part->body.id = generate_message_id();
2554 copy_body(&(part->body), orig_body);
2557 * the idea here is to fetch part into storage object
2559 if((part->body.contents.text.data = (void *) so_get(PART_SO_TYPE, NULL,
2560 EDIT_ACCESS)) != NULL){
2561 if((tmp_text = pine_mail_fetch_body(stream, msgno, section,
2562 &part->body.size.bytes, NIL)) != NULL)
2563 so_nputs((STORE_S *)part->body.contents.text.data, tmp_text,
2564 part->body.size.bytes);
2565 else
2566 mail_free_body(&body);
2568 else
2569 mail_free_body(&body);
2572 gf_clear_so_writec((STORE_S *) msgtext);
2574 return(body);
2580 * bounce_msg_body - build body from specified message suitable
2581 * for sending as bounced message
2583 char *
2584 bounce_msg_body(MAILSTREAM *stream,
2585 long int rawno,
2586 char *part,
2587 char **to,
2588 char *subject,
2589 ENVELOPE **outgoingp,
2590 BODY **bodyp,
2591 int *seenp)
2593 char *h, *p, *errstr = NULL;
2594 int i;
2595 STORE_S *msgtext;
2596 gf_io_t pc;
2598 *outgoingp = mail_newenvelope();
2599 (*outgoingp)->message_id = generate_message_id();
2600 (*outgoingp)->subject = cpystr(subject ? subject : "Resent mail....");
2603 * Fill in destination if we were given one. If so, note that we
2604 * call p_s_s() below such that it won't prompt...
2606 if(to && *to){
2607 static char *fakedomain = "@";
2608 char *tmp_a_string;
2610 /* rfc822_parse_adrlist feels free to destroy input so copy */
2611 tmp_a_string = cpystr(*to);
2612 rfc822_parse_adrlist(&(*outgoingp)->to, tmp_a_string,
2613 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
2614 ? fakedomain : ps_global->maildomain);
2615 fs_give((void **) &tmp_a_string);
2618 /* build remail'd header */
2619 if((h = mail_fetch_header(stream, rawno, part, NULL, 0, FT_PEEK)) != NULL){
2620 for(p = h, i = 0; (p = strchr(p, ':')) != NULL; p++)
2621 i++;
2623 /* allocate it */
2624 (*outgoingp)->remail = (char *) fs_get(strlen(h) + (2 * i) + 1);
2627 * copy it, "X-"ing out transport headers bothersome to
2628 * software but potentially useful to the human recipient...
2630 p = (*outgoingp)->remail;
2631 bounce_mask_header(&p, h);
2633 if(*h == '\015' && *(h+1) == '\012'){
2634 *p++ = *h++; /* copy CR LF */
2635 *p++ = *h++;
2636 bounce_mask_header(&p, h);
2638 while((*p++ = *h++) != '\0');
2640 /* BUG: else complain? */
2642 /* NOT bound for the composer, so no need for PicoText */
2643 if(!(msgtext = so_get(CharStar, NULL, EDIT_ACCESS))){
2644 mail_free_envelope(outgoingp);
2645 return(_("Error allocating message text"));
2648 /* mark object for special handling */
2649 so_attr(msgtext, "rawbody", "1");
2652 * Build a fake body description. It's ignored by pine_rfc822_header,
2653 * but we need to set it to something that makes set_mime_types
2654 * not sniff it and pine_rfc822_output_body not re-encode it.
2655 * Setting the encoding to (ENCMAX + 1) will work and shouldn't cause
2656 * problems unless something tries to access body_encodings[] using
2657 * it without proper precautions. We don't want to use ENCOTHER
2658 * cause that tells set_mime_types to sniff it, and we don't want to
2659 * use ENC8BIT since that tells pine_rfc822_output_body to qp-encode
2660 * it. When there's time, it'd be nice to clean this interaction
2661 * up...
2663 *bodyp = mail_newbody();
2664 (*bodyp)->type = TYPETEXT;
2665 (*bodyp)->encoding = ENCMAX + 1;
2666 (*bodyp)->subtype = cpystr("Plain");
2667 (*bodyp)->contents.text.data = (void *) msgtext;
2668 gf_set_so_writec(&pc, msgtext);
2670 if(seenp && rawno > 0L && stream && rawno <= stream->nmsgs){
2671 MESSAGECACHE *mc;
2673 if((mc = mail_elt(stream, rawno)) != NULL)
2674 *seenp = mc->seen;
2677 /* pass NULL body to force mail_fetchtext */
2678 if(!get_body_part_text(stream, NULL, rawno, part, 0L, pc, NULL, NULL, GBPT_NONE))
2679 errstr = _("Error fetching message contents. Can't Bounce message");
2681 gf_clear_so_writec(msgtext);
2683 return(errstr);
2688 /*----------------------------------------------------------------------
2689 Mask off any header entries we don't want xport software to see
2691 Args: d -- destination string pointer pointer
2692 s -- source string pointer pointer
2694 Postfix uses Delivered-To to detect loops.
2695 Received line counting is also used to detect loops in places.
2697 ----*/
2698 void
2699 bounce_mask_header(char **d, char *s)
2701 if(((*s == 'R' || *s == 'r')
2702 && (!struncmp(s+1, "esent-", 6) || !struncmp(s+1, "eceived:", 8)
2703 || !struncmp(s+1, "eturn-Path", 10)))
2704 || !struncmp(s, "Delivered-To:", 13)){
2705 *(*d)++ = 'X'; /* write mask */
2706 *(*d)++ = '-';
2711 /*----------------------------------------------------------------------
2712 Fetch and format text for forwarding
2714 Args: stream -- Mail stream to fetch text from
2715 body -- Body structure of message being forwarded
2716 msg_no -- Message number of text for forward
2717 part_no -- Part number of text to forward
2718 partial -- If this is > 0 a partial fetch will be done and it will
2719 be done using FT_PEEK so the message will remain unseen.
2720 pc -- Function to write to
2721 prefix -- Prefix for each line
2722 ret_charset -- If we translate to another charset return that
2723 new charset here
2725 Returns: true if OK, false if problem occurred while filtering
2727 If the text is richtext, it will be converted to plain text, since there's
2728 no rich text editing capabilities in Pine (yet).
2730 It's up to calling routines to plug in signature appropriately
2732 As with all internal text, NVT end-of-line conventions are observed.
2733 DOESN'T sanity check the prefix given!!!
2734 ----*/
2736 get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body,
2737 long int msg_no, char *part_no, long partial, gf_io_t pc,
2738 char *prefix, char **ret_charset, unsigned flags)
2740 int we_cancel = 0, dashdata, wrapflags = GFW_FORCOMPOSE, flow_res = 0;
2741 FILTLIST_S filters[12];
2742 long len;
2743 char *err, *charset, *prefix_p = NULL;
2744 int filtcnt = 0;
2745 char *free_this = NULL;
2746 DELQ_S dq;
2748 memset(filters, 0, sizeof(filters));
2749 if(ret_charset)
2750 *ret_charset = NULL;
2752 if(!pc_is_picotext(pc))
2753 we_cancel = busy_cue(NULL, NULL, 1);
2755 /* if null body, we must be talking to a non-IMAP2bis server.
2756 * No MIME parsing provided, so we just grab the message text...
2758 if(body == NULL){
2759 char *text, *decode_error;
2760 gf_io_t gc;
2761 SourceType src = CharStar;
2762 int rv = 0;
2764 (void) pine_mail_fetchstructure(stream, msg_no, NULL);
2766 if((text = pine_mail_fetch_text(stream, msg_no, part_no, NULL, 0)) != NULL){
2767 gf_set_readc(&gc, text, (unsigned long)strlen(text), src, 0);
2769 gf_filter_init(); /* no filters needed */
2770 if(prefix)
2771 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2772 if((decode_error = gf_pipe(gc, pc)) != NULL){
2773 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s [Formatting error: %s]%s",
2774 NEWLINE, NEWLINE,
2775 decode_error, NEWLINE);
2776 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2777 gf_puts(tmp_20k_buf, pc);
2778 rv++;
2781 else{
2782 gf_puts(NEWLINE, pc);
2783 gf_puts(_(" [ERROR fetching text of message]"), pc);
2784 gf_puts(NEWLINE, pc);
2785 gf_puts(NEWLINE, pc);
2786 rv++;
2789 if(we_cancel)
2790 cancel_busy_cue(-1);
2792 return(rv == 0);
2795 charset = parameter_val(body->parameter, "charset");
2797 if(charset && strucmp(charset, "utf-8") && strucmp(charset, "us-ascii")){
2798 if(ret_charset)
2799 *ret_charset = "UTF-8";
2802 if(charset)
2803 fs_give((void **) &charset);
2806 * just use detach, but add an auxiliary filter to insert prefix,
2807 * and, perhaps, digest richtext
2809 if(ps_global->full_header != 2
2810 && !ps_global->postpone_no_flow
2811 && (!body->subtype || !strucmp(body->subtype, "plain"))){
2812 char *parmval;
2814 flow_res = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
2815 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
2816 && (!prefix || (strucmp(prefix,"> ") == 0)
2817 || strucmp(prefix, ">") == 0));
2818 if((parmval = parameter_val(body->parameter,
2819 "format")) != NULL){
2820 if(!strucmp(parmval, "flowed")){
2821 wrapflags |= GFW_FLOWED;
2823 fs_give((void **) &parmval);
2824 if((parmval = parameter_val(body->parameter, "delsp")) != NULL){
2825 if(!strucmp(parmval, "yes")){
2826 filters[filtcnt++].filter = gf_preflow;
2827 wrapflags |= GFW_DELSP;
2830 fs_give((void **) &parmval);
2834 * if there's no prefix we're forwarding text
2835 * otherwise it's reply text. unless the user's
2836 * tied our hands, alter the prefix to continue flowed
2837 * formatting...
2839 if(flow_res && ps_global->reply.use_flowed)
2840 wrapflags |= GFW_FLOW_RESULT;
2842 filters[filtcnt].filter = gf_wrap;
2844 * The 80 will cause longer lines than what is likely
2845 * set by composer_fillcol, so we'll have longer
2846 * quoted and forwarded lines than the lines we type.
2847 * We're fine with that since the alternative is the
2848 * possible wrapping of lines we shouldn't have, which
2849 * is mitigated by this higher 80 limit.
2851 * If we were to go back to using composer_fillcol,
2852 * the correct value is composer_fillcol + 1, pine
2853 * is off-by-one from pico.
2855 filters[filtcnt++].data = gf_wrap_filter_opt(
2856 MAX(MIN(ps_global->ttyo->screen_cols
2857 - (prefix ? strlen(prefix) : 0),
2858 80 - (prefix ? strlen(prefix) : 0)),
2859 30), /* doesn't have to be 30 */
2860 990, /* 998 is the SMTP limit */
2861 NULL, 0, wrapflags);
2866 * if not flowed, remove trailing whitespace to reduce
2867 * confusion, since we're sending out as flowed if we
2868 * can. At some future point, we might try
2869 * plugging in a user-option-controlled heuristic
2870 * flowing filter
2872 * We also want to fold "> " quotes so we get the
2873 * attributions correct.
2875 if(flow_res && ps_global->reply.use_flowed && prefix && !strucmp(prefix, "> "))
2876 *(prefix_p = prefix + 1) = '\0';
2877 ps_global->reply.use_flowed = 1; /* reset for next call */
2878 if(!(wrapflags & GFW_FLOWED)
2879 && flow_res){
2880 filters[filtcnt].filter = gf_line_test;
2881 filters[filtcnt++].data = gf_line_test_opt(twsp_strip, NULL);
2883 filters[filtcnt].filter = gf_line_test;
2884 filters[filtcnt++].data = gf_line_test_opt(quote_fold, NULL);
2887 else if(body->subtype){
2888 int plain_opt = 1;
2890 if(strucmp(body->subtype,"richtext") == 0){
2891 filters[filtcnt].filter = gf_rich2plain;
2892 filters[filtcnt++].data = gf_rich2plain_opt(&plain_opt);
2894 else if(strucmp(body->subtype,"enriched") == 0){
2895 filters[filtcnt].filter = gf_enriched2plain;
2896 filters[filtcnt++].data = gf_enriched2plain_opt(&plain_opt);
2898 else if(strucmp(body->subtype,"html") == 0){
2899 if((flags & GBPT_HTML_OK) != GBPT_HTML_OK){
2900 filters[filtcnt].filter = gf_html2plain;
2901 filters[filtcnt++].data = gf_html2plain_opt(NULL,
2902 ps_global->ttyo->screen_cols,
2903 non_messageview_margin(),
2904 NULL, NULL, GFHP_STRIPPED);
2909 if(prefix){
2910 if(ps_global->reply.strip_signature){
2911 dashdata = 0;
2912 filters[filtcnt].filter = gf_line_test;
2913 filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata);
2916 filters[filtcnt].filter = gf_prefix;
2917 filters[filtcnt++].data = gf_prefix_opt(prefix);
2919 if(wrapflags & GFW_FLOWED || flow_res){
2920 filters[filtcnt].filter = gf_line_test;
2921 filters[filtcnt++].data = gf_line_test_opt(post_quote_space, NULL);
2925 if(flags & GBPT_DELQUOTES){
2926 memset(&dq, 0, sizeof(dq));
2927 dq.lines = Q_DEL_ALL;
2928 dq.is_flowed = 0;
2929 dq.indent_length = 0;
2930 dq.saved_line = &free_this;
2931 dq.handlesp = NULL;
2932 dq.do_color = 0;
2933 dq.delete_all = 1;
2935 filters[filtcnt].filter = gf_line_test;
2936 filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq);
2939 err = detach(stream, msg_no, part_no, partial, &len, pc,
2940 filters[0].filter ? filters : NULL,
2941 ((flags & GBPT_PEEK) ? FT_PEEK : 0)
2942 | ((flags & GBPT_NOINTR) ? DT_NOINTR : 0));
2944 if(free_this)
2945 fs_give((void **) &free_this);
2947 if(prefix_p)
2948 *prefix_p = ' ';
2950 if (err != (char *) NULL)
2951 /* TRANSLATORS: The first arg is error text, the %ld is the message number */
2952 q_status_message2(SM_ORDER, 3, 4, "%s: message number %ld",
2953 err, (void *) msg_no);
2955 if(we_cancel)
2956 cancel_busy_cue(-1);
2958 return((int) len);
2963 quote_fold(long int linenum, char *line, LT_INS_S **ins, void *local)
2965 char *p;
2967 if(*line == '>'){
2968 for(p = line; *p; p++){
2969 if(isspace((unsigned char) *p)){
2970 if(*(p+1) == '>')
2971 ins = gf_line_test_new_ins(ins, p, "", -1);
2973 else if(*p != '>')
2974 break;
2978 return(0);
2983 twsp_strip(long int linenum, char *line, LT_INS_S **ins, void *local)
2985 char *p, *ws = NULL;
2987 for(p = line; *p; p++){
2988 /* don't strip trailing space on signature line */
2989 if(*line == '-' && *(line+1) == '-' && *(line+2) == ' ' && !*(line+3))
2990 break;
2992 if(isspace((unsigned char) *p)){
2993 if(!ws)
2994 ws = p;
2996 else
2997 ws = NULL;
3000 if(ws)
3001 ins = gf_line_test_new_ins(ins, ws, "", -(p - ws));
3003 return(0);
3007 post_quote_space(long int linenum, char *line, LT_INS_S **ins, void *local)
3009 char *p;
3011 for(p = line; *p; p++)
3012 if(*p != '>'){
3013 if(p != line && *p != ' ')
3014 ins = gf_line_test_new_ins(ins, p, " ", 1);
3016 break;
3019 return(0);
3024 sigdash_strip(long int linenum, char *line, LT_INS_S **ins, void *local)
3026 if(*((int *)local)
3027 || (*line == '-' && *(line+1) == '-'
3028 && *(line+2) == ' ' && !*(line+3))){
3029 *((int *) local) = 1;
3030 return(2); /* skip this line! */
3033 return(0);
3037 /*----------------------------------------------------------------------
3038 return the c-client reference name for the given end_body part
3039 ----*/
3040 char *
3041 body_partno(MAILSTREAM *stream, long int msgno, struct mail_bodystruct *end_body)
3043 BODY *body;
3045 (void) pine_mail_fetchstructure(stream, msgno, &body);
3046 return(partno(body, end_body));
3050 /*----------------------------------------------------------------------
3051 return the c-client reference name for the given end_body part
3052 ----*/
3053 char *
3054 partno(struct mail_bodystruct *body, struct mail_bodystruct *end_body)
3056 #define PARTTMPLEN 64
3057 PART *part;
3058 int num = 0;
3059 char tmp[PARTTMPLEN], *p = NULL;
3061 if(body && body->type == TYPEMULTIPART) {
3062 part = body->nested.part; /* first body part */
3064 do { /* for each part */
3065 num++; /* PARTTMPLEN = sizeof(tmp) */
3066 if(&part->body == end_body || (p = partno(&part->body, end_body))){
3067 snprintf(tmp, sizeof(tmp), "%d%s%.*s", num, (p) ? "." : "",
3068 PARTTMPLEN-10, (p) ? p : "");
3069 tmp[sizeof(tmp)-1] = '\0';
3070 if(p)
3071 fs_give((void **)&p);
3073 return(cpystr(tmp));
3075 } while ((part = part->next) != NULL); /* until done */
3077 return(NULL);
3079 else if(body && body->type == TYPEMESSAGE && body->subtype
3080 && !strucmp(body->subtype, "rfc822")){
3081 return(partno(body->nested.msg->body, end_body));
3084 return((body == end_body) ? cpystr("1") : NULL);
3088 /*----------------------------------------------------------------------
3089 Fill in the contents of each body part
3091 Args: stream -- Stream the message is on
3092 msgno -- Message number the body structure is for
3093 section -- body section associated with body pointer
3094 body -- Body pointer to fill in
3096 Result: 1 if all went OK, 0 if there was a problem
3098 This function copies the contents from an original message/body to
3099 a new message/body. It recurses down all multipart levels.
3101 If one or more part (but not all) can't be fetched, a status message
3102 will be queued.
3103 ----*/
3105 fetch_contents(MAILSTREAM *stream, long int msgno, char *section, struct mail_bodystruct *body)
3107 char *tp;
3108 int got_one = 0;
3110 if(!body->id)
3111 body->id = generate_message_id();
3113 if(body->type == TYPEMULTIPART){
3114 char subsection[256], *subp;
3115 int n, last_one = 10; /* remember worst case */
3116 PART *part = body->nested.part;
3118 if(!(part = body->nested.part))
3119 return(0);
3121 subp = subsection;
3122 if(section && *section){
3123 for(n = 0;
3124 n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
3127 *subp++ = '.';
3130 n = 1;
3131 do {
3132 snprintf(subp, sizeof(subsection)-(subp-subsection), "%d", n++);
3133 subsection[sizeof(subsection)-1] = '\0';
3134 got_one = fetch_contents(stream, msgno, subsection, &part->body);
3135 last_one = MIN(last_one, got_one);
3137 while((part = part->next) != NULL);
3139 return(last_one);
3142 if(body->contents.text.data)
3143 return(1); /* already taken care of... */
3145 if(body->type == TYPEMESSAGE){
3146 if(body->subtype && strucmp(body->subtype,"external-body")){
3148 * the idea here is to fetch everything into storage objects
3150 body->contents.text.data = (void *) so_get(PART_SO_TYPE, NULL,
3151 EDIT_ACCESS);
3152 if(body->contents.text.data
3153 && (tp = pine_mail_fetch_body(stream, msgno, section,
3154 &body->size.bytes, NIL))){
3155 so_truncate((STORE_S *)body->contents.text.data,
3156 body->size.bytes + 2048);
3157 so_nputs((STORE_S *)body->contents.text.data, tp,
3158 body->size.bytes);
3159 got_one = 1;
3161 else
3162 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3163 _("Error fetching part %s"), section);
3164 } else {
3165 got_one = 1;
3167 } else {
3169 * the idea here is to fetch everything into storage objects
3170 * so, grab one, then fetch the body part
3172 body->contents.text.data = (void *)so_get(PART_SO_TYPE,NULL,EDIT_ACCESS);
3173 if(body->contents.text.data
3174 && (tp=pine_mail_fetch_body(stream, msgno, section,
3175 &body->size.bytes, NIL))){
3176 so_truncate((STORE_S *)body->contents.text.data,
3177 body->size.bytes + 2048);
3178 so_nputs((STORE_S *)body->contents.text.data, tp,
3179 body->size.bytes);
3180 got_one = 1;
3182 else
3183 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3184 _("Error fetching part %s"), section);
3187 return(got_one);
3191 /*----------------------------------------------------------------------
3192 Copy the body structure
3194 Args: new_body -- Pointer to already allocated body, or NULL, if none
3195 old_body -- The Body to copy
3198 This is traverses the body structure recursively copying all elements.
3199 The new_body parameter can be NULL in which case a new body is
3200 allocated. Alternatively it can point to an already allocated body
3201 structure. This is used when copying body parts since a PART includes a
3202 BODY. The contents fields are *not* filled in.
3203 ----*/
3205 BODY *
3206 copy_body(struct mail_bodystruct *new_body, struct mail_bodystruct *old_body)
3208 if(old_body == NULL)
3209 return(NULL);
3211 if(new_body == NULL)
3212 new_body = mail_newbody();
3214 new_body->type = old_body->type;
3215 new_body->encoding = old_body->encoding;
3217 if(old_body->subtype)
3218 new_body->subtype = cpystr(old_body->subtype);
3220 new_body->parameter = copy_parameters(old_body->parameter);
3222 if(old_body->id)
3223 new_body->id = cpystr(old_body->id);
3225 if(old_body->description)
3226 new_body->description = cpystr(old_body->description);
3228 if(old_body->disposition.type)
3229 new_body->disposition.type = cpystr(old_body->disposition.type);
3231 new_body->disposition.parameter
3232 = copy_parameters(old_body->disposition.parameter);
3234 new_body->size = old_body->size;
3236 if(new_body->type == TYPEMESSAGE
3237 && new_body->subtype && !strucmp(new_body->subtype, "rfc822")){
3238 new_body->nested.msg = mail_newmsg();
3239 new_body->nested.msg->body
3240 = copy_body(NULL, old_body->nested.msg->body);
3242 else if(new_body->type == TYPEMULTIPART) {
3243 PART **new_partp, *old_part;
3245 new_partp = &new_body->nested.part;
3246 for(old_part = old_body->nested.part;
3247 old_part != NULL;
3248 old_part = old_part->next){
3249 *new_partp = mail_newbody_part();
3250 copy_body(&(*new_partp)->body, &old_part->body);
3251 new_partp = &(*new_partp)->next;
3255 return(new_body);
3259 /*----------------------------------------------------------------------
3260 Copy the MIME parameter list
3262 Allocates storage for new part, and returns pointer to new parameter
3263 list. If old_p is NULL, NULL is returned.
3264 ----*/
3265 PARAMETER *
3266 copy_parameters(PARAMETER *old_p)
3268 PARAMETER *new_p, *p1, *p2;
3270 if(old_p == NULL)
3271 return((PARAMETER *)NULL);
3273 new_p = p2 = NULL;
3274 for(p1 = old_p; p1 != NULL; p1 = p1->next){
3275 set_parameter(&p2, p1->attribute, p1->value);
3276 if(new_p == NULL)
3277 new_p = p2;
3280 return(new_p);
3284 /*----------------------------------------------------------------------
3285 Make a complete copy of an envelope and all it's fields
3287 Args: e -- the envelope to copy
3289 Result: returns the new envelope, or NULL, if the given envelope was NULL
3291 ----*/
3293 ENVELOPE *
3294 copy_envelope(register ENVELOPE *e)
3296 register ENVELOPE *e2;
3298 if(!e)
3299 return(NULL);
3301 e2 = mail_newenvelope();
3302 e2->remail = e->remail ? cpystr(e->remail) : NULL;
3303 e2->return_path = e->return_path ? rfc822_cpy_adr(e->return_path) : NULL;
3304 e2->date = e->date ? (unsigned char *)cpystr((char *) e->date)
3305 : NULL;
3306 e2->from = e->from ? rfc822_cpy_adr(e->from) : NULL;
3307 e2->sender = e->sender ? rfc822_cpy_adr(e->sender) : NULL;
3308 e2->reply_to = e->reply_to ? rfc822_cpy_adr(e->reply_to) : NULL;
3309 e2->subject = e->subject ? cpystr(e->subject) : NULL;
3310 e2->to = e->to ? rfc822_cpy_adr(e->to) : NULL;
3311 e2->cc = e->cc ? rfc822_cpy_adr(e->cc) : NULL;
3312 e2->bcc = e->bcc ? rfc822_cpy_adr(e->bcc) : NULL;
3313 e2->in_reply_to = e->in_reply_to ? cpystr(e->in_reply_to) : NULL;
3314 e2->newsgroups = e->newsgroups ? cpystr(e->newsgroups) : NULL;
3315 e2->message_id = e->message_id ? cpystr(e->message_id) : NULL;
3316 e2->references = e->references ? cpystr(e->references) : NULL;
3317 e2->followup_to = e->followup_to ? cpystr(e->references) : NULL;
3318 return(e2);
3322 /*----------------------------------------------------------------------
3323 Generate the "In-reply-to" text from message header
3325 Args: message -- Envelope of original message
3327 Result: returns an alloc'd string or NULL if there is a problem
3328 ----*/
3329 char *
3330 reply_in_reply_to(ENVELOPE *env)
3332 return((env && env->message_id) ? cpystr(env->message_id) : NULL);
3336 /*----------------------------------------------------------------------
3337 Generate a unique message id string.
3339 Args: ps -- The usual pine structure
3341 Result: Alloc'd unique string is returned
3343 Uniqueness is guaranteed by using the host name, process id, date to the
3344 second and a single unique character
3345 *----------------------------------------------------------------------*/
3346 char *
3347 generate_message_id(void)
3349 static short osec = 0, cnt = 0;
3350 char idbuf[128], revisionbuf[128];
3351 char *id;
3352 time_t now;
3353 struct tm *now_x;
3354 char *revision = NULL;
3355 char *hostpart = NULL;
3356 char *alpine_name = NULL;
3357 char *alpine_version = NULL;
3358 char *system_os = NULL;
3360 now = time((time_t *)0);
3361 now_x = localtime(&now);
3363 if(now_x->tm_sec == osec)
3364 cnt++;
3365 else{
3366 cnt = 0;
3367 osec = now_x->tm_sec;
3370 get_alpine_revision_number(revisionbuf, sizeof(revisionbuf));
3371 if(F_ON(F_ROT13_MESSAGE_ID, ps_global)){
3372 hostpart = rot13(ps_global->hostname);
3373 alpine_name = rot13("alpine");
3374 alpine_version = rot5n(ALPINE_VERSION);
3375 system_os = rot13(SYSTYPE);
3376 revision = rot5n(revisionbuf);
3377 } else {
3378 hostpart = cpystr(ps_global->hostname);
3379 alpine_name = cpystr("alpine");
3380 alpine_version = cpystr(ALPINE_VERSION);
3381 system_os = cpystr(SYSTYPE);
3382 revision = cpystr(revisionbuf);
3385 if(!hostpart)
3386 hostpart = cpystr("huh");
3388 snprintf(idbuf, sizeof(idbuf), "<%.6s.%.4s.%.20s.%.10s.%02d%02d%02d%02d%02d%02d%X.%d@%.50s>",
3389 alpine_name, system_os, alpine_version, revision,(now_x->tm_year) % 100, now_x->tm_mon + 1,
3390 now_x->tm_mday, now_x->tm_hour, now_x->tm_min, now_x->tm_sec,
3391 cnt, getpid(), hostpart);
3392 idbuf[sizeof(idbuf)-1] = '\0';
3394 id = cpystr(idbuf);
3396 if(hostpart) fs_give((void **) &hostpart);
3397 if(alpine_name) fs_give((void **) & alpine_name);
3398 if(alpine_version) fs_give((void **)&alpine_version);
3399 if(system_os) fs_give((void **)&system_os);
3400 if(revision) fs_give((void **)&revision);
3402 return(id);
3406 char *
3407 generate_user_agent(void)
3409 char buf[128];
3410 char rev[128];
3412 if(F_ON(F_QUELL_USERAGENT, ps_global))
3413 return(NULL);
3415 snprintf(buf, sizeof(buf),
3416 "%sAlpine %s (%s %s)",
3417 (pith_opt_user_agent_prefix) ? (*pith_opt_user_agent_prefix)() : "",
3418 ALPINE_VERSION, SYSTYPE,
3419 get_alpine_revision_string(rev, sizeof(rev)));
3421 return(cpystr(buf));
3425 char *
3426 rot13(char *src)
3428 char byte, cap, *p, *ret = NULL;
3430 if(src && *src){
3431 ret = (char *) fs_get((strlen(src)+1) * sizeof(char));
3432 p = ret;
3433 while((byte = *src++) != '\0'){
3434 cap = byte & 32;
3435 byte &= ~cap;
3436 *p++ = ((byte >= 'A') && (byte <= 'Z')
3437 ? ((byte - 'A' + 13) % 26 + 'A') : byte) | cap;
3440 *p = '\0';
3443 return(ret);
3446 char *
3447 rot5n(char *src)
3449 char byte, *p, *ret = NULL;
3451 if(src && *src){
3452 ret = (char *) fs_get((strlen(src)+1) * sizeof(char));
3453 p = ret;
3454 while((byte = *src++) != '\0')
3455 *p++ = ((byte >= '0') && (byte <= '9')
3456 ? ((byte - '0' + 5) % 10 + '0') : byte);
3457 *p = '\0';
3460 return(ret);
3464 /*----------------------------------------------------------------------
3465 Return the first true address pointer (modulo group syntax allowance)
3467 Args: addr -- Address list
3469 Result: First real address pointer, or NULL
3470 ----------------------------------------------------------------------*/
3471 ADDRESS *
3472 first_addr(struct mail_address *addr)
3474 while(addr && !addr->host)
3475 addr = addr->next;
3477 return(addr);
3481 /*----------------------------------------------------------------------
3482 lit -- this is the source
3483 prenewlines -- prefix the file contents with this many newlines
3484 postnewlines -- postfix the file contents with this many newlines
3485 is_sig -- this is a signature (not a template)
3486 decode_constants -- change C-style constants into their values
3487 ----*/
3488 char *
3489 get_signature_lit(char *lit, int prenewlines, int postnewlines, int is_sig, int decode_constants)
3491 char *sig = NULL;
3494 * Should make this smart enough not to do the copying and double
3495 * allocation of space.
3497 if(lit){
3498 char *tmplit = NULL, *p, *q, *d, save;
3499 size_t len;
3501 if(decode_constants){
3502 tmplit = (char *) fs_get((strlen(lit)+1) * sizeof(char));
3503 tmplit[0] = '\0';
3504 cstring_to_string(lit, tmplit);
3506 else
3507 tmplit = cpystr(lit);
3509 len = strlen(tmplit) + 5 + (prenewlines+postnewlines) * strlen(NEWLINE);
3510 sig = (char *) fs_get((len+1) * sizeof(char));
3511 memset(sig, 0, len+1);
3512 d = sig;
3513 while(prenewlines--)
3514 sstrncpy(&d, NEWLINE, len-(d-sig));
3516 if(is_sig && F_ON(F_ENABLE_SIGDASHES, ps_global) &&
3517 !sigdashes_are_present(tmplit)){
3518 sstrncpy(&d, SIGDASHES, len-(d-sig));
3519 sstrncpy(&d, NEWLINE, len-(d-sig));
3522 sig[len] = '\0';
3524 p = tmplit;
3525 while(*p){
3526 /* get a line */
3527 q = strpbrk(p, "\n\r");
3528 if(q){
3529 save = *q;
3530 *q = '\0';
3534 * Strip trailing space if we are doing a signature and
3535 * this line is not sigdashes.
3537 if(is_sig && strcmp(p, SIGDASHES))
3538 removing_trailing_white_space(p);
3540 while((d-sig) <= len && (*d = *p++) != '\0')
3541 d++;
3543 if(q){
3544 if((d-sig) <= len)
3545 *d++ = save;
3547 p = q+1;
3549 else
3550 break;
3553 while(postnewlines--)
3554 sstrncpy(&d, NEWLINE, len-(d-sig));
3556 sig[len] = '\0';
3558 if((d-sig) <= len)
3559 *d = '\0';
3561 if(tmplit)
3562 fs_give((void **) &tmplit);
3565 return(sig);
3570 sigdashes_are_present(char *sig)
3572 char *p;
3574 p = srchstr(sig, SIGDASHES);
3575 while(p && !((p == sig || (p[-1] == '\n' || p[-1] == '\r')) &&
3576 (p[3] == '\0' || p[3] == '\n' || p[3] == '\r')))
3577 p = srchstr(p+1, SIGDASHES);
3579 return(p ? 1 : 0);
3583 /*----------------------------------------------------------------------
3584 Acquire the pinerc defined signature file pathname
3586 ----*/
3587 char *
3588 signature_path(char *sname, char *sbuf, size_t len)
3590 *sbuf = '\0';
3591 if(sname && *sname){
3592 size_t spl = strlen(sname);
3593 if(IS_REMOTE(sname)){
3594 if(spl < len - 1)
3595 strncpy(sbuf, sname, len-1);
3597 else if(is_absolute_path(sname)){
3598 strncpy(sbuf, sname, len-1);
3599 sbuf[len-1] = '\0';
3600 fnexpand(sbuf, len);
3602 else if(ps_global->VAR_OPER_DIR){
3603 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
3604 build_path(sbuf, ps_global->VAR_OPER_DIR, sname, len);
3606 else{
3607 char *lc = last_cmpnt(ps_global->pinerc);
3609 sbuf[0] = '\0';
3610 if(lc != NULL){
3611 strncpy(sbuf,ps_global->pinerc,MIN(len-1,lc-ps_global->pinerc));
3612 sbuf[MIN(len-1,lc-ps_global->pinerc)] = '\0';
3615 strncat(sbuf, sname, MAX(len-1-strlen(sbuf), 0));
3616 sbuf[len-1] = '\0';
3620 return(*sbuf ? sbuf : NULL);
3624 char *
3625 simple_read_remote_file(char *name, char *subtype)
3627 int try_cache;
3628 REMDATA_S *rd;
3629 char *file = NULL;
3632 dprint((7, "simple_read_remote_file(%s, %s)\n", name ? name : "?", subtype ? subtype : "?"));
3635 * We could parse the name here to find what type it is. So far we
3636 * only have type RemImap.
3638 rd = rd_create_remote(RemImap, name, subtype,
3639 NULL, _("Error: "), _("Can't fetch remote configuration."));
3640 if(!rd)
3641 goto bail_out;
3643 try_cache = rd_read_metadata(rd);
3645 if(rd->access == MaybeRorW){
3646 if(rd->read_status == 'R')
3647 rd->access = ReadOnly;
3648 else
3649 rd->access = ReadWrite;
3652 if(rd->access != NoExists){
3654 rd_check_remvalid(rd, 1L);
3657 * If the cached info says it is readonly but
3658 * it looks like it's been fixed now, change it to readwrite.
3660 if(rd->read_status == 'R'){
3662 * We go to this trouble since readonly sigfiles
3663 * are likely a mistake. They are usually supposed to be
3664 * readwrite so we open it and check if it's been fixed.
3666 rd_check_readonly_access(rd);
3667 if(rd->read_status == 'W'){
3668 rd->access = ReadWrite;
3669 rd->flags |= REM_OUTOFDATE;
3671 else
3672 rd->access = ReadOnly;
3675 if(rd->flags & REM_OUTOFDATE){
3676 if(rd_update_local(rd) != 0){
3678 dprint((1,
3679 "simple_read_remote_file: rd_update_local failed\n"));
3681 * Don't give up altogether. We still may be
3682 * able to use a cached copy.
3685 else{
3686 dprint((7,
3687 "%s: copied remote to local (%ld)\n",
3688 rd->rn ? rd->rn : "?", (long)rd->last_use));
3692 if(rd->access == ReadWrite)
3693 rd->flags |= DO_REMTRIM;
3696 /* If we couldn't get to remote folder, try using the cached copy */
3697 if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){
3698 if(try_cache){
3699 rd->access = ReadOnly;
3700 rd->flags |= USE_OLD_CACHE;
3701 q_status_message(SM_ORDER, 3, 4,
3702 "Can't contact remote server, using cached copy");
3703 dprint((2,
3704 "Can't open remote file %s, using local cached copy %s readonly\n",
3705 rd->rn ? rd->rn : "?",
3706 rd->lf ? rd->lf : "?"));
3708 else{
3709 rd->flags &= ~DO_REMTRIM;
3710 goto bail_out;
3714 file = read_file(rd->lf, READ_FROM_LOCALE);
3716 bail_out:
3717 if(rd)
3718 rd_close_remdata(&rd);
3720 return(file);
3723 /* special handling for messages that contain a mixed part in the
3724 * multipart alternative section.
3726 BODY *
3727 forward_multi_alt_mixed(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
3728 long int msgno, char *sect_prefix, void *msgtext, gf_io_t pc, int flags)
3730 #define FWDTMPLEN 256
3731 BODY *body = NULL, *text_body = NULL;
3732 PART *part = NULL;
3733 char prefix_buf[FWDTMPLEN];
3734 char *new_charset = NULL;
3735 int partnum;
3736 char *section, sect_buf[256];
3737 int forward_raw_body = 0;
3739 if(orig_body
3740 && orig_body->type == TYPEMULTIPART
3741 && orig_body->subtype
3742 && !strucmp(orig_body->subtype, "alternative"))
3743 for(part = orig_body->nested.part, partnum = 1;
3744 part;
3745 part = part->next, partnum++)
3746 if(part->body.type == TYPEMULTIPART
3747 && part->body.subtype
3748 && !strucmp(part->body.subtype, "MIXED"))
3749 break;
3751 if(part == NULL) return NULL;
3753 snprintf(prefix_buf, sizeof(prefix_buf), "%.*s%s%s%d",
3754 FWDTMPLEN/2, sect_prefix ? sect_prefix : "",
3755 sect_prefix ? "." : "", flags & FWD_NESTED ? "1." : "",
3756 partnum);
3757 prefix_buf[sizeof(prefix_buf)-1] = '\0';
3759 if(ps_global->full_header == 2
3760 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
3761 forward_raw_body = 1;
3762 if(sp_expunge_count(stream))
3763 return(NULL);
3765 if(sect_prefix && forward_raw_body == 0)
3766 snprintf(section = sect_buf, sizeof(sect_buf), "%s.1", sect_prefix);
3767 else if(sect_prefix && forward_raw_body)
3768 section = sect_prefix;
3769 else if(!sect_prefix && forward_raw_body)
3770 section = NULL;
3771 else
3772 section = "1";
3773 sect_buf[sizeof(sect_buf)-1] = '\0';
3775 body = copy_body(NULL, &part->body);
3777 /*--- The text part of the message ---*/
3778 if(!body->nested.part){
3779 q_status_message(SM_ORDER | SM_DING, 3, 6,
3780 "Error referencing body part 1");
3781 mail_free_body(&body);
3783 else if(body->nested.part->body.type == TYPETEXT) {
3784 char *new_charset = NULL;
3786 /*--- The first part is text ----*/
3787 text_body = &body->nested.part->body;
3788 text_body->contents.text.data = msgtext;
3789 if(text_body->subtype && strucmp(text_body->subtype, "Plain")){
3790 /* this text is going to the composer, it should be Plain */
3791 fs_give((void **)&text_body->subtype);
3792 text_body->subtype = cpystr("PLAIN");
3794 if(!(flags & FWD_ANON)){
3795 forward_delimiter(pc);
3796 reply_forward_header(stream, msgno,
3797 sect_prefix, env, pc, "");
3800 if(!(get_body_part_text(stream, &part->body,
3801 msgno, section, 0L, pc,
3802 NULL, &new_charset, GBPT_NONE)
3803 && fetch_contents(stream, msgno, prefix_buf, body)))
3804 mail_free_body(&body);
3805 else if(new_charset)
3806 set_parameter(&text_body->parameter, "charset", new_charset);
3808 return(body);
3812 /*----------------------------------------------------------------------
3813 Build the body for the multipart/alternative part
3815 Args:
3817 Result:
3819 ----------------------------------------------------------------------*/
3820 BODY *
3821 forward_multi_alt(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
3822 long int msgno, char *sect_prefix, void *msgtext, gf_io_t pc, int flags)
3824 #define FWDTMPLEN 256
3825 BODY *body = NULL;
3826 PART *part = NULL, *bestpart = NULL;
3827 char tmp_buf[FWDTMPLEN];
3828 char *new_charset = NULL;
3829 int partnum, bestpartnum;
3831 /* try multipart mixed first */
3832 if((body = forward_multi_alt_mixed(stream, env, orig_body,
3833 msgno, sect_prefix, msgtext, pc, flags)) != NULL)
3834 return body;
3836 if(ps_global->force_prefer_plain
3837 || (!ps_global->force_no_prefer_plain
3838 && F_ON(F_PREFER_PLAIN_TEXT, ps_global))){
3839 for(part = orig_body->nested.part, partnum = 1;
3840 part;
3841 part = part->next, partnum++)
3842 if((!part->body.type || part->body.type == TYPETEXT)
3843 && (!part->body.subtype
3844 || !strucmp(part->body.subtype, "plain")))
3845 break;
3849 * Else choose last alternative among plain or html parts.
3850 * Perhaps we should really be using mime_show() to make this
3851 * potentially more general than just plain or html.
3853 if(!part){
3854 for(part = orig_body->nested.part, partnum = 1;
3855 part;
3856 part = part->next, partnum++){
3857 if((!part->body.type || part->body.type == TYPETEXT)
3858 && ((!part->body.subtype || !strucmp(part->body.subtype, "plain"))
3860 (part->body.subtype && !strucmp(part->body.subtype, "html")))){
3861 bestpart = part;
3862 bestpartnum = partnum;
3866 part = bestpart;
3867 partnum = bestpartnum;
3871 * IF something's interesting insert it
3872 * AND forget the rest of the multipart
3874 if(part){
3875 body = mail_newbody();
3876 body->type = TYPETEXT;
3877 body->contents.text.data = msgtext;
3879 /* record character set, flowing, etc */
3880 body->parameter = copy_parameters(part->body.parameter);
3881 body->size.bytes = part->body.size.bytes;
3883 if(!(flags & FWD_ANON)){
3884 forward_delimiter(pc);
3885 reply_forward_header(stream, msgno, sect_prefix, env, pc, "");
3887 /* FWDTMPLEN = sizeof(tmp_buf) */
3888 snprintf(tmp_buf, sizeof(tmp_buf), "%.*s%s%s%d",
3889 FWDTMPLEN/2, sect_prefix ? sect_prefix : "",
3890 sect_prefix ? "." : "", flags & FWD_NESTED ? "1." : "",
3891 partnum);
3892 tmp_buf[sizeof(tmp_buf)-1] = '\0';
3893 get_body_part_text(stream, &part->body, msgno, tmp_buf, 0L, pc,
3894 NULL, &new_charset, GBPT_NONE);
3897 * get_body_part_text translated the data to a new charset.
3898 * We need to record that fact in body.
3900 if(new_charset)
3901 set_parameter(&body->parameter, "charset", new_charset);
3903 else
3904 q_status_message(SM_ORDER | SM_DING, 3, 3,
3905 "No suitable part found. Forwarding as attachment");
3907 return(body);
3911 void
3912 reply_append_addr(struct mail_address **dest, struct mail_address *src)
3914 for( ; *dest; dest = &(*dest)->next)
3917 *dest = src;