#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-2021 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 = NULL, *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_ALT_REPLY_MENU, pine_state) || 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,env,&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 wants * 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(!same_subject(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 (ps_global->reply.role_chosen) role = ps_global->reply.role_chosen; else if(role_arg) role = copy_action(role_arg); if(!role){ rflags = ROLE_REPLY; if(!ps_global->reply.role_chosen && 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 miscellaneous fields ===================*/ outgoing->in_reply_to = reply_in_reply_to(env); outgoing->references = reply_build_refs(env); outgoing->message_id = generate_message_id(role); 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, ps_global->reply.signature_bottom, 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)) && !ps_global->reply.signature_bottom){ /* * 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(ps_global->reply.include_header) 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(ps_global->reply.include_header) 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 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 \"\" 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) { char prompt[80]; ESCKEY_S ekey[4]; char cmd; ekey[0].name = "Y"; ekey[0].ch = 'y'; ekey[0].rval = 'y'; ekey[0].label = N_("Yes"); ekey[1].name = "N"; ekey[1].ch = 'n'; ekey[1].rval = 'n'; ekey[1].label = N_("No"); ekey[2].name = "P"; ekey[2].ch = 'p'; ekey[2].rval = 'p'; ekey[3].ch = -1; ps_global->reply.preserve_fields = F_ON(F_PRESERVE_ORIGINAL_FIELD, ps_global); loop: ekey[2].label = ps_global->reply.preserve_fields ? N_("Not Preserve") : N_("Preserve"); snprintf(prompt, sizeof(prompt), _("Reply to all recipients%s"), ps_global->reply.preserve_fields ? _(" (preserving fields)? ") : "? "); prompt[sizeof(prompt)-1] = '\0'; switch(cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey, 'n', 'x', h_preserve_field, RB_NORM)){ 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; case 'p' : ps_global->reply.preserve_fields = (ps_global->reply.preserve_fields + 1) % 2; goto loop; /* ugly, but saves me a variable */ 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, or in the case * of alternate reply menu, set values to the answer to all questions * asked during reply. * * Returns: 1 if include the text * 0 if we're NOT to include the text * -1 on cancel or error */ #define MAX_REPLY_OPTIONS 10 int reply_text_query(struct pine *ps, long int many, ENVELOPE *env, char **prefix) { int ret, edited = 0, headers = 0; static ESCKEY_S compose_style[MAX_REPLY_OPTIONS]; int ekey_num; ps->reply.use_flowed = *prefix && **prefix ? (F_OFF(F_QUELL_FLOWED_TEXT, ps) && F_OFF(F_STRIP_WS_BEFORE_SEND, ps) && (strcmp(*prefix, "> ") == 0 || strcmp(*prefix, ">") == 0)) : 0; ps->reply.strip_signature = ps->full_header == 0 && (F_ON(F_ENABLE_STRIP_SIGDASHES, ps) || F_ON(F_ENABLE_SIGDASHES, ps)); ps->reply.keep_attach = F_ON(F_ATTACHMENTS_IN_REPLY, ps); ps->reply.include_header = F_ON(F_INCLUDE_HEADER, ps); ps->reply.preserve_fields = F_ON(F_PRESERVE_ORIGINAL_FIELD, ps); ps->reply.signature_bottom = F_ON(F_SIG_AT_BOTTOM, ps); ps->reply.role_chosen = NULL; if(F_OFF(F_ALT_REPLY_MENU, ps) && F_ON(F_AUTO_INCLUDE_IN_REPLY, ps) && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps) && F_OFF(F_ALT_REPLY_MENU,ps)) return(1); while(1){ compose_style[ekey_num = 0].ch = 'y'; compose_style[ekey_num].rval = 'y'; compose_style[ekey_num].name = "Y"; compose_style[ekey_num++].label = N_("Yes"); compose_style[ekey_num].ch = 'n'; compose_style[ekey_num].rval = 'n'; compose_style[ekey_num].name = "N"; compose_style[ekey_num++].label = N_("No"); if (F_OFF(F_ALT_REPLY_MENU, ps)){ /**** Standard menu ****/ /* TRANSLATORS: The final five %s's can probably be safely ignored */ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s%soriginal message%s in Reply%s%s%s%s%s%s? "), (many > 1L) ? comatose(many) : "", (many > 1L) ? " " : "", (many > 1L) ? "s" : "", (many > 1L) ? "s" : "", F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "", F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "", ps->reply.role_chosen ? "\" and role \"" : "", ps->reply.role_chosen ? ps->reply.role_chosen->nick : "", F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : ""); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; if (F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){ compose_style[ekey_num].ch = ctrl('R'); compose_style[ekey_num].rval = 'r'; compose_style[ekey_num].name = "^R"; compose_style[ekey_num++].label = N_("Edit Indent String"); } } else { /***** Alternate Reply Menu ********/ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s%soriginal message%s in Reply (using \"%s%s%s\")? "), (many > 1L) ? comatose(many) : "", (many > 1L) ? " " : "", (many > 1L) ? "s" : "", *prefix, ps->reply.role_chosen ? "\" and role \"" : "", ps->reply.role_chosen ? ps->reply.role_chosen->nick : ""); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; compose_style[ekey_num].ch = 'h'; compose_style[ekey_num].rval = 'H'; compose_style[ekey_num].name = "H"; compose_style[ekey_num++].label = ps->reply.include_header ? N_("No Header") : N_("Inc Headr"); compose_style[ekey_num].ch = 's'; compose_style[ekey_num].rval = 'S'; compose_style[ekey_num].name = "S"; compose_style[ekey_num++].label = ps->reply.strip_signature ? N_("No Strip"): N_("Strip Sig"); compose_style[ekey_num].ch = 'a'; compose_style[ekey_num].rval = 'A'; compose_style[ekey_num].name = "A"; compose_style[ekey_num++].label = ps->reply.keep_attach ? N_("No Attach"): N_("Inc Attach"); compose_style[ekey_num].ch = 'b'; compose_style[ekey_num].rval = 'B'; compose_style[ekey_num].name = "B"; compose_style[ekey_num++].label = ps->reply.signature_bottom ? N_("Sig Top") : N_("Sig Bottom"); compose_style[ekey_num].ch = 'r'; compose_style[ekey_num].rval = 'R'; compose_style[ekey_num].name = "R"; compose_style[ekey_num++].label = N_("Set Role"); compose_style[ekey_num].ch = ctrl('R'); compose_style[ekey_num].rval = 'r'; compose_style[ekey_num].name = "^R"; compose_style[ekey_num++].label = N_("Edit Indent String"); /***** End Alt Reply Menu *********/ } compose_style[ekey_num].ch = -1; compose_style[ekey_num].name = NULL; compose_style[ekey_num].label = NULL; switch(ret = radio_buttons(tmp_20k_buf, ps->ttyo->screen_rows > 4 ? -FOOTER_ROWS(ps) : -1, compose_style, (edited || headers || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)) ? 'y' : 'n', 'x', NO_HELP, RB_SEQ_SENSITIVE)){ case 'x': cmd_cancelled("Reply"); return(-1); case 'A': ps->reply.keep_attach = (ps->reply.keep_attach + 1) % 2; break; case 'B': ps->reply.signature_bottom = (ps->reply.signature_bottom + 1) % 2; break; case 'F': ps->reply.use_flowed = (ps->reply.use_flowed + 1) % 2; break; case 'S': ps->reply.strip_signature = (ps->reply.strip_signature + 1) % 2; break; case 'H': ps->reply.include_header = (ps->reply.include_header + 1) % 2; headers = ps->reply.include_header; break; case 'R': { void (*prev_screen)(struct pine *) = ps->prev_screen, (*redraw)(void) = ps->redrawer; ps->redrawer = NULL; ps->next_screen = SCREEN_FUN_NULL; if(role_select_screen(ps, &ps->reply.role_chosen, 1) < 0){ cmd_cancelled("Reply"); ps->next_screen = prev_screen; ps->redrawer = redraw; if (ps->redrawer) (*ps->redrawer)(); continue; } ps->next_screen = prev_screen; ps->redrawer = redraw; if(ps->reply.role_chosen) ps->reply.role_chosen = combine_inherited_role(ps->reply.role_chosen); } if (ps->redrawer) (*ps->redrawer)(); break; 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) : -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)); ps->reply.use_flowed = *prefix && **prefix ? (F_OFF(F_QUELL_FLOWED_TEXT, ps) && F_OFF(F_STRIP_WS_BEFORE_SEND, ps) && (strcmp(*prefix, "> ") == 0 || strcmp(*prefix, ">") == 0)) : 0; 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->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 message. ----------------------------------------------------------------------*/ int forward(struct pine *ps, ACTION_S *role_arg) { char *sig; int ret, forward_raw_body = 0, rv = 0, i; long msgno = 0L, j, totalmsgs, rflags; ENVELOPE *env = NULL, *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(); 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); outgoing->message_id = generate_message_id(role); 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 message. ----------------------------------------------------------------------*/ 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(); 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); env->message_id = generate_message_id(role); 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 message. ----------------------------------------------------------------------*/ 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; if(role) snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %ld messages (using role %s) to : "), mn_total_cur(pine_state->msgmap), role->nick); else 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; #ifdef SMIME /* When we bounce a message, we will leave the original message * intact, which means that it will not be signed or encrypted, * so we turn off signing and encrypting now. It will be turned * on again in send_exit_for_pico(). */ if(ps_global->smime) ps_global->smime->do_sign = ps_global->smime->do_encrypt = 0; #endif /* SMIME */ 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, a lot 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 : ""); errbuf[sizeof(errbuf)-1] = '\0'; ret = cpystr(errbuf); dprint((1, "signature_edit: can't open %s: %s", sig_path, problem ? problem : "")); 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, a lot 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; if (VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){ colors->qlcp = new_color_pair(VAR_QUOTE1_FORE_COLOR, VAR_QUOTE1_BACK_COLOR); } else colors->qlcp = NULL; if (VAR_QUOTE2_FORE_COLOR && VAR_QUOTE2_BACK_COLOR){ colors->qllcp = new_color_pair(VAR_QUOTE2_FORE_COLOR, VAR_QUOTE2_BACK_COLOR); } else colors->qllcp = NULL; if (VAR_QUOTE3_FORE_COLOR && VAR_QUOTE3_BACK_COLOR){ colors->qlllcp = new_color_pair(VAR_QUOTE3_FORE_COLOR, VAR_QUOTE3_BACK_COLOR); } else colors->qlllcp = NULL; if (VAR_NORM_FORE_COLOR && VAR_NORM_BACK_COLOR){ colors->ntcp = new_color_pair(VAR_NORM_FORE_COLOR, VAR_NORM_BACK_COLOR); } else colors->ntcp = NULL; if (VAR_REV_FORE_COLOR && VAR_REV_BACK_COLOR){ colors->rtcp = new_color_pair(VAR_REV_FORE_COLOR, VAR_REV_BACK_COLOR); } else colors->rtcp = NULL; if (VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR){ colors->sbcp = new_color_pair(VAR_SIGNATURE_FORE_COLOR, VAR_SIGNATURE_BACK_COLOR); } else colors->sbcp = 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); if ((*colors)->qlcp) free_color_pair(&(*colors)->qlcp); if ((*colors)->qllcp) free_color_pair(&(*colors)->qllcp); if ((*colors)->qlllcp) free_color_pair(&(*colors)->qlllcp); if ((*colors)->ntcp) free_color_pair(&(*colors)->ntcp); if ((*colors)->rtcp) free_color_pair(&(*colors)->rtcp); if ((*colors)->sbcp) free_color_pair(&(*colors)->sbcp); fs_give((void **)colors); *colors = NULL; } }