1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: reply.c 387 2007-01-24 18:42:22Z hubert@u.washington.edu $";
6
* ========================================================================
7
* Copyright 2006-2007 University of Washington
9
* Licensed under the Apache License, Version 2.0 (the "License");
10
* you may not use this file except in compliance with the License.
11
* You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* ========================================================================
18
#include "../pith/headers.h"
19
#include "../pith/reply.h"
20
#include "../pith/init.h"
21
#include "../pith/state.h"
22
#include "../pith/conf.h"
23
#include "../pith/remote.h"
24
#include "../pith/status.h"
25
#include "../pith/mailview.h"
26
#include "../pith/filter.h"
27
#include "../pith/newmail.h"
28
#include "../pith/bldaddr.h"
29
#include "../pith/mailindx.h"
30
#include "../pith/rfc2231.h"
31
#include "../pith/detach.h"
32
#include "../pith/help.h"
33
#include "../pith/pipe.h"
34
#include "../pith/addrstring.h"
35
#include "../pith/news.h"
36
#include "../pith/util.h"
37
#include "../pith/pattern.h"
38
#include "../pith/detoken.h"
39
#include "../pith/stream.h"
40
#include "../pith/busy.h"
41
#include "../pith/readfile.h"
47
void reply_append_addr(ADDRESS **, ADDRESS *);
48
void bounce_mask_header(char **, char *);
51
int (*pith_opt_replyto_prompt)(void);
52
int (*pith_opt_reply_to_all_prompt)(int *);
56
* standard type of storage object used for body parts...
58
#if defined(DOS) && !defined(WIN32)
59
#define PART_SO_TYPE TmpFileStar
61
#define PART_SO_TYPE CharStar
68
* Returns: 1 if addresses successfully copied
69
* 0 on user cancel or error
81
reply_harvest(struct pine *ps, long int msgno, char *section, ENVELOPE *env,
82
struct mail_address **saved_from, struct mail_address **saved_to,
83
struct mail_address **saved_cc, struct mail_address **saved_resent,
86
ADDRESS *ap, *ap2, *rep_address;
87
int ret = 0, sniff_resent = 0;
91
* If Reply-To is same as From just treat it like it was From.
92
* Otherwise, always use the reply-to if we're replying to more
93
* than one msg or say ok to using it, even if it's us.
94
* If there's no reply-to or it's the same as the from, assume
95
* that the user doesn't want to reply to himself, unless there's
98
if(env->reply_to && !addr_lists_same(env->reply_to, env->from)
99
&& (F_ON(F_AUTO_REPLY_TO, ps_global)
100
|| ((*flags) & RSF_FORCE_REPLY_TO)
101
|| (pith_opt_replyto_prompt && (*pith_opt_replyto_prompt)() == 'y'))){
102
rep_field = "reply-to";
103
rep_address = env->reply_to;
107
rep_address = env->from;
110
ap = reply_cp_addr(ps, msgno, section, rep_field, *saved_from,
111
(ADDRESS *) NULL, rep_address, 0);
114
cmd_cancelled("Reply");
118
reply_append_addr(saved_from, ap);
120
/*--------- check for other recipients ---------*/
121
if(((*flags) & (RSF_FORCE_REPLY_ALL | RSF_QUERY_REPLY_ALL))){
123
if(ap = reply_cp_addr(ps, msgno, section, "To", *saved_to,
124
*saved_from, env->to, 0))
125
reply_append_addr(saved_to, ap);
127
if(ap = reply_cp_addr(ps, msgno, section, "Cc", *saved_cc,
128
*saved_from, env->cc, 0))
129
reply_append_addr(saved_cc, ap);
132
* In these cases, we either need to look at the resent headers
133
* to include in the reply-to-all, or to decide whether or not
134
* we need to ask the reply-to-all question.
136
if(((*flags) & RSF_FORCE_REPLY_ALL)
137
|| (((*flags) & RSF_QUERY_REPLY_ALL)
138
&& ((!(*saved_from) && !(*saved_cc))
139
|| (*saved_from && !(*saved_to) && !(*saved_cc))))){
142
if(ap2 = reply_resent(ps, msgno, section)){
144
* look for bogus addr entries and replace
146
if(ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent,
147
*saved_from, ap2, 0))
149
reply_append_addr(saved_resent, ap);
151
mail_free_address(&ap2);
156
* It makes sense to ask reply-to-all now.
158
if(((*flags) & RSF_QUERY_REPLY_ALL)
159
&& ((*saved_from && (*saved_to || *saved_cc || *saved_resent))
160
|| (*saved_cc || *saved_resent))){
161
*flags &= ~RSF_QUERY_REPLY_ALL;
162
if(pith_opt_reply_to_all_prompt
163
&& (*pith_opt_reply_to_all_prompt)(flags) < 0){
164
cmd_cancelled("Reply");
170
* If we just answered yes to the reply-to-all question and
171
* we still haven't collected the resent headers, do so now.
173
if(((*flags) & RSF_FORCE_REPLY_ALL) && !sniff_resent
174
&& (ap2 = reply_resent(ps, msgno, section))){
176
* look for bogus addr entries and replace
178
if(ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent,
179
*saved_from, ap2, 0))
180
reply_append_addr(saved_resent, ap);
182
mail_free_address(&ap2);
190
/*----------------------------------------------------------------------
191
Return a pointer to a copy of the given address list
192
filtering out those already in the "mask" lists and ourself.
194
Args: mask1 -- Don't copy if in this list
195
mask2 -- or if in this list
196
source -- List to be copied
197
us_too -- Don't filter out ourself.
201
reply_cp_addr(struct pine *ps, long int msgno, char *section, char *field,
202
struct mail_address *mask1, struct mail_address *mask2,
203
struct mail_address *source, int us_too)
205
ADDRESS *tmp1, *tmp2, *ret = NULL, **ret_tail;
207
for(tmp1 = source; msgno && tmp1; tmp1 = tmp1->next)
208
if(tmp1->host && tmp1->host[0] == '.'){
213
if(h = pine_fetchheader_lines(ps ? ps->mail_stream : NULL,
214
msgno, section, fields)){
220
strncpy(fname, field, sizeof(fname)-2);
221
fname[sizeof(fname)-2] = '\0';
222
strncat(fname, ":", sizeof(fname)-strlen(fname));
223
fname[sizeof(fname)-1] = '\0';
225
for(p = h; p = strstr(p, fname); )
226
rplstr(p, q-(p-h), strlen(fname), ""); /* strip field strings */
228
sqznewlines(h); /* blat out CR's & LF's */
229
for(p = h; p = strchr(p, TAB); )
230
*p++ = ' '; /* turn TABs to whitespace */
236
ret = (ADDRESS *) fs_get(sizeof(ADDRESS));
237
memset(ret, 0, sizeof(ADDRESS));
239
/* get rid of leading white space */
240
for(p = h; *p == SPACE; p++)
244
memmove(h, p, l = strlen(p));
248
/* base64 armor plate the gunk to protect against
249
* c-client quoting in output.
251
p = (char *) rfc822_binary(h, strlen(h),
252
(unsigned long *) &l);
254
fs_give((void **) &h);
256
* Seems like the 4 ought to be a 2, but I'll leave it
257
* to be safe, in case something else adds 2 chars later.
260
ret->mailbox = (char *) fs_get(ll * sizeof(char));
261
snprintf(ret->mailbox, ll, "&%s", p);
262
ret->mailbox[ll-1] = '\0';
263
fs_give((void **) &p);
264
ret->host = cpystr(".RAW-FIELD.");
272
for(source = first_addr(source); source; source = source->next){
273
for(tmp1 = first_addr(mask1); tmp1; tmp1 = tmp1->next)
274
if(address_is_same(source, tmp1))
277
for(tmp2 = first_addr(mask2); !tmp1 && tmp2; tmp2 = tmp2->next)
278
if(address_is_same(source, tmp2))
282
* If there's no match in masks *and* this address isn't us, copy...
284
if(!tmp1 && !tmp2 && (us_too || !ps || !address_is_us(source, ps))){
286
source->next = NULL; /* only copy one addr! */
287
*ret_tail = rfc822_cpy_adr(source);
288
ret_tail = &(*ret_tail)->next;
289
source->next = tmp1; /* restore rest of list */
298
set_role_from_msg(struct pine *ps, long int rflags, long int msgno, char *section)
300
ACTION_S *role = NULL;
302
SEARCHSET *ss = NULL;
305
if(!nonempty_patterns(rflags, &pstate))
309
ss = mail_newsearchset();
310
ss->first = ss->last = (unsigned long)msgno;
313
/* Go through the possible roles one at a time until we get a match. */
314
pat = first_pattern(&pstate);
316
/* calculate this message's score if needed */
317
if(ss && pat && scores_are_used(SCOREUSE_GET) & SCOREUSE_ROLES &&
318
get_msg_score(ps->mail_stream, msgno) == SCORE_UNDEF)
319
(void)calculate_some_scores(ps->mail_stream, ss, 0);
322
if(match_pattern(pat->patgrp, ps->mail_stream, ss, section,
323
get_msg_score, SO_NOSERVER|SE_NOPREFETCH)){
324
if(!pat->action || pat->action->bogus)
330
pat = next_pattern(&pstate);
334
mail_free_searchset(&ss);
341
* reply_seed - fill in reply header
345
reply_seed(struct pine *ps, ENVELOPE *outgoing, ENVELOPE *env,
346
struct mail_address *saved_from, struct mail_address *saved_to,
347
struct mail_address *saved_cc, struct mail_address *saved_resent,
348
char **fcc, int replytoall, char **errmsg)
350
ADDRESS **to_tail, **cc_tail;
352
to_tail = &outgoing->to;
353
cc_tail = &outgoing->cc;
356
/* Put Reply-To or From in To. */
357
*to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
358
(ADDRESS *) NULL, saved_from, 1);
359
/* and the rest in cc */
361
*cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
362
outgoing->to, saved_to, 1);
363
while(*cc_tail) /* stay on last address */
364
cc_tail = &(*cc_tail)->next;
366
*cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
367
outgoing->to, saved_cc, 1);
369
cc_tail = &(*cc_tail)->next;
371
*cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
372
outgoing->to, saved_resent, 1);
376
/* No From (maybe from us), put To in To. */
377
*to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
378
(ADDRESS *)NULL, saved_to, 1);
379
/* and the rest in cc */
381
*cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
382
outgoing->to, saved_cc, 1);
384
cc_tail = &(*cc_tail)->next;
386
*cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
387
outgoing->to, saved_resent, 1);
391
/* No From or To, put everything else in To if replytoall, */
393
*to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
394
(ADDRESS *) NULL, saved_cc, 1);
396
to_tail = &(*to_tail)->next;
398
*to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
399
(ADDRESS *) NULL, saved_resent, 1);
401
/* else, reply to original From which must be us */
404
* Put self in To if in original From.
406
if(!outgoing->newsgroups)
407
*to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
408
(ADDRESS *) NULL, env->from, 1);
412
/* add any missing personal data */
413
reply_fish_personal(outgoing, env);
416
if(fcc && outgoing->to && outgoing->to->host[0] != '.'){
417
*fcc = get_fcc_based_on_to(outgoing->to);
419
else if(fcc && outgoing->newsgroups){
420
char *newsgroups_returned = NULL;
423
rv = news_grouper(outgoing->newsgroups, &newsgroups_returned, errmsg, fcc, NULL);
425
strcmp(outgoing->newsgroups, newsgroups_returned)){
426
fs_give((void **)&outgoing->newsgroups);
427
outgoing->newsgroups = newsgroups_returned;
430
fs_give((void **) &newsgroups_returned);
435
/*----------------------------------------------------------------------
436
Test the given address lists for equivalence
438
Args: x -- First address list for comparison
439
y -- Second address for comparison
443
addr_lists_same(struct mail_address *x, struct mail_address *y)
445
for(x = first_addr(x), y = first_addr(y);
447
x = first_addr(x->next), y = first_addr(y->next)){
448
if(!address_is_same(x, y))
452
return(!x && !y); /* true if ran off both lists */
456
/*----------------------------------------------------------------------
457
Test the given address against those in the given envelope's to, cc
459
Args: addr -- address for comparison
460
env -- envelope to compare against
464
addr_in_env(struct mail_address *addr, ENVELOPE *env)
468
for(ap = env ? env->to : NULL; ap; ap = ap->next)
469
if(address_is_same(addr, ap))
472
for(ap = env ? env->cc : NULL; ap; ap = ap->next)
473
if(address_is_same(addr, ap))
476
return(0); /* not found! */
480
/*----------------------------------------------------------------------
481
Add missing personal info dest from src envelope
483
Args: dest -- envelope to add personal info to
484
src -- envelope to get personal info from
486
NOTE: This is just kind of a courtesy function. It's really not adding
487
anything needed to get the mail thru, but it is nice for the user
488
under some odd circumstances.
491
reply_fish_personal(ENVELOPE *dest, ENVELOPE *src)
495
for(da = dest ? dest->to : NULL; da; da = da->next){
496
if(da->personal && !da->personal[0])
497
fs_give((void **)&da->personal);
499
for(sa = src ? src->to : NULL; sa && !da->personal ; sa = sa->next)
500
if(address_is_same(da, sa) && sa->personal && sa->personal[0])
501
da->personal = cpystr(sa->personal);
503
for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
504
if(address_is_same(da, sa) && sa->personal && sa->personal[0])
505
da->personal = cpystr(sa->personal);
508
for(da = dest ? dest->cc : NULL; da; da = da->next){
509
if(da->personal && !da->personal[0])
510
fs_give((void **)&da->personal);
512
for(sa = src ? src->to : NULL; sa && !da->personal; sa = sa->next)
513
if(address_is_same(da, sa) && sa->personal && sa->personal[0])
514
da->personal = cpystr(sa->personal);
516
for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
517
if(address_is_same(da, sa) && sa->personal && sa->personal[0])
518
da->personal = cpystr(sa->personal);
523
/*----------------------------------------------------------------------
524
Given a header field and envelope, build "References: " header data
531
reply_build_refs(ENVELOPE *env)
533
int len, id_len, first_ref_len = 0, foldslop;
534
char *p, *refs = NULL, *h = env->references;
535
char *first_ref = NULL, *tail_refs = NULL;
538
if(!(env->message_id && (id_len = strlen(env->message_id))))
543
* The length we have to work with doesn't seem to appear in any
544
* standards. Steve Jones says that in comp.news discussions he
545
* has seen 1024 as the longest length of a header value.
546
* In the inn news source we find MAXHEADERSIZE = 1024. It appears
547
* that is the maximum length of the header value, including
548
* newlines for folded lines (that is, the newlines are counted).
549
* We'll be conservative and figure every reference will take up a
550
* line of its own when we fold. We'll also count 2 for CRLF instead
551
* of just one for LF just to be safe. hubert 2001-jan
552
* J.B. Moreno <planb@newsreaders.com> says "The server limit is
553
* more commonly encountered at 999/1000 bytes [...]". So we'll
554
* back off to 999 instead of 1024.
556
#define MAXHEADERSIZE (999)
558
/* count the total number of potential folds, max of 2 bytes each */
559
for(foldslop = 2, p = h; (p = strstr(p+1, "> <")); )
562
if((len=strlen(h)) + 1+id_len + foldslop >= MAXHEADERSIZE
563
&& (p = strstr(h, "> <"))){
565
* If the references line is so long that we are going to have
566
* to delete some of the references, delete the 2nd, 3rd, ...
567
* We don't want to delete the first message in the thread.
570
first_ref = cpystr(h);
571
first_ref_len = strlen(first_ref)+1; /* len includes space */
574
/* get rid of 2nd, 3rd, ... until it fits */
575
while((len=strlen(tail_refs)) + first_ref_len + 1+id_len +
576
foldslop >= MAXHEADERSIZE
577
&& (p = strstr(tail_refs, "> <"))){
583
* If the individual references are seriously long, somebody
584
* is messing with us and we don't care if it works right.
586
if((len=strlen(tail_refs)) + first_ref_len + 1+id_len +
587
foldslop >= MAXHEADERSIZE){
588
first_ref_len = len = 0;
591
fs_give((void **)&first_ref);
597
refs = (char *)fs_get((first_ref_len + 1+id_len + len + 1) *
599
snprintf(refs, first_ref_len + 1+id_len + len + 1, "%s%s%s%s%s",
600
first_ref ? first_ref : "",
601
first_ref ? " " : "",
602
tail_refs ? tail_refs : "",
603
tail_refs ? " " : "",
605
refs[first_ref_len + 1+id_len + len] = '\0';
609
refs = cpystr(env->message_id);
612
fs_give((void **)&first_ref);
619
/*----------------------------------------------------------------------
620
Snoop for any Resent-* headers, and return an ADDRESS list
625
Returns: either NULL if no Resent-* or parsed ADDRESS struct list
628
reply_resent(struct pine *pine_state, long int msgno, char *section)
633
ADDRESS *rlist = NULL, **a, **b;
634
char *hdrs, *values[RESENTCC+1];
636
static char *fields[] = {"Resent-From", "Resent-To", "Resent-Cc", NULL};
637
static char *fakedomain = "@";
639
if(hdrs = pine_fetchheader_lines(pine_state->mail_stream,
640
msgno, section, fields)){
641
memset(values, 0, (RESENTCC+1) * sizeof(char *));
642
simple_header_parse(hdrs, fields, values);
643
for(i = RESENTFROM; i <= RESENTCC; i++)
644
rfc822_parse_adrlist(&rlist, values[i],
645
(F_ON(F_COMPOSE_REJECTS_UNQUAL, pine_state))
646
? fakedomain : pine_state->maildomain);
649
for(a = &rlist; *a; a = &(*a)->next) /* compare every address */
650
for(b = &(*a)->next; *b; ) /* to the others */
651
if(address_is_same(*a, *b)){
654
if(!(*a)->personal){ /* preserve personal name */
655
(*a)->personal = (*b)->personal;
656
(*b)->personal = NULL;
661
mail_free_address(&t);
668
fs_give((void **) &hdrs);
674
/*----------------------------------------------------------------------
675
Format and return subject suitable for the reply command
677
Args: subject -- subject to build reply subject for
678
buf -- buffer to use for writing. If non supplied, alloc one.
679
buflen -- length of buf if supplied, else ignored
681
Returns: with either "Re:" prepended or not, if already there.
682
returned string is allocated.
685
reply_subject(char *subject, char *buf, size_t buflen)
687
size_t l = (subject && *subject) ? 4*strlen(subject) : 10;
688
char *tmp = fs_get(l + 1), *decoded, *p;
692
buf = fs_get(buflen);
695
/* decode any 8bit into tmp buffer */
696
decoded = (char *) rfc1522_decode((unsigned char *)tmp, l+1, subject, NULL);
699
if(decoded /* already "re:" ? */
700
&& (decoded[0] == 'R' || decoded[0] == 'r')
701
&& (decoded[1] == 'E' || decoded[1] == 'e')){
703
if(decoded[2] == ':')
704
snprintf(buf, buflen, "%.*s", buflen-1, subject);
705
else if((decoded[2] == '[') && (p = strchr(decoded, ']'))){
707
while(*p && isspace((unsigned char)*p)) p++;
709
snprintf(buf, buflen, "%.*s", buflen-1, subject);
714
snprintf(buf, buflen, "Re: %.*s", buflen-1,
715
(subject && *subject) ? subject : "your mail");
717
buf[buflen-1] = '\0';
719
fs_give((void **) &tmp);
724
/*----------------------------------------------------------------------
725
return initials for the given personal name
727
Args: name -- Personal name to extract initials from
729
Returns: pointer to name overwritten with initials
732
reply_quote_initials(char *name)
737
/* while there are still characters to look at */
739
/* skip to next initial */
740
while(*s && isspace((unsigned char) *s))
743
/* skip over cruft like single quotes */
744
while(*s && !isalnum((unsigned char) *s))
751
/* skip to end of this piece of name */
752
while(*s && !isspace((unsigned char) *s))
763
* There is an assumption that MAX_SUBSTITUTION is <= the size of the
764
* tokens being substituted for (only in the size of buf below).
766
#define MAX_SUBSTITUTION 6
767
#define MAX_PREFIX 63
768
static char *from_token = "_FROM_";
769
static char *nick_token = "_NICK_";
770
static char *init_token = "_INIT_";
772
/*----------------------------------------------------------------------
773
return a quoting string, "> " by default, for replied text
775
Args: env -- envelope of message being replied to
777
Returns: malloc'd array containing quoting string, freed by caller
780
reply_quote_str(ENVELOPE *env)
782
char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1];
784
strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1);
785
buf[sizeof(buf)-1] = '\0';
787
/* set up the prefix to quote included text */
788
if(p = strstr(buf, from_token)){
789
repl = (env && env->from && env->from->mailbox) ? env->from->mailbox
791
strncpy(pbf, repl, sizeof(pbf)-1);
792
pbf[sizeof(pbf)-1] = '\0';;
793
rplstr(p, sizeof(buf)-(p-buf), strlen(from_token), pbf);
796
if(p = strstr(buf, nick_token)){
800
get_nickname_from_addr(env->from, tmp_20k_buf, 1000))
802
strncpy(pbf, repl, sizeof(pbf)-1);
803
pbf[sizeof(pbf)-1] = '\0';;
804
rplstr(p, sizeof(buf)-(p-buf), strlen(nick_token), pbf);
807
if(p = strstr(buf, init_token)){
810
char buftmp[MAILTMPLEN];
812
snprintf(buftmp, sizeof(buftmp), "%.200s",
813
(env && env->from && env->from->personal) ? env->from->personal : "");
814
buftmp[sizeof(buftmp)-1] = '\0';
816
repl = (env && env->from && env->from->personal)
817
? reply_quote_initials(q = cpystr((char *)rfc1522_decode(
818
(unsigned char *)tmp_20k_buf,
824
fs_give((void **)&dummy);
826
istrncpy(pbf, repl, sizeof(pbf)-1);
827
pbf[sizeof(pbf)-1] = '\0';;
828
rplstr(p, sizeof(buf)-(p-buf), strlen(init_token), pbf);
830
fs_give((void **)&q);
833
prefix = removing_quotes(cpystr(buf));
839
reply_quote_str_contains_tokens(void)
841
return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] &&
842
(strstr(ps_global->VAR_REPLY_STRING, from_token) ||
843
strstr(ps_global->VAR_REPLY_STRING, nick_token) ||
844
strstr(ps_global->VAR_REPLY_STRING, init_token)));
848
/*----------------------------------------------------------------------
849
Build the body for the message number/part being replied to
853
Result: BODY structure suitable for sending
855
----------------------------------------------------------------------*/
857
reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
858
long int msgno, char *sect_prefix, void *msgtext, char *prefix,
859
int plustext, ACTION_S *role, int toplevel, REDRAFT_POS_S **redraft_pos)
861
char *p, *sig = NULL, *section, sect_buf[256];
862
BODY *body = NULL, *tmp_body = NULL;
865
int impl, template_len = 0, leave_cursor_at_top = 0, reply_raw_body = 0;
868
snprintf(section = sect_buf, sizeof(sect_buf), "%.*s.1", sizeof(sect_buf)-1, sect_prefix);
872
sect_buf[sizeof(sect_buf)-1] = '\0';
874
if(ps_global->full_header == 2
875
&& F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
878
gf_set_so_writec(&pc, (STORE_S *) msgtext);
884
filtered = detoken(role, env, 0,
885
F_ON(F_SIG_AT_BOTTOM, ps_global) ? 1 : 0,
886
0, redraft_pos, &impl);
889
so_puts((STORE_S *)msgtext, filtered);
891
template_len = strlen(filtered);
893
leave_cursor_at_top++;
896
fs_give((void **)&filtered);
905
(sig = reply_signature(role, env, redraft_pos, &impl)) &&
906
F_OFF(F_SIG_AT_BOTTOM, ps_global)){
909
* If CURSORPOS was set explicitly in sig_file, and there was a
910
* template file before that, we need to adjust the offset by the
911
* length of the template file. However, if the template had
912
* a set CURSORPOS in it then impl was 2 before getting to the
913
* signature, so offset wouldn't have been reset by the signature
914
* CURSORPOS and offset would already be correct. That case will
915
* be ok here because template_len will be 0 and adding it does
916
* nothing. If template
917
* didn't have CURSORPOS in it, then impl was 1 and got set to 2
918
* by the CURSORPOS in the sig. In that case we have to adjust the
919
* offset. That's what the next line does. It adjusts it if
920
* template_len is nonzero and if CURSORPOS was set in sig_file.
923
(*redraft_pos)->offset += template_len;
926
so_puts((STORE_S *)msgtext, sig);
929
* Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
930
* is set, we won't have used it yet and want it to be non-NULL.
932
fs_give((void **)&sig);
936
* Only put cursor in sig if there is a cursorpos there but not
937
* one in the template, and sig-at-bottom.
939
if(!(sig && impl == 2 && !leave_cursor_at_top))
940
leave_cursor_at_top++;
944
|| orig_body->type == TYPETEXT
946
|| F_OFF(F_ATTACHMENTS_IN_REPLY, ps_global)){
947
/*------ Simple text-only message ----*/
948
body = mail_newbody();
949
body->type = TYPETEXT;
950
body->contents.text.data = msgtext;
951
reply_delimiter(env, role, pc);
952
if(F_ON(F_INCLUDE_HEADER, ps_global))
953
reply_forward_header(stream, msgno, sect_prefix,
956
if(!orig_body || reply_raw_body || reply_body_text(orig_body, &tmp_body)){
957
get_body_part_text(stream, reply_raw_body ? NULL : tmp_body, msgno,
958
reply_raw_body ? sect_prefix
959
: (p = body_partno(stream, msgno, tmp_body)),
960
0L, pc, prefix, NULL);
961
if(!reply_raw_body && p)
962
fs_give((void **) &p);
965
gf_puts(NEWLINE, pc);
966
gf_puts(" [NON-Text Body part not included]", pc);
967
gf_puts(NEWLINE, pc);
970
else if(orig_body->type == TYPEMULTIPART){
971
/*------ Message is Multipart ------*/
972
if(orig_body->subtype
973
&& !strucmp(orig_body->subtype, "signed")
974
&& orig_body->nested.part){
975
/* operate only on the signed part */
976
body = reply_body(stream, env,
977
&orig_body->nested.part->body,
978
msgno, section, msgtext, prefix,
979
plustext, role, 0, redraft_pos);
981
else if(orig_body->subtype
982
&& !strucmp(orig_body->subtype, "alternative")){
983
/* Set up the simple text reply */
984
body = mail_newbody();
985
body->type = TYPETEXT;
986
body->contents.text.data = msgtext;
988
if(reply_body_text(orig_body, &tmp_body)){
989
reply_delimiter(env, role, pc);
990
if(F_ON(F_INCLUDE_HEADER, ps_global))
991
reply_forward_header(stream, msgno, sect_prefix,
994
get_body_part_text(stream, tmp_body, msgno,
995
p = body_partno(stream,msgno,tmp_body),
996
0L, pc, prefix, NULL);
997
fs_give((void **) &p);
1000
q_status_message(SM_ORDER | SM_DING, 3, 3,
1001
"No suitable multipart text found for inclusion!");
1004
body = copy_body(NULL, orig_body);
1007
* whatever subtype it is, demote it
1008
* to plain old MIXED.
1011
fs_give((void **) &body->subtype);
1013
body->subtype = cpystr("Mixed");
1015
if(body->nested.part &&
1016
body->nested.part->body.type == TYPETEXT) {
1017
char *new_charset = NULL;
1019
/*---- First part of the message is text -----*/
1020
body->nested.part->body.contents.text.data = msgtext;
1021
if(body->nested.part->body.subtype &&
1022
strucmp(body->nested.part->body.subtype, "Plain")){
1023
fs_give((void **)&body->nested.part->body.subtype);
1024
body->nested.part->body.subtype = cpystr("Plain");
1026
reply_delimiter(env, role, pc);
1027
if(F_ON(F_INCLUDE_HEADER, ps_global))
1028
reply_forward_header(stream, msgno, sect_prefix,
1031
if(!(get_body_part_text(stream,
1032
&body->nested.part->body,
1033
msgno, section, 0L, pc, prefix, &new_charset)
1034
&& fetch_contents(stream, msgno, sect_prefix, body)))
1035
q_status_message(SM_ORDER | SM_DING, 3, 4,
1036
_("Error including all message parts"));
1037
else if(new_charset)
1038
fix_charset_parameter(&body->nested.part->body, new_charset);
1040
else if(orig_body->nested.part->body.type == TYPEMULTIPART
1041
&& orig_body->nested.part->body.subtype
1042
&& !strucmp(orig_body->nested.part->body.subtype,
1044
&& reply_body_text(&orig_body->nested.part->body,
1048
reply_delimiter(env, role, pc);
1049
if(F_ON(F_INCLUDE_HEADER, ps_global))
1050
reply_forward_header(stream, msgno, sect_prefix,
1053
snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%.*s",
1054
sizeof(sect_buf)/2-2,
1055
sect_prefix ? sect_prefix : "",
1056
sect_prefix ? "." : "",
1057
sizeof(sect_buf)/2-2,
1058
p = partno(orig_body, tmp_body));
1059
sect_buf[sizeof(sect_buf)-1] = '\0';
1060
fs_give((void **) &p);
1061
get_body_part_text(stream, tmp_body, msgno,
1062
sect_buf, 0L, pc, prefix, NULL);
1064
part = body->nested.part->next;
1065
body->nested.part->next = NULL;
1066
mail_free_body_part(&body->nested.part);
1067
body->nested.part = mail_newbody_part();
1068
body->nested.part->body.type = TYPETEXT;
1069
body->nested.part->body.subtype = cpystr("Plain");
1070
body->nested.part->body.contents.text.data = msgtext;
1071
body->nested.part->next = part;
1073
for(partnum = 2; part != NULL; part = part->next){
1074
snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%d",
1076
sect_prefix ? sect_prefix : "",
1077
sect_prefix ? "." : "", partnum++);
1078
sect_buf[sizeof(sect_buf)-1] = '\0';
1080
if(!fetch_contents(stream, msgno,
1081
sect_buf, &part->body)){
1087
/*--- Fetch the original pieces ---*/
1088
if(!fetch_contents(stream, msgno, sect_prefix, body))
1089
q_status_message(SM_ORDER | SM_DING, 3, 4,
1090
_("Error including all message parts"));
1092
/*--- No text part, create a blank one ---*/
1093
part = mail_newbody_part();
1094
part->next = body->nested.part;
1095
body->nested.part = part;
1096
part->body.contents.text.data = msgtext;
1101
/*---- Single non-text message of some sort ----*/
1102
body = mail_newbody();
1103
body->type = TYPEMULTIPART;
1104
part = mail_newbody_part();
1105
body->nested.part = part;
1107
/*--- The first part, a blank text part to be edited ---*/
1108
part->body.type = TYPETEXT;
1109
part->body.contents.text.data = msgtext;
1111
/*--- The second part, what ever it is ---*/
1112
part->next = mail_newbody_part();
1114
part->body.id = generate_message_id();
1115
copy_body(&(part->body), orig_body);
1118
* the idea here is to fetch part into storage object
1120
if(part->body.contents.text.data = (void *) so_get(PART_SO_TYPE,
1122
#if defined(DOS) && !defined(WIN32)
1123
mail_parameters(ps_global->mail_stream, SET_GETS,
1124
(void *)dos_gets); /* fetched to disk */
1125
append_file = (FILE *)so_text(
1126
(STORE_S *) part->body.contents.text.data);
1128
if(!mail_fetchbody(stream, msgno, section,
1129
&part->body.size.bytes))
1130
mail_free_body(&body);
1132
mail_parameters(stream, SET_GETS,(void *)NULL);
1134
mail_gc(stream, GC_TEXTS);
1136
if(p = pine_mail_fetch_body(stream, msgno, section,
1137
&part->body.size.bytes, NIL)){
1138
so_nputs((STORE_S *)part->body.contents.text.data,
1139
p, part->body.size.bytes);
1142
mail_free_body(&body);
1146
mail_free_body(&body);
1150
/*--------- No text included --------*/
1151
body = mail_newbody();
1152
body->type = TYPETEXT;
1153
body->contents.text.data = msgtext;
1156
if(!leave_cursor_at_top){
1160
/* rewind and count chars to start of sig file */
1161
so_seek((STORE_S *)msgtext, 0L, 0);
1162
while(so_readc(&c, (STORE_S *)msgtext))
1166
*redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(**redraft_pos));
1167
memset((void *)*redraft_pos, 0,sizeof(**redraft_pos));
1168
(*redraft_pos)->hdrname = cpystr(":");
1172
* If explicit cursor positioning in sig file,
1173
* add offset to start of sig file plus offset into sig file.
1174
* Else, just offset to start of sig file.
1176
(*redraft_pos)->offset += cnt;
1181
so_puts((STORE_S *)msgtext, sig);
1183
fs_give((void **)&sig);
1186
gf_clear_so_writec((STORE_S *) msgtext);
1193
* reply_part - first replyable multipart of a multipart.
1196
reply_body_text(struct mail_bodystruct *body, struct mail_bodystruct **new_body)
1204
case TYPEMULTIPART :
1205
if(body->subtype && !strucmp(body->subtype, "alternative")){
1208
/* Pick out the interesting text piece */
1209
for(part = body->nested.part; part; part = part->next)
1210
if((!part->body.type || part->body.type == TYPETEXT)
1211
&& (!part->body.subtype
1212
|| !strucmp(part->body.subtype, "plain"))){
1213
*new_body = &part->body;
1217
else if(body->nested.part)
1218
/* NOTE: we're only interested in "first" part of mixed */
1219
return(reply_body_text(&body->nested.part->body, new_body));
1233
reply_signature(ACTION_S *role, ENVELOPE *env, REDRAFT_POS_S **redraft_pos, int *impl)
1238
sig = detoken(role, env,
1239
2, F_ON(F_SIG_AT_BOTTOM, ps_global) ? 0 : 1, 1,
1242
if(F_OFF(F_SIG_AT_BOTTOM, ps_global) && (!sig || !*sig)){
1244
fs_give((void **)&sig);
1246
l = 2 * strlen(NEWLINE);
1247
sig = (char *)fs_get((l+1) * sizeof(char));
1248
strncpy(sig, NEWLINE, l);
1250
strncat(sig, NEWLINE, l-strlen(sig));
1260
* Buf is at least size maxlen+1
1263
get_addr_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
1265
ADDRESS *addr = NULL;
1266
ADDRESS *last_to = NULL;
1267
ADDRESS *first_addr = NULL, *second_addr = NULL;
1268
ADDRESS *third_addr = NULL, *fourth_addr = NULL;
1277
addr = env ? env->from : NULL;
1281
addr = env ? env->to : NULL;
1285
addr = env ? env->cc : NULL;
1289
addr = env ? env->sender : NULL;
1293
* Recips is To and Cc togeter. We hook the two adrlists together
1297
addr = env ? env->to : NULL;
1298
/* Find end of To list */
1299
for(last_to = addr; last_to && last_to->next; last_to = last_to->next)
1302
/* Make the end of To list point to cc list */
1304
last_to->next = (env ? env->cc : NULL);
1312
if(env && env->from && env->from->personal){
1313
char *name, *initials = NULL, *dummy = NULL;
1315
name = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
1316
SIZEOF_20KBUF, env->from->personal,
1319
fs_give((void **)&dummy);
1321
if(name == env->from->personal){
1322
strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1);
1323
tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
1328
initials = reply_quote_initials(name);
1329
iutf8ncpy(buf, initials, maxlen);
1337
orig_maxlen = maxlen;
1340
/* skip over rest of c-client group addr */
1341
if(first_addr && first_addr->mailbox && !first_addr->host){
1342
for(second_addr = first_addr->next;
1343
second_addr && second_addr->host;
1344
second_addr = second_addr->next)
1347
if(second_addr && !second_addr->host)
1348
second_addr = second_addr->next;
1350
else if(!(first_addr && first_addr->host && first_addr->host[0] == '.'))
1351
second_addr = first_addr ? first_addr->next : NULL;
1353
if(second_addr && second_addr->mailbox && !second_addr->host){
1354
for(third_addr = second_addr->next;
1355
third_addr && third_addr->host;
1356
third_addr = third_addr->next)
1359
if(third_addr && !third_addr->host)
1360
third_addr = third_addr->next;
1362
else if(!(second_addr && second_addr->host && second_addr->host[0] == '.'))
1363
third_addr = second_addr ? second_addr->next : NULL;
1365
if(third_addr && third_addr->mailbox && !third_addr->host){
1366
for(fourth_addr = third_addr->next;
1367
fourth_addr && fourth_addr->host;
1368
fourth_addr = fourth_addr->next)
1371
if(fourth_addr && !fourth_addr->host)
1372
fourth_addr = fourth_addr->next;
1374
else if(!(third_addr && third_addr->host && third_addr->host[0] == '.'))
1375
fourth_addr = third_addr ? third_addr->next : NULL;
1377
/* Just attempting to make a nice display */
1378
if(first_addr && ((first_addr->personal && first_addr->personal[0]) ||
1379
(first_addr->mailbox && first_addr->mailbox[0]))){
1381
if((second_addr->personal && second_addr->personal[0]) ||
1382
(second_addr->mailbox && second_addr->mailbox[0])){
1384
if((third_addr->personal && third_addr->personal[0]) ||
1385
(third_addr->mailbox && third_addr->mailbox[0])){
1408
a_little_addr_string(first_addr, p, maxlen);
1409
else if(cntaddr == 2){
1410
a_little_addr_string(first_addr, p, maxlen);
1411
maxlen -= (l=strlen(p));
1414
strncpy(p, " and ", maxlen);
1417
a_little_addr_string(second_addr, p, maxlen);
1420
else if(cntaddr == 3){
1421
a_little_addr_string(first_addr, p, maxlen);
1422
maxlen -= (l=strlen(p));
1425
strncpy(p, ", ", maxlen);
1428
a_little_addr_string(second_addr, p, maxlen);
1429
maxlen -= (l=strlen(p));
1432
strncpy(p, ", and ", maxlen);
1435
a_little_addr_string(third_addr, p, maxlen);
1439
else if(cntaddr > 3){
1440
a_little_addr_string(first_addr, p, maxlen);
1441
maxlen -= (l=strlen(p));
1444
strncpy(p, ", ", maxlen);
1447
a_little_addr_string(second_addr, p, maxlen);
1448
maxlen -= (l=strlen(p));
1451
strncpy(p, ", ", maxlen);
1454
a_little_addr_string(third_addr, p, maxlen);
1455
maxlen -= (l=strlen(p));
1458
strncpy(p, ", and others", maxlen);
1459
else if(maxlen >= 3)
1460
strncpy(p, "...", maxlen);
1467
a_string = addr_list_string(addr, NULL, 0);
1468
iutf8ncpy(buf, a_string, maxlen);
1470
fs_give((void **)&a_string);
1474
last_to->next = NULL;
1476
buf[orig_maxlen] = '\0';
1481
* Buf is at least size maxlen+1
1484
get_news_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
1486
int cntnews = 0, orig_maxlen;
1487
char *news = NULL, *p, *q;
1493
case iNewsAndRecips:
1494
case iRecipsAndNews:
1495
news = env ? env->newsgroups : NULL;
1499
if(ps_global->mail_stream && IS_NEWS(ps_global->mail_stream))
1500
news = ps_global->cur_folder;
1505
orig_maxlen = maxlen;
1509
while(isspace((unsigned char)*q))
1515
while((q = strindex(q, ',')) != NULL){
1517
while(isspace((unsigned char)*q))
1528
istrncpy(buf, news, maxlen);
1530
removing_leading_and_trailing_white_space(buf);
1532
else if(cntnews == 2){
1535
while(isspace((unsigned char)*q))
1538
while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
1544
strncpy(p, " and ", maxlen);
1549
while(isspace((unsigned char)*q) || *q == ',')
1552
while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
1559
istrncpy(tmp_20k_buf, buf, 10000);
1560
strncpy(buf, tmp_20k_buf, orig_maxlen);
1562
else if(cntnews > 2){
1567
while(isspace((unsigned char)*q))
1570
while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
1576
snprintf(b, sizeof(b), " and %d other newsgroups", cntnews-1);
1577
b[sizeof(b)-1] = '\0';
1578
if(maxlen >= strlen(b))
1579
strncpy(p, b, maxlen);
1580
else if(maxlen >= 3)
1581
strncpy(p, "...", maxlen);
1583
buf[orig_maxlen] = '\0';
1585
istrncpy(tmp_20k_buf, buf, 10000);
1586
tmp_20k_buf[10000-1] = '\0';
1587
strncpy(buf, tmp_20k_buf, orig_maxlen);
1590
buf[orig_maxlen] = '\0';
1595
* Buf is at least size maxlen+1
1598
get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size_t maxlen)
1601
IndexColType addrtype;
1606
case iRDate: case iSDate: case iSTime:
1607
case iS1Date: case iS2Date: case iS3Date: case iS4Date:
1608
case iSDateIso: case iSDateIsoS:
1609
case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
1611
case iSDateTimeIso: case iSDateTimeIsoS:
1612
case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
1614
case iSDateTimeIso24: case iSDateTimeIsoS24:
1615
case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
1616
case iDateIso: case iDateIsoS: case iTime24: case iTime12:
1617
case iDay: case iDayOrdinal: case iDay2Digit:
1618
case iMonAbb: case iMonLong: case iMon: case iMon2Digit:
1619
case iYear: case iYear2Digit:
1620
case iDate: case iLDate:
1621
case iTimezone: case iDayOfWeekAbb: case iDayOfWeek:
1622
if(env && env->date && env->date[0] && maxlen >= 20)
1623
date_str((char *) env->date, type, 1, buf, maxlen+1);
1635
case iCurDayOfWeekAbb:
1641
case iCurYear2Digit:
1647
case iLstMonYear2Digit:
1649
case iLstYear2Digit:
1651
date_str(NULL, type, 1, buf, maxlen+1);
1661
get_addr_data(env, type, buf, maxlen);
1665
if(role && role->nick){
1666
strncpy(buf, role->nick, maxlen);
1673
if(env && env->from && env->from->mailbox && env->from->mailbox[0] &&
1674
strlen(env->from->mailbox) <= maxlen){
1675
strncpy(buf, env->from->mailbox, maxlen);
1677
if(type == iAddress &&
1679
env->from->host[0] &&
1680
env->from->host[0] != '.' &&
1681
strlen(buf) + strlen(env->from->host) + 1 <= maxlen){
1682
strncat(buf, "@", maxlen-strlen(buf));
1684
strncat(buf, env->from->host, maxlen-strlen(buf));
1693
get_news_data(env, type, buf, maxlen);
1698
case iRecipsAndNews:
1699
case iNewsAndRecips:
1700
if(type == iToAndNews || type == iNewsAndTo)
1705
if(env && env->newsgroups){
1706
space = (char *)fs_get((maxlen+1) * sizeof(char));
1707
get_news_data(env, type, space, maxlen);
1710
get_addr_data(env, addrtype, buf, maxlen);
1712
if(space && *space && *buf){
1713
if(strlen(space) + strlen(buf) + 5 > maxlen){
1714
if(strlen(space) > maxlen/2)
1715
get_news_data(env, type, space, maxlen - strlen(buf) - 5);
1717
get_addr_data(env, addrtype, buf, maxlen - strlen(space) - 5);
1720
if(type == iToAndNews || type == iRecipsAndNews)
1721
snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", buf, space);
1723
snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", space, buf);
1725
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1727
strncpy(buf, tmp_20k_buf, maxlen);
1730
else if(space && *space){
1731
strncpy(buf, space, maxlen);
1736
fs_give((void **)&space);
1741
if(env && env->subject){
1743
unsigned char *p, *tmp = NULL;
1746
if((n = 4*strlen(env->subject)) > SIZEOF_20KBUF-1){
1748
p = tmp = (unsigned char *)fs_get(len * sizeof(char));
1751
len = SIZEOF_20KBUF;
1752
p = (unsigned char *)tmp_20k_buf;
1755
istrncpy(buf, (char *)rfc1522_decode(p, len, env->subject, &dummy),
1760
fs_give((void **)&dummy);
1762
fs_give((void **)&tmp);
1768
if(env && env->message_id){
1769
strncpy(buf, env->message_id, maxlen);
1785
* reply_delimiter - output formatted reply delimiter for given envelope
1786
* with supplied character writing function.
1789
reply_delimiter(ENVELOPE *env, ACTION_S *role, gf_io_t pc)
1791
#define MAX_DELIM 2000
1792
char buf[MAX_DELIM+1];
1794
char *filtered = NULL;
1801
strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM);
1802
buf[MAX_DELIM] = '\0';
1803
/* preserve exact default behavior from before */
1804
if(!strcmp(buf, DEFAULT_REPLY_INTRO)){
1808
parse_date((char *) env->date, &d);
1809
include_date = !(d.day == -1 || d.month == -1 || d.year == -1);
1811
gf_puts("On ", pc); /* All delims have... */
1812
if(d.wkday != -1){ /* "On day, date month year" */
1813
gf_puts(week_abbrev(d.wkday), pc); /* in common */
1817
gf_puts(int2string(d.day), pc);
1819
gf_puts(month_abbrev(d.month), pc);
1821
gf_puts(int2string(d.year), pc);
1825
&& ((env->from->personal && env->from->personal[0])
1826
|| (env->from->mailbox && env->from->mailbox[0]))){
1827
char buftmp[MAILTMPLEN];
1829
a_little_addr_string(env->from, buftmp, sizeof(buftmp)-1);
1833
gf_puts(buftmp, pc);
1834
gf_puts(" wrote:", pc);
1838
gf_puts(", it was written", pc);
1840
gf_puts("It was written", pc);
1845
filtered = detoken_src(buf, FOR_REPLY_INTRO, env, role,
1848
/* try to truncate if too long */
1849
if(filtered && (len = strlen(filtered)) > 80){
1850
int ended_with_colon = 0;
1851
int ended_with_quote = 0;
1852
int ended_with_quote_colon = 0;
1854
if(filtered[len-1] == ':'){
1855
ended_with_colon = ':';
1856
if(filtered[len-2] == QUOTE || filtered[len-2] == '\'')
1857
ended_with_quote_colon = filtered[len-2];
1859
else if(filtered[len-1] == QUOTE || filtered[len-1] == '\'')
1860
ended_with_quote = filtered[len-1];
1862
/* try to find space to break at */
1863
for(p = &filtered[75]; p > &filtered[60] &&
1864
!isspace((unsigned char)*p); p--)
1867
if(!isspace((unsigned char)*p))
1873
if(ended_with_quote_colon){
1874
*p++ = ended_with_quote_colon;
1877
else if(ended_with_colon)
1878
*p++ = ended_with_colon;
1879
else if(ended_with_quote)
1880
*p++ = ended_with_quote;
1885
if(filtered && *filtered)
1886
gf_puts(filtered, pc);
1889
/* and end with two newlines unless no leadin at all */
1890
if(!strcmp(buf, DEFAULT_REPLY_INTRO) || (filtered && *filtered)){
1891
gf_puts(NEWLINE, pc);
1892
gf_puts(NEWLINE, pc);
1896
fs_give((void **)&filtered);
1901
free_redraft_pos(REDRAFT_POS_S **redraft_pos)
1903
if(redraft_pos && *redraft_pos){
1904
if((*redraft_pos)->hdrname)
1905
fs_give((void **)&(*redraft_pos)->hdrname);
1907
fs_give((void **)redraft_pos);
1912
/*----------------------------------------------------------------------
1913
Build the body for the message number/part being forwarded as ATTACHMENT
1917
Result: PARTS suitably attached to body
1919
----------------------------------------------------------------------*/
1921
forward_mime_msg(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env, struct mail_body_part **partp, void *msgtext)
1927
*partp = mail_newbody_part();
1928
b = &(*partp)->body;
1929
b->type = TYPEMESSAGE;
1930
b->id = generate_message_id();
1931
b->description = forward_subject(env, FS_CONVERT_QUOTES);
1932
b->nested.msg = mail_newmsg();
1934
/*---- Package each message in a storage object ----*/
1935
if((b->contents.text.data = (void *) so_get(PART_SO_TYPE,NULL,EDIT_ACCESS))
1936
&& (tmp_text = mail_fetch_header(stream,msgno,section,NIL,NIL,FT_PEEK))
1938
so_puts((STORE_S *) b->contents.text.data, tmp_text);
1940
b->size.bytes = strlen(tmp_text);
1941
so_puts((STORE_S *) b->contents.text.data, "\015\012");
1942
if(tmp_text = pine_mail_fetch_text (stream,msgno,section,&len,NIL)){
1943
so_nputs((STORE_S *)b->contents.text.data,tmp_text,(long) len);
1944
b->size.bytes += len;
1954
* forward_delimiter - return delimiter for forwarded text
1957
forward_delimiter(gf_io_t pc)
1959
gf_puts(NEWLINE, pc);
1960
/* TRANSLATORS: When a message is forwarded by the user this is the
1961
text that shows where the forwarded part of the message begins. */
1962
gf_puts(_("---------- Forwarded message ----------"), pc);
1963
gf_puts(NEWLINE, pc);
1967
/*----------------------------------------------------------------------
1968
Wrapper for header formatting tool
1976
Result: header suitable for reply/forward text written using "pc"
1978
----------------------------------------------------------------------*/
1980
reply_forward_header(MAILSTREAM *stream, long int msgno, char *part, ENVELOPE *env,
1981
gf_io_t pc, char *prefix)
1985
char **list, **new_list = NULL;
1987
list = ps_global->VAR_VIEW_HEADERS;
1990
* If VIEW_HEADERS is set, we should remove BCC from the list so that
1991
* the user doesn't inadvertently forward the BCC header.
1993
if(list && list[0]){
2000
p = new_list = (char **) fs_get((cnt+1) * sizeof(char *));
2002
for(i=0; list[i]; i++)
2003
if(strucmp(list[i], "bcc"))
2004
*p++ = cpystr(list[i]);
2008
if(new_list && new_list[0])
2013
HD_INIT(&h, list, ps_global->view_all_except, FE_DEFAULT & ~FE_BCC);
2014
if(rv = format_header(stream, msgno, part, env, &h, prefix, NULL,
2017
gf_puts(" [Error fetching message header data]", pc);
2020
gf_puts(NEWLINE, pc); /* write header delimiter */
2023
free_list_array(&new_list);
2027
/*----------------------------------------------------------------------
2028
Build the subject for the message number being forwarded
2030
Args: pine_state -- The usual pine structure
2031
msgno -- The message number to build subject for
2033
Result: malloc'd string containing new subject or NULL on error
2035
----------------------------------------------------------------------*/
2037
forward_subject(ENVELOPE *env, int flags)
2040
char *p, *cs = NULL, buftmp[MAILTMPLEN];
2045
dprint((9, "checking subject: \"%s\"\n",
2046
env->subject ? env->subject : "NULL"));
2048
if(env->subject && env->subject[0]){ /* add (fwd)? */
2049
snprintf(buftmp, sizeof(buftmp), "%s", env->subject);
2050
buftmp[sizeof(buftmp)-1] = '\0';
2051
/* decode any 8bit (copy to the temp buffer if decoding doesn't) */
2052
if(rfc1522_decode((unsigned char *) tmp_20k_buf,
2053
SIZEOF_20KBUF, buftmp, &cs) == (unsigned char *) buftmp)
2054
strncpy(tmp_20k_buf, buftmp, SIZEOF_20KBUF);
2056
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2058
removing_trailing_white_space(tmp_20k_buf);
2059
if((l=strlen(tmp_20k_buf)) < 1000 &&
2060
(l < 5 || strcmp(tmp_20k_buf+l-5,"(fwd)"))){
2061
snprintf(tmp_20k_buf+2000, SIZEOF_20KBUF-2000, "%s (fwd)",
2062
(!cs || !strucmp(cs, "us-ascii") || !strucmp(cs, "utf-8")) ? tmp_20k_buf : env->subject);
2063
tmp_20k_buf[SIZEOF_20KBUF-2000-1] = '\0';
2064
strncpy(tmp_20k_buf, tmp_20k_buf+2000, SIZEOF_20KBUF);
2065
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2069
* HACK: composer can't handle embedded double quotes in attachment
2070
* comments so we substitute two single quotes.
2072
if(flags & FS_CONVERT_QUOTES)
2073
while(p = strchr(tmp_20k_buf, QUOTE))
2074
(void)rplstr(p, SIZEOF_20KBUF-(p-tmp_20k_buf), 1, "''");
2077
fs_give((void **) &cs);
2079
return(cpystr(tmp_20k_buf));
2083
return(cpystr("Forwarded mail...."));
2087
/*----------------------------------------------------------------------
2088
Build the body for the message number/part being forwarded
2092
Result: BODY structure suitable for sending
2094
----------------------------------------------------------------------*/
2096
forward_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
2097
long int msgno, char *sect_prefix, void *msgtext, int flags)
2099
BODY *body = NULL, *text_body, *tmp_body;
2102
char *tmp_text, *section, sect_buf[256];
2103
int forward_raw_body = 0;
2106
* Check to see if messages got expunged out from underneath us. This
2107
* could have happened during the prompt to the user asking whether to
2108
* include the message as an attachment. Either the message is gone or
2109
* it might be at a different sequence number. We'd better bail.
2111
if(ps_global->full_header == 2
2112
&& F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
2113
forward_raw_body = 1;
2114
if(sp_expunge_count(stream))
2117
if(sect_prefix && forward_raw_body == 0)
2118
snprintf(section = sect_buf, sizeof(sect_buf), "%s.1", sect_prefix);
2119
else if(sect_prefix && forward_raw_body)
2120
section = sect_prefix;
2121
else if(!sect_prefix && forward_raw_body)
2126
sect_buf[sizeof(sect_buf)-1] = '\0';
2128
gf_set_so_writec(&pc, (STORE_S *) msgtext);
2129
if(!orig_body || orig_body->type == TYPETEXT || forward_raw_body) {
2130
/*---- Message has a single text part -----*/
2131
body = mail_newbody();
2132
body->type = TYPETEXT;
2133
body->contents.text.data = msgtext;
2134
if(!(flags & FWD_ANON)){
2135
forward_delimiter(pc);
2136
reply_forward_header(stream, msgno, sect_prefix, env, pc, "");
2139
if(!get_body_part_text(stream, forward_raw_body ? NULL : orig_body, msgno, section, 0L, pc, NULL, NULL)){
2140
mail_free_body(&body);
2144
else if(orig_body->type == TYPEMULTIPART) {
2145
if(orig_body->subtype && !strucmp(orig_body->subtype, "signed")
2146
&& orig_body->nested.part){
2147
/* only operate on the signed data (not the signature) */
2148
body = forward_body(stream, env, &orig_body->nested.part->body,
2149
msgno, sect_prefix, msgtext, flags);
2151
/*---- Message is multipart ----*/
2152
else if(!(orig_body->subtype && !strucmp(orig_body->subtype,
2154
&& (body = forward_multi_alt(stream, env, orig_body, msgno,
2155
sect_prefix, msgtext,
2157
/*--- Copy the body and entire structure ---*/
2158
body = copy_body(NULL, orig_body);
2161
* whatever subtype it is, demote it
2162
* to plain old MIXED.
2165
fs_give((void **) &body->subtype);
2167
body->subtype = cpystr("Mixed");
2169
/*--- The text part of the message ---*/
2170
if(!body->nested.part){
2171
q_status_message(SM_ORDER | SM_DING, 3, 6,
2172
"Error referencing body part 1");
2173
mail_free_body(&body);
2175
else if(body->nested.part->body.type == TYPETEXT) {
2176
char *new_charset = NULL;
2178
/*--- The first part is text ----*/
2179
text_body = &body->nested.part->body;
2180
text_body->contents.text.data = msgtext;
2181
if(text_body->subtype && strucmp(text_body->subtype, "Plain")){
2182
/* this text is going to the composer, it should be Plain */
2183
fs_give((void **)&text_body->subtype);
2184
text_body->subtype = cpystr("PLAIN");
2186
if(!(flags & FWD_ANON)){
2187
forward_delimiter(pc);
2188
reply_forward_header(stream, msgno,
2189
sect_prefix, env, pc, "");
2192
if(!(get_body_part_text(stream, text_body,
2193
msgno, section, 0L, pc, NULL, &new_charset)
2194
&& fetch_contents(stream, msgno, sect_prefix, body)))
2195
mail_free_body(&body);
2196
else if(new_charset)
2197
fix_charset_parameter(text_body, new_charset);
2199
/* BUG: ? matter that we're not setting body.size.bytes */
2201
else if(body->nested.part->body.type == TYPEMULTIPART
2202
&& body->nested.part->body.subtype
2203
&& !strucmp(body->nested.part->body.subtype, "alternative")
2204
&& (tmp_body = forward_multi_alt(stream, env,
2205
&body->nested.part->body,
2208
flags | FWD_NESTED))){
2209
/* for the forward_multi_alt call above, we want to pass
2210
* sect_prefix instead of section so we can obtain the header.
2211
* Set the FWD_NESTED flag so we fetch the right body_part.
2215
part = body->nested.part->next;
2216
body->nested.part->next = NULL;
2217
mail_free_body_part(&body->nested.part);
2218
body->nested.part = mail_newbody_part();
2219
body->nested.part->body = *tmp_body;
2220
body->nested.part->next = part;
2222
for(partnum = 2; part != NULL; part = part->next){
2223
snprintf(sect_buf, sizeof(sect_buf), "%s%s%d",
2224
sect_prefix ? sect_prefix : "",
2225
sect_prefix ? "." : "", partnum++);
2226
sect_buf[sizeof(sect_buf)-1] = '\0';
2228
if(!fetch_contents(stream, msgno, sect_buf, &part->body)){
2229
mail_free_body(&body);
2235
if(fetch_contents(stream, msgno, sect_prefix, body)){
2236
/*--- Create a new blank text part ---*/
2237
part = mail_newbody_part();
2238
part->next = body->nested.part;
2239
body->nested.part = part;
2240
part->body.contents.text.data = msgtext;
2243
mail_free_body(&body);
2248
/*---- A single part message, not of type text ----*/
2249
body = mail_newbody();
2250
body->type = TYPEMULTIPART;
2251
part = mail_newbody_part();
2252
body->nested.part = part;
2254
/*--- The first part, a blank text part to be edited ---*/
2255
part->body.type = TYPETEXT;
2256
part->body.contents.text.data = msgtext;
2258
/*--- The second part, what ever it is ---*/
2259
part->next = mail_newbody_part();
2261
part->body.id = generate_message_id();
2262
copy_body(&(part->body), orig_body);
2265
* the idea here is to fetch part into storage object
2267
if(part->body.contents.text.data = (void *) so_get(PART_SO_TYPE, NULL,
2269
#if defined(DOS) && !defined(WIN32)
2270
/* fetched text to disk */
2271
mail_parameters(stream, SET_GETS, (void *)dos_gets);
2272
append_file = (FILE *)so_text(
2273
(STORE_S *) part->body.contents.text.data);
2275
if(mail_fetchbody(stream, msgno, part, &part->body.size.bytes)){
2276
/* next time body may stay in core */
2277
mail_parameters(stream, SET_GETS, (void *)NULL);
2279
mail_gc(stream, GC_TEXTS);
2282
mail_free_body(&body);
2284
if(tmp_text = pine_mail_fetch_body(stream, msgno, section,
2285
&part->body.size.bytes, NIL))
2286
so_nputs((STORE_S *)part->body.contents.text.data, tmp_text,
2287
part->body.size.bytes);
2289
mail_free_body(&body);
2293
mail_free_body(&body);
2296
gf_clear_so_writec((STORE_S *) msgtext);
2304
* bounce_msg_body - build body from specified message suitable
2305
* for sending as bounced message
2308
bounce_msg_body(MAILSTREAM *stream,
2313
ENVELOPE **outgoingp,
2317
char *h, *p, *errstr = NULL;
2322
*outgoingp = mail_newenvelope();
2323
(*outgoingp)->message_id = generate_message_id();
2324
(*outgoingp)->subject = cpystr(subject ? subject : "Resent mail....");
2327
* Fill in destination if we were given one. If so, note that we
2328
* call p_s_s() below such that it won't prompt...
2331
static char *fakedomain = "@";
2334
/* rfc822_parse_adrlist feels free to destroy input so copy */
2335
tmp_a_string = cpystr(*to);
2336
rfc822_parse_adrlist(&(*outgoingp)->to, tmp_a_string,
2337
(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
2338
? fakedomain : ps_global->maildomain);
2339
fs_give((void **) &tmp_a_string);
2342
/* build remail'd header */
2343
if(h = mail_fetch_header(stream, rawno, part, NULL, 0, FT_PEEK)){
2344
for(p = h, i = 0; p = strchr(p, ':'); p++)
2348
(*outgoingp)->remail = (char *) fs_get(strlen(h) + (2 * i) + 1);
2351
* copy it, "X-"ing out transport headers bothersome to
2352
* software but potentially useful to the human recipient...
2354
p = (*outgoingp)->remail;
2355
bounce_mask_header(&p, h);
2357
if(*h == '\015' && *(h+1) == '\012'){
2358
*p++ = *h++; /* copy CR LF */
2360
bounce_mask_header(&p, h);
2364
/* BUG: else complain? */
2366
/* NOT bound for the composer, so no need for PicoText */
2367
if(!(msgtext = so_get(CharStar, NULL, EDIT_ACCESS))){
2368
mail_free_envelope(outgoingp);
2369
return(_("Error allocating message text"));
2372
/* mark object for special handling */
2373
so_attr(msgtext, "rawbody", "1");
2376
* Build a fake body description. It's ignored by pine_rfc822_header,
2377
* but we need to set it to something that makes set_mime_types
2378
* not sniff it and pine_rfc822_output_body not re-encode it.
2379
* Setting the encoding to (ENCMAX + 1) will work and shouldn't cause
2380
* problems unless something tries to access body_encodings[] using
2381
* it without proper precautions. We don't want to use ENCOTHER
2382
* cause that tells set_mime_types to sniff it, and we don't want to
2383
* use ENC8BIT since that tells pine_rfc822_output_body to qp-encode
2384
* it. When there's time, it'd be nice to clean this interaction
2387
*bodyp = mail_newbody();
2388
(*bodyp)->type = TYPETEXT;
2389
(*bodyp)->encoding = ENCMAX + 1;
2390
(*bodyp)->subtype = cpystr("Plain");
2391
(*bodyp)->contents.text.data = (void *) msgtext;
2392
gf_set_so_writec(&pc, msgtext);
2394
if(seenp && rawno > 0L && stream && rawno <= stream->nmsgs){
2397
if(mc = mail_elt(stream, rawno))
2401
/* pass NULL body to force mail_fetchtext */
2402
if(!get_body_part_text(stream, NULL, rawno, part, 0L, pc, NULL, NULL))
2403
errstr = _("Error fetching message contents. Can't Bounce message");
2405
gf_clear_so_writec(msgtext);
2412
/*----------------------------------------------------------------------
2413
Mask off any header entries we don't want xport software to see
2415
Args: d -- destination string pointer pointer
2416
s -- source string pointer pointer
2418
Postfix uses Delivered-To to detect loops.
2419
Received line counting is also used to detect loops in places.
2423
bounce_mask_header(char **d, char *s)
2425
if(((*s == 'R' || *s == 'r')
2426
&& (!struncmp(s+1, "esent-", 6) || !struncmp(s+1, "eceived:", 8)
2427
|| !struncmp(s+1, "eturn-Path", 10)))
2428
|| !struncmp(s, "Delivered-To:", 13)){
2429
*(*d)++ = 'X'; /* write mask */
2435
/*----------------------------------------------------------------------
2436
Fetch and format text for forwarding
2438
Args: stream -- Mail stream to fetch text from
2439
body -- Body structure of message being forwarded
2440
msg_no -- Message number of text for forward
2441
part_no -- Part number of text to forward
2442
partial -- If this is > 0 a partial fetch will be done and it will
2443
be done using FT_PEEK so the message will remain unseen.
2444
pc -- Function to write to
2445
prefix -- Prefix for each line
2446
ret_charset -- If we translate to another charset return that
2449
Returns: true if OK, false if problem occured while filtering
2451
If the text is richtext, it will be converted to plain text, since there's
2452
no rich text editing capabilities in Pine (yet).
2454
It's up to calling routines to plug in signature appropriately
2456
As with all internal text, NVT end-of-line conventions are observed.
2457
DOESN'T sanity check the prefix given!!!
2460
get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body, long int msg_no,
2461
char *part_no, long partial, gf_io_t pc, char *prefix, char **ret_charset)
2463
int i, we_cancel = 0, dashdata, wrapflags = 0, flow_res = 0;
2464
FILTLIST_S filters[11];
2466
char *err, *charset, *prefix_p = NULL;
2467
int filtcnt = 0, flags = 0;
2469
memset(filters, 0, sizeof(filters));
2471
*ret_charset = NULL;
2473
if(!pc_is_picotext(pc))
2474
we_cancel = busy_cue(NULL, NULL, 1);
2476
/* if null body, we must be talking to a non-IMAP2bis server.
2477
* No MIME parsing provided, so we just grab the message text...
2480
char *text, *decode_error;
2482
SourceType src = CharStar;
2485
(void) pine_mail_fetchstructure(stream, msg_no, NULL);
2487
if(text = pine_mail_fetch_text(stream, msg_no, part_no, NULL, 0)){
2488
gf_set_readc(&gc, text, (unsigned long)strlen(text), src);
2490
gf_filter_init(); /* no filters needed */
2492
gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2493
if(decode_error = gf_pipe(gc, pc)){
2494
snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s [Formatting error: %s]%s",
2496
decode_error, NEWLINE);
2497
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
2498
gf_puts(tmp_20k_buf, pc);
2503
gf_puts(NEWLINE, pc);
2504
gf_puts(_(" [ERROR fetching text of message]"), pc);
2505
gf_puts(NEWLINE, pc);
2506
gf_puts(NEWLINE, pc);
2511
cancel_busy_cue(-1);
2516
charset = rfc2231_get_param(body->parameter, "charset", NULL, NULL);
2519
if(charset && strucmp(charset, "utf-8") && strucmp(charset, "us-ascii")){
2520
/* translate filtered message text to UTF-8? */
2521
if(utf8able(charset)){
2522
filters[filtcnt].filter = gf_utf8;
2523
filters[filtcnt++].data = gf_utf8_opt(charset);
2526
*ret_charset = "UTF-8";
2529
/* BUG: what to do if not transliterable? */
2535
* just use detach, but add an auxiliary filter to insert prefix,
2536
* and, perhaps, digest richtext
2538
if(ps_global->full_header != 2
2539
&& !ps_global->postpone_no_flow
2540
&& (flags & FM_UTF8)
2541
&& (!body->subtype || !strucmp(body->subtype, "plain"))){
2544
flow_res = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
2545
&& F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
2546
&& (!prefix || (strucmp(prefix,"> ") == 0)
2547
|| strucmp(prefix, ">") == 0));
2548
if(parmval = rfc2231_get_param(body->parameter,
2549
"format", NULL, NULL)){
2550
if(!strucmp(parmval, "flowed")){
2551
wrapflags |= GFW_FLOWED;
2553
fs_give((void **) &parmval);
2554
if(parmval = rfc2231_get_param(body->parameter,
2555
"delsp", NULL, NULL)){
2556
if(!strucmp(parmval, "yes")){
2557
filters[filtcnt++].filter = gf_preflow;
2558
wrapflags |= GFW_DELSP;
2561
fs_give((void **) &parmval);
2565
* if there's no prefix we're forwarding text
2566
* otherwise it's reply text. unless the user's
2567
* tied our hands, alter the prefix to continue flowed
2571
wrapflags |= GFW_FLOW_RESULT;
2574
wrapflags |= GFW_UTF8;
2576
filters[filtcnt].filter = gf_wrap;
2578
* The 80 will cause longer lines than what is likely
2579
* set by composer_fillcol, so we'll have longer
2580
* quoted and forwarded lines than the lines we type.
2581
* We're fine with that since the alternative is the
2582
* possible wrapping of lines we shouldn't have, which
2583
* is mitigated by this higher 80 limit.
2585
* If we were to go back to using composer_fillcol,
2586
* the correct value is composer_fillcol + 1, pine
2587
* is off-by-one from pico.
2589
filters[filtcnt++].data = gf_wrap_filter_opt(
2590
MAX(MIN(ps_global->ttyo->screen_cols
2591
- (prefix ? strlen(prefix) : 0),
2592
80 - (prefix ? strlen(prefix) : 0)),
2593
30), /* doesn't have to be 30 */
2594
990, /* 998 is the SMTP limit */
2595
NULL, 0, wrapflags);
2600
* if not flowed, remove trailing whitespace to reduce
2601
* confusion, since we're sending out as flowed if we
2602
* can. At some future point, we might try
2603
* plugging in a user-option-controlled heuristic
2606
* We also want to fold "> " quotes so we get the
2607
* attributions correct.
2609
if(flow_res && prefix && !strucmp(prefix, "> "))
2610
*(prefix_p = prefix + 1) = '\0';
2612
if(!(wrapflags & GFW_FLOWED)
2614
filters[filtcnt].filter = gf_line_test;
2615
filters[filtcnt++].data = gf_line_test_opt(twsp_strip, NULL);
2617
filters[filtcnt].filter = gf_line_test;
2618
filters[filtcnt++].data = gf_line_test_opt(quote_fold, NULL);
2621
else if(body->subtype){
2622
if(strucmp(body->subtype,"richtext") == 0){
2623
filters[filtcnt].filter = gf_rich2plain;
2624
filters[filtcnt++].data = gf_rich2plain_opt(1);
2626
else if(strucmp(body->subtype,"enriched") == 0){
2627
filters[filtcnt].filter = gf_enriched2plain;
2628
filters[filtcnt++].data = gf_enriched2plain_opt(1);
2630
else if(strucmp(body->subtype,"html") == 0){
2631
filters[filtcnt].filter = gf_html2plain;
2632
filters[filtcnt++].data = gf_html2plain_opt(NULL,
2633
ps_global->ttyo->screen_cols,
2634
NULL, NULL, GFHP_STRIPPED);
2639
if(ps_global->full_header != 2
2640
&& (F_ON(F_ENABLE_SIGDASHES, ps_global)
2641
|| F_ON(F_ENABLE_STRIP_SIGDASHES, ps_global))){
2643
filters[filtcnt].filter = gf_line_test;
2644
filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata);
2647
filters[filtcnt].filter = gf_prefix;
2648
filters[filtcnt++].data = gf_prefix_opt(prefix);
2650
if(wrapflags & GFW_FLOWED || flow_res){
2651
filters[filtcnt].filter = gf_line_test;
2652
filters[filtcnt++].data = gf_line_test_opt(post_quote_space, NULL);
2656
err = detach(stream, msg_no, part_no, partial, &len, pc,
2657
filters[0].filter ? filters : NULL, (partial > 0L) ? FT_PEEK : 0);
2662
if (err != (char *) NULL)
2663
/* TRANSLATORS: The first arg is error text, the %ld is the message number */
2664
q_status_message2(SM_ORDER, 3, 4, "%s: message number %ld",
2665
err, (void *) msg_no);
2668
cancel_busy_cue(-1);
2675
quote_fold(long int linenum, char *line, LT_INS_S **ins, void *local)
2680
for(p = line; *p; p++){
2681
if(isspace((unsigned char) *p)){
2683
ins = gf_line_test_new_ins(ins, p, "", -1);
2695
twsp_strip(long int linenum, char *line, LT_INS_S **ins, void *local)
2697
char *p, *ws = NULL;
2699
for(p = line; *p; p++)
2700
if(isspace((unsigned char) *p)){
2708
ins = gf_line_test_new_ins(ins, ws, "", -(p - ws));
2714
post_quote_space(long int linenum, char *line, LT_INS_S **ins, void *local)
2718
for(p = line; *p; p++)
2720
if(p != line && *p != ' ')
2721
ins = gf_line_test_new_ins(ins, p, " ", 1);
2731
sigdash_strip(long int linenum, char *line, LT_INS_S **ins, void *local)
2734
|| (*line == '-' && *(line+1) == '-'
2735
&& *(line+2) == ' ' && !*(line+3))){
2736
*((int *) local) = 1;
2737
return(2); /* skip this line! */
2744
/*----------------------------------------------------------------------
2745
return the c-client reference name for the given end_body part
2748
body_partno(MAILSTREAM *stream, long int msgno, struct mail_bodystruct *end_body)
2752
(void) pine_mail_fetchstructure(stream, msgno, &body);
2753
return(partno(body, end_body));
2757
/*----------------------------------------------------------------------
2758
return the c-client reference name for the given end_body part
2761
partno(struct mail_bodystruct *body, struct mail_bodystruct *end_body)
2765
char tmp[64], *p = NULL;
2767
if(body && body->type == TYPEMULTIPART) {
2768
part = body->nested.part; /* first body part */
2770
do { /* for each part */
2772
if(&part->body == end_body || (p = partno(&part->body, end_body))){
2773
snprintf(tmp, sizeof(tmp), "%d%s%.*s", num, (p) ? "." : "",
2774
sizeof(tmp)-10, (p) ? p : "");
2775
tmp[sizeof(tmp)-1] = '\0';
2777
fs_give((void **)&p);
2779
return(cpystr(tmp));
2781
} while (part = part->next); /* until done */
2785
else if(body && body->type == TYPEMESSAGE && body->subtype
2786
&& !strucmp(body->subtype, "rfc822")){
2787
return(partno(body->nested.msg->body, end_body));
2790
return((body == end_body) ? cpystr("1") : NULL);
2794
/*----------------------------------------------------------------------
2795
Fill in the contents of each body part
2797
Args: stream -- Stream the message is on
2798
msgno -- Message number the body structure is for
2799
section -- body section associated with body pointer
2800
body -- Body pointer to fill in
2802
Result: 1 if all went OK, 0 if there was a problem
2804
This function copies the contents from an original message/body to
2805
a new message/body. It recurses down all multipart levels.
2807
If one or more part (but not all) can't be fetched, a status message
2811
fetch_contents(MAILSTREAM *stream, long int msgno, char *section, struct mail_bodystruct *body)
2817
body->id = generate_message_id();
2819
if(body->type == TYPEMULTIPART){
2820
char subsection[256], *subp;
2821
int n, last_one = 10; /* remember worst case */
2822
PART *part = body->nested.part;
2824
if(!(part = body->nested.part))
2828
if(section && *section){
2830
n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
2838
snprintf(subp, sizeof(subsection)-(subp-subsection), "%d", n++);
2839
subsection[sizeof(subsection)-1] = '\0';
2840
got_one = fetch_contents(stream, msgno, subsection, &part->body);
2841
last_one = MIN(last_one, got_one);
2843
while(part = part->next);
2848
if(body->contents.text.data)
2849
return(1); /* already taken care of... */
2851
if(body->type == TYPEMESSAGE){
2852
if(body->subtype && strucmp(body->subtype,"external-body")){
2854
* the idea here is to fetch everything into storage objects
2856
body->contents.text.data = (void *) so_get(PART_SO_TYPE, NULL,
2858
#if defined(DOS) && !defined(WIN32)
2859
if(body->contents.text.data){
2860
/* fetch text to disk */
2861
mail_parameters(stream, SET_GETS, (void *)dos_gets);
2862
append_file =(FILE *)so_text((STORE_S *)body->contents.text.data);
2864
if(mail_fetchbody(stream, msgno, section, &body->size.bytes)){
2865
so_release((STORE_S *)body->contents.text.data);
2869
q_status_message1(SM_ORDER | SM_DING, 3, 3,
2870
_("Error fetching part %s"), section);
2872
/* next time body may stay in core */
2873
mail_parameters(stream, SET_GETS, (void *)NULL);
2875
mail_gc(stream, GC_TEXTS);
2878
if(body->contents.text.data
2879
&& (tp = pine_mail_fetch_body(stream, msgno, section,
2880
&body->size.bytes, NIL))){
2881
so_truncate((STORE_S *)body->contents.text.data,
2882
body->size.bytes + 2048);
2883
so_nputs((STORE_S *)body->contents.text.data, tp,
2889
q_status_message1(SM_ORDER | SM_DING, 3, 3,
2890
_("Error fetching part %s"), section);
2896
* the idea here is to fetch everything into storage objects
2897
* so, grab one, then fetch the body part
2899
body->contents.text.data = (void *)so_get(PART_SO_TYPE,NULL,EDIT_ACCESS);
2900
#if defined(DOS) && !defined(WIN32)
2901
if(body->contents.text.data){
2902
/* write fetched text to disk */
2903
mail_parameters(stream, SET_GETS, (void *)dos_gets);
2904
append_file = (FILE *)so_text((STORE_S *)body->contents.text.data);
2905
if(mail_fetchbody(stream, msgno, section, &body->size.bytes)){
2906
so_release((STORE_S *)body->contents.text.data);
2910
q_status_message1(SM_ORDER | SM_DING, 3, 3,
2911
_("Error fetching part %s"), section);
2913
/* next time body may stay in core */
2914
mail_parameters(stream, SET_GETS, (void *)NULL);
2916
mail_gc(stream, GC_TEXTS);
2919
if(body->contents.text.data
2920
&& (tp=pine_mail_fetch_body(stream, msgno, section,
2921
&body->size.bytes, NIL))){
2922
so_truncate((STORE_S *)body->contents.text.data,
2923
body->size.bytes + 2048);
2924
so_nputs((STORE_S *)body->contents.text.data, tp,
2930
q_status_message1(SM_ORDER | SM_DING, 3, 3,
2931
_("Error fetching part %s"), section);
2938
/*----------------------------------------------------------------------
2939
Copy the body structure
2941
Args: new_body -- Pointer to already allocated body, or NULL, if none
2942
old_body -- The Body to copy
2945
This is traverses the body structure recursively copying all elements.
2946
The new_body parameter can be NULL in which case a new body is
2947
allocated. Alternatively it can point to an already allocated body
2948
structure. This is used when copying body parts since a PART includes a
2949
BODY. The contents fields are *not* filled in.
2953
copy_body(struct mail_bodystruct *new_body, struct mail_bodystruct *old_body)
2955
if(old_body == NULL)
2958
if(new_body == NULL)
2959
new_body = mail_newbody();
2961
new_body->type = old_body->type;
2962
new_body->encoding = old_body->encoding;
2964
if(old_body->subtype)
2965
new_body->subtype = cpystr(old_body->subtype);
2967
new_body->parameter = copy_parameters(old_body->parameter);
2970
new_body->id = cpystr(old_body->id);
2972
if(old_body->description)
2973
new_body->description = cpystr(old_body->description);
2975
if(old_body->disposition.type)
2976
new_body->disposition.type = cpystr(old_body->disposition.type);
2978
new_body->disposition.parameter
2979
= copy_parameters(old_body->disposition.parameter);
2981
new_body->size = old_body->size;
2983
if(new_body->type == TYPEMESSAGE
2984
&& new_body->subtype && !strucmp(new_body->subtype, "rfc822")){
2985
new_body->nested.msg = mail_newmsg();
2986
new_body->nested.msg->body
2987
= copy_body(NULL, old_body->nested.msg->body);
2989
else if(new_body->type == TYPEMULTIPART) {
2990
PART **new_partp, *old_part;
2992
new_partp = &new_body->nested.part;
2993
for(old_part = old_body->nested.part;
2995
old_part = old_part->next){
2996
*new_partp = mail_newbody_part();
2997
copy_body(&(*new_partp)->body, &old_part->body);
2998
new_partp = &(*new_partp)->next;
3006
/*----------------------------------------------------------------------
3007
Copy the MIME parameter list
3009
Allocates storage for new part, and returns pointer to new paramter
3010
list. If old_p is NULL, NULL is returned.
3014
copy_parameters(struct mail_body_parameter *old_p)
3016
PARAMETER *new_p, *p1, *p2;
3019
return((PARAMETER *)NULL);
3022
for(p1 = old_p; p1 != NULL; p1 = p1->next) {
3024
p2 = mail_newbody_parameter();
3027
p2->next = mail_newbody_parameter();
3030
p2->attribute = cpystr(p1->attribute);
3031
p2->value = cpystr(p1->value);
3037
/*----------------------------------------------------------------------
3038
Make a complete copy of an envelope and all it's fields
3040
Args: e -- the envelope to copy
3042
Result: returns the new envelope, or NULL, if the given envelope was NULL
3047
copy_envelope(register ENVELOPE *e)
3049
register ENVELOPE *e2;
3054
e2 = mail_newenvelope();
3055
e2->remail = e->remail ? cpystr(e->remail) : NULL;
3056
e2->return_path = e->return_path ? rfc822_cpy_adr(e->return_path) : NULL;
3057
e2->date = e->date ? (unsigned char *)cpystr((char *) e->date)
3059
e2->from = e->from ? rfc822_cpy_adr(e->from) : NULL;
3060
e2->sender = e->sender ? rfc822_cpy_adr(e->sender) : NULL;
3061
e2->reply_to = e->reply_to ? rfc822_cpy_adr(e->reply_to) : NULL;
3062
e2->subject = e->subject ? cpystr(e->subject) : NULL;
3063
e2->to = e->to ? rfc822_cpy_adr(e->to) : NULL;
3064
e2->cc = e->cc ? rfc822_cpy_adr(e->cc) : NULL;
3065
e2->bcc = e->bcc ? rfc822_cpy_adr(e->bcc) : NULL;
3066
e2->in_reply_to = e->in_reply_to ? cpystr(e->in_reply_to) : NULL;
3067
e2->newsgroups = e->newsgroups ? cpystr(e->newsgroups) : NULL;
3068
e2->message_id = e->message_id ? cpystr(e->message_id) : NULL;
3069
e2->references = e->references ? cpystr(e->references) : NULL;
3070
e2->followup_to = e->followup_to ? cpystr(e->references) : NULL;
3075
/*----------------------------------------------------------------------
3076
Generate the "In-reply-to" text from message header
3078
Args: message -- Envelope of original message
3080
Result: returns an alloc'd string or NULL if there is a problem
3083
reply_in_reply_to(ENVELOPE *env)
3085
return((env && env->message_id) ? cpystr(env->message_id) : NULL);
3089
/*----------------------------------------------------------------------
3090
Generate a unique message id string.
3092
Args: ps -- The usual pine structure
3094
Result: Alloc'd unique string is returned
3096
Uniqueness is gaurenteed by using the host name, process id, date to the
3097
second and a single unique character
3098
*----------------------------------------------------------------------*/
3100
generate_message_id(void)
3102
static short osec = 0, cnt = 0;
3107
char *hostpart = NULL;
3109
now = time((time_t *)0);
3110
now_x = localtime(&now);
3112
if(now_x->tm_sec == osec)
3116
osec = now_x->tm_sec;
3119
hostpart = F_ON(F_ROT13_MESSAGE_ID, ps_global)
3120
? rot13(ps_global->hostname)
3121
: cpystr(ps_global->hostname);
3124
hostpart = cpystr("huh");
3126
snprintf(idbuf, sizeof(idbuf), "<alpine.%.4s.%.20s.%02d%02d%02d%02d%02d%02d%X.%d@%.50s>",
3127
SYSTYPE, ALPINE_VERSION, (now_x->tm_year) % 100, now_x->tm_mon + 1,
3128
now_x->tm_mday, now_x->tm_hour, now_x->tm_min, now_x->tm_sec,
3129
cnt, getpid(), hostpart);
3130
idbuf[sizeof(idbuf)-1] = '\0';
3135
fs_give((void **) &hostpart);
3144
char byte, cap, *p, *ret = NULL;
3147
ret = (char *) fs_get((strlen(src)+1) * sizeof(char));
3149
while(byte = *src++){
3152
*p++ = ((byte >= 'A') && (byte <= 'Z')
3153
? ((byte - 'A' + 13) % 26 + 'A') : byte) | cap;
3163
/*----------------------------------------------------------------------
3164
Return the first true address pointer (modulo group syntax allowance)
3166
Args: addr -- Address list
3168
Result: First real address pointer, or NULL
3169
----------------------------------------------------------------------*/
3171
first_addr(struct mail_address *addr)
3173
while(addr && !addr->host)
3180
/*----------------------------------------------------------------------
3181
lit -- this is the source
3182
prenewlines -- prefix the file contents with this many newlines
3183
postnewlines -- postfix the file contents with this many newlines
3184
is_sig -- this is a signature (not a template)
3185
decode_constants -- change C-style constants into their values
3188
get_signature_lit(char *lit, int prenewlines, int postnewlines, int is_sig, int decode_constants)
3193
* Should make this smart enough not to do the copying and double
3194
* allocation of space.
3197
char *tmplit = NULL, *p, *q, *d, save;
3200
if(decode_constants){
3201
tmplit = (char *) fs_get((strlen(lit)+1) * sizeof(char));
3203
cstring_to_string(lit, tmplit);
3206
tmplit = cpystr(lit);
3208
len = strlen(tmplit) + 5 + (prenewlines+postnewlines) * strlen(NEWLINE);
3209
sig = (char *) fs_get((len+1) * sizeof(char));
3210
memset(sig, 0, len+1);
3212
while(prenewlines--)
3213
sstrncpy(&d, NEWLINE, len-(d-sig));
3215
if(is_sig && F_ON(F_ENABLE_SIGDASHES, ps_global) &&
3216
!sigdashes_are_present(tmplit)){
3217
sstrncpy(&d, SIGDASHES, len-(d-sig));
3218
sstrncpy(&d, NEWLINE, len-(d-sig));
3226
q = strpbrk(p, "\n\r");
3233
* Strip trailing space if we are doing a signature and
3234
* this line is not sigdashes.
3236
if(is_sig && strcmp(p, SIGDASHES))
3237
removing_trailing_white_space(p);
3239
while((d-sig) <= len && (*d = *p++) != '\0')
3252
while(postnewlines--)
3253
sstrncpy(&d, NEWLINE, len-(d-sig));
3261
fs_give((void **) &tmplit);
3269
sigdashes_are_present(char *sig)
3273
p = srchstr(sig, SIGDASHES);
3274
while(p && !((p == sig || (p[-1] == '\n' || p[-1] == '\r')) &&
3275
(p[3] == '\0' || p[3] == '\n' || p[3] == '\r')))
3276
p = srchstr(p+1, SIGDASHES);
3282
/*----------------------------------------------------------------------
3283
Acquire the pinerc defined signature file pathname
3287
signature_path(char *sname, char *sbuf, size_t len)
3290
if(sname && *sname){
3291
size_t spl = strlen(sname);
3292
if(IS_REMOTE(sname)){
3294
strncpy(sbuf, sname, len-1);
3296
else if(is_absolute_path(sname)){
3297
strncpy(sbuf, sname, len-1);
3299
fnexpand(sbuf, len);
3301
else if(ps_global->VAR_OPER_DIR){
3302
if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
3303
build_path(sbuf, ps_global->VAR_OPER_DIR, sname, len);
3306
char *lc = last_cmpnt(ps_global->pinerc);
3310
strncpy(sbuf,ps_global->pinerc,MIN(len-1,lc-ps_global->pinerc));
3311
sbuf[MIN(len-1,lc-ps_global->pinerc)] = '\0';
3314
strncat(sbuf, sname, MAX(len-1-strlen(sbuf), 0));
3318
return(*sbuf ? sbuf : NULL);
3323
read_remote_sigfile(char *name)
3330
dprint((7, "read_remote_sigfile \"%s\"\n", name ? name : "?"));
3333
* We could parse the name here to find what type it is. So far we
3334
* only have type RemImap.
3336
rd = rd_create_remote(RemImap, name, (void *)REMOTE_SIG_SUBTYPE,
3337
NULL, _("Error: "), _("Can't fetch remote configuration."));
3341
try_cache = rd_read_metadata(rd);
3343
if(rd->access == MaybeRorW){
3344
if(rd->read_status == 'R')
3345
rd->access = ReadOnly;
3347
rd->access = ReadWrite;
3350
if(rd->access != NoExists){
3352
rd_check_remvalid(rd, 1L);
3355
* If the cached info says it is readonly but
3356
* it looks like it's been fixed now, change it to readwrite.
3358
if(rd->read_status == 'R'){
3360
* We go to this trouble since readonly sigfiles
3361
* are likely a mistake. They are usually supposed to be
3362
* readwrite so we open it and check if it's been fixed.
3364
rd_check_readonly_access(rd);
3365
if(rd->read_status == 'W'){
3366
rd->access = ReadWrite;
3367
rd->flags |= REM_OUTOFDATE;
3370
rd->access = ReadOnly;
3373
if(rd->flags & REM_OUTOFDATE){
3374
if(rd_update_local(rd) != 0){
3377
"read_remote_sigfile: rd_update_local failed\n"));
3379
* Don't give up altogether. We still may be
3380
* able to use a cached copy.
3385
"%s: copied remote to local (%ld)\n",
3386
rd->rn ? rd->rn : "?", (long)rd->last_use));
3390
if(rd->access == ReadWrite)
3391
rd->flags |= DO_REMTRIM;
3394
/* If we couldn't get to remote folder, try using the cached copy */
3395
if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){
3397
rd->access = ReadOnly;
3398
rd->flags |= USE_OLD_CACHE;
3399
q_status_message(SM_ORDER, 3, 4,
3400
"Can't contact remote sig server, using cached copy");
3402
"Can't open remote sigfile %s, using local cached copy %s readonly\n",
3403
rd->rn ? rd->rn : "?",
3404
rd->lf ? rd->lf : "?"));
3407
rd->flags &= ~DO_REMTRIM;
3412
file = read_file(rd->lf, 0);
3416
rd_close_remdata(&rd);
3422
/*----------------------------------------------------------------------
3423
Build the body for the multipart/alternative part
3429
----------------------------------------------------------------------*/
3431
forward_multi_alt(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
3432
long int msgno, char *sect_prefix, void *msgtext, gf_io_t pc, int flags)
3437
char *new_charset = NULL;
3440
/* Pick out the interesting text piece */
3441
for(part = orig_body->nested.part, partnum = 1;
3443
part = part->next, partnum++)
3444
if((!part->body.type || part->body.type == TYPETEXT)
3445
&& (!part->body.subtype
3446
|| !strucmp(part->body.subtype, "plain")))
3450
* IF something's interesting insert it
3451
* AND forget the rest of the multipart
3454
body = mail_newbody();
3455
body->type = TYPETEXT;
3456
body->contents.text.data = msgtext;
3458
/* record character set, flowing, etc */
3459
body->parameter = copy_parameters(part->body.parameter);
3460
body->size.bytes = part->body.size.bytes;
3462
if(!(flags & FWD_ANON)){
3463
forward_delimiter(pc);
3464
reply_forward_header(stream, msgno, sect_prefix, env, pc, "");
3467
snprintf(tmp_buf, sizeof(tmp_buf), "%.*s%s%s%d",
3468
sizeof(tmp_buf)/2, sect_prefix ? sect_prefix : "",
3469
sect_prefix ? "." : "", flags & FWD_NESTED ? "1." : "",
3471
tmp_buf[sizeof(tmp_buf)-1] = '\0';
3472
get_body_part_text(stream, body, msgno, tmp_buf, 0L, pc, NULL, &new_charset);
3475
* get_body_part_text translated the data to a new charset.
3476
* We need to record that fact in body.
3479
fix_charset_parameter(body, new_charset);
3482
q_status_message(SM_ORDER | SM_DING, 3, 3,
3483
"No suitable part found. Forwarding as attachment");
3490
* Change existing charset parameter to new value.
3493
fix_charset_parameter(struct mail_bodystruct *body, char *new_charset)
3500
for(pm = body->parameter; pm; pm = pm->next)
3501
if(pm->attribute && !strucmp(pm->attribute, "charset"))
3506
fs_give((void **) &pm->value);
3509
pm->value = cpystr(new_charset);
3515
reply_append_addr(struct mail_address **dest, struct mail_address *src)
3517
for( ; *dest; dest = &(*dest)->next)