From 094ca96844842928810f14844413109fc6cdd890 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 3 Feb 2013 00:59:38 -0700 Subject: Initial Alpine Version --- pith/reply.c | 3575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3575 insertions(+) create mode 100644 pith/reply.c (limited to 'pith/reply.c') diff --git a/pith/reply.c b/pith/reply.c new file mode 100644 index 00000000..03468af3 --- /dev/null +++ b/pith/reply.c @@ -0,0 +1,3575 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2008 University of Washington + * Copyright 2013 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + */ + +#include "../pith/headers.h" +#include "../pith/reply.h" +#include "../pith/send.h" +#include "../pith/init.h" +#include "../pith/state.h" +#include "../pith/conf.h" +#include "../pith/remote.h" +#include "../pith/status.h" +#include "../pith/mailview.h" +#include "../pith/filter.h" +#include "../pith/newmail.h" +#include "../pith/bldaddr.h" +#include "../pith/mailindx.h" +#include "../pith/mimedesc.h" +#include "../pith/detach.h" +#include "../pith/help.h" +#include "../pith/pipe.h" +#include "../pith/addrstring.h" +#include "../pith/news.h" +#include "../pith/util.h" +#include "../pith/pattern.h" +#include "../pith/detoken.h" +#include "../pith/stream.h" +#include "../pith/busy.h" +#include "../pith/readfile.h" +#include "../pith/text.h" +#include "../pith/list.h" +#include "../pith/ablookup.h" +#include "../pith/mailcmd.h" +#include "../pith/margin.h" + + +/* + * Internal prototypes + */ +void bounce_mask_header(char **, char *); + + +int (*pith_opt_replyto_prompt)(void); +int (*pith_opt_reply_to_all_prompt)(int *); + + +/* + * standard type of storage object used for body parts... + */ +#define PART_SO_TYPE CharStar + + +char *(*pith_opt_user_agent_prefix)(void); + + +/* + * reply_harvest - + * + * Returns: 1 if addresses successfully copied + * 0 on user cancel or error + * + * Input flags: + * RSF_FORCE_REPLY_TO + * RSF_QUERY_REPLY_ALL + * RSF_FORCE_REPLY_ALL + * + * Output flags: + * RSF_FORCE_REPLY_ALL + * + */ +int +reply_harvest(struct pine *ps, long int msgno, char *section, ENVELOPE *env, + struct mail_address **saved_from, struct mail_address **saved_to, + struct mail_address **saved_cc, struct mail_address **saved_resent, + int *flags) +{ + ADDRESS *ap, *ap2, *rep_address; + int ret = 0, sniff_resent = 0; + char *rep_field; + + /* + * If Reply-To is same as From just treat it like it was From. + * Otherwise, always use the reply-to if we're replying to more + * than one msg or say ok to using it, even if it's us. + * If there's no reply-to or it's the same as the from, assume + * that the user doesn't want to reply to himself, unless there's + * nobody else. + */ + if(env->reply_to && !addr_lists_same(env->reply_to, env->from) + && (F_ON(F_AUTO_REPLY_TO, ps_global) + || ((*flags) & RSF_FORCE_REPLY_TO) + || (pith_opt_replyto_prompt && (*pith_opt_replyto_prompt)() == 'y'))){ + rep_field = "reply-to"; + rep_address = env->reply_to; + } + else{ + rep_field = "From"; + rep_address = env->from; + } + + ap = reply_cp_addr(ps, msgno, section, rep_field, *saved_from, + (ADDRESS *) NULL, rep_address, RCA_NOT_US); + + if(ret == 'x') { + cmd_cancelled("Reply"); + return(0); + } + + reply_append_addr(saved_from, ap); + + /*--------- check for other recipients ---------*/ + if(((*flags) & (RSF_FORCE_REPLY_ALL | RSF_QUERY_REPLY_ALL))){ + + if((ap = reply_cp_addr(ps, msgno, section, "To", *saved_to, + *saved_from, env->to, RCA_NOT_US)) != NULL) + reply_append_addr(saved_to, ap); + + if((ap = reply_cp_addr(ps, msgno, section, "Cc", *saved_cc, + *saved_from, env->cc, RCA_NOT_US)) != NULL) + reply_append_addr(saved_cc, ap); + + /* + * In these cases, we either need to look at the resent headers + * to include in the reply-to-all, or to decide whether or not + * we need to ask the reply-to-all question. + */ + if(((*flags) & RSF_FORCE_REPLY_ALL) + || (((*flags) & RSF_QUERY_REPLY_ALL) + && ((!(*saved_from) && !(*saved_cc)) + || (*saved_from && !(*saved_to) && !(*saved_cc))))){ + + sniff_resent++; + if((ap2 = reply_resent(ps, msgno, section)) != NULL){ + /* + * look for bogus addr entries and replace + */ + if((ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent, + *saved_from, ap2, RCA_NOT_US)) != NULL) + + reply_append_addr(saved_resent, ap); + + mail_free_address(&ap2); + } + } + + /* + * It makes sense to ask reply-to-all now. + */ + if(((*flags) & RSF_QUERY_REPLY_ALL) + && ((*saved_from && (*saved_to || *saved_cc || *saved_resent)) + || (*saved_cc || *saved_resent))){ + *flags &= ~RSF_QUERY_REPLY_ALL; + if(pith_opt_reply_to_all_prompt + && (*pith_opt_reply_to_all_prompt)(flags) < 0){ + cmd_cancelled("Reply"); + return(0); + } + } + + /* + * If we just answered yes to the reply-to-all question and + * we still haven't collected the resent headers, do so now. + */ + if(((*flags) & RSF_FORCE_REPLY_ALL) && !sniff_resent + && (ap2 = reply_resent(ps, msgno, section))){ + /* + * look for bogus addr entries and replace + */ + if((ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent, + *saved_from, ap2, RCA_NOT_US)) != NULL) + reply_append_addr(saved_resent, ap); + + mail_free_address(&ap2); + } + } + + return(1); +} + + +/*---------------------------------------------------------------------- + Return a pointer to a copy of the given address list + filtering out those already in the "mask" lists and ourself. + +Args: mask1 -- Don't copy if in this list + mask2 -- or if in this list + source -- List to be copied + us_too -- Don't filter out ourself. + flags -- RCA_NOT_US copy all addrs except for our own + RCA_ALL copy all addrs, including our own + RCA_ONLY_US copy only addrs that are our own + + ---*/ +ADDRESS * +reply_cp_addr(struct pine *ps, long int msgno, char *section, char *field, + struct mail_address *mask1, struct mail_address *mask2, + struct mail_address *source, int flags) +{ + ADDRESS *tmp1, *tmp2, *ret = NULL, **ret_tail; + + /* can only choose one of these flags values */ + assert(!((flags & RCA_ALL && flags & RCA_ONLY_US) + || (flags & RCA_ALL && flags & RCA_NOT_US) + || (flags & RCA_ONLY_US && flags & RCA_NOT_US))); + + for(tmp1 = source; msgno && tmp1; tmp1 = tmp1->next) + if(tmp1->host && tmp1->host[0] == '.'){ + char *h, *fields[2]; + + fields[0] = field; + fields[1] = NULL; + if((h = pine_fetchheader_lines(ps ? ps->mail_stream : NULL, + msgno, section, fields)) != NULL){ + char *p, fname[32]; + int q; + + q = strlen(h); + + strncpy(fname, field, sizeof(fname)-2); + fname[sizeof(fname)-2] = '\0'; + strncat(fname, ":", sizeof(fname)-strlen(fname)-1); + fname[sizeof(fname)-1] = '\0'; + + for(p = h; (p = strstr(p, fname)) != NULL; ) + rplstr(p, q-(p-h), strlen(fname), ""); /* strip field strings */ + + sqznewlines(h); /* blat out CR's & LF's */ + for(p = h; (p = strchr(p, TAB)) != NULL; ) + *p++ = ' '; /* turn TABs to whitespace */ + + if(*h){ + long l; + size_t ll; + + ret = (ADDRESS *) fs_get(sizeof(ADDRESS)); + memset(ret, 0, sizeof(ADDRESS)); + + /* get rid of leading white space */ + for(p = h; *p == SPACE; p++) + ; + + if(p != h){ + memmove(h, p, l = strlen(p)); + h[l] = '\0'; + } + + /* base64 armor plate the gunk to protect against + * c-client quoting in output. + */ + p = (char *) rfc822_binary(h, strlen(h), + (unsigned long *) &l); + sqznewlines(p); + fs_give((void **) &h); + /* + * Seems like the 4 ought to be a 2, but I'll leave it + * to be safe, in case something else adds 2 chars later. + */ + ll = strlen(p) + 4; + ret->mailbox = (char *) fs_get(ll * sizeof(char)); + snprintf(ret->mailbox, ll, "&%s", p); + ret->mailbox[ll-1] = '\0'; + fs_give((void **) &p); + ret->host = cpystr(RAWFIELD); + } + } + + return(ret); + } + + ret_tail = &ret; + for(source = first_addr(source); source; source = source->next){ + for(tmp1 = first_addr(mask1); tmp1; tmp1 = tmp1->next) + if(address_is_same(source, tmp1)) /* it is in mask1, skip it */ + break; + + for(tmp2 = first_addr(mask2); !tmp1 && tmp2; tmp2 = tmp2->next) + if(address_is_same(source, tmp2)) /* it is in mask2, skip it */ + break; + + /* + * If there's no match in masks and this address satisfies the + * flags requirement, copy it. + */ + if(!tmp1 && !tmp2 /* no mask match */ + && ((flags & RCA_ALL) /* including everybody */ + || (flags & RCA_ONLY_US && address_is_us(source, ps)) + || (flags & RCA_NOT_US && !address_is_us(source, ps)))){ + tmp1 = source->next; + source->next = NULL; /* only copy one addr! */ + *ret_tail = rfc822_cpy_adr(source); + ret_tail = &(*ret_tail)->next; + source->next = tmp1; /* restore rest of list */ + } + } + + return(ret); +} + + +ACTION_S * +set_role_from_msg(struct pine *ps, long int rflags, long int msgno, char *section) +{ + ACTION_S *role = NULL; + PAT_S *pat = NULL; + SEARCHSET *ss = NULL; + PAT_STATE pstate; + + if(!nonempty_patterns(rflags, &pstate)) + return(role); + + if(msgno > 0L){ + ss = mail_newsearchset(); + ss->first = ss->last = (unsigned long)msgno; + } + + /* Go through the possible roles one at a time until we get a match. */ + pat = first_pattern(&pstate); + + /* calculate this message's score if needed */ + if(ss && pat && scores_are_used(SCOREUSE_GET) & SCOREUSE_ROLES && + get_msg_score(ps->mail_stream, msgno) == SCORE_UNDEF) + (void)calculate_some_scores(ps->mail_stream, ss, 0); + + while(!role && pat){ + if(match_pattern(pat->patgrp, ps->mail_stream, ss, section, + get_msg_score, SE_NOSERVER|SE_NOPREFETCH)){ + if(!pat->action || pat->action->bogus) + break; + + role = pat->action; + } + else + pat = next_pattern(&pstate); + } + + if(ss) + mail_free_searchset(&ss); + + return(role); +} + + +/* + * reply_seed - fill in reply header + * + */ +void +reply_seed(struct pine *ps, ENVELOPE *outgoing, ENVELOPE *env, + struct mail_address *saved_from, struct mail_address *saved_to, + struct mail_address *saved_cc, struct mail_address *saved_resent, + char **fcc, int replytoall, char **errmsg) +{ + ADDRESS **to_tail, **cc_tail; + + to_tail = &outgoing->to; + cc_tail = &outgoing->cc; + + if(saved_from){ + /* Put Reply-To or From in To. */ + *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to, + (ADDRESS *) NULL, saved_from, RCA_ALL); + /* and the rest in cc */ + if(replytoall){ + *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, + outgoing->to, saved_to, RCA_ALL); + while(*cc_tail) /* stay on last address */ + cc_tail = &(*cc_tail)->next; + + *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, + outgoing->to, saved_cc, RCA_ALL); + while(*cc_tail) + cc_tail = &(*cc_tail)->next; + + *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, + outgoing->to, saved_resent, RCA_ALL); + } + } + else if(saved_to){ + /* No From (maybe from us), put To in To. */ + *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to, + (ADDRESS *)NULL, saved_to, RCA_ALL); + /* and the rest in cc */ + if(replytoall){ + *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, + outgoing->to, saved_cc, RCA_ALL); + while(*cc_tail) + cc_tail = &(*cc_tail)->next; + + *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc, + outgoing->to, saved_resent, RCA_ALL); + } + } + else{ + /* No From or To, put everything else in To if replytoall, */ + if(replytoall){ + *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to, + (ADDRESS *) NULL, saved_cc, RCA_ALL); + while(*to_tail) + to_tail = &(*to_tail)->next; + + *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to, + (ADDRESS *) NULL, saved_resent, RCA_ALL); + } + /* else, reply to original From which must be us */ + else{ + /* + * Put self in To if in original From. + */ + if(!outgoing->newsgroups) + *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to, + (ADDRESS *) NULL, env->from, RCA_ALL); + } + } + + /* add any missing personal data */ + reply_fish_personal(outgoing, env); + + /* get fcc */ + if(fcc && outgoing->to && outgoing->to->host[0] != '.'){ + *fcc = get_fcc_based_on_to(outgoing->to); + } + else if(fcc && outgoing->newsgroups){ + char *newsgroups_returned = NULL; + int rv; + + rv = news_grouper(outgoing->newsgroups, &newsgroups_returned, errmsg, fcc, NULL); + if(rv != -1 && + strcmp(outgoing->newsgroups, newsgroups_returned)){ + fs_give((void **)&outgoing->newsgroups); + outgoing->newsgroups = newsgroups_returned; + } + else + fs_give((void **) &newsgroups_returned); + } +} + + +/*---------------------------------------------------------------------- + Test the given address lists for equivalence + +Args: x -- First address list for comparison + y -- Second address for comparison + + ---*/ +int +addr_lists_same(struct mail_address *x, struct mail_address *y) +{ + for(x = first_addr(x), y = first_addr(y); + x && y; + x = first_addr(x->next), y = first_addr(y->next)){ + if(!address_is_same(x, y)) + return(0); + } + + return(!x && !y); /* true if ran off both lists */ +} + + +/*---------------------------------------------------------------------- + Test the given address against those in the given envelope's to, cc + +Args: addr -- address for comparison + env -- envelope to compare against + + ---*/ +int +addr_in_env(struct mail_address *addr, ENVELOPE *env) +{ + ADDRESS *ap; + + for(ap = env ? env->to : NULL; ap; ap = ap->next) + if(address_is_same(addr, ap)) + return(1); + + for(ap = env ? env->cc : NULL; ap; ap = ap->next) + if(address_is_same(addr, ap)) + return(1); + + return(0); /* not found! */ +} + + +/*---------------------------------------------------------------------- + Add missing personal info dest from src envelope + +Args: dest -- envelope to add personal info to + src -- envelope to get personal info from + +NOTE: This is just kind of a courtesy function. It's really not adding + anything needed to get the mail thru, but it is nice for the user + under some odd circumstances. + ---*/ +void +reply_fish_personal(ENVELOPE *dest, ENVELOPE *src) +{ + ADDRESS *da, *sa; + + for(da = dest ? dest->to : NULL; da; da = da->next){ + if(da->personal && !da->personal[0]) + fs_give((void **)&da->personal); + + for(sa = src ? src->to : NULL; sa && !da->personal ; sa = sa->next) + if(address_is_same(da, sa) && sa->personal && sa->personal[0]) + da->personal = cpystr(sa->personal); + + for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next) + if(address_is_same(da, sa) && sa->personal && sa->personal[0]) + da->personal = cpystr(sa->personal); + } + + for(da = dest ? dest->cc : NULL; da; da = da->next){ + if(da->personal && !da->personal[0]) + fs_give((void **)&da->personal); + + for(sa = src ? src->to : NULL; sa && !da->personal; sa = sa->next) + if(address_is_same(da, sa) && sa->personal && sa->personal[0]) + da->personal = cpystr(sa->personal); + + for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next) + if(address_is_same(da, sa) && sa->personal && sa->personal[0]) + da->personal = cpystr(sa->personal); + } +} + + +/*---------------------------------------------------------------------- + Given a header field and envelope, build "References: " header data + +Args: + +Returns: + ---*/ +char * +reply_build_refs(ENVELOPE *env) +{ + int len, id_len, first_ref_len = 0, foldslop; + char *p, *refs = NULL, *h = env->references; + char *first_ref = NULL, *tail_refs = NULL; + + + if(!(env->message_id && (id_len = strlen(env->message_id)))) + return(NULL); + + if(h){ + /* + * The length we have to work with doesn't seem to appear in any + * standards. Steve Jones says that in comp.news discussions he + * has seen 1024 as the longest length of a header value. + * In the inn news source we find MAXHEADERSIZE = 1024. It appears + * that is the maximum length of the header value, including + * newlines for folded lines (that is, the newlines are counted). + * We'll be conservative and figure every reference will take up a + * line of its own when we fold. We'll also count 2 for CRLF instead + * of just one for LF just to be safe. hubert 2001-jan + * J.B. Moreno says "The server limit is + * more commonly encountered at 999/1000 bytes [...]". So we'll + * back off to 999 instead of 1024. + */ +#define MAXHEADERSIZE (999) + + /* count the total number of potential folds, max of 2 bytes each */ + for(foldslop = 2, p = h; (p = strstr(p+1, "> <")); ) + foldslop += 2; + + if((len=strlen(h)) + 1+id_len + foldslop >= MAXHEADERSIZE + && (p = strstr(h, "> <"))){ + /* + * If the references line is so long that we are going to have + * to delete some of the references, delete the 2nd, 3rd, ... + * We don't want to delete the first message in the thread. + */ + p[1] = '\0'; + first_ref = cpystr(h); + first_ref_len = strlen(first_ref)+1; /* len includes space */ + p[1] = ' '; + tail_refs = p+2; + /* get rid of 2nd, 3rd, ... until it fits */ + while((len=strlen(tail_refs)) + first_ref_len + 1+id_len + + foldslop >= MAXHEADERSIZE + && (p = strstr(tail_refs, "> <"))){ + tail_refs = p + 2; + foldslop -= 2; + } + + /* + * If the individual references are seriously long, somebody + * is messing with us and we don't care if it works right. + */ + if((len=strlen(tail_refs)) + first_ref_len + 1+id_len + + foldslop >= MAXHEADERSIZE){ + first_ref_len = len = 0; + tail_refs = NULL; + if(first_ref) + fs_give((void **)&first_ref); + } + } + else + tail_refs = h; + + refs = (char *)fs_get((first_ref_len + 1+id_len + len + 1) * + sizeof(char)); + snprintf(refs, first_ref_len + 1+id_len + len + 1, "%s%s%s%s%s", + first_ref ? first_ref : "", + first_ref ? " " : "", + tail_refs ? tail_refs : "", + tail_refs ? " " : "", + env->message_id); + refs[first_ref_len + 1+id_len + len] = '\0'; + } + + if(!refs && id_len) + refs = cpystr(env->message_id); + + if(first_ref) + fs_give((void **)&first_ref); + + return(refs); +} + + + +/*---------------------------------------------------------------------- + Snoop for any Resent-* headers, and return an ADDRESS list + +Args: stream -- + msgno -- + +Returns: either NULL if no Resent-* or parsed ADDRESS struct list + ---*/ +ADDRESS * +reply_resent(struct pine *pine_state, long int msgno, char *section) +{ +#define RESENTFROM 0 +#define RESENTTO 1 +#define RESENTCC 2 + ADDRESS *rlist = NULL, **a, **b; + char *hdrs, *values[RESENTCC+1]; + int i; + static char *fields[] = {"Resent-From", "Resent-To", "Resent-Cc", NULL}; + static char *fakedomain = "@"; + + if((hdrs = pine_fetchheader_lines(pine_state->mail_stream, + msgno, section, fields)) != NULL){ + memset(values, 0, (RESENTCC+1) * sizeof(char *)); + simple_header_parse(hdrs, fields, values); + for(i = RESENTFROM; i <= RESENTCC; i++) + rfc822_parse_adrlist(&rlist, values[i], + (F_ON(F_COMPOSE_REJECTS_UNQUAL, pine_state)) + ? fakedomain : pine_state->maildomain); + + /* pare dup's ... */ + for(a = &rlist; *a; a = &(*a)->next) /* compare every address */ + for(b = &(*a)->next; *b; ) /* to the others */ + if(address_is_same(*a, *b)){ + ADDRESS *t = *b; + + if(!(*a)->personal){ /* preserve personal name */ + (*a)->personal = (*b)->personal; + (*b)->personal = NULL; + } + + *b = t->next; + t->next = NULL; + mail_free_address(&t); + } + else + b = &(*b)->next; + } + + if(hdrs) + fs_give((void **) &hdrs); + + return(rlist); +} + + +/*---------------------------------------------------------------------- + Format and return subject suitable for the reply command + +Args: subject -- subject to build reply subject for + buf -- buffer to use for writing. If non supplied, alloc one. + buflen -- length of buf if supplied, else ignored + +Returns: with either "Re:" prepended or not, if already there. + returned string is allocated. + ---*/ +char * +reply_subject(char *subject, char *buf, size_t buflen) +{ + size_t l = (subject && *subject) ? 4*strlen(subject) : 10; + char *tmp = fs_get(l + 1), *decoded, *p; + + if(!buf){ + buflen = l + 5; + buf = fs_get(buflen); + } + + /* decode any 8bit into tmp buffer */ + decoded = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp, l+1, subject); + + buf[0] = '\0'; + if(decoded /* already "re:" ? */ + && (decoded[0] == 'R' || decoded[0] == 'r') + && (decoded[1] == 'E' || decoded[1] == 'e')){ + + if(decoded[2] == ':') + snprintf(buf, buflen, "%.*s", buflen-1, subject); + else if((decoded[2] == '[') && (p = strchr(decoded, ']'))){ + p++; + while(*p && isspace((unsigned char)*p)) p++; + if(p[0] == ':') + snprintf(buf, buflen, "%.*s", buflen-1, subject); + } + } + + if(!buf[0]) + snprintf(buf, buflen, "Re: %.*s", buflen-1, + (subject && *subject) ? subject : "your mail"); + + buf[buflen-1] = '\0'; + + fs_give((void **) &tmp); + return(buf); +} + + +/*---------------------------------------------------------------------- + return initials for the given personal name + +Args: name -- Personal name to extract initials from + +Returns: pointer to name overwritten with initials + ---*/ +char * +reply_quote_initials(char *name) +{ + char *s = name, + *w = name; + + /* while there are still characters to look at */ + while(s && *s){ + /* skip to next initial */ + while(*s && isspace((unsigned char) *s)) + s++; + + /* skip over cruft like single quotes */ + while(*s && !isalnum((unsigned char) *s)) + s++; + + /* copy initial */ + if(*s) + *w++ = *s++; + + /* skip to end of this piece of name */ + while(*s && !isspace((unsigned char) *s)) + s++; + } + + if(w) + *w = '\0'; + + return(name); +} + +/* + * There is an assumption that MAX_SUBSTITUTION is <= the size of the + * tokens being substituted for (only in the size of buf below). + */ +#define MAX_SUBSTITUTION 6 +#define MAX_PREFIX 63 +static char *from_token = "_FROM_"; +static char *nick_token = "_NICK_"; +static char *init_token = "_INIT_"; + +/*---------------------------------------------------------------------- + return a quoting string, "> " by default, for replied text + +Args: env -- envelope of message being replied to + +Returns: malloc'd array containing quoting string, freed by caller + ---*/ +char * +reply_quote_str(ENVELOPE *env) +{ + char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1]; + + strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1); + buf[sizeof(buf)-1] = '\0'; + + /* set up the prefix to quote included text */ + if((p = strstr(buf, from_token)) != NULL){ + repl = (env && env->from && env->from->mailbox) ? env->from->mailbox + : ""; + strncpy(pbf, repl, sizeof(pbf)-1); + pbf[sizeof(pbf)-1] = '\0';; + rplstr(p, sizeof(buf)-(p-buf), strlen(from_token), pbf); + } + + if((p = strstr(buf, nick_token)) != NULL){ + repl = (env && + env->from && + env->from && + get_nickname_from_addr(env->from, tmp_20k_buf, 1000)) + ? tmp_20k_buf : ""; + strncpy(pbf, repl, sizeof(pbf)-1); + pbf[sizeof(pbf)-1] = '\0';; + rplstr(p, sizeof(buf)-(p-buf), strlen(nick_token), pbf); + } + + if((p = strstr(buf, init_token)) != NULL){ + char *q = NULL; + char buftmp[MAILTMPLEN]; + + snprintf(buftmp, sizeof(buftmp), "%.200s", + (env && env->from && env->from->personal) ? env->from->personal : ""); + buftmp[sizeof(buftmp)-1] = '\0'; + + repl = (env && env->from && env->from->personal) + ? reply_quote_initials(q = cpystr((char *)rfc1522_decode_to_utf8( + (unsigned char *)tmp_20k_buf, + SIZEOF_20KBUF, buftmp))) + : ""; + + istrncpy(pbf, repl, sizeof(pbf)-1); + pbf[sizeof(pbf)-1] = '\0';; + rplstr(p, sizeof(buf)-(p-buf), strlen(init_token), pbf); + if(q) + fs_give((void **)&q); + } + + prefix = removing_quotes(cpystr(buf)); + + return(prefix); +} + +int +reply_quote_str_contains_tokens(void) +{ + return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] && + (strstr(ps_global->VAR_REPLY_STRING, from_token) || + strstr(ps_global->VAR_REPLY_STRING, nick_token) || + strstr(ps_global->VAR_REPLY_STRING, init_token))); +} + + +/*---------------------------------------------------------------------- + Build the body for the message number/part being replied to + + Args: + + Result: BODY structure suitable for sending + + ----------------------------------------------------------------------*/ +BODY * +reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body, + long int msgno, char *sect_prefix, void *msgtext, char *prefix, + int plustext, ACTION_S *role, int toplevel, REDRAFT_POS_S **redraft_pos) +{ + char *p, *sig = NULL, *section, sect_buf[256]; + BODY *body = NULL, *tmp_body = NULL; + PART *part; + gf_io_t pc; + int impl, template_len = 0, leave_cursor_at_top = 0, reply_raw_body = 0; + + if(sect_prefix) + snprintf(section = sect_buf, sizeof(sect_buf), "%.*s.1", sizeof(sect_buf)-1, sect_prefix); + else + section = "1"; + + sect_buf[sizeof(sect_buf)-1] = '\0'; + + if(ps_global->full_header == 2 + && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)) + reply_raw_body = 1; + + gf_set_so_writec(&pc, (STORE_S *) msgtext); + + if(toplevel){ + char *filtered; + + impl = 0; + filtered = detoken(role, env, 0, + F_ON(F_SIG_AT_BOTTOM, ps_global) ? 1 : 0, + 0, redraft_pos, &impl); + if(filtered){ + if(*filtered){ + so_puts((STORE_S *)msgtext, filtered); + if(impl == 1) + template_len = strlen(filtered); + else if(impl == 2) + leave_cursor_at_top++; + } + + fs_give((void **)&filtered); + } + else + impl = 1; + } + else + impl = 1; + + if(toplevel && + (sig = reply_signature(role, env, redraft_pos, &impl)) && + F_OFF(F_SIG_AT_BOTTOM, ps_global)){ + + /* + * If CURSORPOS was set explicitly in sig_file, and there was a + * template file before that, we need to adjust the offset by the + * length of the template file. However, if the template had + * a set CURSORPOS in it then impl was 2 before getting to the + * signature, so offset wouldn't have been reset by the signature + * CURSORPOS and offset would already be correct. That case will + * be ok here because template_len will be 0 and adding it does + * nothing. If template + * didn't have CURSORPOS in it, then impl was 1 and got set to 2 + * by the CURSORPOS in the sig. In that case we have to adjust the + * offset. That's what the next line does. It adjusts it if + * template_len is nonzero and if CURSORPOS was set in sig_file. + */ + if(impl == 2) + (*redraft_pos)->offset += template_len; + + if(*sig) + so_puts((STORE_S *)msgtext, sig); + + /* + * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM + * is set, we won't have used it yet and want it to be non-NULL. + */ + fs_give((void **)&sig); + } + + /* + * Only put cursor in sig if there is a cursorpos there but not + * one in the template, and sig-at-bottom. + */ + if(!(sig && impl == 2 && !leave_cursor_at_top)) + leave_cursor_at_top++; + + if(plustext){ + if(!orig_body + || orig_body->type == TYPETEXT + || reply_raw_body + || F_OFF(F_ATTACHMENTS_IN_REPLY, ps_global)){ + char *charset = NULL; + + /*------ Simple text-only message ----*/ + body = mail_newbody(); + body->type = TYPETEXT; + body->contents.text.data = msgtext; + reply_delimiter(env, role, pc); + if(F_ON(F_INCLUDE_HEADER, ps_global)) + reply_forward_header(stream, msgno, sect_prefix, + env, pc, prefix); + + if(!orig_body || reply_raw_body || reply_body_text(orig_body, &tmp_body)){ + BODY *bodyp = NULL; + + bodyp = reply_raw_body ? NULL : tmp_body; + + /* + * We set the charset in the outgoing message to the same + * as the one in the message we're replying to unless it + * is the unknown charset. We do that in order to attempt + * to preserve the same charset in the reply if possible. + * It may be safer to just set it to whatever we want instead + * but then the receiver may not be able to read it. + */ + if(bodyp + && (charset = parameter_val(bodyp->parameter, "charset")) + && strucmp(charset, UNKNOWN_CHARSET)) + set_parameter(&body->parameter, "charset", charset); + + if(charset) + fs_give((void **) &charset); + + get_body_part_text(stream, bodyp, msgno, + bodyp ? (p = body_partno(stream, msgno, bodyp)) + : sect_prefix, + 0L, pc, prefix, NULL, GBPT_NONE); + if(bodyp && p) + fs_give((void **) &p); + } + else{ + gf_puts(NEWLINE, pc); + gf_puts(" [NON-Text Body part not included]", pc); + gf_puts(NEWLINE, pc); + } + } + else if(orig_body->type == TYPEMULTIPART){ + /*------ Message is Multipart ------*/ + if(orig_body->subtype + && !strucmp(orig_body->subtype, "signed") + && orig_body->nested.part){ + /* operate only on the signed part */ + body = reply_body(stream, env, + &orig_body->nested.part->body, + msgno, section, msgtext, prefix, + plustext, role, 0, redraft_pos); + } + else if(orig_body->subtype + && !strucmp(orig_body->subtype, "alternative")){ + /* Set up the simple text reply */ + body = mail_newbody(); + body->type = TYPETEXT; + body->contents.text.data = msgtext; + + if(reply_body_text(orig_body, &tmp_body)){ + reply_delimiter(env, role, pc); + if(F_ON(F_INCLUDE_HEADER, ps_global)) + reply_forward_header(stream, msgno, sect_prefix, + env, pc, prefix); + + get_body_part_text(stream, tmp_body, msgno, + p = body_partno(stream,msgno,tmp_body), + 0L, pc, prefix, NULL, GBPT_NONE); + if(p) + fs_give((void **) &p); + } + else + q_status_message(SM_ORDER | SM_DING, 3, 3, + "No suitable multipart text found for inclusion!"); + } + else{ + body = copy_body(NULL, orig_body); + + /* + * whatever subtype it is, demote it + * to plain old MIXED. + */ + if(body->subtype) + fs_give((void **) &body->subtype); + + body->subtype = cpystr("Mixed"); + + if(body->nested.part && + body->nested.part->body.type == TYPETEXT) { + char *new_charset = NULL; + + /*---- First part of the message is text -----*/ + body->nested.part->body.contents.text.data = msgtext; + if(body->nested.part->body.subtype && + strucmp(body->nested.part->body.subtype, "Plain")){ + fs_give((void **)&body->nested.part->body.subtype); + body->nested.part->body.subtype = cpystr("Plain"); + } + reply_delimiter(env, role, pc); + if(F_ON(F_INCLUDE_HEADER, ps_global)) + reply_forward_header(stream, msgno, sect_prefix, + env, pc, prefix); + + if(!(get_body_part_text(stream, + &orig_body->nested.part->body, + msgno, section, 0L, pc, prefix, + &new_charset, GBPT_NONE) + && fetch_contents(stream, msgno, sect_prefix, body))) + q_status_message(SM_ORDER | SM_DING, 3, 4, + _("Error including all message parts")); + else if(new_charset) + set_parameter(&body->nested.part->body.parameter, "charset", new_charset); + } + else if(orig_body->nested.part->body.type == TYPEMULTIPART + && orig_body->nested.part->body.subtype + && !strucmp(orig_body->nested.part->body.subtype, + "alternative") + && reply_body_text(&orig_body->nested.part->body, + &tmp_body)){ + int partnum; + + reply_delimiter(env, role, pc); + if(F_ON(F_INCLUDE_HEADER, ps_global)) + reply_forward_header(stream, msgno, sect_prefix, + env, pc, prefix); + + snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%.*s", + sizeof(sect_buf)/2-2, + sect_prefix ? sect_prefix : "", + sect_prefix ? "." : "", + sizeof(sect_buf)/2-2, + p = partno(orig_body, tmp_body)); + sect_buf[sizeof(sect_buf)-1] = '\0'; + fs_give((void **) &p); + get_body_part_text(stream, tmp_body, msgno, + sect_buf, 0L, pc, prefix, + NULL, GBPT_NONE); + + part = body->nested.part->next; + body->nested.part->next = NULL; + mail_free_body_part(&body->nested.part); + body->nested.part = mail_newbody_part(); + body->nested.part->body.type = TYPETEXT; + body->nested.part->body.subtype = cpystr("Plain"); + body->nested.part->body.contents.text.data = msgtext; + body->nested.part->next = part; + + for(partnum = 2; part != NULL; part = part->next){ + snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%d", + sizeof(sect_buf)/2, + sect_prefix ? sect_prefix : "", + sect_prefix ? "." : "", partnum++); + sect_buf[sizeof(sect_buf)-1] = '\0'; + + if(!fetch_contents(stream, msgno, + sect_buf, &part->body)){ + break; + } + } + } + else { + /*--- Fetch the original pieces ---*/ + if(!fetch_contents(stream, msgno, sect_prefix, body)) + q_status_message(SM_ORDER | SM_DING, 3, 4, + _("Error including all message parts")); + + /*--- No text part, create a blank one ---*/ + part = mail_newbody_part(); + part->next = body->nested.part; + body->nested.part = part; + part->body.contents.text.data = msgtext; + } + } + } + else{ + /*---- Single non-text message of some sort ----*/ + body = mail_newbody(); + body->type = TYPEMULTIPART; + part = mail_newbody_part(); + body->nested.part = part; + + /*--- The first part, a blank text part to be edited ---*/ + part->body.type = TYPETEXT; + part->body.contents.text.data = msgtext; + + /*--- The second part, what ever it is ---*/ + part->next = mail_newbody_part(); + part = part->next; + part->body.id = generate_message_id(); + copy_body(&(part->body), orig_body); + + /* + * the idea here is to fetch part into storage object + */ + if((part->body.contents.text.data = (void *) so_get(PART_SO_TYPE, + NULL,EDIT_ACCESS)) != NULL){ + if((p = pine_mail_fetch_body(stream, msgno, section, + &part->body.size.bytes, NIL)) != NULL){ + so_nputs((STORE_S *)part->body.contents.text.data, + p, part->body.size.bytes); + } + else + mail_free_body(&body); + } + else + mail_free_body(&body); + } + } + else{ + /*--------- No text included --------*/ + body = mail_newbody(); + body->type = TYPETEXT; + body->contents.text.data = msgtext; + } + + if(!leave_cursor_at_top){ + long cnt = 0L; + unsigned char c; + + /* rewind and count chars to start of sig file */ + so_seek((STORE_S *)msgtext, 0L, 0); + while(so_readc(&c, (STORE_S *)msgtext)) + cnt++; + + if(!*redraft_pos){ + *redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(**redraft_pos)); + memset((void *)*redraft_pos, 0,sizeof(**redraft_pos)); + (*redraft_pos)->hdrname = cpystr(":"); + } + + /* + * If explicit cursor positioning in sig file, + * add offset to start of sig file plus offset into sig file. + * Else, just offset to start of sig file. + */ + (*redraft_pos)->offset += cnt; + } + + if(sig){ + if(*sig) + so_puts((STORE_S *)msgtext, sig); + + fs_give((void **)&sig); + } + + gf_clear_so_writec((STORE_S *) msgtext); + + return(body); +} + + +/* + * reply_part - first replyable multipart of a multipart. + */ +int +reply_body_text(struct mail_bodystruct *body, struct mail_bodystruct **new_body) +{ + if(body){ + switch(body->type){ + case TYPETEXT : + *new_body = body; + return(1); + + case TYPEMULTIPART : + if(body->subtype && !strucmp(body->subtype, "alternative")){ + PART *part; + int got_one = 0; + + if(ps_global->force_prefer_plain + || (!ps_global->force_no_prefer_plain + && F_ON(F_PREFER_PLAIN_TEXT, ps_global))){ + for(part = body->nested.part; part; part = part->next) + if((!part->body.type || part->body.type == TYPETEXT) + && (!part->body.subtype + || !strucmp(part->body.subtype, "plain"))){ + *new_body = &part->body; + return(1); + } + } + + /* + * Else choose last alternative among plain or html parts. + * Perhaps we should really be using mime_show() to make this + * potentially more general than just plain or html. + */ + for(part = body->nested.part; part; part = part->next){ + if((!part->body.type || part->body.type == TYPETEXT) + && ((!part->body.subtype || !strucmp(part->body.subtype, "plain")) + || + (part->body.subtype && !strucmp(part->body.subtype, "html")))){ + got_one++; + *new_body = &part->body; + } + } + + if(got_one) + return(1); + } + else if(body->nested.part) + /* NOTE: we're only interested in "first" part of mixed */ + return(reply_body_text(&body->nested.part->body, new_body)); + + break; + + default: + break; + } + } + + return(0); +} + + +char * +reply_signature(ACTION_S *role, ENVELOPE *env, REDRAFT_POS_S **redraft_pos, int *impl) +{ + char *sig; + size_t l; + + sig = detoken(role, env, + 2, F_ON(F_SIG_AT_BOTTOM, ps_global) ? 0 : 1, 1, + redraft_pos, impl); + + if(F_OFF(F_SIG_AT_BOTTOM, ps_global) && (!sig || !*sig)){ + if(sig) + fs_give((void **)&sig); + + l = 2 * strlen(NEWLINE); + sig = (char *)fs_get((l+1) * sizeof(char)); + strncpy(sig, NEWLINE, l); + sig[l] = '\0'; + strncat(sig, NEWLINE, l+1-1-strlen(sig)); + sig[l] = '\0'; + return(sig); + } + + return(sig); +} + + +/* + * Buf is at least size maxlen+1 + */ +void +get_addr_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen) +{ + ADDRESS *addr = NULL; + ADDRESS *last_to = NULL; + ADDRESS *first_addr = NULL, *second_addr = NULL; + ADDRESS *third_addr = NULL, *fourth_addr = NULL; + int cntaddr, l; + size_t orig_maxlen; + char *p; + + buf[0] = '\0'; + + switch(type){ + case iFrom: + addr = env ? env->from : NULL; + break; + + case iTo: + addr = env ? env->to : NULL; + break; + + case iCc: + addr = env ? env->cc : NULL; + break; + + case iSender: + addr = env ? env->sender : NULL; + break; + + /* + * Recips is To and Cc togeter. We hook the two adrlists together + * temporarily. + */ + case iRecips: + addr = env ? env->to : NULL; + /* Find end of To list */ + for(last_to = addr; last_to && last_to->next; last_to = last_to->next) + ; + + /* Make the end of To list point to cc list */ + if(last_to) + last_to->next = (env ? env->cc : NULL); + + break; + + /* + * Initials. + */ + case iInit: + if(env && env->from && env->from->personal){ + char *name, *initials = NULL; + + name = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, + SIZEOF_20KBUF, env->from->personal); + if(name == env->from->personal){ + strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1); + tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0'; + name = tmp_20k_buf; + } + + if(name && *name){ + initials = reply_quote_initials(name); + iutf8ncpy(buf, initials, maxlen); + buf[maxlen] = '\0'; + } + } + + return; + + default: + break; + } + + orig_maxlen = maxlen; + + first_addr = addr; + /* skip over rest of c-client group addr */ + if(first_addr && first_addr->mailbox && !first_addr->host){ + for(second_addr = first_addr->next; + second_addr && second_addr->host; + second_addr = second_addr->next) + ; + + if(second_addr && !second_addr->host) + second_addr = second_addr->next; + } + else if(!(first_addr && first_addr->host && first_addr->host[0] == '.')) + second_addr = first_addr ? first_addr->next : NULL; + + if(second_addr && second_addr->mailbox && !second_addr->host){ + for(third_addr = second_addr->next; + third_addr && third_addr->host; + third_addr = third_addr->next) + ; + + if(third_addr && !third_addr->host) + third_addr = third_addr->next; + } + else if(!(second_addr && second_addr->host && second_addr->host[0] == '.')) + third_addr = second_addr ? second_addr->next : NULL; + + if(third_addr && third_addr->mailbox && !third_addr->host){ + for(fourth_addr = third_addr->next; + fourth_addr && fourth_addr->host; + fourth_addr = fourth_addr->next) + ; + + if(fourth_addr && !fourth_addr->host) + fourth_addr = fourth_addr->next; + } + else if(!(third_addr && third_addr->host && third_addr->host[0] == '.')) + fourth_addr = third_addr ? third_addr->next : NULL; + + /* Just attempting to make a nice display */ + if(first_addr && ((first_addr->personal && first_addr->personal[0]) || + (first_addr->mailbox && first_addr->mailbox[0]))){ + if(second_addr){ + if((second_addr->personal && second_addr->personal[0]) || + (second_addr->mailbox && second_addr->mailbox[0])){ + if(third_addr){ + if((third_addr->personal && third_addr->personal[0]) || + (third_addr->mailbox && third_addr->mailbox[0])){ + if(fourth_addr) + cntaddr = 4; + else + cntaddr = 3; + } + else + cntaddr = -1; + } + else + cntaddr = 2; + } + else + cntaddr = -1; + } + else + cntaddr = 1; + } + else + cntaddr = -1; + + p = buf; + if(cntaddr == 1) + a_little_addr_string(first_addr, p, maxlen); + else if(cntaddr == 2){ + a_little_addr_string(first_addr, p, maxlen); + maxlen -= (l=strlen(p)); + p += l; + if(maxlen > 7){ + strncpy(p, " and ", maxlen); + maxlen -= 5; + p += 5; + a_little_addr_string(second_addr, p, maxlen); + } + } + else if(cntaddr == 3){ + a_little_addr_string(first_addr, p, maxlen); + maxlen -= (l=strlen(p)); + p += l; + if(maxlen > 7){ + strncpy(p, ", ", maxlen); + maxlen -= 2; + p += 2; + a_little_addr_string(second_addr, p, maxlen); + maxlen -= (l=strlen(p)); + p += l; + if(maxlen > 7){ + strncpy(p, ", and ", maxlen); + maxlen -= 6; + p += 6; + a_little_addr_string(third_addr, p, maxlen); + } + } + } + else if(cntaddr > 3){ + a_little_addr_string(first_addr, p, maxlen); + maxlen -= (l=strlen(p)); + p += l; + if(maxlen > 7){ + strncpy(p, ", ", maxlen); + maxlen -= 2; + p += 2; + a_little_addr_string(second_addr, p, maxlen); + maxlen -= (l=strlen(p)); + p += l; + if(maxlen > 7){ + strncpy(p, ", ", maxlen); + maxlen -= 2; + p += 2; + a_little_addr_string(third_addr, p, maxlen); + maxlen -= (l=strlen(p)); + p += l; + if(maxlen >= 12) + strncpy(p, ", and others", maxlen); + else if(maxlen >= 3) + strncpy(p, "...", maxlen); + } + } + } + else if(addr){ + char *a_string; + + a_string = addr_list_string(addr, NULL, 0); + iutf8ncpy(buf, a_string, maxlen); + + fs_give((void **)&a_string); + } + + if(last_to) + last_to->next = NULL; + + buf[orig_maxlen] = '\0'; +} + + +/* + * Buf is at least size maxlen+1 + */ +void +get_news_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen) +{ + int cntnews = 0, orig_maxlen; + char *news = NULL, *p, *q; + + switch(type){ + case iNews: + case iNewsAndTo: + case iToAndNews: + case iNewsAndRecips: + case iRecipsAndNews: + news = env ? env->newsgroups : NULL; + break; + + case iCurNews: + if(ps_global->mail_stream && IS_NEWS(ps_global->mail_stream)) + news = ps_global->cur_folder; + + break; + + default: + break; + } + + orig_maxlen = maxlen; + + if(news){ + q = news; + while(isspace((unsigned char)*q)) + q++; + + if(*q) + cntnews++; + + while((q = strindex(q, ',')) != NULL){ + q++; + while(isspace((unsigned char)*q)) + q++; + + if(*q) + cntnews++; + else + break; + } + } + + if(cntnews == 1){ + istrncpy(buf, news, maxlen); + buf[maxlen] = '\0'; + removing_leading_and_trailing_white_space(buf); + } + else if(cntnews == 2){ + p = buf; + q = news; + while(isspace((unsigned char)*q)) + q++; + + while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){ + *p++ = *q++; + maxlen--; + } + + if(maxlen > 7){ + strncpy(p, " and ", maxlen); + p += 5; + maxlen -= 5; + } + + while(isspace((unsigned char)*q) || *q == ',') + q++; + + while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){ + *p++ = *q++; + maxlen--; + } + + *p = '\0'; + + istrncpy(tmp_20k_buf, buf, 10000); + strncpy(buf, tmp_20k_buf, orig_maxlen); + } + else if(cntnews > 2){ + char b[100]; + + p = buf; + q = news; + while(isspace((unsigned char)*q)) + q++; + + while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){ + *p++ = *q++; + maxlen--; + } + + *p = '\0'; + snprintf(b, sizeof(b), " and %d other newsgroups", cntnews-1); + b[sizeof(b)-1] = '\0'; + if(maxlen >= strlen(b)) + strncpy(p, b, maxlen); + else if(maxlen >= 3) + strncpy(p, "...", maxlen); + + buf[orig_maxlen] = '\0'; + + istrncpy(tmp_20k_buf, buf, 10000); + tmp_20k_buf[10000-1] = '\0'; + strncpy(buf, tmp_20k_buf, orig_maxlen); + } + + buf[orig_maxlen] = '\0'; +} + + +/* + * Buf is at least size maxlen+1 + */ +char * +get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size_t maxlen) +{ + char *space = NULL; + IndexColType addrtype; + + buf[0] = '\0'; + + switch(type){ + case iRDate: case iSDate: case iSTime: + case iS1Date: case iS2Date: case iS3Date: case iS4Date: + case iSDateIso: case iSDateIsoS: + case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4: + case iSDateTime: + case iSDateTimeIso: case iSDateTimeIsoS: + case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4: + case iSDateTime24: + case iSDateTimeIso24: case iSDateTimeIsoS24: + case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424: + case iDateIso: case iDateIsoS: case iTime24: case iTime12: + case iDay: case iDayOrdinal: case iDay2Digit: + case iMonAbb: case iMonLong: case iMon: case iMon2Digit: + case iYear: case iYear2Digit: + case iDate: case iLDate: + case iTimezone: case iDayOfWeekAbb: case iDayOfWeek: + case iPrefDate: case iPrefTime: case iPrefDateTime: + if(env && env->date && env->date[0] && maxlen >= 20) + date_str((char *) env->date, type, 1, buf, maxlen+1, 0); + + break; + + case iCurDate: + case iCurDateIso: + case iCurDateIsoS: + case iCurTime24: + case iCurTime12: + case iCurDay: + case iCurDay2Digit: + case iCurDayOfWeek: + case iCurDayOfWeekAbb: + case iCurMon: + case iCurMon2Digit: + case iCurMonLong: + case iCurMonAbb: + case iCurYear: + case iCurYear2Digit: + case iCurPrefDate: + case iCurPrefDateTime: + case iCurPrefTime: + case iLstMon: + case iLstMon2Digit: + case iLstMonLong: + case iLstMonAbb: + case iLstMonYear: + case iLstMonYear2Digit: + case iLstYear: + case iLstYear2Digit: + if(maxlen >= 20) + date_str(NULL, type, 1, buf, maxlen+1, 0); + + break; + + case iFrom: + case iTo: + case iCc: + case iSender: + case iRecips: + case iInit: + get_addr_data(env, type, buf, maxlen); + break; + + case iRoleNick: + if(role && role->nick){ + strncpy(buf, role->nick, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iNewLine: + if(maxlen >= strlen(NEWLINE)){ + strncpy(buf, NEWLINE, maxlen); + buf[maxlen] = '\0'; + } + break; + + case iAddress: + case iMailbox: + if(env && env->from && env->from->mailbox && env->from->mailbox[0] && + strlen(env->from->mailbox) <= maxlen){ + strncpy(buf, env->from->mailbox, maxlen); + buf[maxlen] = '\0'; + if(type == iAddress && + env->from->host && + env->from->host[0] && + env->from->host[0] != '.' && + strlen(buf) + strlen(env->from->host) + 1 <= maxlen){ + strncat(buf, "@", maxlen+1-1-strlen(buf)); + buf[maxlen] = '\0'; + strncat(buf, env->from->host, maxlen+1-1-strlen(buf)); + buf[maxlen] = '\0'; + } + } + + break; + + case iNews: + case iCurNews: + get_news_data(env, type, buf, maxlen); + break; + + case iToAndNews: + case iNewsAndTo: + case iRecipsAndNews: + case iNewsAndRecips: + if(type == iToAndNews || type == iNewsAndTo) + addrtype = iTo; + else + addrtype = iRecips; + + if(env && env->newsgroups){ + space = (char *)fs_get((maxlen+1) * sizeof(char)); + get_news_data(env, type, space, maxlen); + } + + get_addr_data(env, addrtype, buf, maxlen); + + if(space && *space && *buf){ + if(strlen(space) + strlen(buf) + 5 > maxlen){ + if(strlen(space) > maxlen/2) + get_news_data(env, type, space, maxlen - strlen(buf) - 5); + else + get_addr_data(env, addrtype, buf, maxlen - strlen(space) - 5); + } + + if(type == iToAndNews || type == iRecipsAndNews) + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", buf, space); + else + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", space, buf); + + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + + strncpy(buf, tmp_20k_buf, maxlen); + buf[maxlen] = '\0'; + } + else if(space && *space){ + strncpy(buf, space, maxlen); + buf[maxlen] = '\0'; + } + + if(space) + fs_give((void **)&space); + + break; + + case iSubject: + if(env && env->subject){ + size_t n, len; + unsigned char *p, *tmp = NULL; + + if((n = 4*strlen(env->subject)) > SIZEOF_20KBUF-1){ + len = n+1; + p = tmp = (unsigned char *)fs_get(len * sizeof(char)); + } + else{ + len = SIZEOF_20KBUF; + p = (unsigned char *)tmp_20k_buf; + } + + istrncpy(buf, (char *)rfc1522_decode_to_utf8(p, len, env->subject), maxlen); + + buf[maxlen] = '\0'; + + if(tmp) + fs_give((void **)&tmp); + } + + break; + + case iMsgID: + if(env && env->message_id){ + strncpy(buf, env->message_id, maxlen); + buf[maxlen] = '\0'; + } + + break; + + default: + break; + } + + buf[maxlen] = '\0'; + return(buf); +} + + +/* + * reply_delimiter - output formatted reply delimiter for given envelope + * with supplied character writing function. + */ +void +reply_delimiter(ENVELOPE *env, ACTION_S *role, gf_io_t pc) +{ +#define MAX_DELIM 2000 + char buf[MAX_DELIM+1]; + char *p; + char *filtered = NULL; + int contains_newline_token = 0; + + + if(!env) + return; + + strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM); + buf[MAX_DELIM] = '\0'; + /* preserve exact default behavior from before */ + if(!strcmp(buf, DEFAULT_REPLY_INTRO)){ + struct date d; + int include_date; + + parse_date((char *) env->date, &d); + include_date = !(d.day == -1 || d.month == -1 || d.year == -1); + if(include_date){ + gf_puts("On ", pc); /* All delims have... */ + if(d.wkday != -1){ /* "On day, date month year" */ + gf_puts(day_abbrev(d.wkday), pc); /* in common */ + gf_puts(", ", pc); + } + + gf_puts(int2string(d.day), pc); + (*pc)(' '); + gf_puts(month_abbrev(d.month), pc); + (*pc)(' '); + gf_puts(int2string(d.year), pc); + } + + if(env->from + && ((env->from->personal && env->from->personal[0]) + || (env->from->mailbox && env->from->mailbox[0]))){ + char buftmp[MAILTMPLEN]; + + a_little_addr_string(env->from, buftmp, sizeof(buftmp)-1); + if(include_date) + gf_puts(", ", pc); + + gf_puts(buftmp, pc); + gf_puts(" wrote:", pc); + } + else{ + if(include_date) + gf_puts(", it was written", pc); + else + gf_puts("It was written", pc); + } + + } + else{ + /* + * This is here for backwards compatibility. There didn't used + * to be a _NEWLINE_ token. The user would enter text that should + * all fit on one line and then that was followed by two newlines. + * Also, truncation occurs if it is long. + * Now, if _NEWLINE_ is not in the text, same thing still works + * the same. However, if _NEWLINE_ is in there, then all bets are + * off and the user is on his or her own. No automatic newlines + * are added, only those that come from the tokens. No truncation + * is done, the user is trusted to get it right. Newlines may be + * embedded so that the leadin is multi-line. + */ + contains_newline_token = (strstr(buf, "_NEWLINE_") != NULL); + filtered = detoken_src(buf, FOR_REPLY_INTRO, env, role, + NULL, NULL); + + /* try to truncate if too long */ + if(!contains_newline_token && filtered && utf8_width(filtered) > 80){ + int ended_with_colon = 0; + int ended_with_quote = 0; + int ended_with_quote_colon = 0; + int l; + + l = strlen(filtered); + + if(filtered[l-1] == ':'){ + ended_with_colon = ':'; + if(filtered[l-2] == QUOTE || filtered[l-2] == '\'') + ended_with_quote_colon = filtered[l-2]; + } + else if(filtered[l-1] == QUOTE || filtered[l-1] == '\'') + ended_with_quote = filtered[l-1]; + + /* try to find space to break at */ + for(p = &filtered[75]; p > &filtered[60] && + !isspace((unsigned char)*p); p--) + ; + + if(!isspace((unsigned char)*p)) + p = &filtered[75]; + + *p++ = '.'; + *p++ = '.'; + *p++ = '.'; + if(ended_with_quote_colon){ + *p++ = ended_with_quote_colon; + *p++ = ':'; + } + else if(ended_with_colon) + *p++ = ended_with_colon; + else if(ended_with_quote) + *p++ = ended_with_quote; + + *p = '\0'; + } + + if(filtered && *filtered) + gf_puts(filtered, pc); + } + + /* and end with two newlines unless no leadin at all */ + if(!contains_newline_token){ + if(!strcmp(buf, DEFAULT_REPLY_INTRO) || (filtered && *filtered)){ + gf_puts(NEWLINE, pc); + gf_puts(NEWLINE, pc); + } + } + + if(filtered) + fs_give((void **)&filtered); +} + + +void +free_redraft_pos(REDRAFT_POS_S **redraft_pos) +{ + if(redraft_pos && *redraft_pos){ + if((*redraft_pos)->hdrname) + fs_give((void **)&(*redraft_pos)->hdrname); + + fs_give((void **)redraft_pos); + } +} + + +/*---------------------------------------------------------------------- + Build the body for the message number/part being forwarded as ATTACHMENT + + Args: + + Result: PARTS suitably attached to body + + ----------------------------------------------------------------------*/ +int +forward_mime_msg(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env, struct mail_body_part **partp, void *msgtext) +{ + char *tmp_text; + unsigned long len; + BODY *b; + + *partp = mail_newbody_part(); + b = &(*partp)->body; + b->type = TYPEMESSAGE; + b->id = generate_message_id(); + b->description = cpystr("Forwarded Message"); + b->nested.msg = mail_newmsg(); + b->disposition.type = cpystr("inline"); + + /*---- Package each message in a storage object ----*/ + if((b->contents.text.data = (void *) so_get(PART_SO_TYPE,NULL,EDIT_ACCESS)) + && (tmp_text = mail_fetch_header(stream,msgno,section,NIL,NIL,FT_PEEK)) + && *tmp_text){ + so_puts((STORE_S *) b->contents.text.data, tmp_text); + + b->size.bytes = strlen(tmp_text); + so_puts((STORE_S *) b->contents.text.data, "\015\012"); + if((tmp_text = pine_mail_fetch_text (stream,msgno,section,&len,NIL)) != NULL){ + so_nputs((STORE_S *)b->contents.text.data,tmp_text,(long) len); + b->size.bytes += len; + return(1); + } + } + + return(0); +} + + +/* + * forward_delimiter - return delimiter for forwarded text + */ +void +forward_delimiter(gf_io_t pc) +{ + gf_puts(NEWLINE, pc); + /* TRANSLATORS: When a message is forwarded by the user this is the + text that shows where the forwarded part of the message begins. */ + gf_puts(_("---------- Forwarded message ----------"), pc); + gf_puts(NEWLINE, pc); +} + + +/*---------------------------------------------------------------------- + Wrapper for header formatting tool + + Args: stream -- + msgno -- + env -- + pc -- + prefix -- + + Result: header suitable for reply/forward text written using "pc" + + ----------------------------------------------------------------------*/ +void +reply_forward_header(MAILSTREAM *stream, long int msgno, char *part, ENVELOPE *env, + gf_io_t pc, char *prefix) +{ + int rv; + HEADER_S h; + char **list, **new_list = NULL; + + list = ps_global->VAR_VIEW_HEADERS; + + /* + * If VIEW_HEADERS is set, we should remove BCC from the list so that + * the user doesn't inadvertently forward the BCC header. + */ + if(list && list[0]){ + int i, cnt = 0; + char **p; + + while(list[cnt++]) + ; + + p = new_list = (char **) fs_get((cnt+1) * sizeof(char *)); + + for(i=0; list[i]; i++) + if(strucmp(list[i], "bcc")) + *p++ = cpystr(list[i]); + + *p = NULL; + + if(new_list && new_list[0]) + list = new_list; + + } + + HD_INIT(&h, list, ps_global->view_all_except, FE_DEFAULT & ~FE_BCC); + if((rv = format_header(stream, msgno, part, env, &h, + prefix, NULL, FM_NOINDENT, NULL, pc)) != 0){ + if(rv == 1) + gf_puts(" [Error fetching message header data]", pc); + } + else + gf_puts(NEWLINE, pc); /* write header delimiter */ + + if(new_list) + free_list_array(&new_list); +} + + +/*---------------------------------------------------------------------- + Build the subject for the message number being forwarded + + Args: pine_state -- The usual pine structure + msgno -- The message number to build subject for + + Result: malloc'd string containing new subject or NULL on error + + ----------------------------------------------------------------------*/ +char * +forward_subject(ENVELOPE *env, int flags) +{ + size_t l; + char *p, buftmp[MAILTMPLEN]; + + if(!env) + return(NULL); + + dprint((9, "checking subject: \"%s\"\n", + env->subject ? env->subject : "NULL")); + + if(env->subject && env->subject[0]){ /* add (fwd)? */ + snprintf(buftmp, sizeof(buftmp), "%s", env->subject); + buftmp[sizeof(buftmp)-1] = '\0'; + /* decode any 8bit (copy to the temp buffer if decoding doesn't) */ + if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, + SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp) + strncpy(tmp_20k_buf, buftmp, SIZEOF_20KBUF); + + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + + removing_trailing_white_space(tmp_20k_buf); + if((l = strlen(tmp_20k_buf)) < 1000 && + (l < 5 || strcmp(tmp_20k_buf+l-5,"(fwd)"))){ + snprintf(tmp_20k_buf+2000, SIZEOF_20KBUF-2000, "%s (fwd)", tmp_20k_buf); + tmp_20k_buf[SIZEOF_20KBUF-2000-1] = '\0'; + strncpy(tmp_20k_buf, tmp_20k_buf+2000, SIZEOF_20KBUF); + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + } + + /* + * HACK: composer can't handle embedded double quotes in attachment + * comments so we substitute two single quotes. + */ + if(flags & FS_CONVERT_QUOTES) + while((p = strchr(tmp_20k_buf, QUOTE)) != NULL) + (void)rplstr(p, SIZEOF_20KBUF-(p-tmp_20k_buf), 1, "''"); + + return(cpystr(tmp_20k_buf)); + + } + + return(cpystr("Forwarded mail....")); +} + + +/*---------------------------------------------------------------------- + Build the body for the message number/part being forwarded + + Args: + + Result: BODY structure suitable for sending + + ----------------------------------------------------------------------*/ +BODY * +forward_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body, + long int msgno, char *sect_prefix, void *msgtext, int flags) +{ + BODY *body = NULL, *text_body, *tmp_body; + PART *part; + gf_io_t pc; + char *tmp_text, *section, sect_buf[256]; + int forward_raw_body = 0; + + /* + * Check to see if messages got expunged out from underneath us. This + * could have happened during the prompt to the user asking whether to + * include the message as an attachment. Either the message is gone or + * it might be at a different sequence number. We'd better bail. + */ + if(ps_global->full_header == 2 + && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)) + forward_raw_body = 1; + if(sp_expunge_count(stream)) + return(NULL); + + if(sect_prefix && forward_raw_body == 0) + snprintf(section = sect_buf, sizeof(sect_buf), "%s.1", sect_prefix); + else if(sect_prefix && forward_raw_body) + section = sect_prefix; + else if(!sect_prefix && forward_raw_body) + section = NULL; + else + section = "1"; + + sect_buf[sizeof(sect_buf)-1] = '\0'; + + gf_set_so_writec(&pc, (STORE_S *) msgtext); + if(!orig_body || orig_body->type == TYPETEXT || forward_raw_body) { + char *charset = NULL; + + /*---- Message has a single text part -----*/ + body = mail_newbody(); + body->type = TYPETEXT; + body->contents.text.data = msgtext; + if(orig_body + && (charset = parameter_val(orig_body->parameter, "charset"))) + set_parameter(&body->parameter, "charset", charset); + + if(charset) + fs_give((void **) &charset); + + if(!(flags & FWD_ANON)){ + forward_delimiter(pc); + reply_forward_header(stream, msgno, sect_prefix, env, pc, ""); + } + + if(!get_body_part_text(stream, forward_raw_body ? NULL : orig_body, + msgno, section, 0L, pc, NULL, NULL, GBPT_NONE)){ + mail_free_body(&body); + return(NULL); + } + } + else if(orig_body->type == TYPEMULTIPART) { + if(orig_body->subtype && !strucmp(orig_body->subtype, "signed") + && orig_body->nested.part){ + /* only operate on the signed data (not the signature) */ + body = forward_body(stream, env, &orig_body->nested.part->body, + msgno, sect_prefix, msgtext, flags); + } + /*---- Message is multipart ----*/ + else if(!(orig_body->subtype && !strucmp(orig_body->subtype, + "alternative") + && (body = forward_multi_alt(stream, env, orig_body, msgno, + sect_prefix, msgtext, + pc, flags)))){ + /*--- Copy the body and entire structure ---*/ + body = copy_body(NULL, orig_body); + + /* + * whatever subtype it is, demote it + * to plain old MIXED. + */ + if(body->subtype) + fs_give((void **) &body->subtype); + + body->subtype = cpystr("Mixed"); + + /*--- The text part of the message ---*/ + if(!body->nested.part){ + q_status_message(SM_ORDER | SM_DING, 3, 6, + "Error referencing body part 1"); + mail_free_body(&body); + } + else if(body->nested.part->body.type == TYPETEXT) { + char *new_charset = NULL; + + /*--- The first part is text ----*/ + text_body = &body->nested.part->body; + text_body->contents.text.data = msgtext; + if(text_body->subtype && strucmp(text_body->subtype, "Plain")){ + /* this text is going to the composer, it should be Plain */ + fs_give((void **)&text_body->subtype); + text_body->subtype = cpystr("PLAIN"); + } + if(!(flags & FWD_ANON)){ + forward_delimiter(pc); + reply_forward_header(stream, msgno, + sect_prefix, env, pc, ""); + } + + if(!(get_body_part_text(stream, &orig_body->nested.part->body, + msgno, section, 0L, pc, + NULL, &new_charset, GBPT_NONE) + && fetch_contents(stream, msgno, sect_prefix, body))) + mail_free_body(&body); + else if(new_charset) + set_parameter(&text_body->parameter, "charset", new_charset); + +/* BUG: ? matter that we're not setting body.size.bytes */ + } + else if(body->nested.part->body.type == TYPEMULTIPART + && body->nested.part->body.subtype + && !strucmp(body->nested.part->body.subtype, "alternative") + && (tmp_body = forward_multi_alt(stream, env, + &body->nested.part->body, + msgno, sect_prefix, + msgtext, pc, + flags | FWD_NESTED))){ + /* for the forward_multi_alt call above, we want to pass + * sect_prefix instead of section so we can obtain the header. + * Set the FWD_NESTED flag so we fetch the right body_part. + */ + int partnum; + + part = body->nested.part->next; + body->nested.part->next = NULL; + mail_free_body_part(&body->nested.part); + body->nested.part = mail_newbody_part(); + body->nested.part->body = *tmp_body; + body->nested.part->next = part; + + for(partnum = 2; part != NULL; part = part->next){ + snprintf(sect_buf, sizeof(sect_buf), "%s%s%d", + sect_prefix ? sect_prefix : "", + sect_prefix ? "." : "", partnum++); + sect_buf[sizeof(sect_buf)-1] = '\0'; + + if(!fetch_contents(stream, msgno, sect_buf, &part->body)){ + mail_free_body(&body); + break; + } + } + } + else { + if(fetch_contents(stream, msgno, sect_prefix, body)){ + /*--- Create a new blank text part ---*/ + part = mail_newbody_part(); + part->next = body->nested.part; + body->nested.part = part; + part->body.contents.text.data = msgtext; + } + else + mail_free_body(&body); + } + } + } + else { + /*---- A single part message, not of type text ----*/ + body = mail_newbody(); + body->type = TYPEMULTIPART; + part = mail_newbody_part(); + body->nested.part = part; + + /*--- The first part, a blank text part to be edited ---*/ + part->body.type = TYPETEXT; + part->body.contents.text.data = msgtext; + + /*--- The second part, what ever it is ---*/ + part->next = mail_newbody_part(); + part = part->next; + part->body.id = generate_message_id(); + copy_body(&(part->body), orig_body); + + /* + * the idea here is to fetch part into storage object + */ + if((part->body.contents.text.data = (void *) so_get(PART_SO_TYPE, NULL, + EDIT_ACCESS)) != NULL){ + if((tmp_text = pine_mail_fetch_body(stream, msgno, section, + &part->body.size.bytes, NIL)) != NULL) + so_nputs((STORE_S *)part->body.contents.text.data, tmp_text, + part->body.size.bytes); + else + mail_free_body(&body); + } + else + mail_free_body(&body); + } + + gf_clear_so_writec((STORE_S *) msgtext); + + return(body); +} + + + +/* + * bounce_msg_body - build body from specified message suitable + * for sending as bounced message + */ +char * +bounce_msg_body(MAILSTREAM *stream, + long int rawno, + char *part, + char **to, + char *subject, + ENVELOPE **outgoingp, + BODY **bodyp, + int *seenp) +{ + char *h, *p, *errstr = NULL; + int i; + STORE_S *msgtext; + gf_io_t pc; + + *outgoingp = mail_newenvelope(); + (*outgoingp)->message_id = generate_message_id(); + (*outgoingp)->subject = cpystr(subject ? subject : "Resent mail...."); + + /* + * Fill in destination if we were given one. If so, note that we + * call p_s_s() below such that it won't prompt... + */ + if(to && *to){ + static char *fakedomain = "@"; + char *tmp_a_string; + + /* rfc822_parse_adrlist feels free to destroy input so copy */ + tmp_a_string = cpystr(*to); + rfc822_parse_adrlist(&(*outgoingp)->to, tmp_a_string, + (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)) + ? fakedomain : ps_global->maildomain); + fs_give((void **) &tmp_a_string); + } + + /* build remail'd header */ + if((h = mail_fetch_header(stream, rawno, part, NULL, 0, FT_PEEK)) != NULL){ + for(p = h, i = 0; (p = strchr(p, ':')) != NULL; p++) + i++; + + /* allocate it */ + (*outgoingp)->remail = (char *) fs_get(strlen(h) + (2 * i) + 1); + + /* + * copy it, "X-"ing out transport headers bothersome to + * software but potentially useful to the human recipient... + */ + p = (*outgoingp)->remail; + bounce_mask_header(&p, h); + do + if(*h == '\015' && *(h+1) == '\012'){ + *p++ = *h++; /* copy CR LF */ + *p++ = *h++; + bounce_mask_header(&p, h); + } + while((*p++ = *h++) != '\0'); + } + /* BUG: else complain? */ + + /* NOT bound for the composer, so no need for PicoText */ + if(!(msgtext = so_get(CharStar, NULL, EDIT_ACCESS))){ + mail_free_envelope(outgoingp); + return(_("Error allocating message text")); + } + + /* mark object for special handling */ + so_attr(msgtext, "rawbody", "1"); + + /* + * Build a fake body description. It's ignored by pine_rfc822_header, + * but we need to set it to something that makes set_mime_types + * not sniff it and pine_rfc822_output_body not re-encode it. + * Setting the encoding to (ENCMAX + 1) will work and shouldn't cause + * problems unless something tries to access body_encodings[] using + * it without proper precautions. We don't want to use ENCOTHER + * cause that tells set_mime_types to sniff it, and we don't want to + * use ENC8BIT since that tells pine_rfc822_output_body to qp-encode + * it. When there's time, it'd be nice to clean this interaction + * up... + */ + *bodyp = mail_newbody(); + (*bodyp)->type = TYPETEXT; + (*bodyp)->encoding = ENCMAX + 1; + (*bodyp)->subtype = cpystr("Plain"); + (*bodyp)->contents.text.data = (void *) msgtext; + gf_set_so_writec(&pc, msgtext); + + if(seenp && rawno > 0L && stream && rawno <= stream->nmsgs){ + MESSAGECACHE *mc; + + if((mc = mail_elt(stream, rawno)) != NULL) + *seenp = mc->seen; + } + + /* pass NULL body to force mail_fetchtext */ + if(!get_body_part_text(stream, NULL, rawno, part, 0L, pc, NULL, NULL, GBPT_NONE)) + errstr = _("Error fetching message contents. Can't Bounce message"); + + gf_clear_so_writec(msgtext); + + return(errstr); +} + + + +/*---------------------------------------------------------------------- + Mask off any header entries we don't want xport software to see + +Args: d -- destination string pointer pointer + s -- source string pointer pointer + + Postfix uses Delivered-To to detect loops. + Received line counting is also used to detect loops in places. + + ----*/ +void +bounce_mask_header(char **d, char *s) +{ + if(((*s == 'R' || *s == 'r') + && (!struncmp(s+1, "esent-", 6) || !struncmp(s+1, "eceived:", 8) + || !struncmp(s+1, "eturn-Path", 10))) + || !struncmp(s, "Delivered-To:", 13)){ + *(*d)++ = 'X'; /* write mask */ + *(*d)++ = '-'; + } +} + + +/*---------------------------------------------------------------------- + Fetch and format text for forwarding + +Args: stream -- Mail stream to fetch text from + body -- Body structure of message being forwarded + msg_no -- Message number of text for forward + part_no -- Part number of text to forward + partial -- If this is > 0 a partial fetch will be done and it will + be done using FT_PEEK so the message will remain unseen. + pc -- Function to write to + prefix -- Prefix for each line + ret_charset -- If we translate to another charset return that + new charset here + +Returns: true if OK, false if problem occured while filtering + +If the text is richtext, it will be converted to plain text, since there's +no rich text editing capabilities in Pine (yet). + +It's up to calling routines to plug in signature appropriately + +As with all internal text, NVT end-of-line conventions are observed. +DOESN'T sanity check the prefix given!!! + ----*/ +int +get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body, + long int msg_no, char *part_no, long partial, gf_io_t pc, + char *prefix, char **ret_charset, unsigned flags) +{ + int we_cancel = 0, dashdata, wrapflags = GFW_FORCOMPOSE, flow_res = 0; + FILTLIST_S filters[12]; + long len; + char *err, *charset, *prefix_p = NULL; + int filtcnt = 0; + char *free_this = NULL; + DELQ_S dq; + + memset(filters, 0, sizeof(filters)); + if(ret_charset) + *ret_charset = NULL; + + if(!pc_is_picotext(pc)) + we_cancel = busy_cue(NULL, NULL, 1); + + /* if null body, we must be talking to a non-IMAP2bis server. + * No MIME parsing provided, so we just grab the message text... + */ + if(body == NULL){ + char *text, *decode_error; + gf_io_t gc; + SourceType src = CharStar; + int rv = 0; + + (void) pine_mail_fetchstructure(stream, msg_no, NULL); + + if((text = pine_mail_fetch_text(stream, msg_no, part_no, NULL, 0)) != NULL){ + gf_set_readc(&gc, text, (unsigned long)strlen(text), src, 0); + + gf_filter_init(); /* no filters needed */ + if(prefix) + gf_link_filter(gf_prefix, gf_prefix_opt(prefix)); + if((decode_error = gf_pipe(gc, pc)) != NULL){ + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s [Formatting error: %s]%s", + NEWLINE, NEWLINE, + decode_error, NEWLINE); + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + gf_puts(tmp_20k_buf, pc); + rv++; + } + } + else{ + gf_puts(NEWLINE, pc); + gf_puts(_(" [ERROR fetching text of message]"), pc); + gf_puts(NEWLINE, pc); + gf_puts(NEWLINE, pc); + rv++; + } + + if(we_cancel) + cancel_busy_cue(-1); + + return(rv == 0); + } + + charset = parameter_val(body->parameter, "charset"); + + if(charset && strucmp(charset, "utf-8") && strucmp(charset, "us-ascii")){ + if(ret_charset) + *ret_charset = "UTF-8"; + } + + /* + * just use detach, but add an auxiliary filter to insert prefix, + * and, perhaps, digest richtext + */ + if(ps_global->full_header != 2 + && !ps_global->postpone_no_flow + && (!body->subtype || !strucmp(body->subtype, "plain"))){ + char *parmval; + + flow_res = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global) + && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global) + && (!prefix || (strucmp(prefix,"> ") == 0) + || strucmp(prefix, ">") == 0)); + if((parmval = parameter_val(body->parameter, + "format")) != NULL){ + if(!strucmp(parmval, "flowed")){ + wrapflags |= GFW_FLOWED; + + fs_give((void **) &parmval); + if((parmval = parameter_val(body->parameter, "delsp")) != NULL){ + if(!strucmp(parmval, "yes")){ + filters[filtcnt++].filter = gf_preflow; + wrapflags |= GFW_DELSP; + } + + fs_give((void **) &parmval); + } + + /* + * if there's no prefix we're forwarding text + * otherwise it's reply text. unless the user's + * tied our hands, alter the prefix to continue flowed + * formatting... + */ + if(flow_res) + wrapflags |= GFW_FLOW_RESULT; + + filters[filtcnt].filter = gf_wrap; + /* + * The 80 will cause longer lines than what is likely + * set by composer_fillcol, so we'll have longer + * quoted and forwarded lines than the lines we type. + * We're fine with that since the alternative is the + * possible wrapping of lines we shouldn't have, which + * is mitigated by this higher 80 limit. + * + * If we were to go back to using composer_fillcol, + * the correct value is composer_fillcol + 1, pine + * is off-by-one from pico. + */ + filters[filtcnt++].data = gf_wrap_filter_opt( + MAX(MIN(ps_global->ttyo->screen_cols + - (prefix ? strlen(prefix) : 0), + 80 - (prefix ? strlen(prefix) : 0)), + 30), /* doesn't have to be 30 */ + 990, /* 998 is the SMTP limit */ + NULL, 0, wrapflags); + } + } + + /* + * if not flowed, remove trailing whitespace to reduce + * confusion, since we're sending out as flowed if we + * can. At some future point, we might try + * plugging in a user-option-controlled heuristic + * flowing filter + * + * We also want to fold "> " quotes so we get the + * attributions correct. + */ + if(flow_res && prefix && !strucmp(prefix, "> ")) + *(prefix_p = prefix + 1) = '\0'; + + if(!(wrapflags & GFW_FLOWED) + && flow_res){ + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(twsp_strip, NULL); + + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(quote_fold, NULL); + } + } + else if(body->subtype){ + int plain_opt = 1; + + if(strucmp(body->subtype,"richtext") == 0){ + filters[filtcnt].filter = gf_rich2plain; + filters[filtcnt++].data = gf_rich2plain_opt(&plain_opt); + } + else if(strucmp(body->subtype,"enriched") == 0){ + filters[filtcnt].filter = gf_enriched2plain; + filters[filtcnt++].data = gf_enriched2plain_opt(&plain_opt); + } + else if(strucmp(body->subtype,"html") == 0){ + if((flags & GBPT_HTML_OK) != GBPT_HTML_OK){ + filters[filtcnt].filter = gf_html2plain; + filters[filtcnt++].data = gf_html2plain_opt(NULL, + ps_global->ttyo->screen_cols, + non_messageview_margin(), + NULL, NULL, GFHP_STRIPPED); + } + } + } + + if(prefix){ + if(ps_global->full_header != 2 + && (F_ON(F_ENABLE_SIGDASHES, ps_global) + || F_ON(F_ENABLE_STRIP_SIGDASHES, ps_global))){ + dashdata = 0; + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata); + } + + filters[filtcnt].filter = gf_prefix; + filters[filtcnt++].data = gf_prefix_opt(prefix); + + if(wrapflags & GFW_FLOWED || flow_res){ + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(post_quote_space, NULL); + } + } + + if(flags & GBPT_DELQUOTES){ + memset(&dq, 0, sizeof(dq)); + dq.lines = Q_DEL_ALL; + dq.is_flowed = 0; + dq.indent_length = 0; + dq.saved_line = &free_this; + dq.handlesp = NULL; + dq.do_color = 0; + dq.delete_all = 1; + + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); + } + + err = detach(stream, msg_no, part_no, partial, &len, pc, + filters[0].filter ? filters : NULL, + ((flags & GBPT_PEEK) ? FT_PEEK : 0) + | ((flags & GBPT_NOINTR) ? DT_NOINTR : 0)); + + if(free_this) + fs_give((void **) &free_this); + + if(prefix_p) + *prefix_p = ' '; + + if (err != (char *) NULL) + /* TRANSLATORS: The first arg is error text, the %ld is the message number */ + q_status_message2(SM_ORDER, 3, 4, "%s: message number %ld", + err, (void *) msg_no); + + if(we_cancel) + cancel_busy_cue(-1); + + return((int) len); +} + + +int +quote_fold(long int linenum, char *line, LT_INS_S **ins, void *local) +{ + char *p; + + if(*line == '>'){ + for(p = line; *p; p++){ + if(isspace((unsigned char) *p)){ + if(*(p+1) == '>') + ins = gf_line_test_new_ins(ins, p, "", -1); + } + else if(*p != '>') + break; + } + } + + return(0); +} + + +int +twsp_strip(long int linenum, char *line, LT_INS_S **ins, void *local) +{ + char *p, *ws = NULL; + + for(p = line; *p; p++){ + /* don't strip trailing space on signature line */ + if(*line == '-' && *(line+1) == '-' && *(line+2) == ' ' && !*(line+3)) + break; + + if(isspace((unsigned char) *p)){ + if(!ws) + ws = p; + } + else + ws = NULL; + } + + if(ws) + ins = gf_line_test_new_ins(ins, ws, "", -(p - ws)); + + return(0); +} + +int +post_quote_space(long int linenum, char *line, LT_INS_S **ins, void *local) +{ + char *p; + + for(p = line; *p; p++) + if(*p != '>'){ + if(p != line && *p != ' ') + ins = gf_line_test_new_ins(ins, p, " ", 1); + + break; + } + + return(0); +} + + +int +sigdash_strip(long int linenum, char *line, LT_INS_S **ins, void *local) +{ + if(*((int *)local) + || (*line == '-' && *(line+1) == '-' + && *(line+2) == ' ' && !*(line+3))){ + *((int *) local) = 1; + return(2); /* skip this line! */ + } + + return(0); +} + + +/*---------------------------------------------------------------------- + return the c-client reference name for the given end_body part + ----*/ +char * +body_partno(MAILSTREAM *stream, long int msgno, struct mail_bodystruct *end_body) +{ + BODY *body; + + (void) pine_mail_fetchstructure(stream, msgno, &body); + return(partno(body, end_body)); +} + + +/*---------------------------------------------------------------------- + return the c-client reference name for the given end_body part + ----*/ +char * +partno(struct mail_bodystruct *body, struct mail_bodystruct *end_body) +{ + PART *part; + int num = 0; + char tmp[64], *p = NULL; + + if(body && body->type == TYPEMULTIPART) { + part = body->nested.part; /* first body part */ + + do { /* for each part */ + num++; + if(&part->body == end_body || (p = partno(&part->body, end_body))){ + snprintf(tmp, sizeof(tmp), "%d%s%.*s", num, (p) ? "." : "", + sizeof(tmp)-10, (p) ? p : ""); + tmp[sizeof(tmp)-1] = '\0'; + if(p) + fs_give((void **)&p); + + return(cpystr(tmp)); + } + } while ((part = part->next) != NULL); /* until done */ + + return(NULL); + } + else if(body && body->type == TYPEMESSAGE && body->subtype + && !strucmp(body->subtype, "rfc822")){ + return(partno(body->nested.msg->body, end_body)); + } + + return((body == end_body) ? cpystr("1") : NULL); +} + + +/*---------------------------------------------------------------------- + Fill in the contents of each body part + +Args: stream -- Stream the message is on + msgno -- Message number the body structure is for + section -- body section associated with body pointer + body -- Body pointer to fill in + +Result: 1 if all went OK, 0 if there was a problem + +This function copies the contents from an original message/body to +a new message/body. It recurses down all multipart levels. + +If one or more part (but not all) can't be fetched, a status message +will be queued. + ----*/ +int +fetch_contents(MAILSTREAM *stream, long int msgno, char *section, struct mail_bodystruct *body) +{ + char *tp; + int got_one = 0; + + if(!body->id) + body->id = generate_message_id(); + + if(body->type == TYPEMULTIPART){ + char subsection[256], *subp; + int n, last_one = 10; /* remember worst case */ + PART *part = body->nested.part; + + if(!(part = body->nested.part)) + return(0); + + subp = subsection; + if(section && *section){ + for(n = 0; + n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++) + ; + + *subp++ = '.'; + } + + n = 1; + do { + snprintf(subp, sizeof(subsection)-(subp-subsection), "%d", n++); + subsection[sizeof(subsection)-1] = '\0'; + got_one = fetch_contents(stream, msgno, subsection, &part->body); + last_one = MIN(last_one, got_one); + } + while((part = part->next) != NULL); + + return(last_one); + } + + if(body->contents.text.data) + return(1); /* already taken care of... */ + + if(body->type == TYPEMESSAGE){ + if(body->subtype && strucmp(body->subtype,"external-body")){ + /* + * the idea here is to fetch everything into storage objects + */ + body->contents.text.data = (void *) so_get(PART_SO_TYPE, NULL, + EDIT_ACCESS); + if(body->contents.text.data + && (tp = pine_mail_fetch_body(stream, msgno, section, + &body->size.bytes, NIL))){ + so_truncate((STORE_S *)body->contents.text.data, + body->size.bytes + 2048); + so_nputs((STORE_S *)body->contents.text.data, tp, + body->size.bytes); + got_one = 1; + } + else + q_status_message1(SM_ORDER | SM_DING, 3, 3, + _("Error fetching part %s"), section); + } else { + got_one = 1; + } + } else { + /* + * the idea here is to fetch everything into storage objects + * so, grab one, then fetch the body part + */ + body->contents.text.data = (void *)so_get(PART_SO_TYPE,NULL,EDIT_ACCESS); + if(body->contents.text.data + && (tp=pine_mail_fetch_body(stream, msgno, section, + &body->size.bytes, NIL))){ + so_truncate((STORE_S *)body->contents.text.data, + body->size.bytes + 2048); + so_nputs((STORE_S *)body->contents.text.data, tp, + body->size.bytes); + got_one = 1; + } + else + q_status_message1(SM_ORDER | SM_DING, 3, 3, + _("Error fetching part %s"), section); + } + + return(got_one); +} + + +/*---------------------------------------------------------------------- + Copy the body structure + +Args: new_body -- Pointer to already allocated body, or NULL, if none + old_body -- The Body to copy + + + This is traverses the body structure recursively copying all elements. +The new_body parameter can be NULL in which case a new body is +allocated. Alternatively it can point to an already allocated body +structure. This is used when copying body parts since a PART includes a +BODY. The contents fields are *not* filled in. + ----*/ + +BODY * +copy_body(struct mail_bodystruct *new_body, struct mail_bodystruct *old_body) +{ + if(old_body == NULL) + return(NULL); + + if(new_body == NULL) + new_body = mail_newbody(); + + new_body->type = old_body->type; + new_body->encoding = old_body->encoding; + + if(old_body->subtype) + new_body->subtype = cpystr(old_body->subtype); + + new_body->parameter = copy_parameters(old_body->parameter); + + if(old_body->id) + new_body->id = cpystr(old_body->id); + + if(old_body->description) + new_body->description = cpystr(old_body->description); + + if(old_body->disposition.type) + new_body->disposition.type = cpystr(old_body->disposition.type); + + new_body->disposition.parameter + = copy_parameters(old_body->disposition.parameter); + + new_body->size = old_body->size; + + if(new_body->type == TYPEMESSAGE + && new_body->subtype && !strucmp(new_body->subtype, "rfc822")){ + new_body->nested.msg = mail_newmsg(); + new_body->nested.msg->body + = copy_body(NULL, old_body->nested.msg->body); + } + else if(new_body->type == TYPEMULTIPART) { + PART **new_partp, *old_part; + + new_partp = &new_body->nested.part; + for(old_part = old_body->nested.part; + old_part != NULL; + old_part = old_part->next){ + *new_partp = mail_newbody_part(); + copy_body(&(*new_partp)->body, &old_part->body); + new_partp = &(*new_partp)->next; + } + } + + return(new_body); +} + + +/*---------------------------------------------------------------------- + Copy the MIME parameter list + + Allocates storage for new part, and returns pointer to new paramter +list. If old_p is NULL, NULL is returned. + ----*/ +PARAMETER * +copy_parameters(PARAMETER *old_p) +{ + PARAMETER *new_p, *p1, *p2; + + if(old_p == NULL) + return((PARAMETER *)NULL); + + new_p = p2 = NULL; + for(p1 = old_p; p1 != NULL; p1 = p1->next){ + set_parameter(&p2, p1->attribute, p1->value); + if(new_p == NULL) + new_p = p2; + } + + return(new_p); +} + + +/*---------------------------------------------------------------------- + Make a complete copy of an envelope and all it's fields + +Args: e -- the envelope to copy + +Result: returns the new envelope, or NULL, if the given envelope was NULL + + ----*/ + +ENVELOPE * +copy_envelope(register ENVELOPE *e) +{ + register ENVELOPE *e2; + + if(!e) + return(NULL); + + e2 = mail_newenvelope(); + e2->remail = e->remail ? cpystr(e->remail) : NULL; + e2->return_path = e->return_path ? rfc822_cpy_adr(e->return_path) : NULL; + e2->date = e->date ? (unsigned char *)cpystr((char *) e->date) + : NULL; + e2->from = e->from ? rfc822_cpy_adr(e->from) : NULL; + e2->sender = e->sender ? rfc822_cpy_adr(e->sender) : NULL; + e2->reply_to = e->reply_to ? rfc822_cpy_adr(e->reply_to) : NULL; + e2->subject = e->subject ? cpystr(e->subject) : NULL; + e2->to = e->to ? rfc822_cpy_adr(e->to) : NULL; + e2->cc = e->cc ? rfc822_cpy_adr(e->cc) : NULL; + e2->bcc = e->bcc ? rfc822_cpy_adr(e->bcc) : NULL; + e2->in_reply_to = e->in_reply_to ? cpystr(e->in_reply_to) : NULL; + e2->newsgroups = e->newsgroups ? cpystr(e->newsgroups) : NULL; + e2->message_id = e->message_id ? cpystr(e->message_id) : NULL; + e2->references = e->references ? cpystr(e->references) : NULL; + e2->followup_to = e->followup_to ? cpystr(e->references) : NULL; + return(e2); +} + + +/*---------------------------------------------------------------------- + Generate the "In-reply-to" text from message header + + Args: message -- Envelope of original message + + Result: returns an alloc'd string or NULL if there is a problem + ----*/ +char * +reply_in_reply_to(ENVELOPE *env) +{ + return((env && env->message_id) ? cpystr(env->message_id) : NULL); +} + + +/*---------------------------------------------------------------------- + Generate a unique message id string. + + Args: ps -- The usual pine structure + + Result: Alloc'd unique string is returned + +Uniqueness is gaurenteed by using the host name, process id, date to the +second and a single unique character +*----------------------------------------------------------------------*/ +char * +generate_message_id(void) +{ + static short osec = 0, cnt = 0; + char idbuf[128]; + char *id; + time_t now; + struct tm *now_x; + char *hostpart = NULL; + + now = time((time_t *)0); + now_x = localtime(&now); + + if(now_x->tm_sec == osec) + cnt++; + else{ + cnt = 0; + osec = now_x->tm_sec; + } + + hostpart = F_ON(F_ROT13_MESSAGE_ID, ps_global) + ? rot13(ps_global->hostname) + : cpystr(ps_global->hostname); + + if(!hostpart) + hostpart = cpystr("huh"); + + snprintf(idbuf, sizeof(idbuf), "", + SYSTYPE, ALPINE_VERSION, (now_x->tm_year) % 100, now_x->tm_mon + 1, + now_x->tm_mday, now_x->tm_hour, now_x->tm_min, now_x->tm_sec, + cnt, getpid(), hostpart); + idbuf[sizeof(idbuf)-1] = '\0'; + + id = cpystr(idbuf); + + if(hostpart) + fs_give((void **) &hostpart); + + return(id); +} + + +char * +generate_user_agent(void) +{ + char buf[128]; + char rev[128]; + + if(F_ON(F_QUELL_USERAGENT, ps_global)) + return(NULL); + + snprintf(buf, sizeof(buf), + "%sAlpine %s (%s %s)", + (pith_opt_user_agent_prefix) ? (*pith_opt_user_agent_prefix)() : "", + ALPINE_VERSION, SYSTYPE, + get_alpine_revision_string(rev, sizeof(rev))); + + return(cpystr(buf)); +} + + +char * +rot13(char *src) +{ + char byte, cap, *p, *ret = NULL; + + if(src && *src){ + ret = (char *) fs_get((strlen(src)+1) * sizeof(char)); + p = ret; + while((byte = *src++) != '\0'){ + cap = byte & 32; + byte &= ~cap; + *p++ = ((byte >= 'A') && (byte <= 'Z') + ? ((byte - 'A' + 13) % 26 + 'A') : byte) | cap; + } + + *p = '\0'; + } + + return(ret); +} + + +/*---------------------------------------------------------------------- + Return the first true address pointer (modulo group syntax allowance) + + Args: addr -- Address list + + Result: First real address pointer, or NULL + ----------------------------------------------------------------------*/ +ADDRESS * +first_addr(struct mail_address *addr) +{ + while(addr && !addr->host) + addr = addr->next; + + return(addr); +} + + +/*---------------------------------------------------------------------- + lit -- this is the source + prenewlines -- prefix the file contents with this many newlines + postnewlines -- postfix the file contents with this many newlines + is_sig -- this is a signature (not a template) + decode_constants -- change C-style constants into their values + ----*/ +char * +get_signature_lit(char *lit, int prenewlines, int postnewlines, int is_sig, int decode_constants) +{ + char *sig = NULL; + + /* + * Should make this smart enough not to do the copying and double + * allocation of space. + */ + if(lit){ + char *tmplit = NULL, *p, *q, *d, save; + size_t len; + + if(decode_constants){ + tmplit = (char *) fs_get((strlen(lit)+1) * sizeof(char)); + tmplit[0] = '\0'; + cstring_to_string(lit, tmplit); + } + else + tmplit = cpystr(lit); + + len = strlen(tmplit) + 5 + (prenewlines+postnewlines) * strlen(NEWLINE); + sig = (char *) fs_get((len+1) * sizeof(char)); + memset(sig, 0, len+1); + d = sig; + while(prenewlines--) + sstrncpy(&d, NEWLINE, len-(d-sig)); + + if(is_sig && F_ON(F_ENABLE_SIGDASHES, ps_global) && + !sigdashes_are_present(tmplit)){ + sstrncpy(&d, SIGDASHES, len-(d-sig)); + sstrncpy(&d, NEWLINE, len-(d-sig)); + } + + sig[len] = '\0'; + + p = tmplit; + while(*p){ + /* get a line */ + q = strpbrk(p, "\n\r"); + if(q){ + save = *q; + *q = '\0'; + } + + /* + * Strip trailing space if we are doing a signature and + * this line is not sigdashes. + */ + if(is_sig && strcmp(p, SIGDASHES)) + removing_trailing_white_space(p); + + while((d-sig) <= len && (*d = *p++) != '\0') + d++; + + if(q){ + if((d-sig) <= len) + *d++ = save; + + p = q+1; + } + else + break; + } + + while(postnewlines--) + sstrncpy(&d, NEWLINE, len-(d-sig)); + + sig[len] = '\0'; + + if((d-sig) <= len) + *d = '\0'; + + if(tmplit) + fs_give((void **) &tmplit); + } + + return(sig); +} + + +int +sigdashes_are_present(char *sig) +{ + char *p; + + p = srchstr(sig, SIGDASHES); + while(p && !((p == sig || (p[-1] == '\n' || p[-1] == '\r')) && + (p[3] == '\0' || p[3] == '\n' || p[3] == '\r'))) + p = srchstr(p+1, SIGDASHES); + + return(p ? 1 : 0); +} + + +/*---------------------------------------------------------------------- + Acquire the pinerc defined signature file pathname + + ----*/ +char * +signature_path(char *sname, char *sbuf, size_t len) +{ + *sbuf = '\0'; + if(sname && *sname){ + size_t spl = strlen(sname); + if(IS_REMOTE(sname)){ + if(spl < len - 1) + strncpy(sbuf, sname, len-1); + } + else if(is_absolute_path(sname)){ + strncpy(sbuf, sname, len-1); + sbuf[len-1] = '\0'; + fnexpand(sbuf, len); + } + else if(ps_global->VAR_OPER_DIR){ + if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1) + build_path(sbuf, ps_global->VAR_OPER_DIR, sname, len); + } + else{ + char *lc = last_cmpnt(ps_global->pinerc); + + sbuf[0] = '\0'; + if(lc != NULL){ + strncpy(sbuf,ps_global->pinerc,MIN(len-1,lc-ps_global->pinerc)); + sbuf[MIN(len-1,lc-ps_global->pinerc)] = '\0'; + } + + strncat(sbuf, sname, MAX(len-1-strlen(sbuf), 0)); + sbuf[len-1] = '\0'; + } + } + + return(*sbuf ? sbuf : NULL); +} + + +char * +simple_read_remote_file(char *name, char *subtype) +{ + int try_cache; + REMDATA_S *rd; + char *file = NULL; + + + dprint((7, "simple_read_remote_file(%s, %s)\n", name ? name : "?", subtype ? subtype : "?")); + + /* + * We could parse the name here to find what type it is. So far we + * only have type RemImap. + */ + rd = rd_create_remote(RemImap, name, subtype, + NULL, _("Error: "), _("Can't fetch remote configuration.")); + if(!rd) + goto bail_out; + + try_cache = rd_read_metadata(rd); + + if(rd->access == MaybeRorW){ + if(rd->read_status == 'R') + rd->access = ReadOnly; + else + rd->access = ReadWrite; + } + + if(rd->access != NoExists){ + + rd_check_remvalid(rd, 1L); + + /* + * If the cached info says it is readonly but + * it looks like it's been fixed now, change it to readwrite. + */ + if(rd->read_status == 'R'){ + /* + * We go to this trouble since readonly sigfiles + * are likely a mistake. They are usually supposed to be + * readwrite so we open it and check if it's been fixed. + */ + rd_check_readonly_access(rd); + if(rd->read_status == 'W'){ + rd->access = ReadWrite; + rd->flags |= REM_OUTOFDATE; + } + else + rd->access = ReadOnly; + } + + if(rd->flags & REM_OUTOFDATE){ + if(rd_update_local(rd) != 0){ + + dprint((1, + "simple_read_remote_file: rd_update_local failed\n")); + /* + * Don't give up altogether. We still may be + * able to use a cached copy. + */ + } + else{ + dprint((7, + "%s: copied remote to local (%ld)\n", + rd->rn ? rd->rn : "?", (long)rd->last_use)); + } + } + + if(rd->access == ReadWrite) + rd->flags |= DO_REMTRIM; + } + + /* If we couldn't get to remote folder, try using the cached copy */ + if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){ + if(try_cache){ + rd->access = ReadOnly; + rd->flags |= USE_OLD_CACHE; + q_status_message(SM_ORDER, 3, 4, + "Can't contact remote server, using cached copy"); + dprint((2, + "Can't open remote file %s, using local cached copy %s readonly\n", + rd->rn ? rd->rn : "?", + rd->lf ? rd->lf : "?")); + } + else{ + rd->flags &= ~DO_REMTRIM; + goto bail_out; + } + } + + file = read_file(rd->lf, READ_FROM_LOCALE); + +bail_out: + if(rd) + rd_close_remdata(&rd); + + return(file); +} + + +/*---------------------------------------------------------------------- + Build the body for the multipart/alternative part + + Args: + + Result: + + ----------------------------------------------------------------------*/ +BODY * +forward_multi_alt(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body, + long int msgno, char *sect_prefix, void *msgtext, gf_io_t pc, int flags) +{ + BODY *body = NULL; + PART *part = NULL, *bestpart = NULL; + char tmp_buf[256]; + char *new_charset = NULL; + int partnum, bestpartnum; + + if(ps_global->force_prefer_plain + || (!ps_global->force_no_prefer_plain + && F_ON(F_PREFER_PLAIN_TEXT, ps_global))){ + for(part = orig_body->nested.part, partnum = 1; + part; + part = part->next, partnum++) + if((!part->body.type || part->body.type == TYPETEXT) + && (!part->body.subtype + || !strucmp(part->body.subtype, "plain"))) + break; + } + + /* + * Else choose last alternative among plain or html parts. + * Perhaps we should really be using mime_show() to make this + * potentially more general than just plain or html. + */ + if(!part){ + for(part = orig_body->nested.part, partnum = 1; + part; + part = part->next, partnum++){ + if((!part->body.type || part->body.type == TYPETEXT) + && ((!part->body.subtype || !strucmp(part->body.subtype, "plain")) + || + (part->body.subtype && !strucmp(part->body.subtype, "html")))){ + bestpart = part; + bestpartnum = partnum; + } + } + + part = bestpart; + partnum = bestpartnum; + } + + /* + * IF something's interesting insert it + * AND forget the rest of the multipart + */ + if(part){ + body = mail_newbody(); + body->type = TYPETEXT; + body->contents.text.data = msgtext; + + /* record character set, flowing, etc */ + body->parameter = copy_parameters(part->body.parameter); + body->size.bytes = part->body.size.bytes; + + if(!(flags & FWD_ANON)){ + forward_delimiter(pc); + reply_forward_header(stream, msgno, sect_prefix, env, pc, ""); + } + + snprintf(tmp_buf, sizeof(tmp_buf), "%.*s%s%s%d", + sizeof(tmp_buf)/2, sect_prefix ? sect_prefix : "", + sect_prefix ? "." : "", flags & FWD_NESTED ? "1." : "", + partnum); + tmp_buf[sizeof(tmp_buf)-1] = '\0'; + get_body_part_text(stream, &part->body, msgno, tmp_buf, 0L, pc, + NULL, &new_charset, GBPT_NONE); + + /* + * get_body_part_text translated the data to a new charset. + * We need to record that fact in body. + */ + if(new_charset) + set_parameter(&body->parameter, "charset", new_charset); + } + else + q_status_message(SM_ORDER | SM_DING, 3, 3, + "No suitable part found. Forwarding as attachment"); + + return(body); +} + + +void +reply_append_addr(struct mail_address **dest, struct mail_address *src) +{ + for( ; *dest; dest = &(*dest)->next) + ; + + *dest = src; +} -- cgit v1.2.3-54-g00ecf