1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2014 Eduardo Chappa
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"
54 void bounce_mask_header(char **, char *);
57 int (*pith_opt_replyto_prompt
)(void);
58 int (*pith_opt_reply_to_all_prompt
)(int *);
62 * standard type of storage object used for body parts...
64 #define PART_SO_TYPE CharStar
67 char *(*pith_opt_user_agent_prefix
)(void);
73 * Returns: 1 if addresses successfully copied
74 * 0 on user cancel or error
86 reply_harvest(struct pine
*ps
, long int msgno
, char *section
, ENVELOPE
*env
,
87 struct mail_address
**saved_from
, struct mail_address
**saved_to
,
88 struct mail_address
**saved_cc
, struct mail_address
**saved_resent
,
91 ADDRESS
*ap
, *ap2
, *rep_address
;
92 int ret
= 0, sniff_resent
= 0;
96 * If Reply-To is same as From just treat it like it was From.
97 * Otherwise, always use the reply-to if we're replying to more
98 * than one msg or say ok to using it, even if it's us.
99 * If there's no reply-to or it's the same as the from, assume
100 * that the user doesn't want to reply to himself, unless there's
103 if(env
->reply_to
&& !addr_lists_same(env
->reply_to
, env
->from
)
104 && (F_ON(F_AUTO_REPLY_TO
, ps_global
)
105 || ((*flags
) & RSF_FORCE_REPLY_TO
)
106 || (pith_opt_replyto_prompt
&& (*pith_opt_replyto_prompt
)() == 'y'))){
107 rep_field
= "reply-to";
108 rep_address
= env
->reply_to
;
112 rep_address
= env
->from
;
115 ap
= reply_cp_addr(ps
, msgno
, section
, rep_field
, *saved_from
,
116 (ADDRESS
*) NULL
, rep_address
, RCA_NOT_US
);
119 cmd_cancelled("Reply");
123 reply_append_addr(saved_from
, ap
);
125 /*--------- check for other recipients ---------*/
126 if(((*flags
) & (RSF_FORCE_REPLY_ALL
| RSF_QUERY_REPLY_ALL
))){
128 if((ap
= reply_cp_addr(ps
, msgno
, section
, "To", *saved_to
,
129 *saved_from
, env
->to
, RCA_NOT_US
)) != NULL
)
130 reply_append_addr(saved_to
, ap
);
132 if((ap
= reply_cp_addr(ps
, msgno
, section
, "Cc", *saved_cc
,
133 *saved_from
, env
->cc
, RCA_NOT_US
)) != NULL
)
134 reply_append_addr(saved_cc
, ap
);
137 * In these cases, we either need to look at the resent headers
138 * to include in the reply-to-all, or to decide whether or not
139 * we need to ask the reply-to-all question.
141 if(((*flags
) & RSF_FORCE_REPLY_ALL
)
142 || (((*flags
) & RSF_QUERY_REPLY_ALL
)
143 && ((!(*saved_from
) && !(*saved_cc
))
144 || (*saved_from
&& !(*saved_to
) && !(*saved_cc
))))){
147 if((ap2
= reply_resent(ps
, msgno
, section
)) != NULL
){
149 * look for bogus addr entries and replace
151 if((ap
= reply_cp_addr(ps
, 0, NULL
, NULL
, *saved_resent
,
152 *saved_from
, ap2
, RCA_NOT_US
)) != NULL
)
154 reply_append_addr(saved_resent
, ap
);
156 mail_free_address(&ap2
);
161 * It makes sense to ask reply-to-all now.
163 if(((*flags
) & RSF_QUERY_REPLY_ALL
)
164 && ((*saved_from
&& (*saved_to
|| *saved_cc
|| *saved_resent
))
165 || (*saved_cc
|| *saved_resent
))){
166 *flags
&= ~RSF_QUERY_REPLY_ALL
;
167 if(pith_opt_reply_to_all_prompt
168 && (*pith_opt_reply_to_all_prompt
)(flags
) < 0){
169 cmd_cancelled("Reply");
175 * If we just answered yes to the reply-to-all question and
176 * we still haven't collected the resent headers, do so now.
178 if(((*flags
) & RSF_FORCE_REPLY_ALL
) && !sniff_resent
179 && (ap2
= reply_resent(ps
, msgno
, section
))){
181 * look for bogus addr entries and replace
183 if((ap
= reply_cp_addr(ps
, 0, NULL
, NULL
, *saved_resent
,
184 *saved_from
, ap2
, RCA_NOT_US
)) != NULL
)
185 reply_append_addr(saved_resent
, ap
);
187 mail_free_address(&ap2
);
195 /*----------------------------------------------------------------------
196 Return a pointer to a copy of the given address list
197 filtering out those already in the "mask" lists and ourself.
199 Args: mask1 -- Don't copy if in this list
200 mask2 -- or if in this list
201 source -- List to be copied
202 us_too -- Don't filter out ourself.
203 flags -- RCA_NOT_US copy all addrs except for our own
204 RCA_ALL copy all addrs, including our own
205 RCA_ONLY_US copy only addrs that are our own
209 reply_cp_addr(struct pine
*ps
, long int msgno
, char *section
, char *field
,
210 struct mail_address
*mask1
, struct mail_address
*mask2
,
211 struct mail_address
*source
, int flags
)
213 ADDRESS
*tmp1
, *tmp2
, *ret
= NULL
, **ret_tail
;
215 /* can only choose one of these flags values */
216 assert(!((flags
& RCA_ALL
&& flags
& RCA_ONLY_US
)
217 || (flags
& RCA_ALL
&& flags
& RCA_NOT_US
)
218 || (flags
& RCA_ONLY_US
&& flags
& RCA_NOT_US
)));
220 for(tmp1
= source
; msgno
&& tmp1
; tmp1
= tmp1
->next
)
221 if(tmp1
->host
&& tmp1
->host
[0] == '.'){
226 if((h
= pine_fetchheader_lines(ps
? ps
->mail_stream
: NULL
,
227 msgno
, section
, fields
)) != NULL
){
233 strncpy(fname
, field
, sizeof(fname
)-2);
234 fname
[sizeof(fname
)-2] = '\0';
235 strncat(fname
, ":", sizeof(fname
)-strlen(fname
)-1);
236 fname
[sizeof(fname
)-1] = '\0';
238 for(p
= h
; (p
= strstr(p
, fname
)) != NULL
; )
239 rplstr(p
, q
-(p
-h
), strlen(fname
), ""); /* strip field strings */
241 sqznewlines(h
); /* blat out CR's & LF's */
242 for(p
= h
; (p
= strchr(p
, TAB
)) != NULL
; )
243 *p
++ = ' '; /* turn TABs to whitespace */
249 ret
= (ADDRESS
*) fs_get(sizeof(ADDRESS
));
250 memset(ret
, 0, sizeof(ADDRESS
));
252 /* get rid of leading white space */
253 for(p
= h
; *p
== SPACE
; p
++)
257 memmove(h
, p
, l
= strlen(p
));
261 /* base64 armor plate the gunk to protect against
262 * c-client quoting in output.
264 p
= (char *) rfc822_binary(h
, strlen(h
),
265 (unsigned long *) &l
);
267 fs_give((void **) &h
);
269 * Seems like the 4 ought to be a 2, but I'll leave it
270 * to be safe, in case something else adds 2 chars later.
273 ret
->mailbox
= (char *) fs_get(ll
* sizeof(char));
274 snprintf(ret
->mailbox
, ll
, "&%s", p
);
275 ret
->mailbox
[ll
-1] = '\0';
276 fs_give((void **) &p
);
277 ret
->host
= cpystr(RAWFIELD
);
285 for(source
= first_addr(source
); source
; source
= source
->next
){
286 for(tmp1
= first_addr(mask1
); tmp1
; tmp1
= tmp1
->next
)
287 if(address_is_same(source
, tmp1
)) /* it is in mask1, skip it */
290 for(tmp2
= first_addr(mask2
); !tmp1
&& tmp2
; tmp2
= tmp2
->next
)
291 if(address_is_same(source
, tmp2
)) /* it is in mask2, skip it */
295 * If there's no match in masks and this address satisfies the
296 * flags requirement, copy it.
298 if(!tmp1
&& !tmp2
/* no mask match */
299 && ((flags
& RCA_ALL
) /* including everybody */
300 || (flags
& RCA_ONLY_US
&& address_is_us(source
, ps
))
301 || (flags
& RCA_NOT_US
&& !address_is_us(source
, ps
)))){
303 source
->next
= NULL
; /* only copy one addr! */
304 *ret_tail
= rfc822_cpy_adr(source
);
305 ret_tail
= &(*ret_tail
)->next
;
306 source
->next
= tmp1
; /* restore rest of list */
315 set_role_from_msg(struct pine
*ps
, long int rflags
, long int msgno
, char *section
)
317 ACTION_S
*role
= NULL
;
319 SEARCHSET
*ss
= NULL
;
322 if(!nonempty_patterns(rflags
, &pstate
))
326 ss
= mail_newsearchset();
327 ss
->first
= ss
->last
= (unsigned long)msgno
;
330 /* Go through the possible roles one at a time until we get a match. */
331 pat
= first_pattern(&pstate
);
333 /* calculate this message's score if needed */
334 if(ss
&& pat
&& scores_are_used(SCOREUSE_GET
) & SCOREUSE_ROLES
&&
335 get_msg_score(ps
->mail_stream
, msgno
) == SCORE_UNDEF
)
336 (void)calculate_some_scores(ps
->mail_stream
, ss
, 0);
339 if(match_pattern(pat
->patgrp
, ps
->mail_stream
, ss
, section
,
340 get_msg_score
, SE_NOSERVER
|SE_NOPREFETCH
)){
341 if(!pat
->action
|| pat
->action
->bogus
)
347 pat
= next_pattern(&pstate
);
351 mail_free_searchset(&ss
);
358 * reply_seed - fill in reply header
362 reply_seed(struct pine
*ps
, ENVELOPE
*outgoing
, ENVELOPE
*env
,
363 struct mail_address
*saved_from
, struct mail_address
*saved_to
,
364 struct mail_address
*saved_cc
, struct mail_address
*saved_resent
,
365 char **fcc
, int replytoall
, char **errmsg
)
367 ADDRESS
**to_tail
, **cc_tail
;
369 to_tail
= &outgoing
->to
;
370 cc_tail
= &outgoing
->cc
;
373 /* Put Reply-To or From in To. */
374 *to_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->to
,
375 (ADDRESS
*) NULL
, saved_from
, RCA_ALL
);
379 to_tail
= &(*to_tail
)->next
;
381 *to_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->to
,
382 (ADDRESS
*) NULL
, saved_to
, RCA_ALL
);
385 to_tail
= &(*to_tail
)->next
;
387 *to_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->cc
,
388 outgoing
->to
, saved_resent
, RCA_ALL
);
390 *cc_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->cc
,
391 outgoing
->to
, saved_cc
, RCA_ALL
);
393 else{ /* and the rest in cc */
394 *cc_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->cc
,
395 outgoing
->to
, saved_to
, RCA_ALL
);
396 while(*cc_tail
) /* stay on last address */
397 cc_tail
= &(*cc_tail
)->next
;
399 *cc_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->cc
,
400 outgoing
->to
, saved_cc
, RCA_ALL
);
402 cc_tail
= &(*cc_tail
)->next
;
404 *cc_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->cc
,
405 outgoing
->to
, saved_resent
, RCA_ALL
);
410 /* No From (maybe from us), put To in To. */
411 *to_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->to
,
412 (ADDRESS
*)NULL
, saved_to
, RCA_ALL
);
413 /* and the rest in cc */
415 *cc_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->cc
,
416 outgoing
->to
, saved_cc
, RCA_ALL
);
418 cc_tail
= &(*cc_tail
)->next
;
420 *cc_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->cc
,
421 outgoing
->to
, saved_resent
, RCA_ALL
);
425 /* No From or To, put everything else in To if replytoall, */
427 *to_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->to
,
428 (ADDRESS
*) NULL
, saved_cc
, RCA_ALL
);
430 to_tail
= &(*to_tail
)->next
;
432 *to_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->to
,
433 (ADDRESS
*) NULL
, saved_resent
, RCA_ALL
);
435 /* else, reply to original From which must be us */
438 * Put self in To if in original From.
440 if(!outgoing
->newsgroups
)
441 *to_tail
= reply_cp_addr(ps
, 0, NULL
, NULL
, outgoing
->to
,
442 (ADDRESS
*) NULL
, env
->from
, RCA_ALL
);
446 /* add any missing personal data */
447 reply_fish_personal(outgoing
, env
);
450 if(fcc
&& outgoing
->to
&& outgoing
->to
->host
[0] != '.'){
451 *fcc
= get_fcc_based_on_to(outgoing
->to
);
453 else if(fcc
&& outgoing
->newsgroups
){
454 char *newsgroups_returned
= NULL
;
457 rv
= news_grouper(outgoing
->newsgroups
, &newsgroups_returned
, errmsg
, fcc
, NULL
);
459 strcmp(outgoing
->newsgroups
, newsgroups_returned
)){
460 fs_give((void **)&outgoing
->newsgroups
);
461 outgoing
->newsgroups
= newsgroups_returned
;
464 fs_give((void **) &newsgroups_returned
);
469 /*----------------------------------------------------------------------
470 Test the given address lists for equivalence
472 Args: x -- First address list for comparison
473 y -- Second address for comparison
477 addr_lists_same(struct mail_address
*x
, struct mail_address
*y
)
479 for(x
= first_addr(x
), y
= first_addr(y
);
481 x
= first_addr(x
->next
), y
= first_addr(y
->next
)){
482 if(!address_is_same(x
, y
))
486 return(!x
&& !y
); /* true if ran off both lists */
490 /*----------------------------------------------------------------------
491 Test the given address against those in the given envelope's to, cc
493 Args: addr -- address for comparison
494 env -- envelope to compare against
498 addr_in_env(struct mail_address
*addr
, ENVELOPE
*env
)
502 for(ap
= env
? env
->to
: NULL
; ap
; ap
= ap
->next
)
503 if(address_is_same(addr
, ap
))
506 for(ap
= env
? env
->cc
: NULL
; ap
; ap
= ap
->next
)
507 if(address_is_same(addr
, ap
))
510 return(0); /* not found! */
514 /*----------------------------------------------------------------------
515 Add missing personal info dest from src envelope
517 Args: dest -- envelope to add personal info to
518 src -- envelope to get personal info from
520 NOTE: This is just kind of a courtesy function. It's really not adding
521 anything needed to get the mail thru, but it is nice for the user
522 under some odd circumstances.
525 reply_fish_personal(ENVELOPE
*dest
, ENVELOPE
*src
)
529 for(da
= dest
? dest
->to
: NULL
; da
; da
= da
->next
){
530 if(da
->personal
&& !da
->personal
[0])
531 fs_give((void **)&da
->personal
);
533 for(sa
= src
? src
->to
: NULL
; sa
&& !da
->personal
; sa
= sa
->next
)
534 if(address_is_same(da
, sa
) && sa
->personal
&& sa
->personal
[0])
535 da
->personal
= cpystr(sa
->personal
);
537 for(sa
= src
? src
->cc
: NULL
; sa
&& !da
->personal
; sa
= sa
->next
)
538 if(address_is_same(da
, sa
) && sa
->personal
&& sa
->personal
[0])
539 da
->personal
= cpystr(sa
->personal
);
542 for(da
= dest
? dest
->cc
: NULL
; da
; da
= da
->next
){
543 if(da
->personal
&& !da
->personal
[0])
544 fs_give((void **)&da
->personal
);
546 for(sa
= src
? src
->to
: NULL
; sa
&& !da
->personal
; sa
= sa
->next
)
547 if(address_is_same(da
, sa
) && sa
->personal
&& sa
->personal
[0])
548 da
->personal
= cpystr(sa
->personal
);
550 for(sa
= src
? src
->cc
: NULL
; sa
&& !da
->personal
; sa
= sa
->next
)
551 if(address_is_same(da
, sa
) && sa
->personal
&& sa
->personal
[0])
552 da
->personal
= cpystr(sa
->personal
);
557 /*----------------------------------------------------------------------
558 Given a header field and envelope, build "References: " header data
565 reply_build_refs(ENVELOPE
*env
)
567 int len
, id_len
, first_ref_len
= 0, foldslop
;
568 char *p
, *refs
= NULL
, *h
= env
->references
;
569 char *first_ref
= NULL
, *tail_refs
= NULL
;
572 if(!(env
->message_id
&& (id_len
= strlen(env
->message_id
))))
577 * The length we have to work with doesn't seem to appear in any
578 * standards. Steve Jones says that in comp.news discussions he
579 * has seen 1024 as the longest length of a header value.
580 * In the inn news source we find MAXHEADERSIZE = 1024. It appears
581 * that is the maximum length of the header value, including
582 * newlines for folded lines (that is, the newlines are counted).
583 * We'll be conservative and figure every reference will take up a
584 * line of its own when we fold. We'll also count 2 for CRLF instead
585 * of just one for LF just to be safe. hubert 2001-jan
586 * J.B. Moreno <planb@newsreaders.com> says "The server limit is
587 * more commonly encountered at 999/1000 bytes [...]". So we'll
588 * back off to 999 instead of 1024.
590 #define MAXHEADERSIZE (999)
592 /* count the total number of potential folds, max of 2 bytes each */
593 for(foldslop
= 2, p
= h
; (p
= strstr(p
+1, "> <")); )
596 if((len
=strlen(h
)) + 1+id_len
+ foldslop
>= MAXHEADERSIZE
597 && (p
= strstr(h
, "> <"))){
599 * If the references line is so long that we are going to have
600 * to delete some of the references, delete the 2nd, 3rd, ...
601 * We don't want to delete the first message in the thread.
604 first_ref
= cpystr(h
);
605 first_ref_len
= strlen(first_ref
)+1; /* len includes space */
608 /* get rid of 2nd, 3rd, ... until it fits */
609 while((len
=strlen(tail_refs
)) + first_ref_len
+ 1+id_len
+
610 foldslop
>= MAXHEADERSIZE
611 && (p
= strstr(tail_refs
, "> <"))){
617 * If the individual references are seriously long, somebody
618 * is messing with us and we don't care if it works right.
620 if((len
=strlen(tail_refs
)) + first_ref_len
+ 1+id_len
+
621 foldslop
>= MAXHEADERSIZE
){
622 first_ref_len
= len
= 0;
625 fs_give((void **)&first_ref
);
631 refs
= (char *)fs_get((first_ref_len
+ 1+id_len
+ len
+ 1) *
633 snprintf(refs
, first_ref_len
+ 1+id_len
+ len
+ 1, "%s%s%s%s%s",
634 first_ref
? first_ref
: "",
635 first_ref
? " " : "",
636 tail_refs
? tail_refs
: "",
637 tail_refs
? " " : "",
639 refs
[first_ref_len
+ 1+id_len
+ len
] = '\0';
643 refs
= cpystr(env
->message_id
);
646 fs_give((void **)&first_ref
);
653 /*----------------------------------------------------------------------
654 Snoop for any Resent-* headers, and return an ADDRESS list
659 Returns: either NULL if no Resent-* or parsed ADDRESS struct list
662 reply_resent(struct pine
*pine_state
, long int msgno
, char *section
)
667 ADDRESS
*rlist
= NULL
, **a
, **b
;
668 char *hdrs
, *values
[RESENTCC
+1];
670 static char *fields
[] = {"Resent-From", "Resent-To", "Resent-Cc", NULL
};
671 static char *fakedomain
= "@";
673 if((hdrs
= pine_fetchheader_lines(pine_state
->mail_stream
,
674 msgno
, section
, fields
)) != NULL
){
675 memset(values
, 0, (RESENTCC
+1) * sizeof(char *));
676 simple_header_parse(hdrs
, fields
, values
);
677 for(i
= RESENTFROM
; i
<= RESENTCC
; i
++)
678 rfc822_parse_adrlist(&rlist
, values
[i
],
679 (F_ON(F_COMPOSE_REJECTS_UNQUAL
, pine_state
))
680 ? fakedomain
: pine_state
->maildomain
);
683 for(a
= &rlist
; *a
; a
= &(*a
)->next
) /* compare every address */
684 for(b
= &(*a
)->next
; *b
; ) /* to the others */
685 if(address_is_same(*a
, *b
)){
688 if(!(*a
)->personal
){ /* preserve personal name */
689 (*a
)->personal
= (*b
)->personal
;
690 (*b
)->personal
= NULL
;
695 mail_free_address(&t
);
702 fs_give((void **) &hdrs
);
708 /*----------------------------------------------------------------------
709 Format and return subject suitable for the reply command
711 Args: subject -- subject to build reply subject for
712 buf -- buffer to use for writing. If non supplied, alloc one.
713 buflen -- length of buf if supplied, else ignored
715 Returns: with either "Re:" prepended or not, if already there.
716 returned string is allocated.
719 reply_subject(char *subject
, char *buf
, size_t buflen
)
721 size_t l
= (subject
&& *subject
) ? 4*strlen(subject
) : 10;
722 char *tmp
= fs_get(l
+ 1), *decoded
, *p
;
726 buf
= fs_get(buflen
);
729 /* decode any 8bit into tmp buffer */
730 decoded
= (char *) rfc1522_decode_to_utf8((unsigned char *)tmp
, l
+1, subject
);
733 if(decoded
/* already "re:" ? */
734 && (decoded
[0] == 'R' || decoded
[0] == 'r')
735 && (decoded
[1] == 'E' || decoded
[1] == 'e')){
737 if(decoded
[2] == ':')
738 snprintf(buf
, buflen
, "%.*s", buflen
-1, subject
);
739 else if((decoded
[2] == '[') && (p
= strchr(decoded
, ']'))){
741 while(*p
&& isspace((unsigned char)*p
)) p
++;
743 snprintf(buf
, buflen
, "%.*s", buflen
-1, subject
);
748 snprintf(buf
, buflen
, "Re: %.*s", buflen
-1,
749 (subject
&& *subject
) ? subject
: "your mail");
751 buf
[buflen
-1] = '\0';
753 fs_give((void **) &tmp
);
758 /*----------------------------------------------------------------------
759 return initials for the given personal name
761 Args: name -- Personal name to extract initials from
763 Returns: pointer to name overwritten with initials
766 reply_quote_initials(char *name
)
774 cbuf
.cbuf
[i
= 0] = '\0';
775 cbuf
.cbufp
= cbuf
.cbuf
;
776 cbuf
.cbufend
= cbuf
.cbuf
;
778 /* while there are still characters to look at */
780 /* skip to next initial */
781 while(*s
&& (unsigned char) *s
== ' ')
784 if(!utf8_to_ucs4_oneatatime((unsigned char) *s
++ & 0xff, &cbuf
, &ucs
, NULL
)){
790 for(j
= 0; j
<= i
; j
++) *w
++ = cbuf
.cbuf
[j
];
792 /* skip to end of this piece of name */
793 while(*s
&& (unsigned char) *s
!= ' ')
796 cbuf
.cbuf
[i
= 0] = '\0';
797 cbuf
.cbufp
= cbuf
.cbuf
;
798 cbuf
.cbufend
= cbuf
.cbuf
;
808 * There is an assumption that MAX_SUBSTITUTION is <= the size of the
809 * tokens being substituted for (only in the size of buf below).
811 #define MAX_SUBSTITUTION 6
812 #define MAX_PREFIX 63
813 static char *from_token
= "_FROM_";
814 static char *nick_token
= "_NICK_";
815 static char *init_token
= "_INIT_";
817 /*----------------------------------------------------------------------
818 return a quoting string, "> " by default, for replied text
820 Args: env -- envelope of message being replied to
822 Returns: malloc'd array containing quoting string, freed by caller
825 reply_quote_str(ENVELOPE
*env
)
827 char *prefix
, *repl
, *p
, buf
[MAX_PREFIX
+1], pbf
[MAX_SUBSTITUTION
+1];
829 strncpy(buf
, ps_global
->VAR_REPLY_STRING
, sizeof(buf
)-1);
830 buf
[sizeof(buf
)-1] = '\0';
832 /* set up the prefix to quote included text */
833 if((p
= strstr(buf
, from_token
)) != NULL
){
834 repl
= (env
&& env
->from
&& env
->from
->mailbox
) ? env
->from
->mailbox
836 strncpy(pbf
, repl
, sizeof(pbf
)-1);
837 pbf
[sizeof(pbf
)-1] = '\0';;
838 rplstr(p
, sizeof(buf
)-(p
-buf
), strlen(from_token
), pbf
);
841 if((p
= strstr(buf
, nick_token
)) != NULL
){
845 get_nickname_from_addr(env
->from
, tmp_20k_buf
, 1000))
847 strncpy(pbf
, repl
, sizeof(pbf
)-1);
848 pbf
[sizeof(pbf
)-1] = '\0';;
849 rplstr(p
, sizeof(buf
)-(p
-buf
), strlen(nick_token
), pbf
);
852 if((p
= strstr(buf
, init_token
)) != NULL
){
854 char buftmp
[MAILTMPLEN
];
856 snprintf(buftmp
, sizeof(buftmp
), "%.200s",
857 (env
&& env
->from
&& env
->from
->personal
) ? env
->from
->personal
: "");
858 buftmp
[sizeof(buftmp
)-1] = '\0';
860 repl
= (env
&& env
->from
&& env
->from
->personal
)
861 ? reply_quote_initials(q
= cpystr((char *)rfc1522_decode_to_utf8(
862 (unsigned char *)tmp_20k_buf
,
863 SIZEOF_20KBUF
, buftmp
)))
866 istrncpy(pbf
, repl
, sizeof(pbf
)-1);
867 pbf
[sizeof(pbf
)-1] = '\0';;
868 rplstr(p
, sizeof(buf
)-(p
-buf
), strlen(init_token
), pbf
);
870 fs_give((void **)&q
);
873 prefix
= removing_quotes(cpystr(buf
));
879 reply_quote_str_contains_tokens(void)
881 return(ps_global
->VAR_REPLY_STRING
&& ps_global
->VAR_REPLY_STRING
[0] &&
882 (strstr(ps_global
->VAR_REPLY_STRING
, from_token
) ||
883 strstr(ps_global
->VAR_REPLY_STRING
, nick_token
) ||
884 strstr(ps_global
->VAR_REPLY_STRING
, init_token
)));
888 /*----------------------------------------------------------------------
889 Build the body for the message number/part being replied to
893 Result: BODY structure suitable for sending
895 ----------------------------------------------------------------------*/
897 reply_body(MAILSTREAM
*stream
, ENVELOPE
*env
, struct mail_bodystruct
*orig_body
,
898 long int msgno
, char *sect_prefix
, void *msgtext
, char *prefix
,
899 int plustext
, ACTION_S
*role
, int toplevel
, REDRAFT_POS_S
**redraft_pos
)
901 char *p
, *sig
= NULL
, *section
, sect_buf
[256];
902 BODY
*body
= NULL
, *tmp_body
= NULL
;
905 int impl
, template_len
= 0, leave_cursor_at_top
= 0, reply_raw_body
= 0;
908 snprintf(section
= sect_buf
, sizeof(sect_buf
), "%.*s.1", sizeof(sect_buf
)-1, sect_prefix
);
912 sect_buf
[sizeof(sect_buf
)-1] = '\0';
914 if(ps_global
->full_header
== 2
915 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
918 gf_set_so_writec(&pc
, (STORE_S
*) msgtext
);
924 filtered
= detoken(role
, env
, 0,
925 F_ON(F_SIG_AT_BOTTOM
, ps_global
) ? 1 : 0,
926 0, redraft_pos
, &impl
);
929 so_puts((STORE_S
*)msgtext
, filtered
);
931 template_len
= strlen(filtered
);
933 leave_cursor_at_top
++;
936 fs_give((void **)&filtered
);
945 (sig
= reply_signature(role
, env
, redraft_pos
, &impl
)) &&
946 F_OFF(F_SIG_AT_BOTTOM
, ps_global
)){
949 * If CURSORPOS was set explicitly in sig_file, and there was a
950 * template file before that, we need to adjust the offset by the
951 * length of the template file. However, if the template had
952 * a set CURSORPOS in it then impl was 2 before getting to the
953 * signature, so offset wouldn't have been reset by the signature
954 * CURSORPOS and offset would already be correct. That case will
955 * be ok here because template_len will be 0 and adding it does
956 * nothing. If template
957 * didn't have CURSORPOS in it, then impl was 1 and got set to 2
958 * by the CURSORPOS in the sig. In that case we have to adjust the
959 * offset. That's what the next line does. It adjusts it if
960 * template_len is nonzero and if CURSORPOS was set in sig_file.
963 (*redraft_pos
)->offset
+= template_len
;
966 so_puts((STORE_S
*)msgtext
, sig
);
969 * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
970 * is set, we won't have used it yet and want it to be non-NULL.
972 fs_give((void **)&sig
);
976 * Only put cursor in sig if there is a cursorpos there but not
977 * one in the template, and sig-at-bottom.
979 if(!(sig
&& impl
== 2 && !leave_cursor_at_top
))
980 leave_cursor_at_top
++;
984 || orig_body
->type
== TYPETEXT
986 || F_OFF(F_ATTACHMENTS_IN_REPLY
, ps_global
)){
987 char *charset
= NULL
;
989 /*------ Simple text-only message ----*/
990 body
= mail_newbody();
991 body
->type
= TYPETEXT
;
992 body
->contents
.text
.data
= msgtext
;
993 reply_delimiter(env
, role
, pc
);
994 if(F_ON(F_INCLUDE_HEADER
, ps_global
))
995 reply_forward_header(stream
, msgno
, sect_prefix
,
998 if(!orig_body
|| reply_raw_body
|| reply_body_text(orig_body
, &tmp_body
)){
1001 bodyp
= reply_raw_body
? NULL
: tmp_body
;
1004 * We set the charset in the outgoing message to the same
1005 * as the one in the message we're replying to unless it
1006 * is the unknown charset. We do that in order to attempt
1007 * to preserve the same charset in the reply if possible.
1008 * It may be safer to just set it to whatever we want instead
1009 * but then the receiver may not be able to read it.
1012 && (charset
= parameter_val(bodyp
->parameter
, "charset"))
1013 && strucmp(charset
, UNKNOWN_CHARSET
))
1014 set_parameter(&body
->parameter
, "charset", charset
);
1017 fs_give((void **) &charset
);
1019 get_body_part_text(stream
, bodyp
, msgno
,
1020 bodyp
? (p
= body_partno(stream
, msgno
, bodyp
))
1022 0L, pc
, prefix
, NULL
, GBPT_NONE
);
1024 fs_give((void **) &p
);
1027 gf_puts(NEWLINE
, pc
);
1028 gf_puts(" [NON-Text Body part not included]", pc
);
1029 gf_puts(NEWLINE
, pc
);
1032 else if(orig_body
->type
== TYPEMULTIPART
){
1033 /*------ Message is Multipart ------*/
1034 if(orig_body
->subtype
1035 && !strucmp(orig_body
->subtype
, "signed")
1036 && orig_body
->nested
.part
){
1037 /* operate only on the signed part */
1038 body
= reply_body(stream
, env
,
1039 &orig_body
->nested
.part
->body
,
1040 msgno
, section
, msgtext
, prefix
,
1041 plustext
, role
, 0, redraft_pos
);
1043 else if(orig_body
->subtype
1044 && !strucmp(orig_body
->subtype
, "alternative")){
1045 /* Set up the simple text reply */
1046 body
= mail_newbody();
1047 body
->type
= TYPETEXT
;
1048 body
->contents
.text
.data
= msgtext
;
1050 if(reply_body_text(orig_body
, &tmp_body
)){
1051 reply_delimiter(env
, role
, pc
);
1052 if(F_ON(F_INCLUDE_HEADER
, ps_global
))
1053 reply_forward_header(stream
, msgno
, sect_prefix
,
1056 get_body_part_text(stream
, tmp_body
, msgno
,
1057 p
= body_partno(stream
,msgno
,tmp_body
),
1058 0L, pc
, prefix
, NULL
, GBPT_NONE
);
1060 fs_give((void **) &p
);
1063 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
1064 "No suitable multipart text found for inclusion!");
1067 body
= copy_body(NULL
, orig_body
);
1070 * whatever subtype it is, demote it
1071 * to plain old MIXED.
1074 fs_give((void **) &body
->subtype
);
1076 body
->subtype
= cpystr("Mixed");
1078 if(body
->nested
.part
&&
1079 body
->nested
.part
->body
.type
== TYPETEXT
) {
1080 char *new_charset
= NULL
;
1082 /*---- First part of the message is text -----*/
1083 body
->nested
.part
->body
.contents
.text
.data
= msgtext
;
1084 if(body
->nested
.part
->body
.subtype
&&
1085 strucmp(body
->nested
.part
->body
.subtype
, "Plain")){
1086 fs_give((void **)&body
->nested
.part
->body
.subtype
);
1087 body
->nested
.part
->body
.subtype
= cpystr("Plain");
1089 reply_delimiter(env
, role
, pc
);
1090 if(F_ON(F_INCLUDE_HEADER
, ps_global
))
1091 reply_forward_header(stream
, msgno
, sect_prefix
,
1094 if(!(get_body_part_text(stream
,
1095 &orig_body
->nested
.part
->body
,
1096 msgno
, section
, 0L, pc
, prefix
,
1097 &new_charset
, GBPT_NONE
)
1098 && fetch_contents(stream
, msgno
, sect_prefix
, body
)))
1099 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1100 _("Error including all message parts"));
1101 else if(new_charset
)
1102 set_parameter(&body
->nested
.part
->body
.parameter
, "charset", new_charset
);
1104 else if(orig_body
->nested
.part
->body
.type
== TYPEMULTIPART
1105 && orig_body
->nested
.part
->body
.subtype
1106 && !strucmp(orig_body
->nested
.part
->body
.subtype
,
1108 && reply_body_text(&orig_body
->nested
.part
->body
,
1112 reply_delimiter(env
, role
, pc
);
1113 if(F_ON(F_INCLUDE_HEADER
, ps_global
))
1114 reply_forward_header(stream
, msgno
, sect_prefix
,
1117 snprintf(sect_buf
, sizeof(sect_buf
), "%.*s%s%.*s",
1118 sizeof(sect_buf
)/2-2,
1119 sect_prefix
? sect_prefix
: "",
1120 sect_prefix
? "." : "",
1121 sizeof(sect_buf
)/2-2,
1122 p
= partno(orig_body
, tmp_body
));
1123 sect_buf
[sizeof(sect_buf
)-1] = '\0';
1124 fs_give((void **) &p
);
1125 get_body_part_text(stream
, tmp_body
, msgno
,
1126 sect_buf
, 0L, pc
, prefix
,
1129 part
= body
->nested
.part
->next
;
1130 body
->nested
.part
->next
= NULL
;
1131 mail_free_body_part(&body
->nested
.part
);
1132 body
->nested
.part
= mail_newbody_part();
1133 body
->nested
.part
->body
.type
= TYPETEXT
;
1134 body
->nested
.part
->body
.subtype
= cpystr("Plain");
1135 body
->nested
.part
->body
.contents
.text
.data
= msgtext
;
1136 body
->nested
.part
->next
= part
;
1138 for(partnum
= 2; part
!= NULL
; part
= part
->next
){
1139 snprintf(sect_buf
, sizeof(sect_buf
), "%.*s%s%d",
1141 sect_prefix
? sect_prefix
: "",
1142 sect_prefix
? "." : "", partnum
++);
1143 sect_buf
[sizeof(sect_buf
)-1] = '\0';
1145 if(!fetch_contents(stream
, msgno
,
1146 sect_buf
, &part
->body
)){
1152 /*--- Fetch the original pieces ---*/
1153 if(!fetch_contents(stream
, msgno
, sect_prefix
, body
))
1154 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1155 _("Error including all message parts"));
1157 /*--- No text part, create a blank one ---*/
1158 part
= mail_newbody_part();
1159 part
->next
= body
->nested
.part
;
1160 body
->nested
.part
= part
;
1161 part
->body
.contents
.text
.data
= msgtext
;
1166 /*---- Single non-text message of some sort ----*/
1167 body
= mail_newbody();
1168 body
->type
= TYPEMULTIPART
;
1169 part
= mail_newbody_part();
1170 body
->nested
.part
= part
;
1172 /*--- The first part, a blank text part to be edited ---*/
1173 part
->body
.type
= TYPETEXT
;
1174 part
->body
.contents
.text
.data
= msgtext
;
1176 /*--- The second part, what ever it is ---*/
1177 part
->next
= mail_newbody_part();
1179 part
->body
.id
= generate_message_id();
1180 copy_body(&(part
->body
), orig_body
);
1183 * the idea here is to fetch part into storage object
1185 if((part
->body
.contents
.text
.data
= (void *) so_get(PART_SO_TYPE
,
1186 NULL
,EDIT_ACCESS
)) != NULL
){
1187 if((p
= pine_mail_fetch_body(stream
, msgno
, section
,
1188 &part
->body
.size
.bytes
, NIL
)) != NULL
){
1189 so_nputs((STORE_S
*)part
->body
.contents
.text
.data
,
1190 p
, part
->body
.size
.bytes
);
1193 mail_free_body(&body
);
1196 mail_free_body(&body
);
1200 /*--------- No text included --------*/
1201 body
= mail_newbody();
1202 body
->type
= TYPETEXT
;
1203 body
->contents
.text
.data
= msgtext
;
1206 if(!leave_cursor_at_top
){
1210 /* rewind and count chars to start of sig file */
1211 so_seek((STORE_S
*)msgtext
, 0L, 0);
1212 while(so_readc(&c
, (STORE_S
*)msgtext
))
1216 *redraft_pos
= (REDRAFT_POS_S
*)fs_get(sizeof(**redraft_pos
));
1217 memset((void *)*redraft_pos
, 0,sizeof(**redraft_pos
));
1218 (*redraft_pos
)->hdrname
= cpystr(":");
1222 * If explicit cursor positioning in sig file,
1223 * add offset to start of sig file plus offset into sig file.
1224 * Else, just offset to start of sig file.
1226 (*redraft_pos
)->offset
+= cnt
;
1231 so_puts((STORE_S
*)msgtext
, sig
);
1233 fs_give((void **)&sig
);
1236 gf_clear_so_writec((STORE_S
*) msgtext
);
1243 * reply_part - first replyable multipart of a multipart.
1246 reply_body_text(struct mail_bodystruct
*body
, struct mail_bodystruct
**new_body
)
1254 case TYPEMULTIPART
:
1255 if(body
->subtype
&& !strucmp(body
->subtype
, "alternative")){
1259 if(ps_global
->force_prefer_plain
1260 || (!ps_global
->force_no_prefer_plain
1261 && F_ON(F_PREFER_PLAIN_TEXT
, ps_global
))){
1262 for(part
= body
->nested
.part
; part
; part
= part
->next
)
1263 if((!part
->body
.type
|| part
->body
.type
== TYPETEXT
)
1264 && (!part
->body
.subtype
1265 || !strucmp(part
->body
.subtype
, "plain"))){
1266 *new_body
= &part
->body
;
1272 * Else choose last alternative among plain or html parts.
1273 * Perhaps we should really be using mime_show() to make this
1274 * potentially more general than just plain or html.
1276 for(part
= body
->nested
.part
; part
; part
= part
->next
){
1277 if((!part
->body
.type
|| part
->body
.type
== TYPETEXT
)
1278 && ((!part
->body
.subtype
|| !strucmp(part
->body
.subtype
, "plain"))
1280 (part
->body
.subtype
&& !strucmp(part
->body
.subtype
, "html")))){
1282 *new_body
= &part
->body
;
1289 else if(body
->nested
.part
)
1290 /* NOTE: we're only interested in "first" part of mixed */
1291 return(reply_body_text(&body
->nested
.part
->body
, new_body
));
1305 reply_signature(ACTION_S
*role
, ENVELOPE
*env
, REDRAFT_POS_S
**redraft_pos
, int *impl
)
1310 sig
= detoken(role
, env
,
1311 2, F_ON(F_SIG_AT_BOTTOM
, ps_global
) ? 0 : 1, 1,
1314 if(F_OFF(F_SIG_AT_BOTTOM
, ps_global
) && (!sig
|| !*sig
)){
1316 fs_give((void **)&sig
);
1318 l
= 2 * strlen(NEWLINE
);
1319 sig
= (char *)fs_get((l
+1) * sizeof(char));
1320 strncpy(sig
, NEWLINE
, l
);
1322 strncat(sig
, NEWLINE
, l
+1-1-strlen(sig
));
1332 * Buf is at least size maxlen+1
1335 get_addr_data(ENVELOPE
*env
, IndexColType type
, char *buf
, size_t maxlen
)
1337 ADDRESS
*addr
= NULL
;
1338 ADDRESS
*last_to
= NULL
;
1339 ADDRESS
*first_addr
= NULL
, *second_addr
= NULL
;
1340 ADDRESS
*third_addr
= NULL
, *fourth_addr
= NULL
;
1349 addr
= env
? env
->from
: NULL
;
1353 addr
= env
? env
->to
: NULL
;
1357 addr
= env
? env
->cc
: NULL
;
1361 addr
= env
? env
->sender
: NULL
;
1365 * Recips is To and Cc togeter. We hook the two adrlists together
1369 addr
= env
? env
->to
: NULL
;
1370 /* Find end of To list */
1371 for(last_to
= addr
; last_to
&& last_to
->next
; last_to
= last_to
->next
)
1374 /* Make the end of To list point to cc list */
1376 last_to
->next
= (env
? env
->cc
: NULL
);
1384 if(env
&& env
->from
&& env
->from
->personal
){
1385 char *name
, *initials
= NULL
;
1387 name
= (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
1388 SIZEOF_20KBUF
, env
->from
->personal
);
1389 if(name
== env
->from
->personal
){
1390 strncpy(tmp_20k_buf
, name
, SIZEOF_20KBUF
-1);
1391 tmp_20k_buf
[SIZEOF_20KBUF
- 1] = '\0';
1396 initials
= reply_quote_initials(name
);
1397 iutf8ncpy(buf
, initials
, maxlen
);
1408 orig_maxlen
= maxlen
;
1411 /* skip over rest of c-client group addr */
1412 if(first_addr
&& first_addr
->mailbox
&& !first_addr
->host
){
1413 for(second_addr
= first_addr
->next
;
1414 second_addr
&& second_addr
->host
;
1415 second_addr
= second_addr
->next
)
1418 if(second_addr
&& !second_addr
->host
)
1419 second_addr
= second_addr
->next
;
1421 else if(!(first_addr
&& first_addr
->host
&& first_addr
->host
[0] == '.'))
1422 second_addr
= first_addr
? first_addr
->next
: NULL
;
1424 if(second_addr
&& second_addr
->mailbox
&& !second_addr
->host
){
1425 for(third_addr
= second_addr
->next
;
1426 third_addr
&& third_addr
->host
;
1427 third_addr
= third_addr
->next
)
1430 if(third_addr
&& !third_addr
->host
)
1431 third_addr
= third_addr
->next
;
1433 else if(!(second_addr
&& second_addr
->host
&& second_addr
->host
[0] == '.'))
1434 third_addr
= second_addr
? second_addr
->next
: NULL
;
1436 if(third_addr
&& third_addr
->mailbox
&& !third_addr
->host
){
1437 for(fourth_addr
= third_addr
->next
;
1438 fourth_addr
&& fourth_addr
->host
;
1439 fourth_addr
= fourth_addr
->next
)
1442 if(fourth_addr
&& !fourth_addr
->host
)
1443 fourth_addr
= fourth_addr
->next
;
1445 else if(!(third_addr
&& third_addr
->host
&& third_addr
->host
[0] == '.'))
1446 fourth_addr
= third_addr
? third_addr
->next
: NULL
;
1448 /* Just attempting to make a nice display */
1449 if(first_addr
&& ((first_addr
->personal
&& first_addr
->personal
[0]) ||
1450 (first_addr
->mailbox
&& first_addr
->mailbox
[0]))){
1452 if((second_addr
->personal
&& second_addr
->personal
[0]) ||
1453 (second_addr
->mailbox
&& second_addr
->mailbox
[0])){
1455 if((third_addr
->personal
&& third_addr
->personal
[0]) ||
1456 (third_addr
->mailbox
&& third_addr
->mailbox
[0])){
1479 a_little_addr_string(first_addr
, p
, maxlen
);
1480 else if(cntaddr
== 2){
1481 a_little_addr_string(first_addr
, p
, maxlen
);
1482 maxlen
-= (l
=strlen(p
));
1485 strncpy(p
, " and ", maxlen
);
1488 a_little_addr_string(second_addr
, p
, maxlen
);
1491 else if(cntaddr
== 3){
1492 a_little_addr_string(first_addr
, p
, maxlen
);
1493 maxlen
-= (l
=strlen(p
));
1496 strncpy(p
, ", ", maxlen
);
1499 a_little_addr_string(second_addr
, p
, maxlen
);
1500 maxlen
-= (l
=strlen(p
));
1503 strncpy(p
, ", and ", maxlen
);
1506 a_little_addr_string(third_addr
, p
, maxlen
);
1510 else if(cntaddr
> 3){
1511 a_little_addr_string(first_addr
, p
, maxlen
);
1512 maxlen
-= (l
=strlen(p
));
1515 strncpy(p
, ", ", maxlen
);
1518 a_little_addr_string(second_addr
, p
, maxlen
);
1519 maxlen
-= (l
=strlen(p
));
1522 strncpy(p
, ", ", maxlen
);
1525 a_little_addr_string(third_addr
, p
, maxlen
);
1526 maxlen
-= (l
=strlen(p
));
1529 strncpy(p
, ", and others", maxlen
);
1530 else if(maxlen
>= 3)
1531 strncpy(p
, "...", maxlen
);
1538 a_string
= addr_list_string(addr
, NULL
, 0);
1539 iutf8ncpy(buf
, a_string
, maxlen
);
1541 fs_give((void **)&a_string
);
1545 last_to
->next
= NULL
;
1547 buf
[orig_maxlen
] = '\0';
1552 * Buf is at least size maxlen+1
1555 get_news_data(ENVELOPE
*env
, IndexColType type
, char *buf
, size_t maxlen
)
1557 int cntnews
= 0, orig_maxlen
;
1558 char *news
= NULL
, *p
, *q
;
1564 case iNewsAndRecips
:
1565 case iRecipsAndNews
:
1566 news
= env
? env
->newsgroups
: NULL
;
1570 if(ps_global
->mail_stream
&& IS_NEWS(ps_global
->mail_stream
))
1571 news
= ps_global
->cur_folder
;
1579 orig_maxlen
= maxlen
;
1583 while(isspace((unsigned char)*q
))
1589 while((q
= strindex(q
, ',')) != NULL
){
1591 while(isspace((unsigned char)*q
))
1602 istrncpy(buf
, news
, maxlen
);
1604 removing_leading_and_trailing_white_space(buf
);
1606 else if(cntnews
== 2){
1609 while(isspace((unsigned char)*q
))
1612 while(maxlen
> 0 && *q
&& !isspace((unsigned char)*q
) && *q
!= ','){
1618 strncpy(p
, " and ", maxlen
);
1623 while(isspace((unsigned char)*q
) || *q
== ',')
1626 while(maxlen
> 0 && *q
&& !isspace((unsigned char)*q
) && *q
!= ','){
1633 istrncpy(tmp_20k_buf
, buf
, 10000);
1634 strncpy(buf
, tmp_20k_buf
, orig_maxlen
);
1636 else if(cntnews
> 2){
1641 while(isspace((unsigned char)*q
))
1644 while(maxlen
> 0 && *q
&& !isspace((unsigned char)*q
) && *q
!= ','){
1650 snprintf(b
, sizeof(b
), " and %d other newsgroups", cntnews
-1);
1651 b
[sizeof(b
)-1] = '\0';
1652 if(maxlen
>= strlen(b
))
1653 strncpy(p
, b
, maxlen
);
1654 else if(maxlen
>= 3)
1655 strncpy(p
, "...", maxlen
);
1657 buf
[orig_maxlen
] = '\0';
1659 istrncpy(tmp_20k_buf
, buf
, 10000);
1660 tmp_20k_buf
[10000-1] = '\0';
1661 strncpy(buf
, tmp_20k_buf
, orig_maxlen
);
1664 buf
[orig_maxlen
] = '\0';
1669 * Buf is at least size maxlen+1
1672 get_reply_data(ENVELOPE
*env
, ACTION_S
*role
, IndexColType type
, char *buf
, size_t maxlen
)
1675 IndexColType addrtype
;
1680 case iRDate
: case iSDate
: case iSTime
:
1681 case iS1Date
: case iS2Date
: case iS3Date
: case iS4Date
:
1682 case iSDateIso
: case iSDateIsoS
:
1683 case iSDateS1
: case iSDateS2
: case iSDateS3
: case iSDateS4
:
1685 case iSDateTimeIso
: case iSDateTimeIsoS
:
1686 case iSDateTimeS1
: case iSDateTimeS2
: case iSDateTimeS3
: case iSDateTimeS4
:
1688 case iSDateTimeIso24
: case iSDateTimeIsoS24
:
1689 case iSDateTimeS124
: case iSDateTimeS224
: case iSDateTimeS324
: case iSDateTimeS424
:
1690 case iDateIso
: case iDateIsoS
: case iTime24
: case iTime12
:
1691 case iDay
: case iDayOrdinal
: case iDay2Digit
:
1692 case iMonAbb
: case iMonLong
: case iMon
: case iMon2Digit
:
1693 case iYear
: case iYear2Digit
:
1694 case iDate
: case iLDate
:
1695 case iTimezone
: case iDayOfWeekAbb
: case iDayOfWeek
:
1696 case iPrefDate
: case iPrefTime
: case iPrefDateTime
:
1697 if(env
&& env
->date
&& env
->date
[0] && maxlen
>= 20)
1698 date_str((char *) env
->date
, type
, 1, buf
, maxlen
+1, 0);
1710 case iCurDayOfWeekAbb
:
1716 case iCurYear2Digit
:
1718 case iCurPrefDateTime
:
1725 case iLstMonYear2Digit
:
1727 case iLstYear2Digit
:
1729 date_str(NULL
, type
, 1, buf
, maxlen
+1, 0);
1739 get_addr_data(env
, type
, buf
, maxlen
);
1743 if(role
&& role
->nick
){
1744 strncpy(buf
, role
->nick
, maxlen
);
1750 if(maxlen
>= strlen(NEWLINE
)){
1751 strncpy(buf
, NEWLINE
, maxlen
);
1758 if(env
&& env
->from
&& env
->from
->mailbox
&& env
->from
->mailbox
[0] &&
1759 strlen(env
->from
->mailbox
) <= maxlen
){
1760 strncpy(buf
, env
->from
->mailbox
, maxlen
);
1762 if(type
== iAddress
&&
1764 env
->from
->host
[0] &&
1765 env
->from
->host
[0] != '.' &&
1766 strlen(buf
) + strlen(env
->from
->host
) + 1 <= maxlen
){
1767 strncat(buf
, "@", maxlen
+1-1-strlen(buf
));
1769 strncat(buf
, env
->from
->host
, maxlen
+1-1-strlen(buf
));
1778 get_news_data(env
, type
, buf
, maxlen
);
1783 case iRecipsAndNews
:
1784 case iNewsAndRecips
:
1785 if(type
== iToAndNews
|| type
== iNewsAndTo
)
1790 if(env
&& env
->newsgroups
){
1791 space
= (char *)fs_get((maxlen
+1) * sizeof(char));
1792 get_news_data(env
, type
, space
, maxlen
);
1795 get_addr_data(env
, addrtype
, buf
, maxlen
);
1797 if(space
&& *space
&& *buf
){
1798 if(strlen(space
) + strlen(buf
) + 5 > maxlen
){
1799 if(strlen(space
) > maxlen
/2)
1800 get_news_data(env
, type
, space
, maxlen
- strlen(buf
) - 5);
1802 get_addr_data(env
, addrtype
, buf
, maxlen
- strlen(space
) - 5);
1805 if(type
== iToAndNews
|| type
== iRecipsAndNews
)
1806 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s and %s", buf
, space
);
1808 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s and %s", space
, buf
);
1810 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1812 strncpy(buf
, tmp_20k_buf
, maxlen
);
1815 else if(space
&& *space
){
1816 strncpy(buf
, space
, maxlen
);
1821 fs_give((void **)&space
);
1826 if(env
&& env
->subject
){
1828 unsigned char *p
, *tmp
= NULL
;
1830 if((n
= 4*strlen(env
->subject
)) > SIZEOF_20KBUF
-1){
1832 p
= tmp
= (unsigned char *)fs_get(len
* sizeof(char));
1835 len
= SIZEOF_20KBUF
;
1836 p
= (unsigned char *)tmp_20k_buf
;
1839 istrncpy(buf
, (char *)rfc1522_decode_to_utf8(p
, len
, env
->subject
), maxlen
);
1844 fs_give((void **)&tmp
);
1850 if(env
&& env
->message_id
){
1851 strncpy(buf
, env
->message_id
, maxlen
);
1867 * reply_delimiter - output formatted reply delimiter for given envelope
1868 * with supplied character writing function.
1871 reply_delimiter(ENVELOPE
*env
, ACTION_S
*role
, gf_io_t pc
)
1873 #define MAX_DELIM 2000
1874 char buf
[MAX_DELIM
+1];
1876 char *filtered
= NULL
;
1877 int contains_newline_token
= 0;
1883 strncpy(buf
, ps_global
->VAR_REPLY_INTRO
, MAX_DELIM
);
1884 buf
[MAX_DELIM
] = '\0';
1885 /* preserve exact default behavior from before */
1886 if(!strcmp(buf
, DEFAULT_REPLY_INTRO
)){
1890 parse_date((char *) env
->date
, &d
);
1891 include_date
= !(d
.day
== -1 || d
.month
== -1 || d
.year
== -1);
1893 gf_puts("On ", pc
); /* All delims have... */
1894 if(d
.wkday
!= -1){ /* "On day, date month year" */
1895 gf_puts(day_abbrev(d
.wkday
), pc
); /* in common */
1899 gf_puts(int2string(d
.day
), pc
);
1901 gf_puts(month_abbrev(d
.month
), pc
);
1903 gf_puts(int2string(d
.year
), pc
);
1907 && ((env
->from
->personal
&& env
->from
->personal
[0])
1908 || (env
->from
->mailbox
&& env
->from
->mailbox
[0]))){
1909 char buftmp
[MAILTMPLEN
];
1911 a_little_addr_string(env
->from
, buftmp
, sizeof(buftmp
)-1);
1915 gf_puts(buftmp
, pc
);
1916 gf_puts(" wrote:", pc
);
1920 gf_puts(", it was written", pc
);
1922 gf_puts("It was written", pc
);
1928 * This is here for backwards compatibility. There didn't used
1929 * to be a _NEWLINE_ token. The user would enter text that should
1930 * all fit on one line and then that was followed by two newlines.
1931 * Also, truncation occurs if it is long.
1932 * Now, if _NEWLINE_ is not in the text, same thing still works
1933 * the same. However, if _NEWLINE_ is in there, then all bets are
1934 * off and the user is on his or her own. No automatic newlines
1935 * are added, only those that come from the tokens. No truncation
1936 * is done, the user is trusted to get it right. Newlines may be
1937 * embedded so that the leadin is multi-line.
1939 contains_newline_token
= (strstr(buf
, "_NEWLINE_") != NULL
);
1940 filtered
= detoken_src(buf
, FOR_REPLY_INTRO
, env
, role
,
1943 /* try to truncate if too long */
1944 if(!contains_newline_token
&& filtered
&& utf8_width(filtered
) > 80){
1945 int ended_with_colon
= 0;
1946 int ended_with_quote
= 0;
1947 int ended_with_quote_colon
= 0;
1950 l
= strlen(filtered
);
1952 if(filtered
[l
-1] == ':'){
1953 ended_with_colon
= ':';
1954 if(filtered
[l
-2] == QUOTE
|| filtered
[l
-2] == '\'')
1955 ended_with_quote_colon
= filtered
[l
-2];
1957 else if(filtered
[l
-1] == QUOTE
|| filtered
[l
-1] == '\'')
1958 ended_with_quote
= filtered
[l
-1];
1960 /* try to find space to break at */
1961 for(p
= &filtered
[75]; p
> &filtered
[60] &&
1962 !isspace((unsigned char)*p
); p
--)
1965 if(!isspace((unsigned char)*p
))
1971 if(ended_with_quote_colon
){
1972 *p
++ = ended_with_quote_colon
;
1975 else if(ended_with_colon
)
1976 *p
++ = ended_with_colon
;
1977 else if(ended_with_quote
)
1978 *p
++ = ended_with_quote
;
1983 if(filtered
&& *filtered
)
1984 gf_puts(filtered
, pc
);
1987 /* and end with two newlines unless no leadin at all */
1988 if(!contains_newline_token
){
1989 if(!strcmp(buf
, DEFAULT_REPLY_INTRO
) || (filtered
&& *filtered
)){
1990 gf_puts(NEWLINE
, pc
);
1991 gf_puts(NEWLINE
, pc
);
1996 fs_give((void **)&filtered
);
2001 free_redraft_pos(REDRAFT_POS_S
**redraft_pos
)
2003 if(redraft_pos
&& *redraft_pos
){
2004 if((*redraft_pos
)->hdrname
)
2005 fs_give((void **)&(*redraft_pos
)->hdrname
);
2007 fs_give((void **)redraft_pos
);
2012 /*----------------------------------------------------------------------
2013 Build the body for the message number/part being forwarded as ATTACHMENT
2017 Result: PARTS suitably attached to body
2019 ----------------------------------------------------------------------*/
2021 forward_mime_msg(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
, struct mail_body_part
**partp
, void *msgtext
)
2027 *partp
= mail_newbody_part();
2028 b
= &(*partp
)->body
;
2029 b
->type
= TYPEMESSAGE
;
2030 b
->id
= generate_message_id();
2031 b
->description
= cpystr("Forwarded Message");
2032 b
->nested
.msg
= mail_newmsg();
2033 b
->disposition
.type
= cpystr("inline");
2035 /*---- Package each message in a storage object ----*/
2036 if((b
->contents
.text
.data
= (void *) so_get(PART_SO_TYPE
,NULL
,EDIT_ACCESS
))
2037 && (tmp_text
= mail_fetch_header(stream
,msgno
,section
,NIL
,NIL
,FT_PEEK
))
2039 so_puts((STORE_S
*) b
->contents
.text
.data
, tmp_text
);
2041 b
->size
.bytes
= strlen(tmp_text
);
2042 so_puts((STORE_S
*) b
->contents
.text
.data
, "\015\012");
2043 if((tmp_text
= pine_mail_fetch_text (stream
,msgno
,section
,&len
,NIL
)) != NULL
){
2044 so_nputs((STORE_S
*)b
->contents
.text
.data
,tmp_text
,(long) len
);
2045 b
->size
.bytes
+= len
;
2055 * forward_delimiter - return delimiter for forwarded text
2058 forward_delimiter(gf_io_t pc
)
2060 gf_puts(NEWLINE
, pc
);
2061 /* TRANSLATORS: When a message is forwarded by the user this is the
2062 text that shows where the forwarded part of the message begins. */
2063 gf_puts(_("---------- Forwarded message ----------"), pc
);
2064 gf_puts(NEWLINE
, pc
);
2068 /*----------------------------------------------------------------------
2069 Wrapper for header formatting tool
2077 Result: header suitable for reply/forward text written using "pc"
2079 ----------------------------------------------------------------------*/
2081 reply_forward_header(MAILSTREAM
*stream
, long int msgno
, char *part
, ENVELOPE
*env
,
2082 gf_io_t pc
, char *prefix
)
2086 char **list
, **new_list
= NULL
;
2088 list
= ps_global
->VAR_VIEW_HEADERS
;
2091 * If VIEW_HEADERS is set, we should remove BCC from the list so that
2092 * the user doesn't inadvertently forward the BCC header.
2094 if(list
&& list
[0]){
2101 p
= new_list
= (char **) fs_get((cnt
+1) * sizeof(char *));
2103 for(i
=0; list
[i
]; i
++)
2104 if(strucmp(list
[i
], "bcc"))
2105 *p
++ = cpystr(list
[i
]);
2109 if(new_list
&& new_list
[0])
2114 HD_INIT(&h
, list
, ps_global
->view_all_except
, FE_DEFAULT
& ~FE_BCC
);
2115 if((rv
= format_header(stream
, msgno
, part
, env
, &h
,
2116 prefix
, NULL
, FM_NOINDENT
, NULL
, pc
)) != 0){
2118 gf_puts(" [Error fetching message header data]", pc
);
2121 gf_puts(NEWLINE
, pc
); /* write header delimiter */
2124 free_list_array(&new_list
);
2128 /*----------------------------------------------------------------------
2129 Build the subject for the message number being forwarded
2131 Args: pine_state -- The usual pine structure
2132 msgno -- The message number to build subject for
2134 Result: malloc'd string containing new subject or NULL on error
2136 ----------------------------------------------------------------------*/
2138 forward_subject(ENVELOPE
*env
, int flags
)
2141 char *p
, buftmp
[MAILTMPLEN
];
2146 dprint((9, "checking subject: \"%s\"\n",
2147 env
->subject
? env
->subject
: "NULL"));
2149 if(env
->subject
&& env
->subject
[0]){ /* add (fwd)? */
2150 snprintf(buftmp
, sizeof(buftmp
), "%s", env
->subject
);
2151 buftmp
[sizeof(buftmp
)-1] = '\0';
2152 /* decode any 8bit (copy to the temp buffer if decoding doesn't) */
2153 if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2154 SIZEOF_20KBUF
, buftmp
) == (unsigned char *) buftmp
)
2155 strncpy(tmp_20k_buf
, buftmp
, SIZEOF_20KBUF
);
2157 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2159 removing_trailing_white_space(tmp_20k_buf
);
2160 if((l
= strlen(tmp_20k_buf
)) < 1000 &&
2161 (l
< 5 || strcmp(tmp_20k_buf
+l
-5,"(fwd)"))){
2162 snprintf(tmp_20k_buf
+2000, SIZEOF_20KBUF
-2000, "%s (fwd)", tmp_20k_buf
);
2163 tmp_20k_buf
[SIZEOF_20KBUF
-2000-1] = '\0';
2164 strncpy(tmp_20k_buf
, tmp_20k_buf
+2000, SIZEOF_20KBUF
);
2165 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2169 * HACK: composer can't handle embedded double quotes in attachment
2170 * comments so we substitute two single quotes.
2172 if(flags
& FS_CONVERT_QUOTES
)
2173 while((p
= strchr(tmp_20k_buf
, QUOTE
)) != NULL
)
2174 (void)rplstr(p
, SIZEOF_20KBUF
-(p
-tmp_20k_buf
), 1, "''");
2176 return(cpystr(tmp_20k_buf
));
2180 return(cpystr("Forwarded mail...."));
2184 /*----------------------------------------------------------------------
2185 Build the body for the message number/part being forwarded
2189 Result: BODY structure suitable for sending
2191 ----------------------------------------------------------------------*/
2193 forward_body(MAILSTREAM
*stream
, ENVELOPE
*env
, struct mail_bodystruct
*orig_body
,
2194 long int msgno
, char *sect_prefix
, void *msgtext
, int flags
)
2196 BODY
*body
= NULL
, *text_body
, *tmp_body
;
2199 char *tmp_text
, *section
, sect_buf
[256];
2200 int forward_raw_body
= 0;
2203 * Check to see if messages got expunged out from underneath us. This
2204 * could have happened during the prompt to the user asking whether to
2205 * include the message as an attachment. Either the message is gone or
2206 * it might be at a different sequence number. We'd better bail.
2208 if(ps_global
->full_header
== 2
2209 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
2210 forward_raw_body
= 1;
2211 if(sp_expunge_count(stream
))
2214 if(sect_prefix
&& forward_raw_body
== 0)
2215 snprintf(section
= sect_buf
, sizeof(sect_buf
), "%s.1", sect_prefix
);
2216 else if(sect_prefix
&& forward_raw_body
)
2217 section
= sect_prefix
;
2218 else if(!sect_prefix
&& forward_raw_body
)
2223 sect_buf
[sizeof(sect_buf
)-1] = '\0';
2225 gf_set_so_writec(&pc
, (STORE_S
*) msgtext
);
2226 if(!orig_body
|| orig_body
->type
== TYPETEXT
|| forward_raw_body
) {
2227 char *charset
= NULL
;
2229 /*---- Message has a single text part -----*/
2230 body
= mail_newbody();
2231 body
->type
= TYPETEXT
;
2232 body
->contents
.text
.data
= msgtext
;
2234 && (charset
= parameter_val(orig_body
->parameter
, "charset")))
2235 set_parameter(&body
->parameter
, "charset", charset
);
2238 fs_give((void **) &charset
);
2240 if(!(flags
& FWD_ANON
)){
2241 forward_delimiter(pc
);
2242 reply_forward_header(stream
, msgno
, sect_prefix
, env
, pc
, "");
2245 if(!get_body_part_text(stream
, forward_raw_body
? NULL
: orig_body
,
2246 msgno
, section
, 0L, pc
, NULL
, NULL
, GBPT_NONE
)){
2247 mail_free_body(&body
);
2251 else if(orig_body
->type
== TYPEMULTIPART
) {
2252 if(orig_body
->subtype
&& !strucmp(orig_body
->subtype
, "signed")
2253 && orig_body
->nested
.part
){
2254 /* only operate on the signed data (not the signature) */
2255 body
= forward_body(stream
, env
, &orig_body
->nested
.part
->body
,
2256 msgno
, sect_prefix
, msgtext
, flags
);
2258 /*---- Message is multipart ----*/
2259 else if(!(orig_body
->subtype
&& !strucmp(orig_body
->subtype
,
2261 && (body
= forward_multi_alt(stream
, env
, orig_body
, msgno
,
2262 sect_prefix
, msgtext
,
2264 /*--- Copy the body and entire structure ---*/
2265 body
= copy_body(NULL
, orig_body
);
2268 * whatever subtype it is, demote it
2269 * to plain old MIXED.
2272 fs_give((void **) &body
->subtype
);
2274 body
->subtype
= cpystr("Mixed");
2276 /*--- The text part of the message ---*/
2277 if(!body
->nested
.part
){
2278 q_status_message(SM_ORDER
| SM_DING
, 3, 6,
2279 "Error referencing body part 1");
2280 mail_free_body(&body
);
2282 else if(body
->nested
.part
->body
.type
== TYPETEXT
) {
2283 char *new_charset
= NULL
;
2285 /*--- The first part is text ----*/
2286 text_body
= &body
->nested
.part
->body
;
2287 text_body
->contents
.text
.data
= msgtext
;
2288 if(text_body
->subtype
&& strucmp(text_body
->subtype
, "Plain")){
2289 /* this text is going to the composer, it should be Plain */
2290 fs_give((void **)&text_body
->subtype
);
2291 text_body
->subtype
= cpystr("PLAIN");
2293 if(!(flags
& FWD_ANON
)){
2294 forward_delimiter(pc
);
2295 reply_forward_header(stream
, msgno
,
2296 sect_prefix
, env
, pc
, "");
2299 if(!(get_body_part_text(stream
, &orig_body
->nested
.part
->body
,
2300 msgno
, section
, 0L, pc
,
2301 NULL
, &new_charset
, GBPT_NONE
)
2302 && fetch_contents(stream
, msgno
, sect_prefix
, body
)))
2303 mail_free_body(&body
);
2304 else if(new_charset
)
2305 set_parameter(&text_body
->parameter
, "charset", new_charset
);
2307 /* BUG: ? matter that we're not setting body.size.bytes */
2309 else if(body
->nested
.part
->body
.type
== TYPEMULTIPART
2310 && body
->nested
.part
->body
.subtype
2311 && !strucmp(body
->nested
.part
->body
.subtype
, "alternative")
2312 && (tmp_body
= forward_multi_alt(stream
, env
,
2313 &body
->nested
.part
->body
,
2316 flags
| FWD_NESTED
))){
2317 /* for the forward_multi_alt call above, we want to pass
2318 * sect_prefix instead of section so we can obtain the header.
2319 * Set the FWD_NESTED flag so we fetch the right body_part.
2323 part
= body
->nested
.part
->next
;
2324 body
->nested
.part
->next
= NULL
;
2325 mail_free_body_part(&body
->nested
.part
);
2326 body
->nested
.part
= mail_newbody_part();
2327 body
->nested
.part
->body
= *tmp_body
;
2328 body
->nested
.part
->next
= part
;
2330 for(partnum
= 2; part
!= NULL
; part
= part
->next
){
2331 snprintf(sect_buf
, sizeof(sect_buf
), "%s%s%d",
2332 sect_prefix
? sect_prefix
: "",
2333 sect_prefix
? "." : "", partnum
++);
2334 sect_buf
[sizeof(sect_buf
)-1] = '\0';
2336 if(!fetch_contents(stream
, msgno
, sect_buf
, &part
->body
)){
2337 mail_free_body(&body
);
2343 if(fetch_contents(stream
, msgno
, sect_prefix
, body
)){
2344 /*--- Create a new blank text part ---*/
2345 part
= mail_newbody_part();
2346 part
->next
= body
->nested
.part
;
2347 body
->nested
.part
= part
;
2348 part
->body
.contents
.text
.data
= msgtext
;
2351 mail_free_body(&body
);
2356 /*---- A single part message, not of type text ----*/
2357 body
= mail_newbody();
2358 body
->type
= TYPEMULTIPART
;
2359 part
= mail_newbody_part();
2360 body
->nested
.part
= part
;
2362 /*--- The first part, a blank text part to be edited ---*/
2363 part
->body
.type
= TYPETEXT
;
2364 part
->body
.contents
.text
.data
= msgtext
;
2366 /*--- The second part, what ever it is ---*/
2367 part
->next
= mail_newbody_part();
2369 part
->body
.id
= generate_message_id();
2370 copy_body(&(part
->body
), orig_body
);
2373 * the idea here is to fetch part into storage object
2375 if((part
->body
.contents
.text
.data
= (void *) so_get(PART_SO_TYPE
, NULL
,
2376 EDIT_ACCESS
)) != NULL
){
2377 if((tmp_text
= pine_mail_fetch_body(stream
, msgno
, section
,
2378 &part
->body
.size
.bytes
, NIL
)) != NULL
)
2379 so_nputs((STORE_S
*)part
->body
.contents
.text
.data
, tmp_text
,
2380 part
->body
.size
.bytes
);
2382 mail_free_body(&body
);
2385 mail_free_body(&body
);
2388 gf_clear_so_writec((STORE_S
*) msgtext
);
2396 * bounce_msg_body - build body from specified message suitable
2397 * for sending as bounced message
2400 bounce_msg_body(MAILSTREAM
*stream
,
2405 ENVELOPE
**outgoingp
,
2409 char *h
, *p
, *errstr
= NULL
;
2414 *outgoingp
= mail_newenvelope();
2415 (*outgoingp
)->message_id
= generate_message_id();
2416 (*outgoingp
)->subject
= cpystr(subject
? subject
: "Resent mail....");
2419 * Fill in destination if we were given one. If so, note that we
2420 * call p_s_s() below such that it won't prompt...
2423 static char *fakedomain
= "@";
2426 /* rfc822_parse_adrlist feels free to destroy input so copy */
2427 tmp_a_string
= cpystr(*to
);
2428 rfc822_parse_adrlist(&(*outgoingp
)->to
, tmp_a_string
,
2429 (F_ON(F_COMPOSE_REJECTS_UNQUAL
, ps_global
))
2430 ? fakedomain
: ps_global
->maildomain
);
2431 fs_give((void **) &tmp_a_string
);
2434 /* build remail'd header */
2435 if((h
= mail_fetch_header(stream
, rawno
, part
, NULL
, 0, FT_PEEK
)) != NULL
){
2436 for(p
= h
, i
= 0; (p
= strchr(p
, ':')) != NULL
; p
++)
2440 (*outgoingp
)->remail
= (char *) fs_get(strlen(h
) + (2 * i
) + 1);
2443 * copy it, "X-"ing out transport headers bothersome to
2444 * software but potentially useful to the human recipient...
2446 p
= (*outgoingp
)->remail
;
2447 bounce_mask_header(&p
, h
);
2449 if(*h
== '\015' && *(h
+1) == '\012'){
2450 *p
++ = *h
++; /* copy CR LF */
2452 bounce_mask_header(&p
, h
);
2454 while((*p
++ = *h
++) != '\0');
2456 /* BUG: else complain? */
2458 /* NOT bound for the composer, so no need for PicoText */
2459 if(!(msgtext
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
2460 mail_free_envelope(outgoingp
);
2461 return(_("Error allocating message text"));
2464 /* mark object for special handling */
2465 so_attr(msgtext
, "rawbody", "1");
2468 * Build a fake body description. It's ignored by pine_rfc822_header,
2469 * but we need to set it to something that makes set_mime_types
2470 * not sniff it and pine_rfc822_output_body not re-encode it.
2471 * Setting the encoding to (ENCMAX + 1) will work and shouldn't cause
2472 * problems unless something tries to access body_encodings[] using
2473 * it without proper precautions. We don't want to use ENCOTHER
2474 * cause that tells set_mime_types to sniff it, and we don't want to
2475 * use ENC8BIT since that tells pine_rfc822_output_body to qp-encode
2476 * it. When there's time, it'd be nice to clean this interaction
2479 *bodyp
= mail_newbody();
2480 (*bodyp
)->type
= TYPETEXT
;
2481 (*bodyp
)->encoding
= ENCMAX
+ 1;
2482 (*bodyp
)->subtype
= cpystr("Plain");
2483 (*bodyp
)->contents
.text
.data
= (void *) msgtext
;
2484 gf_set_so_writec(&pc
, msgtext
);
2486 if(seenp
&& rawno
> 0L && stream
&& rawno
<= stream
->nmsgs
){
2489 if((mc
= mail_elt(stream
, rawno
)) != NULL
)
2493 /* pass NULL body to force mail_fetchtext */
2494 if(!get_body_part_text(stream
, NULL
, rawno
, part
, 0L, pc
, NULL
, NULL
, GBPT_NONE
))
2495 errstr
= _("Error fetching message contents. Can't Bounce message");
2497 gf_clear_so_writec(msgtext
);
2504 /*----------------------------------------------------------------------
2505 Mask off any header entries we don't want xport software to see
2507 Args: d -- destination string pointer pointer
2508 s -- source string pointer pointer
2510 Postfix uses Delivered-To to detect loops.
2511 Received line counting is also used to detect loops in places.
2515 bounce_mask_header(char **d
, char *s
)
2517 if(((*s
== 'R' || *s
== 'r')
2518 && (!struncmp(s
+1, "esent-", 6) || !struncmp(s
+1, "eceived:", 8)
2519 || !struncmp(s
+1, "eturn-Path", 10)))
2520 || !struncmp(s
, "Delivered-To:", 13)){
2521 *(*d
)++ = 'X'; /* write mask */
2527 /*----------------------------------------------------------------------
2528 Fetch and format text for forwarding
2530 Args: stream -- Mail stream to fetch text from
2531 body -- Body structure of message being forwarded
2532 msg_no -- Message number of text for forward
2533 part_no -- Part number of text to forward
2534 partial -- If this is > 0 a partial fetch will be done and it will
2535 be done using FT_PEEK so the message will remain unseen.
2536 pc -- Function to write to
2537 prefix -- Prefix for each line
2538 ret_charset -- If we translate to another charset return that
2541 Returns: true if OK, false if problem occured while filtering
2543 If the text is richtext, it will be converted to plain text, since there's
2544 no rich text editing capabilities in Pine (yet).
2546 It's up to calling routines to plug in signature appropriately
2548 As with all internal text, NVT end-of-line conventions are observed.
2549 DOESN'T sanity check the prefix given!!!
2552 get_body_part_text(MAILSTREAM
*stream
, struct mail_bodystruct
*body
,
2553 long int msg_no
, char *part_no
, long partial
, gf_io_t pc
,
2554 char *prefix
, char **ret_charset
, unsigned flags
)
2556 int we_cancel
= 0, dashdata
, wrapflags
= GFW_FORCOMPOSE
, flow_res
= 0;
2557 FILTLIST_S filters
[12];
2559 char *err
, *charset
, *prefix_p
= NULL
;
2561 char *free_this
= NULL
;
2564 memset(filters
, 0, sizeof(filters
));
2566 *ret_charset
= NULL
;
2568 if(!pc_is_picotext(pc
))
2569 we_cancel
= busy_cue(NULL
, NULL
, 1);
2571 /* if null body, we must be talking to a non-IMAP2bis server.
2572 * No MIME parsing provided, so we just grab the message text...
2575 char *text
, *decode_error
;
2577 SourceType src
= CharStar
;
2580 (void) pine_mail_fetchstructure(stream
, msg_no
, NULL
);
2582 if((text
= pine_mail_fetch_text(stream
, msg_no
, part_no
, NULL
, 0)) != NULL
){
2583 gf_set_readc(&gc
, text
, (unsigned long)strlen(text
), src
, 0);
2585 gf_filter_init(); /* no filters needed */
2587 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2588 if((decode_error
= gf_pipe(gc
, pc
)) != NULL
){
2589 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s [Formatting error: %s]%s",
2591 decode_error
, NEWLINE
);
2592 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
2593 gf_puts(tmp_20k_buf
, pc
);
2598 gf_puts(NEWLINE
, pc
);
2599 gf_puts(_(" [ERROR fetching text of message]"), pc
);
2600 gf_puts(NEWLINE
, pc
);
2601 gf_puts(NEWLINE
, pc
);
2606 cancel_busy_cue(-1);
2611 charset
= parameter_val(body
->parameter
, "charset");
2613 if(charset
&& strucmp(charset
, "utf-8") && strucmp(charset
, "us-ascii")){
2615 *ret_charset
= "UTF-8";
2619 * just use detach, but add an auxiliary filter to insert prefix,
2620 * and, perhaps, digest richtext
2622 if(ps_global
->full_header
!= 2
2623 && !ps_global
->postpone_no_flow
2624 && (!body
->subtype
|| !strucmp(body
->subtype
, "plain"))){
2627 flow_res
= (F_OFF(F_QUELL_FLOWED_TEXT
, ps_global
)
2628 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps_global
)
2629 && (!prefix
|| (strucmp(prefix
,"> ") == 0)
2630 || strucmp(prefix
, ">") == 0));
2631 if((parmval
= parameter_val(body
->parameter
,
2632 "format")) != NULL
){
2633 if(!strucmp(parmval
, "flowed")){
2634 wrapflags
|= GFW_FLOWED
;
2636 fs_give((void **) &parmval
);
2637 if((parmval
= parameter_val(body
->parameter
, "delsp")) != NULL
){
2638 if(!strucmp(parmval
, "yes")){
2639 filters
[filtcnt
++].filter
= gf_preflow
;
2640 wrapflags
|= GFW_DELSP
;
2643 fs_give((void **) &parmval
);
2647 * if there's no prefix we're forwarding text
2648 * otherwise it's reply text. unless the user's
2649 * tied our hands, alter the prefix to continue flowed
2653 wrapflags
|= GFW_FLOW_RESULT
;
2655 filters
[filtcnt
].filter
= gf_wrap
;
2657 * The 80 will cause longer lines than what is likely
2658 * set by composer_fillcol, so we'll have longer
2659 * quoted and forwarded lines than the lines we type.
2660 * We're fine with that since the alternative is the
2661 * possible wrapping of lines we shouldn't have, which
2662 * is mitigated by this higher 80 limit.
2664 * If we were to go back to using composer_fillcol,
2665 * the correct value is composer_fillcol + 1, pine
2666 * is off-by-one from pico.
2668 filters
[filtcnt
++].data
= gf_wrap_filter_opt(
2669 MAX(MIN(ps_global
->ttyo
->screen_cols
2670 - (prefix
? strlen(prefix
) : 0),
2671 80 - (prefix
? strlen(prefix
) : 0)),
2672 30), /* doesn't have to be 30 */
2673 990, /* 998 is the SMTP limit */
2674 NULL
, 0, wrapflags
);
2679 * if not flowed, remove trailing whitespace to reduce
2680 * confusion, since we're sending out as flowed if we
2681 * can. At some future point, we might try
2682 * plugging in a user-option-controlled heuristic
2685 * We also want to fold "> " quotes so we get the
2686 * attributions correct.
2688 if(flow_res
&& prefix
&& !strucmp(prefix
, "> "))
2689 *(prefix_p
= prefix
+ 1) = '\0';
2691 if(!(wrapflags
& GFW_FLOWED
)
2693 filters
[filtcnt
].filter
= gf_line_test
;
2694 filters
[filtcnt
++].data
= gf_line_test_opt(twsp_strip
, NULL
);
2696 filters
[filtcnt
].filter
= gf_line_test
;
2697 filters
[filtcnt
++].data
= gf_line_test_opt(quote_fold
, NULL
);
2700 else if(body
->subtype
){
2703 if(strucmp(body
->subtype
,"richtext") == 0){
2704 filters
[filtcnt
].filter
= gf_rich2plain
;
2705 filters
[filtcnt
++].data
= gf_rich2plain_opt(&plain_opt
);
2707 else if(strucmp(body
->subtype
,"enriched") == 0){
2708 filters
[filtcnt
].filter
= gf_enriched2plain
;
2709 filters
[filtcnt
++].data
= gf_enriched2plain_opt(&plain_opt
);
2711 else if(strucmp(body
->subtype
,"html") == 0){
2712 if((flags
& GBPT_HTML_OK
) != GBPT_HTML_OK
){
2713 filters
[filtcnt
].filter
= gf_html2plain
;
2714 filters
[filtcnt
++].data
= gf_html2plain_opt(NULL
,
2715 ps_global
->ttyo
->screen_cols
,
2716 non_messageview_margin(),
2717 NULL
, NULL
, GFHP_STRIPPED
);
2723 if(ps_global
->full_header
!= 2
2724 && (F_ON(F_ENABLE_SIGDASHES
, ps_global
)
2725 || F_ON(F_ENABLE_STRIP_SIGDASHES
, ps_global
))){
2727 filters
[filtcnt
].filter
= gf_line_test
;
2728 filters
[filtcnt
++].data
= gf_line_test_opt(sigdash_strip
, &dashdata
);
2731 filters
[filtcnt
].filter
= gf_prefix
;
2732 filters
[filtcnt
++].data
= gf_prefix_opt(prefix
);
2734 if(wrapflags
& GFW_FLOWED
|| flow_res
){
2735 filters
[filtcnt
].filter
= gf_line_test
;
2736 filters
[filtcnt
++].data
= gf_line_test_opt(post_quote_space
, NULL
);
2740 if(flags
& GBPT_DELQUOTES
){
2741 memset(&dq
, 0, sizeof(dq
));
2742 dq
.lines
= Q_DEL_ALL
;
2744 dq
.indent_length
= 0;
2745 dq
.saved_line
= &free_this
;
2750 filters
[filtcnt
].filter
= gf_line_test
;
2751 filters
[filtcnt
++].data
= gf_line_test_opt(delete_quotes
, &dq
);
2754 err
= detach(stream
, msg_no
, part_no
, partial
, &len
, pc
,
2755 filters
[0].filter
? filters
: NULL
,
2756 ((flags
& GBPT_PEEK
) ? FT_PEEK
: 0)
2757 | ((flags
& GBPT_NOINTR
) ? DT_NOINTR
: 0));
2760 fs_give((void **) &free_this
);
2765 if (err
!= (char *) NULL
)
2766 /* TRANSLATORS: The first arg is error text, the %ld is the message number */
2767 q_status_message2(SM_ORDER
, 3, 4, "%s: message number %ld",
2768 err
, (void *) msg_no
);
2771 cancel_busy_cue(-1);
2778 quote_fold(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
2783 for(p
= line
; *p
; p
++){
2784 if(isspace((unsigned char) *p
)){
2786 ins
= gf_line_test_new_ins(ins
, p
, "", -1);
2798 twsp_strip(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
2800 char *p
, *ws
= NULL
;
2802 for(p
= line
; *p
; p
++){
2803 /* don't strip trailing space on signature line */
2804 if(*line
== '-' && *(line
+1) == '-' && *(line
+2) == ' ' && !*(line
+3))
2807 if(isspace((unsigned char) *p
)){
2816 ins
= gf_line_test_new_ins(ins
, ws
, "", -(p
- ws
));
2822 post_quote_space(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
2826 for(p
= line
; *p
; p
++)
2828 if(p
!= line
&& *p
!= ' ')
2829 ins
= gf_line_test_new_ins(ins
, p
, " ", 1);
2839 sigdash_strip(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
2842 || (*line
== '-' && *(line
+1) == '-'
2843 && *(line
+2) == ' ' && !*(line
+3))){
2844 *((int *) local
) = 1;
2845 return(2); /* skip this line! */
2852 /*----------------------------------------------------------------------
2853 return the c-client reference name for the given end_body part
2856 body_partno(MAILSTREAM
*stream
, long int msgno
, struct mail_bodystruct
*end_body
)
2860 (void) pine_mail_fetchstructure(stream
, msgno
, &body
);
2861 return(partno(body
, end_body
));
2865 /*----------------------------------------------------------------------
2866 return the c-client reference name for the given end_body part
2869 partno(struct mail_bodystruct
*body
, struct mail_bodystruct
*end_body
)
2873 char tmp
[64], *p
= NULL
;
2875 if(body
&& body
->type
== TYPEMULTIPART
) {
2876 part
= body
->nested
.part
; /* first body part */
2878 do { /* for each part */
2880 if(&part
->body
== end_body
|| (p
= partno(&part
->body
, end_body
))){
2881 snprintf(tmp
, sizeof(tmp
), "%d%s%.*s", num
, (p
) ? "." : "",
2882 sizeof(tmp
)-10, (p
) ? p
: "");
2883 tmp
[sizeof(tmp
)-1] = '\0';
2885 fs_give((void **)&p
);
2887 return(cpystr(tmp
));
2889 } while ((part
= part
->next
) != NULL
); /* until done */
2893 else if(body
&& body
->type
== TYPEMESSAGE
&& body
->subtype
2894 && !strucmp(body
->subtype
, "rfc822")){
2895 return(partno(body
->nested
.msg
->body
, end_body
));
2898 return((body
== end_body
) ? cpystr("1") : NULL
);
2902 /*----------------------------------------------------------------------
2903 Fill in the contents of each body part
2905 Args: stream -- Stream the message is on
2906 msgno -- Message number the body structure is for
2907 section -- body section associated with body pointer
2908 body -- Body pointer to fill in
2910 Result: 1 if all went OK, 0 if there was a problem
2912 This function copies the contents from an original message/body to
2913 a new message/body. It recurses down all multipart levels.
2915 If one or more part (but not all) can't be fetched, a status message
2919 fetch_contents(MAILSTREAM
*stream
, long int msgno
, char *section
, struct mail_bodystruct
*body
)
2925 body
->id
= generate_message_id();
2927 if(body
->type
== TYPEMULTIPART
){
2928 char subsection
[256], *subp
;
2929 int n
, last_one
= 10; /* remember worst case */
2930 PART
*part
= body
->nested
.part
;
2932 if(!(part
= body
->nested
.part
))
2936 if(section
&& *section
){
2938 n
< sizeof(subsection
)-20 && (*subp
= section
[n
]); n
++, subp
++)
2946 snprintf(subp
, sizeof(subsection
)-(subp
-subsection
), "%d", n
++);
2947 subsection
[sizeof(subsection
)-1] = '\0';
2948 got_one
= fetch_contents(stream
, msgno
, subsection
, &part
->body
);
2949 last_one
= MIN(last_one
, got_one
);
2951 while((part
= part
->next
) != NULL
);
2956 if(body
->contents
.text
.data
)
2957 return(1); /* already taken care of... */
2959 if(body
->type
== TYPEMESSAGE
){
2960 if(body
->subtype
&& strucmp(body
->subtype
,"external-body")){
2962 * the idea here is to fetch everything into storage objects
2964 body
->contents
.text
.data
= (void *) so_get(PART_SO_TYPE
, NULL
,
2966 if(body
->contents
.text
.data
2967 && (tp
= pine_mail_fetch_body(stream
, msgno
, section
,
2968 &body
->size
.bytes
, NIL
))){
2969 so_truncate((STORE_S
*)body
->contents
.text
.data
,
2970 body
->size
.bytes
+ 2048);
2971 so_nputs((STORE_S
*)body
->contents
.text
.data
, tp
,
2976 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2977 _("Error fetching part %s"), section
);
2983 * the idea here is to fetch everything into storage objects
2984 * so, grab one, then fetch the body part
2986 body
->contents
.text
.data
= (void *)so_get(PART_SO_TYPE
,NULL
,EDIT_ACCESS
);
2987 if(body
->contents
.text
.data
2988 && (tp
=pine_mail_fetch_body(stream
, msgno
, section
,
2989 &body
->size
.bytes
, NIL
))){
2990 so_truncate((STORE_S
*)body
->contents
.text
.data
,
2991 body
->size
.bytes
+ 2048);
2992 so_nputs((STORE_S
*)body
->contents
.text
.data
, tp
,
2997 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2998 _("Error fetching part %s"), section
);
3005 /*----------------------------------------------------------------------
3006 Copy the body structure
3008 Args: new_body -- Pointer to already allocated body, or NULL, if none
3009 old_body -- The Body to copy
3012 This is traverses the body structure recursively copying all elements.
3013 The new_body parameter can be NULL in which case a new body is
3014 allocated. Alternatively it can point to an already allocated body
3015 structure. This is used when copying body parts since a PART includes a
3016 BODY. The contents fields are *not* filled in.
3020 copy_body(struct mail_bodystruct
*new_body
, struct mail_bodystruct
*old_body
)
3022 if(old_body
== NULL
)
3025 if(new_body
== NULL
)
3026 new_body
= mail_newbody();
3028 new_body
->type
= old_body
->type
;
3029 new_body
->encoding
= old_body
->encoding
;
3031 if(old_body
->subtype
)
3032 new_body
->subtype
= cpystr(old_body
->subtype
);
3034 new_body
->parameter
= copy_parameters(old_body
->parameter
);
3037 new_body
->id
= cpystr(old_body
->id
);
3039 if(old_body
->description
)
3040 new_body
->description
= cpystr(old_body
->description
);
3042 if(old_body
->disposition
.type
)
3043 new_body
->disposition
.type
= cpystr(old_body
->disposition
.type
);
3045 new_body
->disposition
.parameter
3046 = copy_parameters(old_body
->disposition
.parameter
);
3048 new_body
->size
= old_body
->size
;
3050 if(new_body
->type
== TYPEMESSAGE
3051 && new_body
->subtype
&& !strucmp(new_body
->subtype
, "rfc822")){
3052 new_body
->nested
.msg
= mail_newmsg();
3053 new_body
->nested
.msg
->body
3054 = copy_body(NULL
, old_body
->nested
.msg
->body
);
3056 else if(new_body
->type
== TYPEMULTIPART
) {
3057 PART
**new_partp
, *old_part
;
3059 new_partp
= &new_body
->nested
.part
;
3060 for(old_part
= old_body
->nested
.part
;
3062 old_part
= old_part
->next
){
3063 *new_partp
= mail_newbody_part();
3064 copy_body(&(*new_partp
)->body
, &old_part
->body
);
3065 new_partp
= &(*new_partp
)->next
;
3073 /*----------------------------------------------------------------------
3074 Copy the MIME parameter list
3076 Allocates storage for new part, and returns pointer to new paramter
3077 list. If old_p is NULL, NULL is returned.
3080 copy_parameters(PARAMETER
*old_p
)
3082 PARAMETER
*new_p
, *p1
, *p2
;
3085 return((PARAMETER
*)NULL
);
3088 for(p1
= old_p
; p1
!= NULL
; p1
= p1
->next
){
3089 set_parameter(&p2
, p1
->attribute
, p1
->value
);
3098 /*----------------------------------------------------------------------
3099 Make a complete copy of an envelope and all it's fields
3101 Args: e -- the envelope to copy
3103 Result: returns the new envelope, or NULL, if the given envelope was NULL
3108 copy_envelope(register ENVELOPE
*e
)
3110 register ENVELOPE
*e2
;
3115 e2
= mail_newenvelope();
3116 e2
->remail
= e
->remail
? cpystr(e
->remail
) : NULL
;
3117 e2
->return_path
= e
->return_path
? rfc822_cpy_adr(e
->return_path
) : NULL
;
3118 e2
->date
= e
->date
? (unsigned char *)cpystr((char *) e
->date
)
3120 e2
->from
= e
->from
? rfc822_cpy_adr(e
->from
) : NULL
;
3121 e2
->sender
= e
->sender
? rfc822_cpy_adr(e
->sender
) : NULL
;
3122 e2
->reply_to
= e
->reply_to
? rfc822_cpy_adr(e
->reply_to
) : NULL
;
3123 e2
->subject
= e
->subject
? cpystr(e
->subject
) : NULL
;
3124 e2
->to
= e
->to
? rfc822_cpy_adr(e
->to
) : NULL
;
3125 e2
->cc
= e
->cc
? rfc822_cpy_adr(e
->cc
) : NULL
;
3126 e2
->bcc
= e
->bcc
? rfc822_cpy_adr(e
->bcc
) : NULL
;
3127 e2
->in_reply_to
= e
->in_reply_to
? cpystr(e
->in_reply_to
) : NULL
;
3128 e2
->newsgroups
= e
->newsgroups
? cpystr(e
->newsgroups
) : NULL
;
3129 e2
->message_id
= e
->message_id
? cpystr(e
->message_id
) : NULL
;
3130 e2
->references
= e
->references
? cpystr(e
->references
) : NULL
;
3131 e2
->followup_to
= e
->followup_to
? cpystr(e
->references
) : NULL
;
3136 /*----------------------------------------------------------------------
3137 Generate the "In-reply-to" text from message header
3139 Args: message -- Envelope of original message
3141 Result: returns an alloc'd string or NULL if there is a problem
3144 reply_in_reply_to(ENVELOPE
*env
)
3146 return((env
&& env
->message_id
) ? cpystr(env
->message_id
) : NULL
);
3150 /*----------------------------------------------------------------------
3151 Generate a unique message id string.
3153 Args: ps -- The usual pine structure
3155 Result: Alloc'd unique string is returned
3157 Uniqueness is gaurenteed by using the host name, process id, date to the
3158 second and a single unique character
3159 *----------------------------------------------------------------------*/
3161 generate_message_id(void)
3163 static short osec
= 0, cnt
= 0;
3168 char *hostpart
= NULL
;
3170 now
= time((time_t *)0);
3171 now_x
= localtime(&now
);
3173 if(now_x
->tm_sec
== osec
)
3177 osec
= now_x
->tm_sec
;
3180 hostpart
= F_ON(F_ROT13_MESSAGE_ID
, ps_global
)
3181 ? rot13(ps_global
->hostname
)
3182 : cpystr(ps_global
->hostname
);
3185 hostpart
= cpystr("huh");
3187 snprintf(idbuf
, sizeof(idbuf
), "<alpine.%.4s.%.20s.%02d%02d%02d%02d%02d%02d%X.%d@%.50s>",
3188 SYSTYPE
, ALPINE_VERSION
, (now_x
->tm_year
) % 100, now_x
->tm_mon
+ 1,
3189 now_x
->tm_mday
, now_x
->tm_hour
, now_x
->tm_min
, now_x
->tm_sec
,
3190 cnt
, getpid(), hostpart
);
3191 idbuf
[sizeof(idbuf
)-1] = '\0';
3196 fs_give((void **) &hostpart
);
3203 generate_user_agent(void)
3208 if(F_ON(F_QUELL_USERAGENT
, ps_global
))
3211 snprintf(buf
, sizeof(buf
),
3212 "%sAlpine %s (%s %s)",
3213 (pith_opt_user_agent_prefix
) ? (*pith_opt_user_agent_prefix
)() : "",
3214 ALPINE_VERSION
, SYSTYPE
,
3215 get_alpine_revision_string(rev
, sizeof(rev
)));
3217 return(cpystr(buf
));
3224 char byte
, cap
, *p
, *ret
= NULL
;
3227 ret
= (char *) fs_get((strlen(src
)+1) * sizeof(char));
3229 while((byte
= *src
++) != '\0'){
3232 *p
++ = ((byte
>= 'A') && (byte
<= 'Z')
3233 ? ((byte
- 'A' + 13) % 26 + 'A') : byte
) | cap
;
3243 /*----------------------------------------------------------------------
3244 Return the first true address pointer (modulo group syntax allowance)
3246 Args: addr -- Address list
3248 Result: First real address pointer, or NULL
3249 ----------------------------------------------------------------------*/
3251 first_addr(struct mail_address
*addr
)
3253 while(addr
&& !addr
->host
)
3260 /*----------------------------------------------------------------------
3261 lit -- this is the source
3262 prenewlines -- prefix the file contents with this many newlines
3263 postnewlines -- postfix the file contents with this many newlines
3264 is_sig -- this is a signature (not a template)
3265 decode_constants -- change C-style constants into their values
3268 get_signature_lit(char *lit
, int prenewlines
, int postnewlines
, int is_sig
, int decode_constants
)
3273 * Should make this smart enough not to do the copying and double
3274 * allocation of space.
3277 char *tmplit
= NULL
, *p
, *q
, *d
, save
;
3280 if(decode_constants
){
3281 tmplit
= (char *) fs_get((strlen(lit
)+1) * sizeof(char));
3283 cstring_to_string(lit
, tmplit
);
3286 tmplit
= cpystr(lit
);
3288 len
= strlen(tmplit
) + 5 + (prenewlines
+postnewlines
) * strlen(NEWLINE
);
3289 sig
= (char *) fs_get((len
+1) * sizeof(char));
3290 memset(sig
, 0, len
+1);
3292 while(prenewlines
--)
3293 sstrncpy(&d
, NEWLINE
, len
-(d
-sig
));
3295 if(is_sig
&& F_ON(F_ENABLE_SIGDASHES
, ps_global
) &&
3296 !sigdashes_are_present(tmplit
)){
3297 sstrncpy(&d
, SIGDASHES
, len
-(d
-sig
));
3298 sstrncpy(&d
, NEWLINE
, len
-(d
-sig
));
3306 q
= strpbrk(p
, "\n\r");
3313 * Strip trailing space if we are doing a signature and
3314 * this line is not sigdashes.
3316 if(is_sig
&& strcmp(p
, SIGDASHES
))
3317 removing_trailing_white_space(p
);
3319 while((d
-sig
) <= len
&& (*d
= *p
++) != '\0')
3332 while(postnewlines
--)
3333 sstrncpy(&d
, NEWLINE
, len
-(d
-sig
));
3341 fs_give((void **) &tmplit
);
3349 sigdashes_are_present(char *sig
)
3353 p
= srchstr(sig
, SIGDASHES
);
3354 while(p
&& !((p
== sig
|| (p
[-1] == '\n' || p
[-1] == '\r')) &&
3355 (p
[3] == '\0' || p
[3] == '\n' || p
[3] == '\r')))
3356 p
= srchstr(p
+1, SIGDASHES
);
3362 /*----------------------------------------------------------------------
3363 Acquire the pinerc defined signature file pathname
3367 signature_path(char *sname
, char *sbuf
, size_t len
)
3370 if(sname
&& *sname
){
3371 size_t spl
= strlen(sname
);
3372 if(IS_REMOTE(sname
)){
3374 strncpy(sbuf
, sname
, len
-1);
3376 else if(is_absolute_path(sname
)){
3377 strncpy(sbuf
, sname
, len
-1);
3379 fnexpand(sbuf
, len
);
3381 else if(ps_global
->VAR_OPER_DIR
){
3382 if(strlen(ps_global
->VAR_OPER_DIR
) + spl
< len
- 1)
3383 build_path(sbuf
, ps_global
->VAR_OPER_DIR
, sname
, len
);
3386 char *lc
= last_cmpnt(ps_global
->pinerc
);
3390 strncpy(sbuf
,ps_global
->pinerc
,MIN(len
-1,lc
-ps_global
->pinerc
));
3391 sbuf
[MIN(len
-1,lc
-ps_global
->pinerc
)] = '\0';
3394 strncat(sbuf
, sname
, MAX(len
-1-strlen(sbuf
), 0));
3399 return(*sbuf
? sbuf
: NULL
);
3404 simple_read_remote_file(char *name
, char *subtype
)
3411 dprint((7, "simple_read_remote_file(%s, %s)\n", name
? name
: "?", subtype
? subtype
: "?"));
3414 * We could parse the name here to find what type it is. So far we
3415 * only have type RemImap.
3417 rd
= rd_create_remote(RemImap
, name
, subtype
,
3418 NULL
, _("Error: "), _("Can't fetch remote configuration."));
3422 try_cache
= rd_read_metadata(rd
);
3424 if(rd
->access
== MaybeRorW
){
3425 if(rd
->read_status
== 'R')
3426 rd
->access
= ReadOnly
;
3428 rd
->access
= ReadWrite
;
3431 if(rd
->access
!= NoExists
){
3433 rd_check_remvalid(rd
, 1L);
3436 * If the cached info says it is readonly but
3437 * it looks like it's been fixed now, change it to readwrite.
3439 if(rd
->read_status
== 'R'){
3441 * We go to this trouble since readonly sigfiles
3442 * are likely a mistake. They are usually supposed to be
3443 * readwrite so we open it and check if it's been fixed.
3445 rd_check_readonly_access(rd
);
3446 if(rd
->read_status
== 'W'){
3447 rd
->access
= ReadWrite
;
3448 rd
->flags
|= REM_OUTOFDATE
;
3451 rd
->access
= ReadOnly
;
3454 if(rd
->flags
& REM_OUTOFDATE
){
3455 if(rd_update_local(rd
) != 0){
3458 "simple_read_remote_file: rd_update_local failed\n"));
3460 * Don't give up altogether. We still may be
3461 * able to use a cached copy.
3466 "%s: copied remote to local (%ld)\n",
3467 rd
->rn
? rd
->rn
: "?", (long)rd
->last_use
));
3471 if(rd
->access
== ReadWrite
)
3472 rd
->flags
|= DO_REMTRIM
;
3475 /* If we couldn't get to remote folder, try using the cached copy */
3476 if(rd
->access
== NoExists
|| rd
->flags
& REM_OUTOFDATE
){
3478 rd
->access
= ReadOnly
;
3479 rd
->flags
|= USE_OLD_CACHE
;
3480 q_status_message(SM_ORDER
, 3, 4,
3481 "Can't contact remote server, using cached copy");
3483 "Can't open remote file %s, using local cached copy %s readonly\n",
3484 rd
->rn
? rd
->rn
: "?",
3485 rd
->lf
? rd
->lf
: "?"));
3488 rd
->flags
&= ~DO_REMTRIM
;
3493 file
= read_file(rd
->lf
, READ_FROM_LOCALE
);
3497 rd_close_remdata(&rd
);
3503 /*----------------------------------------------------------------------
3504 Build the body for the multipart/alternative part
3510 ----------------------------------------------------------------------*/
3512 forward_multi_alt(MAILSTREAM
*stream
, ENVELOPE
*env
, struct mail_bodystruct
*orig_body
,
3513 long int msgno
, char *sect_prefix
, void *msgtext
, gf_io_t pc
, int flags
)
3516 PART
*part
= NULL
, *bestpart
= NULL
;
3518 char *new_charset
= NULL
;
3519 int partnum
, bestpartnum
;
3521 if(ps_global
->force_prefer_plain
3522 || (!ps_global
->force_no_prefer_plain
3523 && F_ON(F_PREFER_PLAIN_TEXT
, ps_global
))){
3524 for(part
= orig_body
->nested
.part
, partnum
= 1;
3526 part
= part
->next
, partnum
++)
3527 if((!part
->body
.type
|| part
->body
.type
== TYPETEXT
)
3528 && (!part
->body
.subtype
3529 || !strucmp(part
->body
.subtype
, "plain")))
3534 * Else choose last alternative among plain or html parts.
3535 * Perhaps we should really be using mime_show() to make this
3536 * potentially more general than just plain or html.
3539 for(part
= orig_body
->nested
.part
, partnum
= 1;
3541 part
= part
->next
, partnum
++){
3542 if((!part
->body
.type
|| part
->body
.type
== TYPETEXT
)
3543 && ((!part
->body
.subtype
|| !strucmp(part
->body
.subtype
, "plain"))
3545 (part
->body
.subtype
&& !strucmp(part
->body
.subtype
, "html")))){
3547 bestpartnum
= partnum
;
3552 partnum
= bestpartnum
;
3556 * IF something's interesting insert it
3557 * AND forget the rest of the multipart
3560 body
= mail_newbody();
3561 body
->type
= TYPETEXT
;
3562 body
->contents
.text
.data
= msgtext
;
3564 /* record character set, flowing, etc */
3565 body
->parameter
= copy_parameters(part
->body
.parameter
);
3566 body
->size
.bytes
= part
->body
.size
.bytes
;
3568 if(!(flags
& FWD_ANON
)){
3569 forward_delimiter(pc
);
3570 reply_forward_header(stream
, msgno
, sect_prefix
, env
, pc
, "");
3573 snprintf(tmp_buf
, sizeof(tmp_buf
), "%.*s%s%s%d",
3574 sizeof(tmp_buf
)/2, sect_prefix
? sect_prefix
: "",
3575 sect_prefix
? "." : "", flags
& FWD_NESTED
? "1." : "",
3577 tmp_buf
[sizeof(tmp_buf
)-1] = '\0';
3578 get_body_part_text(stream
, &part
->body
, msgno
, tmp_buf
, 0L, pc
,
3579 NULL
, &new_charset
, GBPT_NONE
);
3582 * get_body_part_text translated the data to a new charset.
3583 * We need to record that fact in body.
3586 set_parameter(&body
->parameter
, "charset", new_charset
);
3589 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3590 "No suitable part found. Forwarding as attachment");
3597 reply_append_addr(struct mail_address
**dest
, struct mail_address
*src
)
3599 for( ; *dest
; dest
= &(*dest
)->next
)