diff options
author | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
---|---|---|
committer | Eduardo Chappa <echappa@gmx.com> | 2013-02-03 00:59:38 -0700 |
commit | 094ca96844842928810f14844413109fc6cdd890 (patch) | |
tree | e60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /alpine/reply.c | |
download | alpine-094ca96844842928810f14844413109fc6cdd890.tar.xz |
Initial Alpine Version
Diffstat (limited to 'alpine/reply.c')
-rw-r--r-- | alpine/reply.c | 2549 |
1 files changed, 2549 insertions, 0 deletions
diff --git a/alpine/reply.c b/alpine/reply.c new file mode 100644 index 00000000..ee90ebd0 --- /dev/null +++ b/alpine/reply.c @@ -0,0 +1,2549 @@ +#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 + * + * ======================================================================== + */ + +/*====================================================================== + Code here for forward and reply to mail + A few support routines as well + + This code will forward and reply to MIME messages. The Alpine composer +at this time will only support non-text segments at the end of a +message so, things don't always come out as one would like. If you +always forward a message in MIME format, all will be correct. Forwarding +of nested MULTIPART messages will work. There's still a problem with +MULTIPART/ALTERNATIVE as the "first text part" rule doesn't allow modifying +the equivalent parts. Ideally, we should probably such segments as a +single attachment when forwarding/replying. It would also be real nice to +flatten out the nesting in the composer so pieces inside can get snipped. + +The evolution continues... + =====*/ + + +#include "headers.h" +#include "reply.h" +#include "status.h" +#include "radio.h" +#include "send.h" +#include "titlebar.h" +#include "mailindx.h" +#include "help.h" +#include "signal.h" +#include "mailcmd.h" +#include "alpine.h" +#include "roleconf.h" +#include "../pith/state.h" +#include "../pith/conf.h" +#include "../pith/init.h" +#include "../pith/filter.h" +#include "../pith/pattern.h" +#include "../pith/charset.h" +#include "../pith/mimedesc.h" +#include "../pith/remote.h" +#include "../pith/news.h" +#include "../pith/util.h" +#include "../pith/detoken.h" +#include "../pith/newmail.h" +#include "../pith/readfile.h" +#include "../pith/tempfile.h" +#include "../pith/busy.h" +#include "../pith/ablookup.h" + + +/* + * Internal Prototypes + */ +int reply_poster_followup(ENVELOPE *); +int sigedit_exit_for_pico(struct headerentry *, void (*)(void), int, char **); +long new_mail_for_pico(int, int); +void cmd_input_for_pico(void); +int display_message_for_pico(int); +char *checkpoint_dir_for_pico(char *, size_t); +void resize_for_pico(void); +PCOLORS *colors_for_pico(void); +void free_pcolors(PCOLORS **); + + +/*---------------------------------------------------------------------- + Fill in an outgoing message for reply and pass off to send + + Args: pine_state -- The usual pine structure + + Result: Reply is formatted and passed off to composer/mailer + +Reply + + - put senders address in To field + - search to and cc fields to see if we aren't the only recipients + - if other than us, ask if we should reply to all. + - if answer yes, fill out the To and Cc fields + - fill in the fcc argument + - fill in the subject field + - fill out the body and the attachments + - pass off to pine_send() + ---*/ +int +reply(struct pine *pine_state, ACTION_S *role_arg) +{ + ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent; + ADDRESS *us_in_to_and_cc, *ap; + ENVELOPE *env, *outgoing; + BODY *body, *orig_body = NULL; + REPLY_S reply; + void *msgtext = NULL; + char *tmpfix = NULL, *prefix = NULL, *fcc = NULL, *errmsg = NULL; + long msgno, j, totalm, rflags, *seq = NULL; + int i, include_text = 0, times = -1, warned = 0, rv = 0, + flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0; + int rolemsg = 0, copytomsg = 0; + gf_io_t pc; + PAT_STATE dummy; + REDRAFT_POS_S *redraft_pos = NULL; + ACTION_S *role = NULL, *nrole; +#if defined(DOS) && !defined(_WINDOWS) + char *reserve; +#endif + + outgoing = mail_newenvelope(); + totalm = mn_total_cur(pine_state->msgmap); + seq = (long *)fs_get(((size_t)totalm + 1) * sizeof(long)); + + dprint((4,"\n - reply (%s msgs) -\n", comatose(totalm))); + + saved_from = (ADDRESS *) NULL; + saved_to = (ADDRESS *) NULL; + saved_cc = (ADDRESS *) NULL; + saved_resent = (ADDRESS *) NULL; + + us_in_to_and_cc = (ADDRESS *) NULL; + + outgoing->subject = NULL; + + memset((void *)&reply, 0, sizeof(reply)); + + if(ps_global->full_header == 2 + && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)) + reply_raw_body = 1; + + /* + * We may have to loop through first to figure out what default + * reply-indent-string to offer... + */ + if(mn_total_cur(pine_state->msgmap) > 1 && + F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state) && + reply_quote_str_contains_tokens()){ + for(msgno = mn_first_cur(pine_state->msgmap); + msgno > 0L && !tmpfix; + msgno = mn_next_cur(pine_state->msgmap)){ + + env = pine_mail_fetchstructure(pine_state->mail_stream, + mn_m2raw(pine_state->msgmap, msgno), + NULL); + if(!env) { + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't reply to it."), + long2string(msgno)); + goto done_early; + } + + if(!tmpfix){ /* look for prefix? */ + tmpfix = reply_quote_str(env); + if(prefix){ + i = strcmp(tmpfix, prefix); + fs_give((void **) &tmpfix); + if(i){ /* don't check back if dissimilar */ + fs_give((void **) &prefix); + /* + * We free prefix, not tmpfix. We set tmpfix to prefix + * so that we won't come check again. + */ + tmpfix = prefix = cpystr("> "); + } + } + else{ + prefix = tmpfix; + tmpfix = NULL; /* check back later? */ + } + } + } + + tmpfix = prefix; + } + + /* + * Loop thru the selected messages building the + * outgoing envelope's destinations... + */ + for(msgno = mn_first_cur(pine_state->msgmap); + msgno > 0L; + msgno = mn_next_cur(pine_state->msgmap)){ + + /*--- Grab current envelope ---*/ + env = pine_mail_fetchstructure(pine_state->mail_stream, + seq[++times] = mn_m2raw(pine_state->msgmap, msgno), + NULL); + if(!env) { + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't reply to it."), + long2string(msgno)); + goto done_early; + } + + /* + * We check for the prefix here if we didn't do it in the first + * loop above. This is just to save having to go through the loop + * twice in the cases where we don't need to. + */ + if(!tmpfix){ + tmpfix = reply_quote_str(env); + if(prefix){ + i = strcmp(tmpfix, prefix); + fs_give((void **) &tmpfix); + if(i){ /* don't check back if dissimilar */ + fs_give((void **) &prefix); + tmpfix = prefix = cpystr("> "); + } + } + else{ + prefix = tmpfix; + tmpfix = NULL; /* check back later? */ + } + } + + /* + * For consistency, the first question is always "include text?" + */ + if(!times){ /* only first time */ + char *p = cpystr(prefix); + + if((include_text=reply_text_query(pine_state,totalm,&prefix)) < 0) + goto done_early; + + /* edited prefix? */ + if(strcmp(p, prefix)) + tmpfix = prefix; /* stop looking */ + + fs_give((void **)&p); + } + + /* + * If we're agg-replying or there's a newsgroup and the user want's + * to post to news *and* via email, add relevant addresses to the + * outgoing envelope... + * + * The single message case gets us around the aggregate reply + * to messages in a mixed mail-news archive where some might + * have newsgroups and others not or whatever. + */ + if(totalm > 1L || ((i = reply_news_test(env, outgoing)) & 1)){ + if(totalm > 1) + flags |= RSF_FORCE_REPLY_TO; + + if(!reply_harvest(pine_state, seq[times], NULL, env, + &saved_from, &saved_to, &saved_cc, + &saved_resent, &flags)) + goto done_early; + } + else if(i == 0) + goto done_early; + + /* collect a list of addresses that are us in to and cc fields */ + if(env->to) + if((ap=reply_cp_addr(pine_state, 0L, NULL, + NULL, us_in_to_and_cc, NULL, + env->to, RCA_ONLY_US)) != NULL) + reply_append_addr(&us_in_to_and_cc, ap); + + if(env->cc) + if((ap=reply_cp_addr(pine_state, 0L, NULL, + NULL, us_in_to_and_cc, NULL, + env->cc, RCA_ONLY_US)) != NULL) + reply_append_addr(&us_in_to_and_cc, ap); + + /*------------ Format the subject line ---------------*/ + if(outgoing->subject){ + /* + * if reply to more than one message, and all subjects + * match, so be it. otherwise set it to something generic... + */ + if(strucmp(outgoing->subject, + reply_subject(env->subject,tmp_20k_buf,SIZEOF_20KBUF))){ + fs_give((void **)&outgoing->subject); + outgoing->subject = cpystr("Re: several messages"); + } + } + else + outgoing->subject = reply_subject(env->subject, NULL, 0); + } + + /* fill reply header */ + reply_seed(pine_state, outgoing, env, saved_from, + saved_to, saved_cc, saved_resent, + &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg); + if(errmsg){ + if(*errmsg){ + q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg); + display_message(NO_OP_COMMAND); + } + + fs_give((void **)&errmsg); + } + + if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */ + goto done_early; + + /* Setup possible role */ + if(role_arg) + role = copy_action(role_arg); + + if(!role){ + rflags = ROLE_REPLY; + if(nonempty_patterns(rflags, &dummy)){ + /* setup default role */ + nrole = NULL; + j = mn_first_cur(pine_state->msgmap); + do { + role = nrole; + nrole = set_role_from_msg(pine_state, rflags, + mn_m2raw(pine_state->msgmap, j), + NULL); + } while(nrole && (!role || nrole == role) + && (j=mn_next_cur(pine_state->msgmap)) > 0L); + + if(!role || nrole == role) + role = nrole; + else + role = NULL; + + if(confirm_role(rflags, &role)) + role = combine_inherited_role(role); + else{ /* cancel reply */ + role = NULL; + cmd_cancelled("Reply"); + goto done_early; + } + } + } + + /* + * Reply_seed may call c-client in get_fcc_based_on_to, so env may + * no longer be valid. Get it again. + * Similarly for set_role_from_message. + */ + env = pine_mail_fetchstructure(pine_state->mail_stream, seq[times], NULL); + + if(role){ + rolemsg++; + /* override fcc gotten in reply_seed */ + if(role->fcc && fcc) + fs_give((void **) &fcc); + } + + if(F_ON(F_COPY_TO_TO_FROM, pine_state) && !(role && role->from)){ + /* + * A list of all of our addresses that appear in the To + * and cc fields is in us_in_to_and_cc. + * If there is exactly one address in that list then + * use it for the outgoing From. + */ + if(us_in_to_and_cc && !us_in_to_and_cc->next){ + PINEFIELD *custom, *pf; + ADDRESS *a = NULL; + char *addr = NULL; + + /* + * Check to see if this address is different from what + * we would have used anyway. If it is, notify the user + * with a status message. This is pretty hokey because we're + * mimicking how pine_send would set the From address and + * there is no coordination between the two. + */ + + /* in case user has a custom From value */ + custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef); + + pf = (PINEFIELD *) fs_get(sizeof(*pf)); + memset((void *) pf, 0, sizeof(*pf)); + pf->name = cpystr("From"); + pf->addr = &a; + if(set_default_hdrval(pf, custom) >= UseAsDef + && pf->textbuf && pf->textbuf[0]){ + removing_trailing_white_space(pf->textbuf); + (void)removing_double_quotes(pf->textbuf); + build_address(pf->textbuf, &addr, NULL, NULL, NULL); + rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain); + if(addr) + fs_give((void **) &addr); + } + + if(!*pf->addr) + *pf->addr = generate_from(); + + if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){ + copytomsg++; + if(!role){ + role = (ACTION_S *) fs_get(sizeof(*role)); + memset((void *) role, 0, sizeof(*role)); + role->is_a_role = 1; + } + + role->from = us_in_to_and_cc; + us_in_to_and_cc = NULL; + } + + free_customs(custom); + free_customs(pf); + } + } + + if(role){ + if(rolemsg && copytomsg) + q_status_message1(SM_ORDER, 3, 4, + _("Replying using role \"%s\" and To as From"), role->nick); + else if(rolemsg) + q_status_message1(SM_ORDER, 3, 4, + _("Replying using role \"%s\""), role->nick); + else if(copytomsg) + q_status_message(SM_ORDER, 3, 4, + _("Replying using incoming To as outgoing From")); + } + + if(us_in_to_and_cc) + mail_free_address(&us_in_to_and_cc); + + + seq[++times] = -1L; /* mark end of sequence list */ + + /*========== Other miscelaneous fields ===================*/ + outgoing->in_reply_to = reply_in_reply_to(env); + outgoing->references = reply_build_refs(env); + outgoing->message_id = generate_message_id(); + + if(!outgoing->to && + !outgoing->cc && + !outgoing->bcc && + !outgoing->newsgroups) + q_status_message(SM_ORDER | SM_DING, 3, 6, + _("Warning: no valid addresses to reply to!")); + + + /*==================== Now fix up the message body ====================*/ + + /* + * create storage object to be used for message text + */ + if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){ + q_status_message(SM_ORDER | SM_DING, 3, 4, + _("Error allocating message text")); + goto done_early; + } + + gf_set_so_writec(&pc, (STORE_S *) msgtext); + + /*---- Include the original text if requested ----*/ + if(include_text && totalm > 1L){ + char *sig; + int impl, template_len = 0, leave_cursor_at_top = 0; + + + env = NULL; + if(role && role->template){ + 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((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++; + + body = mail_newbody(); + body->type = TYPETEXT; + body->contents.text.data = msgtext; + + for(msgno = mn_first_cur(pine_state->msgmap); + msgno > 0L; + msgno = mn_next_cur(pine_state->msgmap)){ + + if(env){ /* put 2 between messages */ + gf_puts(NEWLINE, pc); + gf_puts(NEWLINE, pc); + } + + /*--- Grab current envelope ---*/ + env = pine_mail_fetchstructure(pine_state->mail_stream, + mn_m2raw(pine_state->msgmap, msgno), + &orig_body); + if(!env){ + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't reply to it."), + long2string(mn_get_cur(pine_state->msgmap))); + goto done_early; + } + + if(orig_body == NULL || orig_body->type == TYPETEXT || reply_raw_body) { + reply_delimiter(env, role, pc); + if(F_ON(F_INCLUDE_HEADER, pine_state)) + reply_forward_header(pine_state->mail_stream, + mn_m2raw(pine_state->msgmap,msgno), + NULL, env, pc, prefix); + + get_body_part_text(pine_state->mail_stream, reply_raw_body ? NULL : orig_body, + mn_m2raw(pine_state->msgmap, msgno), + reply_raw_body ? NULL : "1", 0L, pc, prefix, + NULL, GBPT_NONE); + } + else if(orig_body->type == TYPEMULTIPART) { + if(!warned++) + q_status_message(SM_ORDER,3,7, + _("WARNING! Attachments not included in multiple reply.")); + + if(orig_body->nested.part + && orig_body->nested.part->body.type == TYPETEXT) { + /*---- First part of the message is text -----*/ + reply_delimiter(env, role, pc); + if(F_ON(F_INCLUDE_HEADER, pine_state)) + reply_forward_header(pine_state->mail_stream, + mn_m2raw(pine_state->msgmap, + msgno), + NULL, env, pc, prefix); + + get_body_part_text(pine_state->mail_stream, + &orig_body->nested.part->body, + mn_m2raw(pine_state->msgmap, msgno), + "1", 0L, pc, prefix, NULL, GBPT_NONE); + } + else{ + q_status_message(SM_ORDER,0,3, + _("Multipart with no leading text part.")); + } + } + else{ + /*---- Single non-text message of some sort ----*/ + q_status_message(SM_ORDER,3,3, + _("Non-text message not included.")); + } + } + + 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); + } + } + else{ + msgno = mn_m2raw(pine_state->msgmap, + mn_get_cur(pine_state->msgmap)); + + /*--- Grab current envelope ---*/ + env = pine_mail_fetchstructure(pine_state->mail_stream, msgno, + &orig_body); + + /* + * If the charset of the body part is different from ascii and + * charset conversion is _not_ happening, then preserve the original + * charset from the message so that if we don't enter any new + * chars with the hibit set we can use the original charset. + * If not all those things, then don't try to preserve it. + */ + if(orig_body){ + char *charset; + + charset = parameter_val(orig_body->parameter, "charset"); + if(charset && strucmp(charset, "us-ascii") != 0){ + CONV_TABLE *ct; + + /* + * There is a non-ascii charset, is there conversion happening? + */ + if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){ + reply.orig_charset = charset; + charset = NULL; + } + } + + if(charset) + fs_give((void **) &charset); + } + + if(env) { + if(!(body = reply_body(pine_state->mail_stream, env, orig_body, + msgno, NULL, msgtext, prefix, + include_text, role, 1, &redraft_pos))) + goto done_early; + } + else{ + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't reply to it."), + long2string(mn_get_cur(pine_state->msgmap))); + goto done_early; + } + } + + /* fill in reply structure */ + reply.prefix = prefix; + reply.mailbox = cpystr(pine_state->mail_stream->mailbox); + reply.origmbox = cpystr(pine_state->mail_stream->original_mailbox + ? pine_state->mail_stream->original_mailbox + : pine_state->mail_stream->mailbox); + reply.data.uid.msgs = (imapuid_t *) fs_get((times + 1) * sizeof(imapuid_t)); + if((reply.data.uid.validity = pine_state->mail_stream->uid_validity) != 0){ + reply.uid = 1; + for(i = 0; i < times ; i++) + reply.data.uid.msgs[i] = mail_uid(pine_state->mail_stream, seq[i]); + } + else{ + reply.msgno = 1; + for(i = 0; i < times ; i++) + reply.data.uid.msgs[i] = seq[i]; + } + + reply.data.uid.msgs[i] = 0; /* tie off list */ + +#if defined(DOS) && !defined(_WINDOWS) + free((void *)reserve); +#endif + + /* partially formatted outgoing message */ + pine_send(outgoing, &body, _("COMPOSE MESSAGE REPLY"), + role, fcc, &reply, redraft_pos, NULL, NULL, 0); + + rv++; + pine_free_body(&body); + if(reply.mailbox) + fs_give((void **) &reply.mailbox); + if(reply.origmbox) + fs_give((void **) &reply.origmbox); + if(reply.orig_charset) + fs_give((void **) &reply.orig_charset); + fs_give((void **) &reply.data.uid.msgs); + done_early: + if((STORE_S *) msgtext) + gf_clear_so_writec((STORE_S *) msgtext); + + mail_free_envelope(&outgoing); + mail_free_address(&saved_from); + mail_free_address(&saved_to); + mail_free_address(&saved_cc); + mail_free_address(&saved_resent); + + fs_give((void **)&seq); + + if(prefix) + fs_give((void **)&prefix); + + if(fcc) + fs_give((void **) &fcc); + + free_redraft_pos(&redraft_pos); + free_action(&role); + return rv; +} + + +/* + * Ask user to confirm role choice, or choose another role. + * + * Args role -- A pointer into the pattern_h space at the default + * role to use. This can't be a copy, the comparison + * relies on it pointing at the actual role. + * This arg is also used to return a pointer to the + * chosen role. + * + * Returns 1 -- Yes, use role which is now in *role. This may not be + * the same as the role passed in and it may even be NULL. + * 0 -- Cancel reply. + */ +int +confirm_role(long int rflags, ACTION_S **role) +{ + ACTION_S *role_p = NULL; + ACTION_S *default_role = NULL; + char prompt[80], *prompt_fodder; + int cmd, done, ret = 1; + void (*prev_screen)(struct pine *) = ps_global->prev_screen, + (*redraw)(void) = ps_global->redrawer; + PAT_S *curpat, *pat; + PAT_STATE pstate; + HelpType help; + ESCKEY_S ekey[4]; + + if(!nonempty_patterns(ROLE_DO_ROLES, &pstate) || !role) + return(ret); + + /* + * If this is a reply or forward and the role doesn't require confirmation, + * then we just return with what was passed in. + */ + if(((rflags & ROLE_REPLY) && + *role && (*role)->repl_type == ROLE_REPL_NOCONF) || + ((rflags & ROLE_FORWARD) && + *role && (*role)->forw_type == ROLE_FORW_NOCONF) || + ((rflags & ROLE_COMPOSE) && + *role && (*role)->comp_type == ROLE_COMP_NOCONF) || + (!*role && F_OFF(F_ROLE_CONFIRM_DEFAULT, ps_global) + && !ps_global->default_role)) + return(ret); + + /* + * Check that there is at least one role available. This is among all + * roles, not just the reply roles or just the forward roles. That's + * because we have ^T take us to all the roles, not the category-specific + * roles. + */ + if(!(pat = last_pattern(&pstate))) + return(ret); + + ekey[0].ch = 'y'; + ekey[0].rval = 'y'; + + ekey[1].ch = 'n'; + ekey[1].rval = 'n'; + + ekey[2].ch = ctrl('T'); + ekey[2].rval = 2; + ekey[2].name = "^T"; + + ekey[3].ch = -1; + + /* check for more than one role available (or no role set) */ + if(pat == first_pattern(&pstate) && *role) /* no ^T */ + ekey[2].ch = -1; + + /* + * Setup default role + * Go through the loop just in case default_role doesn't point + * to a real current role. + */ + if(ps_global->default_role){ + for(pat = first_pattern(&pstate); + pat; + pat = next_pattern(&pstate)){ + if(pat->action == ps_global->default_role){ + default_role = ps_global->default_role; + break; + } + } + } + + curpat = NULL; + + /* override default */ + if(*role){ + for(pat = first_pattern(&pstate); + pat; + pat = next_pattern(&pstate)){ + if(pat->action == *role){ + curpat = pat; + break; + } + } + } + + if(rflags & ROLE_REPLY) + prompt_fodder = _("Reply"); + else if(rflags & ROLE_FORWARD) + prompt_fodder = _("Forward"); + else + prompt_fodder = _("Compose"); + + done = 0; + while(!done){ + if(curpat){ + char buf[100]; + + help = h_role_confirm; + ekey[0].name = "Y"; + ekey[0].label = N_("Yes"); + ekey[1].name = "N"; + if(default_role) + ekey[1].label = N_("No, use default role"); + else + ekey[1].label = N_("No, use default settings"); + + ekey[2].label = N_("To Select Alternate Role"); + + if(curpat->patgrp && curpat->patgrp->nick) + /* TRANSLATORS: This is something like Use role <nickname of role> for Reply? */ + snprintf(prompt, sizeof(prompt), _("Use role \"%s\" for %s? "), + short_str(curpat->patgrp->nick, buf, sizeof(buf), 50, MidDots), + prompt_fodder); + else + snprintf(prompt, sizeof(prompt), + _("Use role \"<a role without a nickname>\" for %s? "), + prompt_fodder); + } + else{ + help = h_norole_confirm; + ekey[0].name = "Ret"; + ekey[0].label = prompt_fodder; + ekey[1].name = ""; + ekey[1].label = ""; + ekey[2].label = N_("To Select Role"); + snprintf(prompt, sizeof(prompt), + _("Press Return to %s using %s role, or ^T to select a role "), + prompt_fodder, default_role ? _("default") : _("no")); + } + + prompt[sizeof(prompt)-1] = '\0'; + + cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey, + 'y', 'x', help, RB_NORM); + + switch(cmd){ + case 'y': /* Accept */ + done++; + *role = curpat ? curpat->action : default_role; + break; + + case 'x': /* Cancel */ + ret = 0; + /* fall through */ + + case 'n': /* NoRole */ + done++; + *role = default_role; + break; + + case 2: /* ^T */ + if(role_select_screen(ps_global, &role_p, 0) >= 0){ + if(role_p){ + for(pat = first_pattern(&pstate); + pat; + pat = next_pattern(&pstate)){ + if(pat->action == role_p){ + curpat = pat; + break; + } + } + } + else + curpat = NULL; + } + + ClearBody(); + ps_global->mangled_body = 1; + ps_global->prev_screen = prev_screen; + ps_global->redrawer = redraw; + break; + } + } + + return(ret); +} + + +/* + * reply_to_all_query - Ask user about replying to all recipients + * + * Returns: -1 if cancel, 0 otherwise + * by reference: flagp + */ +int +reply_to_all_query(int *flagp) +{ + switch(want_to("Reply to all recipients", + 'n', 'x', NO_HELP, WT_SEQ_SENSITIVE)){ + case 'x' : + return(-1); + + case 'y' : /* set reply-all bit */ + (*flagp) |= RSF_FORCE_REPLY_ALL; + break; + + case 'n' : /* clear reply-all bit */ + (*flagp) &= ~RSF_FORCE_REPLY_ALL; + break; + } + + return(0); +} + + +/* + * reply_using_replyto_query - Ask user about replying with reply-to value + * + * Returns: 'y' if yes + * 'x' if cancel + */ +int +reply_using_replyto_query(void) +{ + return(want_to("Use \"Reply-To:\" address instead of \"From:\" address", + 'y', 'x', NO_HELP,WT_SEQ_SENSITIVE)); +} + + +/* + * reply_text_query - Ask user about replying with text... + * + * Returns: 1 if include the text + * 0 if we're NOT to include the text + * -1 on cancel or error + */ +int +reply_text_query(struct pine *ps, long int many, char **prefix) +{ + int ret, edited = 0; + static ESCKEY_S rtq_opts[] = { + {'y', 'y', "Y", N_("Yes")}, + {'n', 'n', "N", N_("No")}, + {-1, 0, NULL, NULL}, /* may be overridden below */ + {-1, 0, NULL, NULL} + }; + + if(F_ON(F_AUTO_INCLUDE_IN_REPLY, ps) + && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps)) + return(1); + + while(1){ + if(many > 1L) + /* TRANSLATORS: The final three %s's can probably be safely ignored */ + snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s original messages in Reply%s%s%s? "), + comatose(many), + F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "", + F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "", + F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : ""); + else + snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include original message in Reply%s%s%s? "), + F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "", + F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "", + F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : ""); + + if(F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){ + rtq_opts[2].ch = ctrl('R'); + rtq_opts[2].rval = 'r'; + rtq_opts[2].name = "^R"; + rtq_opts[2].label = N_("Edit Indent String"); + } + else + rtq_opts[2].ch = -1; + + switch(ret = radio_buttons(tmp_20k_buf, + ps->ttyo->screen_rows > 4 + ? -FOOTER_ROWS(ps_global) : -1, + rtq_opts, + (edited || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)) + ? 'y' : 'n', + 'x', NO_HELP, RB_SEQ_SENSITIVE)){ + case 'x': + cmd_cancelled("Reply"); + return(-1); + + case 'r': + if(prefix && *prefix){ + int done = 0; + char buf[64]; + int flags; + + while(!done){ + strncpy(buf, *prefix, sizeof(buf)-1); + buf[sizeof(buf)-1] = '\0'; + + flags = OE_APPEND_CURRENT | + OE_KEEP_TRAILING_SPACE | + OE_DISALLOW_HELP | + OE_SEQ_SENSITIVE; + + switch(optionally_enter(buf, ps->ttyo->screen_rows > 4 + ? -FOOTER_ROWS(ps_global) : -1, + 0, sizeof(buf), "Reply prefix : ", + NULL, NO_HELP, &flags)){ + case 0: /* entry successful, continue */ + if(flags & OE_USER_MODIFIED){ + fs_give((void **)prefix); + *prefix = removing_quotes(cpystr(buf)); + edited = 1; + } + + done++; + break; + + case 1: + cmd_cancelled("Reply"); + + case -1: + return(-1); + + case 4: + EndInverse(); + ClearScreen(); + redraw_titlebar(); + if(ps_global->redrawer != NULL) + (*ps_global->redrawer)(); + + redraw_keymenu(); + break; + + case 3: + break; + + case 2: + default: + q_status_message(SM_ORDER, 3, 4, + "Programmer botch in reply_text_query()"); + return(-1); + } + } + } + + break; + + case 'y': + return(1); + + case 'n': + return(0); + + default: + q_status_message1(SM_ORDER, 3, 4, + "Invalid rval \'%s\'", pretty_command(ret)); + return(-1); + } + } +} + + +/* + * reply_poster_followup - return TRUE if "followup-to" set to "poster" + * + * NOTE: queues status message indicating such + */ +int +reply_poster_followup(ENVELOPE *e) +{ + if(e && e->followup_to && !strucmp(e->followup_to, "poster")){ + q_status_message(SM_ORDER, 2, 3, + _("Replying to Poster as specified in \"Followup-To\"")); + return(1); + } + + return(0); +} + + +/* + * reply_news_test - Test given envelope for newsgroup data and copy + * it at the users request + * RETURNS: + * 0 if error or cancel + * 1 reply via email + * 2 follow-up via news + * 3 do both + */ +int +reply_news_test(ENVELOPE *env, ENVELOPE *outgoing) +{ + int ret = 1; + static ESCKEY_S news_opt[] = { {'f', 'f', "F", N_("Follow-up")}, + {'r', 'r', "R", N_("Reply")}, + {'b', 'b', "B", N_("Both")}, + {-1, 0, NULL, NULL} }; + + if(env->newsgroups && *env->newsgroups && !reply_poster_followup(env)) + /* + * Now that we know a newsgroups field is present, + * ask if the user is posting a follow-up article... + */ + switch(radio_buttons( + _("Follow-up to news group(s), Reply via email to author or Both? "), + -FOOTER_ROWS(ps_global), news_opt, 'r', 'x', + NO_HELP, RB_NORM)){ + case 'r' : /* Reply */ + ret = 1; + break; + + case 'f' : /* Follow-Up via news ONLY! */ + ret = 2; + break; + + case 'b' : /* BOTH */ + ret = 3; + break; + + case 'x' : /* cancel or unknown response */ + default : + cmd_cancelled("Reply"); + ret = 0; + break; + } + + if(ret > 1){ + if(env->followup_to){ + q_status_message(SM_ORDER, 2, 3, + _("Posting to specified Followup-To groups")); + outgoing->newsgroups = cpystr(env->followup_to); + } + else if(!outgoing->newsgroups) + outgoing->newsgroups = cpystr(env->newsgroups); + } + + return(ret); +} + + +/*---------------------------------------------------------------------- + Acquire the pinerc defined signature file + It is allocated here and freed by the caller. + + file -- use this file + 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) + ----*/ +char * +get_signature_file(char *file, int prenewlines, int postnewlines, int is_sig) +{ + char *sig, *tmp_sig = NULL, sig_path[MAXPATH+1]; + int len, do_the_pipe_thang = 0; + long sigsize = 0L, cntdown; + + sig_path[0] = '\0'; + if(!signature_path(file, sig_path, MAXPATH)) + return(NULL); + + dprint((5, "get_signature(%s)\n", sig_path)); + + if(sig_path[(len=strlen(sig_path))-1] == '|'){ + if(is_sig && F_ON(F_DISABLE_PIPES_IN_SIGS, ps_global)){ + q_status_message(SM_ORDER | SM_DING, 3, 4, + _("Pipes for signatures are administratively disabled")); + return(NULL); + } + else if(!is_sig && F_ON(F_DISABLE_PIPES_IN_TEMPLATES, ps_global)){ + q_status_message(SM_ORDER | SM_DING, 3, 4, + _("Pipes for templates are administratively disabled")); + return(NULL); + } + + sig_path[len-1] = '\0'; + removing_trailing_white_space(sig_path); + do_the_pipe_thang++; + } + + if(!IS_REMOTE(sig_path) && ps_global->VAR_OPER_DIR && + !in_dir(ps_global->VAR_OPER_DIR, sig_path)){ + q_status_message2(SM_ORDER | SM_DING, 3, 4, + /* TRANSLATORS: First arg is the directory name, second is + the file user wants to read but can't. */ + _("Can't read file outside %s: %s"), + ps_global->VAR_OPER_DIR, file); + + return(NULL); + } + + if(IS_REMOTE(sig_path) || can_access(sig_path, ACCESS_EXISTS) == 0){ + if(do_the_pipe_thang){ + if(can_access(sig_path, EXECUTE_ACCESS) == 0){ + STORE_S *store; + int flags; + PIPE_S *syspipe; + gf_io_t pc, gc; + long start; + + if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){ + + flags = PIPE_READ | PIPE_STDERR | PIPE_NOSHELL; + + start = time(0); + + if((syspipe = open_system_pipe(sig_path, NULL, NULL, flags, 5, + pipe_callback, pipe_report_error)) != NULL){ + unsigned char c; + char *error, *q; + + gf_set_so_writec(&pc, store); + gf_set_readc(&gc, (void *)syspipe, 0, PipeStar, READ_FROM_LOCALE); + gf_filter_init(); + + if((error = gf_pipe(gc, pc)) != NULL){ + (void)close_system_pipe(&syspipe, NULL, pipe_callback); + gf_clear_so_writec(store); + so_give(&store); + q_status_message1(SM_ORDER | SM_DING, 3, 4, + _("Can't get file: %s"), error); + return(NULL); + } + + if(close_system_pipe(&syspipe, NULL, pipe_callback)){ + long now; + + now = time(0); + q_status_message2(SM_ORDER, 3, 4, + _("Error running program \"%s\"%s"), + file, + (now - start > 4) ? ": timed out" : ""); + } + + gf_clear_so_writec(store); + + /* rewind and count chars */ + so_seek(store, 0L, 0); + while(so_readc(&c, store) && sigsize < 100000L) + sigsize++; + + /* allocate space */ + tmp_sig = fs_get((sigsize + 1) * sizeof(char)); + tmp_sig[0] = '\0'; + q = tmp_sig; + + /* rewind and copy chars, no prenewlines... */ + so_seek(store, 0L, 0); + cntdown = sigsize; + while(so_readc(&c, store) && cntdown-- > 0L) + *q++ = c; + + *q = '\0'; + so_give(&store); + } + else{ + so_give(&store); + q_status_message1(SM_ORDER | SM_DING, 3, 4, + _("Error running program \"%s\""), + file); + } + } + else + q_status_message(SM_ORDER | SM_DING, 3, 4, + "Error allocating space for sig or template program"); + } + else + q_status_message1(SM_ORDER | SM_DING, 3, 4, + /* TRANSLATORS: Arg is a program name */ + _("Can't execute \"%s\": Permission denied"), + sig_path); + } + else if((IS_REMOTE(sig_path) && + (tmp_sig = simple_read_remote_file(sig_path, REMOTE_SIG_SUBTYPE))) || + (tmp_sig = read_file(sig_path, READ_FROM_LOCALE))) + sigsize = strlen(tmp_sig); + else + q_status_message2(SM_ORDER | SM_DING, 3, 4, + /* TRANSLATORS: First arg is error description, 2nd is + filename */ + _("Error \"%s\" reading file \"%s\""), + error_description(errno), sig_path); + } + + sig = get_signature_lit(tmp_sig, prenewlines, postnewlines, is_sig, 0); + if(tmp_sig) + fs_give((void **)&tmp_sig); + + return(sig); +} + + + +/*---------------------------------------------------------------------- + Partially set up message to forward and pass off to composer/mailer + + Args: pine_state -- The usual pine structure + + Result: outgoing envelope and body created and passed off to composer/mailer + + Create the outgoing envelope for the mail being forwarded, which is +not much more than filling in the subject, and create the message body +of the outgoing message which requires formatting the header from the +envelope of the original messasge. + ----------------------------------------------------------------------*/ +int +forward(struct pine *ps, ACTION_S *role_arg) +{ + char *sig; + int ret, forward_raw_body = 0, rv = 0, i; + long msgno, j, totalmsgs, rflags; + ENVELOPE *env, *outgoing; + BODY *orig_body, *body = NULL; + REPLY_S reply; + void *msgtext = NULL; + gf_io_t pc; + int impl, template_len = 0; + PAT_STATE dummy; + REDRAFT_POS_S *redraft_pos = NULL; + ACTION_S *role = NULL, *nrole; +#if defined(DOS) && !defined(_WINDOWS) + char *reserve; +#endif + + dprint((4, "\n - forward -\n")); + + memset((void *)&reply, 0, sizeof(reply)); + outgoing = mail_newenvelope(); + outgoing->message_id = generate_message_id(); + + if(ps_global->full_header == 2 + && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)) + forward_raw_body = 1; + + if((totalmsgs = mn_total_cur(ps->msgmap)) > 1L){ + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s forwarded messages...", comatose(totalmsgs)); + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + outgoing->subject = cpystr(tmp_20k_buf); + } + else{ + /*---------- Get the envelope of message we're forwarding ------*/ + msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap)); + if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL)) + && (outgoing->subject = forward_subject(env, 0)))){ + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't forward it."), + long2string(msgno)); + goto clean; + } + } + + /* + * as with all text bound for the composer, build it in + * a storage object of the type it understands... + */ + if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){ + q_status_message(SM_ORDER | SM_DING, 3, 4, + _("Error allocating message text")); + goto clean; + } + + ret = (F_ON(F_FORWARD_AS_ATTACHMENT, ps_global)) + ? 'y' + : (totalmsgs > 1L) + ? want_to(_("Forward messages as a MIME digest"), 'y', 'x', NO_HELP, WT_SEQ_SENSITIVE) + : (ps->full_header == 2) + ? want_to(_("Forward message as an attachment"), 'n', 'x', NO_HELP, WT_SEQ_SENSITIVE) + : 0; + + if(ret == 'x'){ + cmd_cancelled("Forward"); + so_give((STORE_S **)&msgtext); + goto clean; + } + + /* Setup possible role */ + if(role_arg) + role = copy_action(role_arg); + + if(!role){ + rflags = ROLE_FORWARD; + if(nonempty_patterns(rflags, &dummy)){ + /* setup default role */ + nrole = NULL; + j = mn_first_cur(ps->msgmap); + do { + role = nrole; + nrole = set_role_from_msg(ps, rflags, + mn_m2raw(ps->msgmap, j), NULL); + } while(nrole && (!role || nrole == role) + && (j=mn_next_cur(ps->msgmap)) > 0L); + + if(!role || nrole == role) + role = nrole; + else + role = NULL; + + if(confirm_role(rflags, &role)) + role = combine_inherited_role(role); + else{ /* cancel reply */ + role = NULL; + cmd_cancelled("Forward"); + so_give((STORE_S **)&msgtext); + goto clean; + } + } + } + + if(role) + q_status_message1(SM_ORDER, 3, 4, + _("Forwarding using role \"%s\""), role->nick); + + if(role && role->template){ + char *filtered; + + impl = 1; + filtered = detoken(role, (totalmsgs == 1L) ? env : NULL, + 0, 0, 0, &redraft_pos, &impl); + if(filtered){ + if(*filtered){ + so_puts((STORE_S *)msgtext, filtered); + if(impl == 1) + template_len = strlen(filtered); + } + + fs_give((void **)&filtered); + } + } + else + impl = 1; + + if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){ + if(impl == 2) + redraft_pos->offset += template_len; + + so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE); + + fs_give((void **)&sig); + } + else + so_puts((STORE_S *)msgtext, NEWLINE); + + gf_set_so_writec(&pc, (STORE_S *)msgtext); + +#if defined(DOS) && !defined(_WINDOWS) +#if defined(LWP) || defined(PCTCP) || defined(PCNFS) +#define IN_RESERVE 8192 +#else +#define IN_RESERVE 16384 +#endif + if((reserve=(char *)malloc(IN_RESERVE)) == NULL){ + gf_clear_so_writec((STORE_S *) msgtext); + so_give((STORE_S **)&msgtext); + q_status_message(SM_ORDER | SM_DING, 3, 4, + _("Insufficient memory for message text")); + goto clean; + } +#endif + + /* + * If we're forwarding multiple messages *or* the forward-as-mime + * is turned on and the users wants it done that way, package things + * up... + */ + if(ret == 'y'){ /* attach message[s]!!! */ + PART **pp; + long totalsize = 0L; + + /*---- New Body to start with ----*/ + body = mail_newbody(); + body->type = TYPEMULTIPART; + + /*---- The TEXT part/body ----*/ + body->nested.part = mail_newbody_part(); + body->nested.part->body.type = TYPETEXT; + body->nested.part->body.contents.text.data = msgtext; + + if(totalmsgs > 1L){ + /*---- The MULTIPART/DIGEST part ----*/ + body->nested.part->next = mail_newbody_part(); + body->nested.part->next->body.type = TYPEMULTIPART; + body->nested.part->next->body.subtype = cpystr("Digest"); + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Digest of %s messages", comatose(totalmsgs)); + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + body->nested.part->next->body.description = cpystr(tmp_20k_buf); + pp = &(body->nested.part->next->body.nested.part); + } + else + pp = &(body->nested.part->next); + + /*---- The Message body subparts ----*/ + for(msgno = mn_first_cur(ps->msgmap); + msgno > 0L; + msgno = mn_next_cur(ps->msgmap)){ + + msgno = mn_m2raw(ps->msgmap, msgno); + env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL); + + if(forward_mime_msg(ps->mail_stream,msgno,NULL,env,pp,msgtext)){ + totalsize += (*pp)->body.size.bytes; + pp = &((*pp)->next); + } + else + goto bomb; + } + + if(totalmsgs > 1L) + body->nested.part->next->body.size.bytes = totalsize; + } + else if(totalmsgs > 1L){ + int warned = 0; + body = mail_newbody(); + body->type = TYPETEXT; + body->contents.text.data = msgtext; + env = NULL; + + for(msgno = mn_first_cur(ps->msgmap); + msgno > 0L; + msgno = mn_next_cur(ps->msgmap)){ + + if(env){ /* put 2 between messages */ + gf_puts(NEWLINE, pc); + gf_puts(NEWLINE, pc); + } + + /*--- Grab current envelope ---*/ + env = pine_mail_fetchstructure(ps->mail_stream, + mn_m2raw(ps->msgmap, msgno), + &orig_body); + if(!env || !orig_body){ + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't forward it."), + long2string(msgno)); + goto bomb; + } + + if(orig_body == NULL || orig_body->type == TYPETEXT || forward_raw_body) { + forward_delimiter(pc); + reply_forward_header(ps->mail_stream, + mn_m2raw(ps->msgmap, msgno), + NULL, env, pc, ""); + + if(!get_body_part_text(ps->mail_stream, forward_raw_body ? NULL : orig_body, + mn_m2raw(ps->msgmap, msgno), + forward_raw_body ? NULL : "1", 0L, pc, + NULL, NULL, GBPT_NONE)) + goto bomb; + } else if(orig_body->type == TYPEMULTIPART) { + if(!warned++) + q_status_message(SM_ORDER,3,7, + _("WARNING! Attachments not included in multiple forward.")); + + if(orig_body->nested.part && + orig_body->nested.part->body.type == TYPETEXT) { + /*---- First part of the message is text -----*/ + forward_delimiter(pc); + reply_forward_header(ps->mail_stream, + mn_m2raw(ps->msgmap,msgno), + NULL, env, pc, ""); + + if(!get_body_part_text(ps->mail_stream, + &orig_body->nested.part->body, + mn_m2raw(ps->msgmap, msgno), + "1", 0L, pc, + NULL, NULL, GBPT_NONE)) + goto bomb; + } else { + q_status_message(SM_ORDER,0,3, + _("Multipart with no leading text part!")); + } + } else { + /*---- Single non-text message of some sort ----*/ + q_status_message(SM_ORDER,0,3, + _("Non-text message not included!")); + } + } + } + else if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno, + &orig_body)) + && (body = forward_body(ps->mail_stream, env, orig_body, msgno, + NULL, msgtext, + FWD_NONE)))){ + q_status_message1(SM_ORDER,3,4, + _("Error fetching message %s. Can't forward it."), + long2string(msgno)); + goto clean; + } + + if(ret != 'y' && totalmsgs == 1L && orig_body){ + char *charset; + + charset = parameter_val(orig_body->parameter, "charset"); + if(charset && strucmp(charset, "us-ascii") != 0){ + CONV_TABLE *ct; + + /* + * There is a non-ascii charset, is there conversion happening? + */ + if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){ + reply.orig_charset = charset; + charset = NULL; + } + } + + if(charset) + fs_give((void **) &charset); + + /* + * I don't think orig_charset is ever used except possibly + * right here. Hubert 2008-01-15. + */ + if(reply.orig_charset) + reply.forw = 1; + } + + /* fill in reply structure */ + reply.forwarded = 1; + reply.mailbox = cpystr(ps->mail_stream->mailbox); + reply.origmbox = cpystr(ps->mail_stream->original_mailbox + ? ps->mail_stream->original_mailbox + : ps->mail_stream->mailbox); + reply.data.uid.msgs = (imapuid_t *) fs_get((totalmsgs + 1) * sizeof(imapuid_t)); + if((reply.data.uid.validity = ps->mail_stream->uid_validity) != 0){ + reply.uid = 1; + for(msgno = mn_first_cur(ps->msgmap), i = 0; + msgno > 0L; + msgno = mn_next_cur(ps->msgmap), i++) + reply.data.uid.msgs[i] = mail_uid(ps->mail_stream, mn_m2raw(ps->msgmap, msgno)); + } + else{ + reply.msgno = 1; + for(msgno = mn_first_cur(ps->msgmap), i = 0; + msgno > 0L; + msgno = mn_next_cur(ps->msgmap), i++) + reply.data.uid.msgs[i] = mn_m2raw(ps->msgmap, msgno); + } + + reply.data.uid.msgs[i] = 0; /* tie off list */ + +#if defined(DOS) && !defined(_WINDOWS) + free((void *)reserve); +#endif + pine_send(outgoing, &body, "FORWARD MESSAGE", + role, NULL, &reply, redraft_pos, + NULL, NULL, 0); + rv++; + + clean: + if(body) + pine_free_body(&body); + + if((STORE_S *) msgtext) + gf_clear_so_writec((STORE_S *) msgtext); + + mail_free_envelope(&outgoing); + free_redraft_pos(&redraft_pos); + free_action(&role); + + if(reply.orig_charset) + fs_give((void **)&reply.orig_charset); + + return rv; + + bomb: + q_status_message(SM_ORDER | SM_DING, 4, 5, + _("Error fetching message contents. Can't forward message.")); + goto clean; +} + + +/*---------------------------------------------------------------------- + Partially set up message to forward and pass off to composer/mailer + + Args: pine_state -- The usual pine structure + message -- The MESSAGECACHE of entry to reply to + + Result: outgoing envelope and body created and passed off to composer/mailer + + Create the outgoing envelope for the mail being forwarded, which is +not much more than filling in the subject, and create the message body +of the outgoing message which requires formatting the header from the +envelope of the original messasge. + ----------------------------------------------------------------------*/ +void +forward_text(struct pine *pine_state, void *text, SourceType source) +{ + ENVELOPE *env; + BODY *body; + gf_io_t pc, gc; + STORE_S *msgtext; + char *enc_error, *sig; + ACTION_S *role = NULL; + PAT_STATE dummy; + long rflags = ROLE_COMPOSE; + + if((msgtext = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){ + env = mail_newenvelope(); + env->message_id = generate_message_id(); + body = mail_newbody(); + body->type = TYPETEXT; + body->contents.text.data = (void *) msgtext; + + if(nonempty_patterns(rflags, &dummy)){ + /* + * This is really more like Compose, even though it + * is called Forward. + */ + if(confirm_role(rflags, &role)) + role = combine_inherited_role(role); + else{ /* cancel */ + cmd_cancelled("Composition"); + display_message('x'); + mail_free_envelope(&env); + pine_free_body(&body); + return; + } + } + + if(role) + q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""), + role->nick); + + sig = detoken(role, NULL, 2, 0, 1, NULL, NULL); + so_puts(msgtext, (sig && *sig) ? sig : NEWLINE); + so_puts(msgtext, NEWLINE); + so_puts(msgtext, "----- Included text -----"); + so_puts(msgtext, NEWLINE); + if(sig) + fs_give((void **)&sig); + + gf_filter_init(); + gf_set_so_writec(&pc, msgtext); + gf_set_readc(&gc,text,(source == CharStar) ? strlen((char *)text) : 0L, + source, 0); + + if((enc_error = gf_pipe(gc, pc)) == NULL){ + pine_send(env, &body, "SEND MESSAGE", role, NULL, NULL, NULL, + NULL, NULL, 0); + pine_state->mangled_screen = 1; + } + else{ + q_status_message1(SM_ORDER | SM_DING, 3, 5, + _("Error reading text \"%s\""),enc_error); + display_message('x'); + } + + gf_clear_so_writec(msgtext); + mail_free_envelope(&env); + pine_free_body(&body); + } + else { + q_status_message(SM_ORDER | SM_DING, 3, 4, + _("Error allocating message text")); + display_message('x'); + } + + free_action(&role); +} + + +/*---------------------------------------------------------------------- + Partially set up message to resend and pass off to mailer + + Args: pine_state -- The usual pine structure + + Result: outgoing envelope and body created and passed off to mailer + + Create the outgoing envelope for the mail being resent, which is +not much more than filling in the subject, and create the message body +of the outgoing message which requires formatting the header from the +envelope of the original messasge. + ----------------------------------------------------------------------*/ +int +bounce(struct pine *pine_state, ACTION_S *role) +{ + ENVELOPE *env; + long msgno, rawno; + char *save_to = NULL, **save_toptr = NULL, *errstr = NULL, + *prmpt_who = NULL, *prmpt_cnf = NULL; + + dprint((4, "\n - bounce -\n")); + + if(mn_total_cur(pine_state->msgmap) > 1L){ + save_toptr = &save_to; + snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %ld messages to : "), + mn_total_cur(pine_state->msgmap)); + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + prmpt_who = cpystr(tmp_20k_buf); + snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Send %ld messages "), + mn_total_cur(pine_state->msgmap)); + tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; + prmpt_cnf = cpystr(tmp_20k_buf); + } + + for(msgno = mn_first_cur(pine_state->msgmap); + msgno > 0L; + msgno = mn_next_cur(pine_state->msgmap)){ + + rawno = mn_m2raw(pine_state->msgmap, msgno); + if((env = pine_mail_fetchstructure(pine_state->mail_stream, rawno, NULL)) != NULL) + errstr = bounce_msg(pine_state->mail_stream, rawno, NULL, role, + save_toptr, env->subject, prmpt_who, prmpt_cnf); + else + errstr = _("Can't fetch Subject for Bounce"); + + + if(errstr){ + if(*errstr) + q_status_message(SM_ORDER | SM_DING, 4, 7, errstr); + + break; + } + } + + if(save_to) + fs_give((void **)&save_to); + + if(prmpt_who) + fs_give((void **) &prmpt_who); + + if(prmpt_cnf) + fs_give((void **) &prmpt_cnf); + + return(errstr ? 0 : 1); +} + + + +char * +bounce_msg(MAILSTREAM *stream, + long int rawno, + char *part, + ACTION_S *role, + char **to, + char *subject, + char *pmt_who, + char *pmt_cnf) +{ + char *errstr = NULL; + int was_seen = -1; + ENVELOPE *outgoing; + BODY *body = NULL; + MESSAGECACHE *mc; + + if((errstr = bounce_msg_body(stream, rawno, part, to, subject, &outgoing, &body, &was_seen)) == NULL){ + if(pine_simple_send(outgoing, &body, role, pmt_who, pmt_cnf, to, + !(to && *to) ? SS_PROMPTFORTO : 0) < 0){ + errstr = ""; /* p_s_s() better have explained! */ + /* clear seen flag */ + if(was_seen == 0 && rawno > 0L + && stream && rawno <= stream->nmsgs + && (mc = mail_elt(stream, rawno)) && mc->seen) + mail_flag(stream, long2string(rawno), "\\SEEN", 0); + } + } + + /* Just for good measure... */ + mail_free_envelope(&outgoing); + pine_free_body(&body); + + return(errstr); /* no problem-o */ +} + + +/*---------------------------------------------------------------------- + Serve up the current signature within pico for editing + + Args: file to edit + + Result: signature changed or not. + ---*/ +char * +signature_edit(char *sigfile, char *title) +{ + int editor_result; + char sig_path[MAXPATH+1], errbuf[2000], *errstr = NULL; + char *ret = NULL; + STORE_S *msgso, *tmpso = NULL; + gf_io_t gc, pc; + PICO pbf; + struct variable *vars = ps_global->vars; + REMDATA_S *rd = NULL; + + if(!signature_path(sigfile, sig_path, MAXPATH)) + return(cpystr(_("No signature file defined."))); + + if(IS_REMOTE(sigfile)){ + rd = rd_create_remote(RemImap, sig_path, REMOTE_SIG_SUBTYPE, + NULL, "Error: ", + _("Can't access remote configuration.")); + if(!rd) + return(cpystr(_("Error attempting to edit remote configuration"))); + + (void)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'){ + 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, + "signature_edit: rd_update_local failed\n")); + rd_close_remdata(&rd); + return(cpystr(_("Can't access remote sig"))); + } + } + else + rd_open_remote(rd); + + if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){ + rd_close_remdata(&rd); + return(cpystr(_("Can't get write permission for remote sig"))); + } + + rd->flags |= DO_REMTRIM; + + strncpy(sig_path, rd->lf, sizeof(sig_path)-1); + sig_path[sizeof(sig_path)-1] = '\0'; + } + + standard_picobuf_setup(&pbf); + pbf.tty_fix = PineRaw; + pbf.composer_help = h_composer_sigedit; + pbf.exittest = sigedit_exit_for_pico; + pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0]) + ? upload_msg_to_pico : NULL; + pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0]) + ? VAR_EDITOR : NULL; + pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL; + pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global); + pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global); + pbf.allow_flowed_text = 0; + + pbf.pine_anchor = set_titlebar(title, + ps_global->mail_stream, + ps_global->context_current, + ps_global->cur_folder, + ps_global->msgmap, + 0, FolderName, 0, 0, NULL); + + /* NOTE: at this point, alot of pico struct fields are null'd out + * thanks to the leading memset; in particular "headents" which tells + * pico to behave like a normal editor (though modified slightly to + * let the caller dictate the file to edit and such)... + */ + + if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, sig_path)){ + size_t l; + + l = strlen(VAR_OPER_DIR) + 100; + ret = (char *) fs_get((l+1) * sizeof(char)); + snprintf(ret, l+1, _("Can't edit file outside of %s"), VAR_OPER_DIR); + ret[l] = '\0'; + return(ret); + } + + /* + * Now alloc and init the text to pass pico + */ + if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){ + ret = cpystr(_("Error allocating space for file")); + dprint((1, "Can't alloc space for signature_edit")); + return(ret); + } + else + pbf.msgtext = so_text(msgso); + + if(can_access(sig_path, READ_ACCESS) == 0 + && !(tmpso = so_get(FileStar, sig_path, READ_ACCESS|READ_FROM_LOCALE))){ + char *problem = error_description(errno); + + snprintf(errbuf, sizeof(errbuf), _("Error editing \"%s\": %s"), + sig_path, problem ? problem : "<NULL>"); + errbuf[sizeof(errbuf)-1] = '\0'; + ret = cpystr(errbuf); + + dprint((1, "signature_edit: can't open %s: %s", sig_path, + problem ? problem : "<NULL>")); + return(ret); + } + else if(tmpso){ /* else, fill pico's edit buffer */ + gf_set_so_readc(&gc, tmpso); /* read from file, write pico buf */ + gf_set_so_writec(&pc, msgso); + gf_filter_init(); /* no filters needed */ + if((errstr = gf_pipe(gc, pc)) != NULL){ + snprintf(errbuf, sizeof(errbuf), _("Error reading file: \"%s\""), errstr); + errbuf[sizeof(errbuf)-1] = '\0'; + ret = cpystr(errbuf); + } + + gf_clear_so_readc(tmpso); + gf_clear_so_writec(msgso); + so_give(&tmpso); + } + + if(!errstr){ +#ifdef _WINDOWS + mswin_setwindowmenu (MENU_COMPOSER); +#endif + + /*------ OK, Go edit the signature ------*/ + editor_result = pico(&pbf); + +#ifdef _WINDOWS + mswin_setwindowmenu (MENU_DEFAULT); +#endif + if(editor_result & COMP_GOTHUP){ + hup_signal(); /* do what's normal for a hup */ + } + else{ + fix_windsize(ps_global); + init_signals(); + } + + if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){ + } + else{ + /*------ Must have an edited buffer, write it to .sig -----*/ + our_unlink(sig_path); /* blast old copy */ + if((tmpso = so_get(FileStar, sig_path, WRITE_ACCESS|WRITE_TO_LOCALE)) != NULL){ + so_seek(msgso, 0L, 0); + gf_set_so_readc(&gc, msgso); /* read from pico buf */ + gf_set_so_writec(&pc, tmpso); /* write sig file */ + gf_filter_init(); /* no filters needed */ + if((errstr = gf_pipe(gc, pc)) != NULL){ + snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""), + errstr); + errbuf[sizeof(errbuf)-1] = '\0'; + ret = cpystr(errbuf); + } + + gf_clear_so_readc(msgso); + gf_clear_so_writec(tmpso); + if(so_give(&tmpso)){ + errstr = error_description(errno); + snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""), + errstr); + errbuf[sizeof(errbuf)-1] = '\0'; + ret = cpystr(errbuf); + } + + if(IS_REMOTE(sigfile)){ + int e, we_cancel; + char datebuf[200]; + + datebuf[0] = '\0'; + + we_cancel = busy_cue("Copying to remote sig", NULL, 1); + if((e = rd_update_remote(rd, datebuf)) != 0){ + if(e == -1){ + q_status_message2(SM_ORDER | SM_DING, 3, 5, + _("Error opening temporary sig file %s: %s"), + rd->lf, error_description(errno)); + dprint((1, + "write_remote_sig: error opening temp file %s\n", + rd->lf ? rd->lf : "?")); + } + else{ + q_status_message2(SM_ORDER | SM_DING, 3, 5, + _("Error copying to %s: %s"), + rd->rn, error_description(errno)); + dprint((1, + "write_remote_sig: error copying from %s to %s\n", + rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?")); + } + + q_status_message(SM_ORDER | SM_DING, 5, 5, + _("Copy of sig to remote folder failed, changes NOT saved remotely")); + } + else{ + rd_update_metadata(rd, datebuf); + rd->read_status = 'W'; + } + + rd_close_remdata(&rd); + + if(we_cancel) + cancel_busy_cue(-1); + } + } + else{ + snprintf(errbuf, sizeof(errbuf), _("Error writing \"%s\""), sig_path); + errbuf[sizeof(errbuf)-1] = '\0'; + ret = cpystr(errbuf); + dprint((1, "signature_edit: can't write %s", + sig_path)); + } + } + } + + standard_picobuf_teardown(&pbf); + so_give(&msgso); + return(ret); +} + + +/*---------------------------------------------------------------------- + Serve up the current signature within pico for editing + + Args: literal signature to edit + + Result: raw edited signature is returned in result arg + ---*/ +char * +signature_edit_lit(char *litsig, char **result, char *title, HelpType composer_help) +{ + int editor_result; + char *errstr = NULL; + char *ret = NULL; + STORE_S *msgso; + PICO pbf; + struct variable *vars = ps_global->vars; + + standard_picobuf_setup(&pbf); + pbf.tty_fix = PineRaw; + pbf.search_help = h_sigedit_search; + pbf.composer_help = composer_help; + pbf.exittest = sigedit_exit_for_pico; + pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0]) + ? upload_msg_to_pico : NULL; + pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0]) + ? VAR_EDITOR : NULL; + pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL; + pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global); + pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global); + pbf.allow_flowed_text = 0; + + pbf.pine_anchor = set_titlebar(title, + ps_global->mail_stream, + ps_global->context_current, + ps_global->cur_folder, + ps_global->msgmap, + 0, FolderName, 0, 0, NULL); + + /* NOTE: at this point, alot of pico struct fields are null'd out + * thanks to the leading memset; in particular "headents" which tells + * pico to behave like a normal editor (though modified slightly to + * let the caller dictate the file to edit and such)... + */ + + /* + * Now alloc and init the text to pass pico + */ + if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){ + ret = cpystr(_("Error allocating space")); + dprint((1, "Can't alloc space for signature_edit_lit")); + return(ret); + } + else + pbf.msgtext = so_text(msgso); + + so_puts(msgso, litsig ? litsig : ""); + + + if(!errstr){ +#ifdef _WINDOWS + mswin_setwindowmenu (MENU_COMPOSER); +#endif + + /*------ OK, Go edit the signature ------*/ + editor_result = pico(&pbf); + +#ifdef _WINDOWS + mswin_setwindowmenu (MENU_DEFAULT); +#endif + if(editor_result & COMP_GOTHUP){ + hup_signal(); /* do what's normal for a hup */ + } + else{ + fix_windsize(ps_global); + init_signals(); + } + + if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){ + ret = cpystr(_("Edit Cancelled")); + } + else{ + /*------ Must have an edited buffer, write it to .sig -----*/ + unsigned char c; + int cnt = 0; + char *p; + + so_seek(msgso, 0L, 0); + while(so_readc(&c, msgso)) + cnt++; + + *result = (char *)fs_get((cnt+1) * sizeof(char)); + p = *result; + so_seek(msgso, 0L, 0); + while(so_readc(&c, msgso)) + *p++ = c; + + *p = '\0'; + } + } + + standard_picobuf_teardown(&pbf); + so_give(&msgso); + return(ret); +} + + +/* + * Returns 0 for Save Changes and exit + * 1 for Cancel Exit + * -1 exit but Dont Save Changes + */ +int +sigedit_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed, + char **result) +{ + int rv; + char *rstr = NULL; + void (*redraw)(void) = ps_global->redrawer; + static ESCKEY_S opts[] = { + {'s', 's', "S", N_("Save changes")}, + {'d', 'd', "D", N_("Don't save changes")}, + {-1, 0, NULL, NULL} + }; + + ps_global->redrawer = redraw_pico; + fix_windsize(ps_global); + + while(1){ + rv = radio_buttons(_("Exit editor? "), + -FOOTER_ROWS(ps_global), opts, + 's', 'x', h_exit_editor, RB_NORM); + if(rv == 's'){ /* user ACCEPTS! */ + break; + } + else if(rv == 'd'){ /* Declined! */ + rstr = _("No Changes Saved"); + break; + } + else if(rv == 'x'){ /* Cancelled! */ + rstr = _("Exit Cancelled"); + break; + } + } + + if(result) + *result = rstr; + + ps_global->redrawer = redraw; + return((rv == 's') ? 0 : (rv == 'd') ? -1 : 1); +} + + +/* + * Common stuff we almost always want to set when calling pico. + */ +void +standard_picobuf_setup(PICO *pbf) +{ + memset(pbf, 0, sizeof(*pbf)); + + pbf->pine_version = ALPINE_VERSION; + pbf->fillcolumn = ps_global->composer_fillcol; + pbf->menu_rows = FOOTER_ROWS(ps_global) - 1; + pbf->colors = colors_for_pico(); + pbf->wordseps = user_wordseps(ps_global->VAR_WORDSEPS); + pbf->helper = helper; + pbf->showmsg = display_message_for_pico; + pbf->suspend = do_suspend; + pbf->keybinput = cmd_input_for_pico; + pbf->tty_fix = ttyfix; /* watch out for this one */ + pbf->newmail = new_mail_for_pico; + pbf->ckptdir = checkpoint_dir_for_pico; + pbf->resize = resize_for_pico; + pbf->input_cs = ps_global->input_cs; + pbf->winch_cleanup = winch_cleanup; + pbf->search_help = h_composer_search; + pbf->ins_help = h_composer_ins; + pbf->ins_m_help = h_composer_ins_m; + pbf->composer_help = h_composer; + pbf->browse_help = h_composer_browse; + pbf->attach_help = h_composer_ctrl_j; + + pbf->pine_flags = + ( (F_ON(F_CAN_SUSPEND,ps_global) ? P_SUSPEND : 0L) + | (F_ON(F_USE_FK,ps_global) ? P_FKEYS : 0L) + | (ps_global->restricted ? P_SECURE : 0L) + | (F_ON(F_ALT_ED_NOW,ps_global) ? P_ALTNOW : 0L) + | (F_ON(F_USE_CURRENT_DIR,ps_global) ? P_CURDIR : 0L) + | (F_ON(F_SUSPEND_SPAWNS,ps_global) ? P_SUBSHELL : 0L) + | (F_ON(F_COMPOSE_MAPS_DEL,ps_global) ? P_DELRUBS : 0L) + | (F_ON(F_ENABLE_TAB_COMPLETE,ps_global) ? P_COMPLETE : 0L) + | (F_ON(F_SHOW_CURSOR,ps_global) ? P_SHOCUR : 0L) + | (F_ON(F_DEL_FROM_DOT,ps_global) ? P_DOTKILL : 0L) + | (F_ON(F_ENABLE_DOT_FILES,ps_global) ? P_DOTFILES : 0L) + | (F_ON(F_ALLOW_GOTO,ps_global) ? P_ALLOW_GOTO : 0L) + | (F_ON(F_ENABLE_SEARCH_AND_REPL,ps_global) ? P_REPLACE : 0L) + | (!ps_global->pass_ctrl_chars + && !ps_global->pass_c1_ctrl_chars ? P_HICTRL : 0L) + | ((F_ON(F_ENABLE_ALT_ED,ps_global) + || F_ON(F_ALT_ED_NOW,ps_global) + || (ps_global->VAR_EDITOR + && ps_global->VAR_EDITOR[0] + && ps_global->VAR_EDITOR[0][0])) + ? P_ADVANCED : 0L) + | ((!ps_global->keyboard_charmap + || !strucmp(ps_global->keyboard_charmap, "US-ASCII")) + ? P_HIBITIGN : 0L)); + + if(ps_global->VAR_OPER_DIR){ + pbf->oper_dir = ps_global->VAR_OPER_DIR; + pbf->pine_flags |= P_TREE; + } + + pbf->home_dir = ps_global->home_dir; +} + + +void +standard_picobuf_teardown(PICO *pbf) +{ + if(pbf){ + if(pbf->colors) + free_pcolors(&pbf->colors); + + if(pbf->wordseps) + fs_give((void **) &pbf->wordseps); + } +} + + +/*---------------------------------------------------------------------- + Call back for pico to use to check for new mail. + +Args: cursor -- pointer to in to tell caller if cursor location changed + if NULL, turn off cursor positioning. + timing -- whether or not it's a good time to check + + +Returns: returns 1 on success, zero on error. +----*/ +long +new_mail_for_pico(int timing, int status) +{ + /* + * If we're not interested in the status, don't display the busy + * cue either... + */ + /* don't know where the cursor's been, reset it */ + clear_cursor_pos(); + return(new_mail(0, timing, + (status ? NM_STATUS_MSG : NM_NONE) | NM_DEFER_SORT + | NM_FROM_COMPOSER)); +} + + +void +cmd_input_for_pico(void) +{ + zero_new_mail_count(); +} + + +/*---------------------------------------------------------------------- + Call back for pico to get newmail status messages displayed + +Args: x -- char processed + +Returns: +----*/ +int +display_message_for_pico(int x) +{ + int rv; + + clear_cursor_pos(); /* can't know where cursor is */ + mark_status_dirty(); /* don't count on cached text */ + fix_windsize(ps_global); + init_sigwinch(); + display_message(x); + rv = ps_global->mangled_screen; + ps_global->mangled_screen = 0; + return(rv); +} + + +/*---------------------------------------------------------------------- + Call back for pico to get desired directory for its check point file + + Args: s -- buffer to write directory name + n -- length of that buffer + + Returns: pointer to static buffer +----*/ +char * +checkpoint_dir_for_pico(char *s, size_t n) +{ +#if defined(DOS) || defined(OS2) + /* + * we can't assume anything about root or home dirs, so + * just plunk it down in the same place as the pinerc + */ + if(!getenv("HOME")){ + char *lc = last_cmpnt(ps_global->pinerc); + + if(lc != NULL){ + strncpy(s, ps_global->pinerc, MIN(n-1,lc-ps_global->pinerc)); + s[MIN(n-1,lc-ps_global->pinerc)] = '\0'; + } + else{ + strncpy(s, ".\\", n-1); + s[n-1] = '\0'; + } + } + else +#endif + strncpy(s, ps_global->home_dir, n-1); + s[n-1] = '\0'; + + return(s); +} + + +/*---------------------------------------------------------------------- + Call back for pico to tell us the window size's changed + + Args: none + + Returns: none (but pine's ttyo structure may have been updated) +----*/ +void +resize_for_pico(void) +{ + fix_windsize(ps_global); +} + + +PCOLORS * +colors_for_pico(void) +{ + PCOLORS *colors = NULL; + struct variable *vars = ps_global->vars; + + if (pico_usingcolor()){ + colors = (PCOLORS *)fs_get(sizeof(PCOLORS)); + + colors->tbcp = current_titlebar_color(); + + if (VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR){ + colors->klcp = new_color_pair(VAR_KEYLABEL_FORE_COLOR, + VAR_KEYLABEL_BACK_COLOR); + if (!pico_is_good_colorpair(colors->klcp)) + free_color_pair(&colors->klcp); + } + else colors->klcp = NULL; + + if (colors->klcp && VAR_KEYNAME_FORE_COLOR && VAR_KEYNAME_BACK_COLOR){ + colors->kncp = new_color_pair(VAR_KEYNAME_FORE_COLOR, + VAR_KEYNAME_BACK_COLOR); + } + else colors->kncp = NULL; + + if (VAR_STATUS_FORE_COLOR && VAR_STATUS_BACK_COLOR){ + colors->stcp = new_color_pair(VAR_STATUS_FORE_COLOR, + VAR_STATUS_BACK_COLOR); + } + else colors->stcp = NULL; + + if (VAR_PROMPT_FORE_COLOR && VAR_PROMPT_BACK_COLOR){ + colors->prcp = new_color_pair(VAR_PROMPT_FORE_COLOR, + VAR_PROMPT_BACK_COLOR); + } + else colors->prcp = NULL; + } + + return colors; +} + + +void +free_pcolors(PCOLORS **colors) +{ + if (*colors){ + if ((*colors)->tbcp) + free_color_pair(&(*colors)->tbcp); + if ((*colors)->kncp) + free_color_pair(&(*colors)->kncp); + if ((*colors)->klcp) + free_color_pair(&(*colors)->klcp); + if ((*colors)->stcp) + free_color_pair(&(*colors)->stcp); + if ((*colors)->prcp) + free_color_pair(&(*colors)->prcp); + fs_give((void **)colors); + fs_give((void **)colors); + *colors = NULL; + } +} |