#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: send.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $"; #endif /* * ======================================================================== * Copyright 2013-2021 Eduardo Chappa * Copyright 2006-2008 University of Washington * * 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 * * ======================================================================== */ /*====================================================================== Functions for composing and sending mail ====*/ #include "headers.h" #include "send.h" #include "status.h" #include "mailview.h" #include "mailindx.h" #include "dispfilt.h" #include "keymenu.h" #include "folder.h" #include "radio.h" #include "addrbook.h" #include "reply.h" #include "titlebar.h" #include "signal.h" #include "mailcmd.h" #include "roleconf.h" #include "adrbkcmd.h" #include "busy.h" #include "../pith/debug.h" #include "../pith/state.h" #include "../pith/conf.h" #include "../pith/flag.h" #include "../pith/bldaddr.h" #include "../pith/copyaddr.h" #include "../pith/detach.h" #include "../pith/mimedesc.h" #include "../pith/pipe.h" #include "../pith/addrstring.h" #include "../pith/news.h" #include "../pith/detoken.h" #include "../pith/util.h" #include "../pith/init.h" #include "../pith/mailcmd.h" #include "../pith/ablookup.h" #include "../pith/reply.h" #include "../pith/hist.h" #include "../pith/list.h" #include "../pith/icache.h" #include "../pith/busy.h" #include "../pith/mimetype.h" #include "../pith/send.h" #include "../pith/smime.h" typedef struct body_particulars { unsigned short type, encoding, had_csp; char *subtype, *charset; PARAMETER *parameter; } BODY_PARTICULARS_S; /* * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook */ #define HE(PF) ((struct headerentry *)((PF)->extdata)) /* * Internal Prototypes */ int redraft(MAILSTREAM **, ENVELOPE **, BODY **, char **, char **, REPLY_S **, REDRAFT_POS_S **, PINEFIELD **, ACTION_S **, int); int redraft_prompt(char *, char *, int); int check_for_subject(METAENV *); int check_for_fcc(char *); void free_prompts(PINEFIELD *); int postpone_prompt(void); METAENV *pine_simple_send_header(ENVELOPE *, char **, char ***); void call_mailer_file_result(char *, int); void mark_address_failure_for_pico(METAENV *); BODY_PARTICULARS_S *save_body_particulars(BODY *); void reset_body_particulars(BODY_PARTICULARS_S *, BODY *); void free_body_particulars(BODY_PARTICULARS_S *); long message_format_for_pico(long, int (*)(int)); int send_exit_for_pico(struct headerentry *, void (*)(void), int, char **); void new_thread_on_blank_subject(void); char *choose_a_priority(char *); int dont_flow_this_time(void); int mime_type_for_pico(char *); char *cancel_for_pico(void (*)(void)); int filter_message_text(char *, ENVELOPE *, BODY *, STORE_S **, METAENV *); void pine_send_newsgroup_name(char *, char*, size_t); void outgoing2strings(METAENV *, BODY *, void **, PATMT **, int); void strings2outgoing(METAENV *, BODY **, PATMT *, int); void create_message_body_text(BODY *, int); void set_body_size(BODY *); int view_as_rich(char *, int); int background_posting(int); int valid_subject(char *, char **, char **,BUILDER_ARG *,int *); int build_addr_lcc(char *, char **, char **, BUILDER_ARG *, int *); int news_build(char *, char **, char **, BUILDER_ARG *, int *); void news_build_busy(void); #if defined(DOS) || defined(OS2) int dos_valid_from(void); #endif /* defined(DOS) || defined(OS2) */ /* * Pointer to buffer to hold pointers into pine data that's needed by pico. */ static PICO *pbf; static char *g_rolenick = NULL; static char *sending_filter_requested; static char background_requested, flowing_requested; static unsigned call_mailer_flags; static char *priority_requested; /* local global to save busy_cue state */ static int news_busy_cue = 0; /* * Various useful strings */ #define INTRPT_PMT \ _("Continue INTERRUPTED composition (answering \"n\" won't erase it)") #define PSTPND_PMT \ _("Continue postponed composition (answering \"No\" won't erase it)") #define FORM_PMT \ _("Start composition from Form Letter Folder") #define PSTPN_FORM_PMT \ _("Save to Postponed or Form letter folder? ") #define POST_PMT \ _("Posted message may go to thousands of readers. Really post") #define INTR_DEL_PMT \ _("Deleted messages will be removed from folder after use. Proceed") /* * Macros to help sort out posting results */ #define P_MAIL_WIN 0x01 #define P_MAIL_LOSE 0x02 #define P_MAIL_BITS 0x03 #define P_NEWS_WIN 0x04 #define P_NEWS_LOSE 0x08 #define P_NEWS_BITS 0x0C #define P_FCC_WIN 0x10 #define P_FCC_LOSE 0x20 #define P_FCC_BITS 0x30 #define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE" /* * For check_for_subject and check_for_fcc */ #define CF_OK 0x1 #define CF_MISSING 0x2 /*---------------------------------------------------------------------- Compose screen (not forward or reply). Set up envelope, call composer Args: pine_state -- The usual pine structure Little front end for the compose screen ---*/ void compose_screen(struct pine *pine_state) { void (*prev_screen)(struct pine *) = pine_state->prev_screen, (*redraw)(void) = pine_state->redrawer; pine_state->redrawer = NULL; ps_global->next_screen = SCREEN_FUN_NULL; mailcap_free(); /* free resources we won't be using for a while */ compose_mail(NULL, NULL, NULL, NULL, NULL); pine_state->next_screen = prev_screen; pine_state->redrawer = redraw; } /*---------------------------------------------------------------------- Alternate compose screen. Set up role and call regular compose. Args: pine_state -- The usual pine structure ---*/ void alt_compose_screen(struct pine *pine_state) { ACTION_S *role = NULL; void (*prev_screen)(struct pine *) = pine_state->prev_screen, (*redraw)(void) = pine_state->redrawer; pine_state->redrawer = NULL; ps_global->next_screen = SCREEN_FUN_NULL; mailcap_free(); /* free resources we won't be using for a while */ /* Setup role */ if(role_select_screen(pine_state, &role, MC_COMPOSE) < 0){ cmd_cancelled("Composition"); pine_state->next_screen = prev_screen; pine_state->redrawer = redraw; return; } /* * If default role was selected (NULL) we need to make up a role which * won't do anything, but will cause compose_mail to think there's * already a role so that it won't try to confirm the default. */ if(role) role = combine_inherited_role(role); else{ role = (ACTION_S *)fs_get(sizeof(*role)); memset((void *)role, 0, sizeof(*role)); role->nick = cpystr("Default Role"); } pine_state->redrawer = NULL; compose_mail(NULL, NULL, role, NULL, NULL); free_action(&role); pine_state->next_screen = prev_screen; pine_state->redrawer = redraw; } /*---------------------------------------------------------------------- Format envelope for outgoing message and call editor Args: given_to -- An address to send mail to (usually from command line invocation) fcc_arg -- The fcc that goes with this address. If a "To" line is given format that into the envelope and get ready to call the composer If there's a message postponed, offer to continue it, and set it up, otherwise just fill in the outgoing envelope as blank. NOTE: we ignore postponed and interrupted messages in nr mode ----*/ void compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg, PATMT *attach, gf_io_t inc_text_getc) { BODY *body = NULL; ENVELOPE *outgoing = NULL; PINEFIELD *custom = NULL; REPLY_S *reply = NULL; REDRAFT_POS_S *redraft_pos = NULL; ACTION_S *role = NULL; MAILSTREAM *stream; char *fcc_to_free, *fcc = NULL, *lcc = NULL, *sig = NULL; int fcc_is_sticky = 0, to_is_sticky = 0, intrptd = 0, postponed = 0, form = 0; dprint((1, "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n")); /*-- Check for INTERRUPTED mail --*/ if(!role_arg && !(given_to || attach)){ char file_path[MAXPATH+1]; /* build filename and see if it exists. build_path creates * an explicit local path name, so all c-client access is thru * local drivers. */ file_path[0] = '\0'; build_path(file_path, ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR : ps_global->home_dir, INTERRUPTED_MAIL, sizeof(file_path)); /* check to see if the folder exists, the user wants to continue * and that we can actually read something in... */ if(folder_exists(NULL, file_path) & FEX_ISFILE) intrptd = 1; } /*-- Check for postponed mail --*/ if(!role_arg && !outgoing /* not replying/forwarding */ && !(given_to || attach) /* not command line send */ && ps_global->VAR_POSTPONED_FOLDER /* folder to look in */ && ps_global->VAR_POSTPONED_FOLDER[0]) postponed = 1; /*-- Check for form letter folder --*/ if(!role_arg && !outgoing /* not replying/forwarding */ && !(given_to || attach) /* not command line send */ && ps_global->VAR_FORM_FOLDER /* folder to look in */ && ps_global->VAR_FORM_FOLDER[0]) form = 1; if(!outgoing && !(given_to || attach) && !role_arg && F_ON(F_ALT_COMPOSE_MENU, ps_global)){ char prompt[80]; char letters[30]; char chosen_task; char *new = "New"; char *intrpt = "Interrupted"; char *postpnd = "Postponed"; char *formltr = "FormLetter"; char *roles = "setRole"; HelpType help = h_composer_browse; ESCKEY_S compose_style[6]; unsigned which_help; int ekey_num; ekey_num = 0; compose_style[ekey_num].ch = 'n'; compose_style[ekey_num].rval = 'n'; compose_style[ekey_num].name = "N"; compose_style[ekey_num++].label = new; if(intrptd){ compose_style[ekey_num].ch = 'i'; compose_style[ekey_num].rval = 'i'; compose_style[ekey_num].name = "I"; compose_style[ekey_num++].label = intrpt; } if(postponed){ compose_style[ekey_num].ch = 'p'; compose_style[ekey_num].rval = 'p'; compose_style[ekey_num].name = "P"; compose_style[ekey_num++].label = postpnd; } if(form){ compose_style[ekey_num].ch = 'f'; compose_style[ekey_num].rval = 'f'; compose_style[ekey_num].name = "F"; compose_style[ekey_num++].label = formltr; } compose_style[ekey_num].ch = 'r'; compose_style[ekey_num].rval = 'r'; compose_style[ekey_num].name = "R"; compose_style[ekey_num++].label = roles; compose_style[ekey_num].ch = -1; if(F_ON(F_BLANK_KEYMENU,ps_global)){ char *p; p = letters; *p = '\0'; for(ekey_num = 0; compose_style[ekey_num].ch != -1; ekey_num++){ if(p - letters < sizeof(letters)) *p++ = (char) compose_style[ekey_num].ch; if(compose_style[ekey_num + 1].ch != -1 && p - letters < sizeof(letters)) *p++ = ','; } if(p - letters < sizeof(letters)) *p = '\0'; } which_help = intrptd + 2 * postponed + 4 * form; switch(which_help){ case 1: help = h_compose_intrptd; break; case 2: help = h_compose_postponed; break; case 3: help = h_compose_intrptd_postponed; break; case 4: help = h_compose_form; break; case 5: help = h_compose_intrptd_form; break; case 6: help = h_compose_postponed_form; break; case 7: help = h_compose_intrptd_postponed_form; break; default: help = h_compose_default; break; } snprintf(prompt, sizeof(prompt), "Choose a compose method from %s : ", F_ON(F_BLANK_KEYMENU,ps_global) ? letters : "the menu below"); prompt[sizeof(prompt)-1] = '\0'; chosen_task = radio_buttons(prompt, -FOOTER_ROWS(ps_global), compose_style, 'n', 'x', help, RB_NORM); intrptd = postponed = form = 0; switch(chosen_task){ case 'i': intrptd = 1; break; case 'p': postponed = 1; break; case 'r': { void (*prev_screen)(struct pine *) = ps_global->prev_screen, (*redraw)(void) = ps_global->redrawer; ps_global->redrawer = NULL; ps_global->next_screen = SCREEN_FUN_NULL; if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){ cmd_cancelled("Composition"); ps_global->next_screen = prev_screen; ps_global->redrawer = redraw; return; } ps_global->next_screen = prev_screen; ps_global->redrawer = redraw; if(role) role = combine_inherited_role(role); } break; case 'f': form = 1; break; case 'x': q_status_message(SM_ORDER, 0, 3, "Composition cancelled"); return; break; default: break; } } if(intrptd && !outgoing){ char file_path[MAXPATH+1]; int ret = 'n'; file_path[0] = '\0'; build_path(file_path, ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR : ps_global->home_dir, INTERRUPTED_MAIL, sizeof(file_path)); if(folder_exists(NULL, file_path) & FEX_ISFILE){ if((stream = pine_mail_open(NULL, file_path, SP_USEPOOL|SP_TEMPUSE, NULL)) && !stream->halfopen){ if(F_ON(F_ALT_COMPOSE_MENU, ps_global) || (ret = redraft_prompt("Interrupted",INTRPT_PMT,'n')) =='y'){ if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply, &redraft_pos, &custom, &role, REDRAFT_DEL)){ if(stream) pine_mail_close(stream); return; } to_is_sticky++; /* redraft() may or may not have closed stream */ if(stream) pine_mail_close(stream); postponed = form = 0; } else{ pine_mail_close(stream); if(ret == 'x'){ q_status_message(SM_ORDER, 0, 3, _("Composition cancelled")); return; } } } else{ q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Can't open Interrupted mailbox: %s"), file_path); if(stream) pine_mail_close(stream); } } } if(postponed && !outgoing){ int ret = 'n', done = 0; int exists; if((exists=postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0)) & FEX_ISFILE){ if(F_ON(F_ALT_COMPOSE_MENU, ps_global) || (ret = redraft_prompt("Postponed",PSTPND_PMT,'n')) == 'y'){ if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply, &redraft_pos, &custom, &role, REDRAFT_DEL | REDRAFT_PPND)) done++; /* stream may or may not be closed in redraft() */ if(stream && (stream != ps_global->mail_stream)) pine_mail_close(stream); to_is_sticky++; intrptd = form = 0; } else{ if(stream != ps_global->mail_stream) pine_mail_close(stream); if(ret == 'x'){ q_status_message(SM_ORDER, 0, 3, _("Composition cancelled")); done++; } } } else if(F_ON(F_ALT_COMPOSE_MENU, ps_global)) done++; if(done) return; } if(form && !outgoing){ int ret = 'n', done = 0; int exists; if((exists=postponed_stream(&stream, ps_global->VAR_FORM_FOLDER, "Form letter", 1)) & FEX_ISFILE){ if(F_ON(F_ALT_COMPOSE_MENU, ps_global) || (ret = want_to(FORM_PMT,'y','x',NO_HELP,WT_NORM))=='y'){ if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply, &redraft_pos, &custom, &role, REDRAFT_NONE)) done++; /* stream may or may not be closed in redraft() */ if(stream && (stream != ps_global->mail_stream)) pine_mail_close(stream); to_is_sticky++; intrptd = postponed = 0; } else{ if(stream != ps_global->mail_stream) pine_mail_close(stream); if(ret == 'x'){ q_status_message(SM_ORDER, 0, 3, _("Composition cancelled")); done++; } } } else{ if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Form letter folder doesn't exist!")); return; } } if(done) return; } /*-- normal composition --*/ if(!outgoing){ int impl, template_len = 0; long rflags = ROLE_COMPOSE; PAT_STATE dummy; /*================= Compose new message ===============*/ body = mail_newbody(); outgoing = mail_newenvelope(); if(given_to) rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain); /* * Setup possible role */ if(role_arg) role = copy_action(role_arg); if(!role){ /* Setup possible compose role */ if(nonempty_patterns(rflags, &dummy)){ /* * setup default role * Msgno = -1 means there is no msg. * This will match roles which have the Compose Use turned * on, and have no patterns set, and match the Current * Folder Type. */ role = set_role_from_msg(ps_global, rflags, -1L, NULL); if(confirm_role(rflags, &role)) role = combine_inherited_role(role); else{ /* cancel reply */ role = NULL; cmd_cancelled("Composition"); return; } } } if(role) q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""), role->nick); outgoing->message_id = generate_message_id(role); /* * The type of storage object allocated below is vitally * important. See SIMPLIFYING ASSUMPTION #37 */ if((body->contents.text.data = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){ char ch; if(inc_text_getc){ while((*inc_text_getc)(&ch)) if(!so_writec(ch, (STORE_S *)body->contents.text.data)){ break; } } } else{ q_status_message(SM_ORDER | SM_DING, 3, 4, _("Problem creating space for message text.")); return; } if(role && role->template){ char *filtered; impl = 1; /* leave cursor in header if not explicit */ filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl); if(filtered){ if(*filtered){ so_puts((STORE_S *)body->contents.text.data, 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; if(*sig) so_puts((STORE_S *)body->contents.text.data, sig); fs_give((void **)&sig); } body->type = TYPETEXT; if(attach) create_message_body(&body, attach, 0); } ps_global->prev_screen = compose_screen; if(!(fcc_to_free = fcc) && !(role && role->fcc)) fcc = fcc_arg; /* Didn't pick up fcc, use given */ /* * check whether a build_address-produced fcc is different from * fcc. If same, do nothing, if different, set sticky bit in pine_send. */ if(fcc){ char *tmp_fcc = NULL; if(outgoing->to){ tmp_fcc = get_fcc_based_on_to(outgoing->to); if(strcmp(fcc, tmp_fcc ? tmp_fcc : "")) fcc_is_sticky++; /* cause sticky bit to get set */ } else if((tmp_fcc = get_fcc(NULL)) != NULL && !strcmp(fcc, tmp_fcc)){ /* not sticky */ } else fcc_is_sticky++; if(tmp_fcc) fs_give((void **)&tmp_fcc); } pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc, reply, redraft_pos, lcc, custom, (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0)); if(reply){ if(reply->mailbox) fs_give((void **) &reply->mailbox); if(reply->origmbox) fs_give((void **) &reply->origmbox); if(reply->prefix) fs_give((void **) &reply->prefix); if(reply->data.uid.msgs) fs_give((void **) &reply->data.uid.msgs); fs_give((void **) &reply); } if(fcc_to_free) fs_give((void **)&fcc_to_free); if(lcc) fs_give((void **)&lcc); mail_free_envelope(&outgoing); pine_free_body(&body); free_redraft_pos(&redraft_pos); free_action(&role); } /*---------------------------------------------------------------------- Args: stream -- This is where we get the postponed messages from We'll expunge and close it here unless it is mail_stream. These are all return values: ================ outgoing -- body -- fcc -- lcc -- reply -- redraft_pos -- custom -- role -- ================ flags -- ----*/ int redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body, char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos, PINEFIELD **custom, ACTION_S **role, int flags) { MAILSTREAM *stream; long cont_msg = 1L; STORE_S *so; if(!(streamp && *streamp)) return(0); stream = *streamp; /* * If we're manipulating the current folder, don't bother * with index */ if(!stream->nmsgs){ if(REDRAFT_PPND&flags) q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!")); else q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!")); return(redraft_cleanup(streamp, FALSE, flags)); } else if(stream == ps_global->mail_stream && ps_global->prev_screen == mail_index_screen){ /* * Since the user's got this folder already opened and they're * on a selected message, pick that one rather than rebuild * another index screen... */ cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap)); } else if(stream->nmsgs > 1L){ /* offer browser ? */ int rv; if(REDRAFT_PPND&flags){ /* set to last message postponed */ mn_set_cur(sp_msgmap(stream), mn_get_revsort(sp_msgmap(stream)) ? 1L : mn_get_total(sp_msgmap(stream))); } else{ /* set to top form letter */ mn_set_cur(sp_msgmap(stream), 1L); } clear_index_cache(stream, 0); while(1){ void *ti; ti = stop_threading_temporarily(); rv = index_lister(ps_global, NULL, stream->mailbox, stream, sp_msgmap(stream)); restore_threading(&ti); cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream))); if(count_flagged(stream, F_DEL) && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){ if(REDRAFT_PPND&flags) q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message")); else q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message")); continue; } break; } clear_index_cache(stream, 0); if(rv){ q_status_message(SM_ORDER, 0, 3, _("Composition cancelled")); (void) redraft_cleanup(streamp, FALSE, flags); if(!*streamp && !ps_global->mail_stream){ q_status_message2(SM_ORDER, 3, 7, "No more %.200s, returning to \"%.200s\"", (REDRAFT_PPND&flags) ? "postponed messages" : "form letters", ps_global->inbox_name); if(ps_global && ps_global->ttyo){ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0); ps_global->mangled_footer = 1; } do_broach_folder(ps_global->inbox_name, ps_global->context_list, NULL, DB_INBOXWOCNTXT); ps_global->next_screen = mail_index_screen; } return(0); /* special case */ } } if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL) return(redraft_work(streamp, cont_msg, outgoing, body, fcc, lcc, reply, redraft_pos, custom, role, flags, so)); else return(0); } int redraft_prompt(char *type, char *prompt, int failure) { if(background_posting(FALSE)){ q_status_message1(SM_ORDER, 0, 3, _("%s folder unavailable while background posting"), type); return(failure); } return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)); } /* this is for initializing the fixed header elements in pine_send() */ /* prompt::name::help::prwid::maxlen::realaddr:: builder::affected_entry::next_affected::selector::key_label::fileedit:: display_it::break_on_comma::is_attach::rich_header::only_file_chars:: single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR */ static struct headerentry he_template[]={ {"", "X-Auth-Received", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"From : ", "From", h_composer_from, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}, {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}, {"To : ", "To", h_composer_to, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK}, {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}, {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}, {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL, news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL, NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, fcc_tab_complete, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE}, {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL, build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL, NULL, NULL, NULL, NULL, "To Files", NULL, NULL, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE}, {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL, valid_subject, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "References", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "Date", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "In-Reply-To", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "Message-ID", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "X-Priority", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "User-Agent", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "To", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "X-Post-Error",NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "X-Reply-UID", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "X-Reply-Mbox", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "X-SMTP-Server", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "X-Cursor-Pos", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "X-Our-ReplyTo", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", OUR_HDRS_LIST, NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, {"", "X-Auth-Received", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}, #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH) {"", "Sender", NO_HELP, 10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE} #endif }; static struct headerentry he_custom_addr_templ={ NULL, NULL, h_composer_custom_addr,10, 0, NULL, build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK}; static struct headerentry he_custom_free_templ={ NULL, NULL, h_composer_custom_free,10, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE}; /*---------------------------------------------------------------------- Get addressee for message, then post message Args: outgoing -- Partially formatted outgoing ENVELOPE body -- Body of outgoing message prmpt_who -- Optional prompt for optionally_enter call prmpt_cnf -- Optional prompt for confirmation call used_tobufval -- The string that the to was eventually set equal to. This gets passed back if non-NULL on entry. flagsarg -- SS_PROMPTFORTO - Allow user to change recipient SS_NULLRP - Use null return-path so we'll send an SMTP MAIL FROM: <> Result: message "To: " field is provided and message is sent or cancelled. Fields: remail - return_path - date added here from added here sender - reply_to - subject passed in, NOT edited but maybe canonized here to possibly passed in, edited and canonized here cc - bcc - in_reply_to - message_id - Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART with the first part TYPETEXT! All newlines in the text here also end with CRLF. Returns 0 on success, -1 on failure. ----*/ int pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */ struct mail_bodystruct **body, ACTION_S **rolep, char *prmpt_who, char *prmpt_cnf, char **used_tobufval, int flagsarg) { char **tobufp, *p, tmp[MAILTMPLEN]; void *messagebuf; int done = 0, retval = 0, x; int lastrc, rc = 0, ku, i, resize_len, result, fcc_result = 0; int og2s_done = 0; HelpType help; static HISTORY_S *history = NULL; ESCKEY_S ekey[6]; BUILDER_ARG ba_fcc; METAENV *header; ACTION_S *role = rolep ? *rolep : NULL; PAT_STATE pstate; dprint((1,"\n === simple send called === \n")); memset(&ba_fcc, 0, sizeof(BUILDER_ARG)); init_hist(&history, HISTSIZE); header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp); /*----- Fill in a few general parts of the envelope ----*/ if(!outgoing->date){ if(F_ON(F_QUELL_TIMEZONE, ps_global)) mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE); rfc822_date(tmp_20k_buf); /* format and copy new date */ if(F_ON(F_QUELL_TIMEZONE, ps_global)) mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE); outgoing->date = (unsigned char *) cpystr(tmp_20k_buf); } if(!outgoing->from){ if(role && role->from){ if(ps_global->never_allow_changing_from) q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect")); else outgoing->from = copyaddrlist(role->from); } else outgoing->from = generate_from(); } if(!(flagsarg & SS_NULLRP)) outgoing->return_path = rfc822_cpy_adr(outgoing->from); ekey[i = 0].ch = ctrl('T'); ekey[i].rval = 2; ekey[i].name = "^T"; ekey[i++].label = N_("To AddrBk"); if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){ ekey[i].ch = ctrl('I'); ekey[i].rval = 11; ekey[i].name = "TAB"; ekey[i++].label = N_("Complete"); } if(nonempty_patterns(ROLE_DO_ROLES, &pstate) && first_pattern(&pstate)){ ekey[i].ch = ctrl('R'); ekey[i].rval = 15; ekey[i].name = "^R"; ekey[i++].label = "Set Role"; } ekey[i].ch = KEY_UP; ekey[i].rval = 30; ekey[i].name = ""; ku = i; ekey[i++].label = ""; ekey[i].ch = KEY_DOWN; ekey[i].rval = 31; ekey[i].name = ""; ekey[i++].label = ""; ekey[i].ch = -1; if(outgoing->remail == NULL) strcpy(tmp, _("FORWARD (as e-mail) to : ")); /*---------------------------------------------------------------------- Loop editing the "To: " field until everything goes well ----*/ help = NO_HELP; while(!done){ int flags; if(outgoing->message_id) fs_give((void **) &outgoing->message_id); outgoing->message_id = generate_message_id(role); if(outgoing->remail){ if(role) snprintf(tmp, sizeof(tmp), _("BOUNCE (redirect) message using role \"%s\" to : "), role->nick); else strncpy(tmp, _("BOUNCE (redirect) message to : "), sizeof(tmp)); tmp[sizeof(tmp)-1] = '\0'; } if(!og2s_done){ og2s_done++; outgoing2strings(header, *body, &messagebuf, NULL, 1); } lastrc = rc; if(flagsarg & SS_PROMPTFORTO){ if(!*tobufp) *tobufp = cpystr(""); resize_len = MAX(MAXPATH, strlen(*tobufp)); fs_resize((void **) tobufp, resize_len+1); if(items_in_hist(history) > 0){ ekey[ku].name = HISTORY_UP_KEYNAME; ekey[ku].label = HISTORY_KEYLABEL; ekey[ku+1].name = HISTORY_DOWN_KEYNAME; ekey[ku+1].label = HISTORY_KEYLABEL; } else{ ekey[ku].name = ""; ekey[ku].label = ""; ekey[ku+1].name = ""; ekey[ku+1].label = ""; } flags = OE_APPEND_CURRENT; rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global), 0, resize_len, prmpt_who ? prmpt_who : tmp, ekey, help, &flags); } else rc = 0; switch(rc){ case -1: q_status_message(SM_ORDER | SM_DING, 3, 4, "Internal problem encountered"); retval = -1; done++; break; case 15 : /* set a role */ {void (*prev_screen)(struct pine *) = NULL, (*redraw)(void) = NULL; redraw = ps_global->redrawer; ps_global->redrawer = NULL; prev_screen = ps_global->prev_screen; role = NULL; ps_global->next_screen = SCREEN_FUN_NULL; if(role_select_screen(ps_global, &role, outgoing->remail ? MC_BOUNCE : MC_FORWARD) < 0) cmd_cancelled(_("Set Role")); else{ if(role) role = combine_inherited_role(role); else{ role = (ACTION_S *) fs_get(sizeof(*role)); memset((void *) role, 0, sizeof(*role)); role->nick = cpystr("Default Role"); } } if(redraw) (*redraw)(); ps_global->next_screen = prev_screen; ps_global->redrawer = redraw; ps_global->mangled_screen = 1; if(role && role->from && !ps_global->never_allow_changing_from){ mail_free_address (&outgoing->from); outgoing->from = copyaddrlist(role->from); if(!(flagsarg & SS_NULLRP)){ fs_give((void **) &outgoing->return_path); outgoing->return_path = rfc822_cpy_adr(outgoing->from); } } if(rolep) *rolep = role; } break; case 30 : if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){ strncpy(*tobufp, p, resize_len); (*tobufp)[resize_len-1] = '\0'; } else Writechar(BELL, 0); break; case 31 : if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){ strncpy(*tobufp, p, resize_len); (*tobufp)[resize_len-1] = '\0'; } else Writechar(BELL, 0); break; case 2: /* ^T */ case 0: {void (*redraw) (void) = ps_global->redrawer; char *returned_addr = NULL; int len, l; if(rc == 2){ int got_something = 0; push_titlebar_state(); returned_addr = addr_book_bounce(); /* * Just make it look like user typed this list in. */ if(returned_addr){ got_something++; if((l=resize_len) < (len = strlen(returned_addr)) + 1){ l = len; fs_resize((void **) tobufp, (size_t) (l+1)); } strncpy(*tobufp, returned_addr, l); (*tobufp)[l] = '\0'; fs_give((void **)&returned_addr); } ClearScreen(); pop_titlebar_state(); redraw_titlebar(); if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */ (*ps_global->redrawer)(); if(!got_something) continue; } if(*tobufp && **tobufp != '\0'){ char *errbuf, *addr; int tolen; save_hist(history, *tobufp, 0, NULL); errbuf = NULL; /* * If role has an fcc, use it instead of what build_address * tells us. */ if(role && role->fcc){ if(ba_fcc.tptr) fs_give((void **) &ba_fcc.tptr); ba_fcc.tptr = cpystr(role->fcc); } if(build_address(*tobufp, &addr, &errbuf, (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){ int sendit = 0; if(errbuf) fs_give((void **)&errbuf); if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){ l = tolen; fs_resize((void **) tobufp, (size_t) (l+1)); } strncpy(*tobufp, addr, l); (*tobufp)[l] = '\0'; if(used_tobufval) *used_tobufval = cpystr(addr); /* confirm address */ if(flagsarg & SS_PROMPTFORTO){ char dsn_string[30]; int dsn_label = 0, dsn_show, i; int verbose_label = 0; ESCKEY_S opts[13]; strings2outgoing(header, body, NULL, 0); if((flagsarg & SS_PROMPTFORTO) && ((x = check_addresses(header)) == CA_BAD || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE, ps_global)))) /*--- Addresses didn't check out---*/ continue; i = 0; opts[i].ch = 'y'; opts[i].rval = 'y'; opts[i].name = "Y"; opts[i++].label = N_("Yes"); opts[i].ch = 'n'; opts[i].rval = 'n'; opts[i].name = "N"; opts[i++].label = N_("No"); call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */ if(F_ON(F_VERBOSE_POST, ps_global)){ /* setup keymenu slot to toggle verbose mode */ opts[i].ch = ctrl('W'); opts[i].rval = 12; opts[i].name = "^W"; verbose_label = i++; if(F_ON(F_DSN, ps_global)){ opts[i].ch = 0; opts[i].rval = 0; opts[i].name = ""; opts[i++].label = ""; } } /* clear DSN flags */ call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL); if(F_ON(F_DSN, ps_global)){ /* setup keymenu slots to toggle dsn bits */ opts[i].ch = 'd'; opts[i].rval = 'd'; opts[i].name = "D"; opts[i].label = "DSNOpts"; dsn_label = i++; opts[i].ch = -2; opts[i].rval = 's'; opts[i].name = "S"; opts[i++].label = ""; opts[i].ch = -2; opts[i].rval = 'x'; opts[i].name = "X"; opts[i++].label = ""; opts[i].ch = -2; opts[i].rval = 'h'; opts[i].name = "H"; opts[i++].label = ""; } opts[i].ch = -1; while(1){ int rv; dsn_show = (call_mailer_flags & CM_DSN_SHOW); snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s%sto \"%s\" ? ", prmpt_cnf ? prmpt_cnf : "Send message ", ((call_mailer_flags & CM_VERBOSE) || (dsn_show)) ? "(" : "", (call_mailer_flags & CM_VERBOSE) ? "in verbose mode" : "", (dsn_show && (call_mailer_flags & CM_VERBOSE)) ? ", " : "", (dsn_show) ? dsn_string : "", ((call_mailer_flags & CM_VERBOSE) || dsn_show) ? ") " : "", (addr && *addr) ? addr : (F_ON(F_FCC_ON_BOUNCE, ps_global) && ba_fcc.tptr && ba_fcc.tptr[0]) ? ba_fcc.tptr : ""); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; if((strlen(tmp_20k_buf) > ps_global->ttyo->screen_cols - 2) && ps_global->ttyo->screen_cols >= 7) strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7, "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; if(verbose_label) opts[verbose_label].label = /* TRANSLATORS: several possible key labels follow */ (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose"); if(F_ON(F_DSN, ps_global)){ if(call_mailer_flags & CM_DSN_SHOW){ opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY) ? N_("NoDelay") : N_("Delay"); opts[dsn_label+1].ch = 's'; opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS) ? N_("NoSuccess") : N_("Success"); opts[dsn_label+2].ch = 'x'; opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER) ? N_("ErrRets") : N_("NoErrRets"); opts[dsn_label+3].ch = 'h'; opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL) ? N_("RetHdrs") : N_("RetFull"); } } rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts, 'y', 'z', NO_HELP, RB_NORM); if(rv == 'y'){ /* user ACCEPTS! */ sendit = 1; break; } else if(rv == 'n'){ /* Declined! */ break; } else if(rv == 'z'){ /* Cancelled! */ break; } else if(rv == 12){ /* flip verbose bit */ if(call_mailer_flags & CM_VERBOSE) call_mailer_flags &= ~CM_VERBOSE; else call_mailer_flags |= CM_VERBOSE; } else if(call_mailer_flags & CM_DSN_SHOW){ if(rv == 's'){ /* flip success bit */ call_mailer_flags ^= CM_DSN_SUCCESS; /* turn off related bits */ if(call_mailer_flags & CM_DSN_SUCCESS) call_mailer_flags &= ~(CM_DSN_NEVER); } else if(rv == 'd'){ /* flip delay bit */ call_mailer_flags ^= CM_DSN_DELAY; /* turn off related bits */ if(call_mailer_flags & CM_DSN_DELAY) call_mailer_flags &= ~(CM_DSN_NEVER); } else if(rv == 'x'){ /* flip never bit */ call_mailer_flags ^= CM_DSN_NEVER; /* turn off related bits */ if(call_mailer_flags & CM_DSN_NEVER) call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY); } else if(rv == 'h'){ /* flip full bit */ call_mailer_flags ^= CM_DSN_FULL; } } else if(rv == 'd'){ /* show dsn options */ call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL); } snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"), (call_mailer_flags & CM_DSN_NEVER) ? _("Never") : "F", (call_mailer_flags & CM_DSN_DELAY) ? "D" : "", (call_mailer_flags & CM_DSN_SUCCESS) ? "S" : "", (call_mailer_flags & CM_DSN_NEVER) ? "" : (call_mailer_flags & CM_DSN_FULL) ? "-Full" : "-Hdrs"); dsn_string[sizeof(dsn_string)-1] = '\0'; } } if(addr) fs_give((void **)&addr); if(!(flagsarg & SS_PROMPTFORTO) || sendit){ char *fcc = NULL; CONTEXT_S *fcc_cntxt = NULL; if(F_ON(F_FCC_ON_BOUNCE, ps_global)){ if(ba_fcc.tptr) fcc = cpystr(ba_fcc.tptr); set_last_fcc(fcc); /* * If special name "inbox" then replace it with the * real inbox path. */ if(ps_global->VAR_INBOX_PATH && strucmp(fcc, ps_global->inbox_name) == 0){ char *replace_fcc; replace_fcc = cpystr(ps_global->VAR_INBOX_PATH); fs_give((void **) &fcc); fcc = replace_fcc; } } /*---- Check out fcc -----*/ if(fcc && *fcc){ (void) commence_fcc(fcc, &fcc_cntxt, FALSE); if(!lmc.so){ dprint((4,"can't open fcc, cont\n")); if(!(flagsarg & SS_PROMPTFORTO)){ retval = -1; fs_give((void **)&fcc); fcc = NULL; goto finish; } else continue; } else so_truncate(lmc.so, fcc_size_guess(*body) + 2048); } else lmc.so = NULL; if(!(outgoing->to || outgoing->cc || outgoing->bcc || lmc.so)){ q_status_message(SM_ORDER, 3, 5, _("No recipients specified!")); continue; } if(outgoing->to || outgoing->cc || outgoing->bcc){ char **alt_smtp = NULL; if(role && role->smtp){ if(ps_global->FIX_SMTP_SERVER && ps_global->FIX_SMTP_SERVER[0]) q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited")); else alt_smtp = role->smtp; } result = call_mailer(header, *body, alt_smtp, call_mailer_flags, call_mailer_file_result, pipe_callback); mark_address_failure_for_pico(header); } else result = 0; if(result == 1 && !lmc.so) q_status_message(SM_ORDER, 0, 3, _("Message sent")); /*----- Was there an fcc involved? -----*/ if(lmc.so){ if(result == 1 || (result == 0 && pine_rfc822_output(header, *body, NULL, NULL))){ char label[50]; strncpy(label, "Fcc", sizeof(label)); label[sizeof(label)-1] = '\0'; if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){ snprintf(label + 3, sizeof(label)-3, " to %s", fcc); label[sizeof(label)-1] = '\0'; } /* Now actually copy to fcc folder and close */ fcc_result = write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label, F_ON(F_MARK_FCC_SEEN, ps_global) ? "\\SEEN" : NULL); } else if(result == 0){ q_status_message(SM_ORDER,3,5, _("Fcc Failed!. No message saved.")); retval = -1; dprint((1, "explicit fcc write failed!\n")); } so_give(&lmc.so); } if(result < 0){ dprint((1, "Bounce failed\n")); if(!(flagsarg & SS_PROMPTFORTO)) retval = -1; else continue; } else if(result == 1){ if(!fcc) q_status_message(SM_ORDER, 0, 3, _("Message sent")); else{ int avail = ps_global->ttyo->screen_cols-2; int need, fcclen; char *part1 = "Message sent and "; char *part2 = fcc_result ? "" : "NOT "; char *part3 = "copied to "; fcclen = strlen(fcc); need = 2 + strlen(part1) + strlen(part2) + strlen(part3) + fcclen; if(need > avail && fcclen > 6) fcclen -= MIN(fcclen-6, need-avail); q_status_message4(SM_ORDER, 0, 3, "%s%s%s\"%s\"", part1, part2, part3, short_str(fcc, (char *)tmp_20k_buf, SIZEOF_20KBUF, fcclen, FrontDots)); } } if(fcc) fs_give((void **)&fcc); } else{ q_status_message(SM_ORDER, 0, 3, _("Send cancelled")); retval = -1; } } else{ q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error in address: %s"), errbuf); if(errbuf) fs_give((void **)&errbuf); if(!(flagsarg & SS_PROMPTFORTO)) retval = -1; else continue; } } else{ q_status_message(SM_ORDER | SM_DING, 3, 5, _("No addressee! No e-mail sent.")); retval = -1; } } done++; break; case 1: q_status_message(SM_ORDER, 0, 3, _("Send cancelled")); done++; retval = -1; break; case 3: help = (help == NO_HELP) ? (outgoing->remail == NULL ? h_anon_forward : h_bounce) : NO_HELP; break; case 11: if(**tobufp){ char *new_nickname = NULL; int l; int ambiguity; ambiguity = abook_nickname_complete(*tobufp, &new_nickname, (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA); if(new_nickname){ if(*new_nickname){ if((l=strlen(new_nickname)) > resize_len){ resize_len = l; fs_resize((void **) tobufp, resize_len+1); } strncpy(*tobufp, new_nickname, l); (*tobufp)[l] = '\0'; } fs_give((void **) &new_nickname); } if(ambiguity != 2) Writechar(BELL, 0); } break; case 4: /* can't suspend */ default: break; } } finish: if(ba_fcc.tptr) fs_give((void **)&ba_fcc.tptr); pine_free_env(&header); return(retval); } /* * pine_simple_send_header - generate header suitable for simple_sending */ METAENV * pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp) { METAENV *header; PINEFIELD *pf; static struct headerentry he_dummy; header = pine_new_env(outgoing, fccp, tobufpp, NULL); /* assign he_dummy to "To:" field "he" for strings2outgoing */ for(pf = header->local; pf && pf->name; pf = pf->next) if(pf->type == Address && !strucmp(pf->name, "to")){ memset((void *) &he_dummy, 0, sizeof(he_dummy)); pf->extdata = (void *) &he_dummy; HE(pf)->dirty = 1; break; } return(header); } /*---------------------------------------------------------------------- Prepare data structures for pico, call pico, then post message Args: outgoing -- Partially formatted outgoing ENVELOPE body -- Body of outgoing message editor_title -- Title for anchor line in composer fcc_arg -- The file carbon copy field reply -- Struct describing set of msgs being replied-to lcc_arg -- custom -- custom header list. sticky_fcc -- Result: message is edited, then postponed, cancelled or sent. Fields: remail - return_path - date added here from added here sender - reply_to - subject passed in, edited and cannonized here to possibly passed in, edited and cannonized here cc possibly passed in, edited and cannonized here bcc edited and cannonized here in_reply_to generated in reply() and passed in message_id - Storage for these fields comes from anywhere outside. It is remalloced here so the composer can realloc them if needed. The copies here are also freed here. Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART with the first part TYPETEXT! All newlines in the text here also end with CRLF. There's a further assumption that the text in the TYPETEXT part is stored in a storage object (see filter.c). ----*/ void pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body, char *editor_title, ACTION_S *role, char *fcc_arg, REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg, PINEFIELD *custom, int flags) { int i, fixed_cnt, total_cnt, index, editor_result = 0, body_start = 0, use_news_order = 0; char *p, *addr, *fcc, *fcc_to_free = NULL; char *start_here_name = NULL; char *suggested_nntp_server = NULL; char *title = NULL; struct headerentry *he, *headents, *he_to = NULL, *he_fcc = NULL, *he_news = NULL, *he_lcc = NULL, *he_from = NULL; PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL, *pf_smtp_server = NULL, *pf_nntp_server = NULL, *pf_fcc = NULL, *pf_err = NULL, *pf_uid = NULL, *pf_mbox = NULL, *pf_curpos = NULL, *pf_ourrep = NULL, *pf_ourhdrs = NULL, **sending_order; METAENV header; ADDRESS *lcc_addr = NULL; ADDRESS *nobody_addr = NULL; BODY_PARTICULARS_S *bp; STORE_S *orig_so = NULL; PICO pbuf1, *save_previous_pbuf; CustomType ct; REDRAFT_POS_S *local_redraft_pos = NULL; dprint((1,"\n=== send called ===\n")); save_previous_pbuf = pbf; pbf = &pbuf1; standard_picobuf_setup(pbf); /* * Cancel any pending initial commands since pico uses a different * input routine. If we didn't cancel them, they would happen after * we returned from the editor, which would be confusing. */ if(ps_global->in_init_seq){ ps_global->in_init_seq = 0; ps_global->save_in_init_seq = 0; clear_cursor_pos(); if(ps_global->initial_cmds){ if(ps_global->free_initial_cmds) fs_give((void **)&(ps_global->free_initial_cmds)); ps_global->initial_cmds = 0; } F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys); } #if defined(DOS) || defined(OS2) if(!dos_valid_from()){ pbf = save_previous_pbuf; return; } pbf->upload = NULL; #else pbf->upload = (ps_global->VAR_UPLOAD_CMD && ps_global->VAR_UPLOAD_CMD[0]) ? upload_msg_to_pico : NULL; #endif pbf->msgntext = message_format_for_pico; pbf->mimetype = mime_type_for_pico; pbf->exittest = send_exit_for_pico; pbf->user_says_noflow = dont_flow_this_time; pbf->newthread = new_thread_on_blank_subject; ps_global->newthread = 0; /* reset this value */ if(F_OFF(F_CANCEL_CONFIRM, ps_global)) pbf->canceltest = cancel_for_pico; #ifdef _WINDOWS pbf->dict = (ps_global->VAR_DICTIONARY && ps_global->VAR_DICTIONARY[0] && ps_global->VAR_DICTIONARY[0][0]) ? ps_global->VAR_DICTIONARY : NULL; pbf->chosen_dict = -1; /* not chosen yet */ #endif /* _WINDOWS */ pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] && ps_global->VAR_EDITOR[0][0]) ? ps_global->VAR_EDITOR : NULL; pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0]) ? ps_global->VAR_SPELLER : NULL; pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global); pbf->quote_str = reply && reply->prefix ? reply->prefix : "> "; /* We actually want to set this only if message we're sending is flowed */ pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global); pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global) && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global) && (strcmp(pbf->quote_str, "> ") == 0 || strcmp(pbf->quote_str, ">") == 0)); pbf->edit_offset = 0; title = cpystr(set_titlebar(editor_title, ps_global->mail_stream, ps_global->context_current, ps_global->cur_folder,ps_global->msgmap, 0, FolderName, 0, 0, NULL)); pbf->pine_anchor = title; #if defined(DOS) || defined(OS2) if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){ pbf->oper_dir = ps_global->VAR_FILE_DIR; } #endif if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE)) pbf->pine_flags |= P_CHKPTNOW; /* NOTE: initial cursor position set below */ dprint((9, "flags: %x\n", pbf->pine_flags)); /* * When user runs compose and the current folder is a newsgroup, * offer to post to the current newsgroup. */ if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups)) && IS_NEWS(ps_global->mail_stream)){ char prompt[200], news_group[MAILTMPLEN]; pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group, sizeof(news_group)); /* * Replies don't get this far because To or Newsgroups will already * be filled in. So must be either ordinary compose or forward. * Forward sets subject, so use that to tell the difference. */ if(news_group[0] && !outgoing->subject){ int ch = 'y'; int ret_val; char *errmsg = NULL; BUILDER_ARG *fcc_build = NULL; if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){ snprintf(prompt, sizeof(prompt), _("Post to current newsgroup (%s)"), news_group); prompt[sizeof(prompt)-1] = '\0'; ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM); } switch(ch){ case 'y': if(outgoing->newsgroups) fs_give((void **)&outgoing->newsgroups); if(!fcc_arg && !(role && role->fcc)){ fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG)); memset((void *)fcc_build, 0, sizeof(BUILDER_ARG)); fcc_build->tptr = fcc_to_free; } ret_val = news_build(news_group, &outgoing->newsgroups, &errmsg, fcc_build, NULL); if(ret_val == -1){ if(outgoing->newsgroups) fs_give((void **)&outgoing->newsgroups); outgoing->newsgroups = cpystr(news_group); } if(!fcc_arg && !(role && role->fcc)){ fcc_arg = fcc_to_free = fcc_build->tptr; fs_give((void **)&fcc_build); } if(errmsg){ if(*errmsg){ q_status_message(SM_ORDER, 3, 3, errmsg); display_message(NO_OP_COMMAND); } fs_give((void **)&errmsg); } break; case 'x': /* ^C */ q_status_message(SM_ORDER, 0, 3, _("Message cancelled")); dprint((4, "=== send: cancelled\n")); pbf = save_previous_pbuf; return; case 'n': break; default: break; } } } if(F_ON(F_PREDICT_NNTP_SERVER, ps_global) && outgoing->newsgroups && *outgoing->newsgroups && IS_NEWS(ps_global->mail_stream)){ NETMBX news_mb; if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox, &news_mb)) if(!strucmp(news_mb.service, "nntp")){ if(*ps_global->mail_stream->original_mailbox == '{'){ char *svcp = NULL, *psvcp; suggested_nntp_server = cpystr(ps_global->mail_stream->original_mailbox + 1); if((p = strindex(suggested_nntp_server, '}')) != NULL) *p = '\0'; for(p = strindex(suggested_nntp_server, '/'); p && *p; p = strindex(p, '/')){ /* take out /nntp, which gets added in nntp_open */ if(!struncmp(p, "/nntp", 5)) svcp = p + 5; else if(!struncmp(p, "/service=nntp", 13)) svcp = p + 13; else if(!struncmp(p, "/service=\"nntp\"", 15)) svcp = p + 15; else p++; if(svcp){ if(*svcp == '\0') *p = '\0'; else if(*svcp == '/' || *svcp == ':'){ for(psvcp = p; *svcp; svcp++, psvcp++) *psvcp = *svcp; *psvcp = '\0'; } svcp = NULL; } } } else suggested_nntp_server = cpystr(news_mb.orighost); } } /* * If we don't already have custom headers set and the role has custom * headers, then incorporate those custom headers into "custom". */ if(!custom){ PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL; dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef); /* * If we allow the Combine argument here, we're saying that we want to * combine the values from the envelope and the role for the fields To, * Cc, Bcc, and Newsgroups. For example, if we are replying to a message * we'll have a To in the envelope because we're replying. If our role also * has a To action, then Combine would combine those two and offer both * to the user. We've decided against doing this. Instead, we always use * Replace, and the role's header value replaces the value from the * envelope. It might also make sense in some cases to do the opposite, * which would be treating the role headers as defaults, just like * customized-hdrs. */ #ifdef WANT_TO_COMBINE_ADDRESSES if(role && role->cstm) rolehdrs = parse_custom_hdrs(role->cstm, Combine); #else if(role && role->cstm) rolehdrs = parse_custom_hdrs(role->cstm, Replace); #endif if(rolehdrs){ custom = combine_custom_headers(dflthdrs, rolehdrs); if(dflthdrs){ free_prompts(dflthdrs); free_customs(dflthdrs); } if(rolehdrs){ free_prompts(rolehdrs); free_customs(rolehdrs); } } else custom = dflthdrs; } g_rolenick = role ? role->nick : NULL; /* how many fixed fields are there? */ for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++) ; total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1); /* the fixed part of the PINEFIELDs */ i = fixed_cnt * sizeof(PINEFIELD); pfields = (PINEFIELD *)fs_get((size_t) i); memset(pfields, 0, (size_t) i); /* temporary headerentry array for pico */ i = (total_cnt + 1) * sizeof(struct headerentry); headents = (struct headerentry *)fs_get((size_t) i); memset(headents, 0, (size_t) i); i = total_cnt * sizeof(PINEFIELD *); sending_order = (PINEFIELD **)fs_get((size_t) i); memset(sending_order, 0, (size_t) i); pbf->headents = headents; header.env = outgoing; header.local = pfields; header.sending_order = sending_order; /* custom part of PINEFIELDs */ header.custom = custom; he = headents; pf = pfields; /* * For Address types, pf->addr points to an ADDRESS *. * If that address is in the "outgoing" envelope, it will * be freed by the caller, otherwise, it should be freed here. * Pf->textbuf for an Address is used a little to set up a default, * but then is freed right away below. Pf->scratch is used for a * pointer to some alloced space for pico to edit in. Addresses in * the custom area are freed by free_customs(). * * For FreeText types, pf->addr is not used. Pf->text points to a * pointer that points to the text. Pf->textbuf points to a copy of * the text that must be freed before we leave, otherwise, it is * probably a pointer into the envelope and that gets freed by the * caller. * * He->realaddr is the pointer to the text that pico actually edits. */ #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH) # define NN 4 #else # define NN 3 #endif if(outgoing->newsgroups && *outgoing->newsgroups) use_news_order++; /* initialize the fixed header elements of the two temp arrays */ for(i=0; i < fixed_cnt; i++, pf++){ static int news_order[] = { N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC, N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY, N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX, N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH) , N_SENDER #endif }; index = i; /* slightly different editing order if sending to news */ if(use_news_order && index >= 0 && index < sizeof(news_order)/sizeof(news_order[0])) index = news_order[i]; /* copy the templates */ *he = he_template[index]; pf->name = cpystr(pf_template[index].name); if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global)){ /* slide string over so it is Sender instead of X-X-Sender */ for(p = pf->name+4; *p != '\0'; p++) *(p-4) = *p; *(p-4) = '\0'; } pf->type = pf_template[index].type; pf->canedit = pf_template[index].canedit; pf->rcptto = pf_template[index].rcptto; pf->writehdr = pf_template[index].writehdr; pf->localcopy = pf_template[index].localcopy; pf->extdata = he; pf->next = pf + 1; he->rich_header = view_as_rich(pf->name, he->rich_header); if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global)) he->nickcmpl = NULL; switch(pf->type){ case FreeText: /* realaddr points to c-client env */ if(index == N_NEWS){ sending_order[1] = pf; he->realaddr = &outgoing->newsgroups; he_news = he; switch(set_default_hdrval(pf, custom)){ case Replace: if(*he->realaddr) fs_give((void **)he->realaddr); *he->realaddr = pf->textbuf; pf->textbuf = NULL; he->sticky = 1; break; case Combine: if(*he->realaddr){ /* combine values */ if(pf->textbuf && *pf->textbuf){ char *combined_hdr; size_t l; l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1; combined_hdr = (char *) fs_get((l+1) * sizeof(char)); strncpy(combined_hdr, *he->realaddr, l); combined_hdr[l] = '\0'; strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr)); combined_hdr[l] = '\0'; strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr)); combined_hdr[l] = '\0'; fs_give((void **)he->realaddr); *he->realaddr = combined_hdr; q_status_message(SM_ORDER, 3, 3, "Adding newsgroup from role"); he->sticky = 1; } } else{ *he->realaddr = pf->textbuf; pf->textbuf = NULL; } break; case UseAsDef: /* if no value, use default */ if(!*he->realaddr){ *he->realaddr = pf->textbuf; pf->textbuf = NULL; } break; case NoMatch: break; } /* If there is a newsgroup, we'd better show it */ if(outgoing->newsgroups && *outgoing->newsgroups) he->rich_header = 0; /* force on by default */ if(pf->textbuf) fs_give((void **)&pf->textbuf); pf->text = he->realaddr; } else if(index == N_DATE){ sending_order[2] = pf; pf->text = (char **) &outgoing->date; pf->extdata = NULL; } else if(index == N_INREPLY){ sending_order[NN+9] = pf; pf->text = &outgoing->in_reply_to; pf->extdata = NULL; } else if(index == N_MSGID){ sending_order[NN+10] = pf; pf->text = &outgoing->message_id; pf->extdata = NULL; } else if(index == N_REF){ sending_order[NN+11] = pf; pf->text = &outgoing->references; pf->extdata = NULL; } else if(index == N_PRIORITY){ sending_order[NN+12] = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_USERAGENT){ sending_order[NN+13] = pf; pf->text = &pf->textbuf; pf->textbuf = generate_user_agent(); pf->extdata = NULL; } else if(index == N_POSTERR){ sending_order[NN+14] = pf; pf_err = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_RPLUID){ sending_order[NN+15] = pf; pf_uid = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_RPLMBOX){ sending_order[NN+16] = pf; pf_mbox = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_SMTP){ sending_order[NN+17] = pf; pf_smtp_server = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_NNTP){ sending_order[NN+18] = pf; pf_nntp_server = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_CURPOS){ sending_order[NN+19] = pf; pf_curpos = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_OURREPLYTO){ sending_order[NN+20] = pf; pf_ourrep = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_OURHDRS){ sending_order[NN+21] = pf; pf_ourhdrs = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else if(index == N_AUTHRCVD){ sending_order[0] = pf; pf_ourhdrs = pf; pf->text = &pf->textbuf; pf->extdata = NULL; } else{ q_status_message(SM_ORDER | SM_DING, 3, 7, "Botched: Unmatched FreeText header in pine_send"); } break; /* can't do a default for this one */ case Attachment: /* If there is an attachment already, we'd better show them */ if(body && *body && (*body)->type != TYPETEXT) he->rich_header = 0; /* force on by default */ break; case Address: switch(index){ case N_FROM: sending_order[3] = pf; pf->addr = &outgoing->from; if(role && role->from){ if(ps_global->never_allow_changing_from) q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect")); else{ outgoing->from = copyaddrlist(role->from); he->display_it = 1; /* show it */ he->rich_header = 0; } } he_from = he; break; case N_TO: sending_order[NN+2] = pf; pf->addr = &outgoing->to; /* If already set, make it act like we typed it in */ if(outgoing->to && outgoing->to->mailbox && outgoing->to->mailbox[0] && flags & PS_STICKY_TO) he->sticky = 1; he_to = he; pf_to = pf; break; case N_NOBODY: sending_order[NN+5] = pf; pf_nobody = pf; if(ps_global->VAR_EMPTY_HDR_MSG && !ps_global->VAR_EMPTY_HDR_MSG[0]){ pf->addr = NULL; } else{ nobody_addr = mail_newaddr(); nobody_addr->next = mail_newaddr(); nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF, (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG ? ps_global->VAR_EMPTY_HDR_MSG : "undisclosed-recipients"), ps_global->posting_charmap)); pf->addr = &nobody_addr; } break; case N_CC: sending_order[NN+3] = pf; pf->addr = &outgoing->cc; break; case N_BCC: sending_order[NN+4] = pf; pf->addr = &outgoing->bcc; /* if bcc exists, make sure it's exposed so nothing's * sent by mistake... */ if(outgoing->bcc) he->display_it = 1; break; case N_REPLYTO: sending_order[NN+1] = pf; pf->addr = &outgoing->reply_to; if(role && role->replyto){ if(outgoing->reply_to) mail_free_address(&outgoing->reply_to); outgoing->reply_to = copyaddrlist(role->replyto); he->display_it = 1; /* show it */ he->rich_header = 0; } break; case N_LCC: sending_order[NN+7] = pf; pf->addr = &lcc_addr; he_lcc = he; if(lcc_arg){ build_address(lcc_arg, &addr, NULL, NULL, NULL); rfc822_parse_adrlist(&lcc_addr, addr, ps_global->maildomain); fs_give((void **)&addr); he->display_it = 1; } break; #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH) case N_SENDER: sending_order[4] = pf; pf->addr = &outgoing->sender; break; #endif default: q_status_message1(SM_ORDER,3,7, "Internal error: Address header %s", comatose(index)); break; } /* * If this is a reply to news, don't show the regular email * recipient headers (unless they are non-empty). */ if((outgoing->newsgroups && *outgoing->newsgroups) && (index == N_TO || index == N_CC || index == N_BCC || index == N_LCC) && (pf->addr && !*pf->addr)){ if((ct=set_default_hdrval(pf, custom)) >= UseAsDef && pf->textbuf && *pf->textbuf){ 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); fs_give((void **)&addr); if(ct > UseAsDef) he->sticky = 1; } else he->rich_header = 1; /* hide */ } /* * If this address doesn't already have a value, then we check * for a default value assigned by the user. */ else if(pf->addr && !*pf->addr){ if((ct=set_default_hdrval(pf, custom)) >= UseAsDef && (index != N_FROM || (!ps_global->never_allow_changing_from && F_ON(F_ALLOW_CHANGING_FROM, ps_global))) && pf->textbuf && *pf->textbuf){ removing_trailing_white_space(pf->textbuf); (void)removing_double_quotes(pf->textbuf); /* * Try to set To based on Lcc. Don't attempt Fcc. */ if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){ BUILDER_ARG *barg = NULL; char *ppp = NULL; if(*pf_to->addr) ppp = addr_list_string(*pf_to->addr, NULL, 1); if(!ppp) ppp = cpystr(""); barg = (BUILDER_ARG *) fs_get(sizeof(*barg)); memset(barg, 0, sizeof(*barg)); barg->me = &(he->bldr_private); barg->aff = &(he_to->bldr_private); barg->tptr = cpystr(ppp); build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL); he->display_it = 1; rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain); if(addr) fs_give((void **) &addr); if(ct > UseAsDef) he->sticky = 1; if(barg && barg->tptr && strcmp(ppp, barg->tptr)){ ADDRESS *a = NULL; rfc822_parse_adrlist(&a, barg->tptr, ps_global->maildomain); if(a){ if(pf_to->addr) mail_free_address(pf_to->addr); *pf_to->addr = a; } } if(barg){ if(barg->tptr) fs_give((void **) &barg->tptr); fs_give((void **) &barg); } if(ppp) fs_give((void **) &ppp); } else{ build_address(pf->textbuf, &addr, NULL, NULL, NULL); rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain); if(addr) fs_give((void **) &addr); if(ct > UseAsDef) he->sticky = 1; } } /* if we still don't have a from */ if(index == N_FROM && !*pf->addr) *pf->addr = generate_from(); } /* * Addr is already set in the rest of the cases. */ else if((index == N_FROM || index == N_REPLYTO) && pf->addr){ ADDRESS *adr = NULL; /* * We get to this case of the ifelse if the from or reply-to * addr was set by a role above. */ /* figure out the default value */ (void)set_default_hdrval(pf, custom); if(pf->textbuf && *pf->textbuf){ removing_trailing_white_space(pf->textbuf); (void)removing_double_quotes(pf->textbuf); build_address(pf->textbuf, &addr, NULL, NULL, NULL); rfc822_parse_adrlist(&adr, addr, ps_global->maildomain); fs_give((void **)&addr); } /* if value set by role is different from default, show it */ if(adr && !address_is_same(*pf->addr, adr)) he->display_it = 1; /* start this off showing */ /* malformed */ if(!(*pf->addr)->mailbox){ fs_give((void **)pf->addr); he->display_it = 1; } if(adr) mail_free_address(&adr); } else if((index == N_TO || index == N_CC || index == N_BCC) && pf->addr){ ADDRESS *a = NULL, **tail; /* * These three are different from the others because we * might add the addresses to what is already there instead * of replacing. */ switch(set_default_hdrval(pf, custom)){ case Replace: if(*pf->addr) mail_free_address(pf->addr); 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); fs_give((void **)&addr); he->sticky = 1; break; case Combine: removing_trailing_white_space(pf->textbuf); (void)removing_double_quotes(pf->textbuf); build_address(pf->textbuf, &addr, NULL, NULL, NULL); rfc822_parse_adrlist(&a, addr, ps_global->maildomain); fs_give((void **)&addr); he->sticky = 1; if(a){ for(tail = pf->addr; *tail; tail = &(*tail)->next) ; *tail = reply_cp_addr(ps_global, 0, NULL, NULL, *pf->addr, NULL, a, RCA_ALL); q_status_message(SM_ORDER, 3, 3, "Adding addresses from role"); mail_free_address(&a); } break; case UseAsDef: case NoMatch: break; } he->display_it = 1; /* start this off showing */ } else if(pf->addr){ switch(set_default_hdrval(pf, custom)){ case Replace: case Combine: if(*pf->addr) mail_free_address(pf->addr); 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); fs_give((void **)&addr); he->sticky = 1; break; case UseAsDef: case NoMatch: break; } he->display_it = 1; } if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){ mail_free_address(pf->addr); he->display_it = 1; /* start this off showing */ } if(pf->textbuf) /* free default value in any case */ fs_give((void **)&pf->textbuf); /* outgoing2strings will alloc the string pf->scratch below */ he->realaddr = &pf->scratch; break; case Fcc: sending_order[NN+8] = pf; pf_fcc = pf; if(role && role->fcc) fcc = role->fcc; else fcc = get_fcc(fcc_arg); if(fcc_to_free){ fs_give((void **)&fcc_to_free); fcc_arg = NULL; } if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc)) he->sticky = 1; if(role) role->fcc = NULL; if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0) he->display_it = 1; /* start this off showing */ he->realaddr = &fcc; pf->text = &fcc; he_fcc = he; break; case Subject : sending_order[NN+6] = pf; switch(set_default_hdrval(pf, custom)){ case Replace: case Combine: pf->scratch = pf->textbuf; pf->textbuf = NULL; he->sticky = 1; if(outgoing->subject) fs_give((void **)&outgoing->subject); break; case UseAsDef: case NoMatch: /* if no value, use default */ if(outgoing->subject){ pf->scratch = cpystr(outgoing->subject); } else{ pf->scratch = pf->textbuf; pf->textbuf = NULL; } break; } he->realaddr = &pf->scratch; pf->text = &outgoing->subject; break; default: q_status_message1(SM_ORDER,3,7, "Unknown header type %d in pine_send", (void *)pf->type); break; } /* * We may or may not want to give the user the chance to edit * the From and Reply-To lines. If they are listed in either * Default-composer-hdrs or Customized-hdrs, then they can edit * them, else no. * If canedit is not set, that means that this header is not in * the user's customized-hdrs. If rich_header is set, that * means that this header is not in the user's * default-composer-hdrs (since From and Reply-To are rich * by default). So, don't give it an he to edit with in that case. * * For other types, just not setting canedit will cause it to be * uneditable, regardless of what the user does. */ switch(index){ case N_FROM: /* to allow it, we let this fall through to the reply-to case below */ if(ps_global->never_allow_changing_from || (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) && !(role && role->from))){ if(pf->canedit || !he->rich_header) q_status_message(SM_ORDER, 3, 3, _("Not allowed to change header \"From\"")); memset(he, 0, (size_t)sizeof(*he)); pf->extdata = NULL; break; } case N_REPLYTO: if(!pf->canedit && he->rich_header){ memset(he, 0, (size_t)sizeof(*he)); pf->extdata = NULL; } else{ pf->canedit = 1; he++; } break; default: if(!pf->canedit){ memset(he, 0, (size_t)sizeof(*he)); pf->extdata = NULL; } else he++; break; } } /* * This is so the builder can tell the composer to fill the affected * field based on the value in the field on the left. * * Note that this mechanism isn't completely general. Each entry has * only a single next_affected, so if some other entry points an * affected entry at an entry with a next_affected, they all inherit * that next_affected. Since this isn't used much a careful ordering * of the affected fields should make it a sufficient mechanism. */ he_to->affected_entry = he_fcc; he_news->affected_entry = he_fcc; he_lcc->affected_entry = he_to; he_to->next_affected = he_fcc; (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL; i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */ /* * Set up headerentries for custom fields. * NOTE: "i" is assumed to now index first custom field in sending * order. */ for(pf = pf->next; pf && pf->name; pf = pf->next){ char *addr; if(pf->standard) continue; pf->extdata = he; pf->canedit = 1; pf->rcptto = 0; pf->writehdr = 1; pf->localcopy = 1; switch(pf->type){ case Address: if(pf->addr){ /* better be set */ sending_order[i++] = pf; *he = he_custom_addr_templ; /* change default text into an ADDRESS */ /* strip quotes around whole default */ 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); fs_give((void **)&addr); if(pf->textbuf) fs_give((void **)&pf->textbuf); he->realaddr = &pf->scratch; if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global)) he->nickcmpl = NULL; } break; case FreeText: sending_order[i++] = pf; *he = he_custom_free_templ; he->realaddr = &pf->textbuf; pf->text = &pf->textbuf; if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) || (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val)))) he->display_it = 1; /* show it */ break; default: q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d", (void *)pf->type); break; } he->name = pf->name; /* use first 8 characters for prompt */ he->prompt = cpystr(" : "); strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2)); he->rich_header = view_as_rich(he->name, he->rich_header); he++; } /* * Make sure at least *one* field is displayable... */ for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++) if(HE(pf) && !HE(pf)->rich_header){ index = i; break; } /* * None displayable!!! Warn and display defaults. */ if(index == -1){ q_status_message(SM_ORDER,0,5, "No default-composer-hdrs matched, displaying defaults"); for(i = 0, pf = header.local; pf; pf = pf->next, i++) if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH) && HE(pf)) HE(pf)->rich_header = 0; } /* * Save information about body which set_mime_type_by_grope might change. * Then, if we get an error sending, we reset these things so that * grope can do it's thing again after we edit some more. */ if ((*body)->type == TYPEMULTIPART) bp = save_body_particulars(&(*body)->nested.part->body); else bp = save_body_particulars(*body); local_redraft_pos = redraft_pos; /*---------------------------------------------------------------------- Loop calling the editor until everything goes well ----*/ while(1){ int saved_user_timeout; /* Reset body to what it was when we started. */ if ((*body)->type == TYPEMULTIPART) reset_body_particulars(bp, &(*body)->nested.part->body); else reset_body_particulars(bp,*body); /* * set initial cursor position based on how many times we've been * thru the loop... */ if(reply && reply->pseudo){ pbf->pine_flags |= reply->data.pico_flags; } else if(body_start){ pbf->pine_flags |= P_BODY; body_start = 0; /* maybe not next time */ } else if(local_redraft_pos){ pbf->edit_offset = local_redraft_pos->offset; /* set the start_here bit in correct header */ for(pf = header.local; pf && pf->name; pf = pf->next) if(strcmp(pf->name, local_redraft_pos->hdrname) == 0 && HE(pf)){ HE(pf)->start_here = 1; break; } /* If didn't find it, we start in body. */ if(!pf || !pf->name) pbf->pine_flags |= P_BODY; } else if(reply && (!reply->forw && !reply->forwarded)){ pbf->pine_flags |= P_BODY; } /* in case these were turned on in previous pass through loop */ if(pf_nobody){ pf_nobody->writehdr = 0; pf_nobody->localcopy = 0; } if(pf_fcc) pf_fcc->localcopy = 0; /* * If a sending attempt failed after we passed the message text * thru a user-defined filter, "orig_so" points to the original * text. Replace the body's encoded data with the original... */ if(orig_so){ STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART) ? &(*body)->nested.part->body.contents.text.data : &(*body)->contents.text.data); so_give(so); *so = orig_so; orig_so = NULL; } /* * Convert the envelope and body to the string format that * pico can edit */ outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0); for(pf = header.local; pf && pf->name; pf = pf->next){ /* * If this isn't the first time through this loop, we may have * freed some of the FreeText headers below so that they wouldn't * show up as empty headers in the finished message. Need to * alloc them again here so they can be edited. */ if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr) *HE(pf)->realaddr = cpystr(""); if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr) HE(pf)->maxlen = strlen(*HE(pf)->realaddr); } /* * If From is exposed, probably by a role, then start the cursor * on the first line which isn't filled in. If it isn't, then we * don't move the cursor, mostly for back-compat. */ if((!reply || reply->forw || reply->forwarded) && !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from && (he_from->display_it || !he_from->rich_header)){ for(pf = header.local; pf && pf->name; pf = pf->next) if(HE(pf) && (HE(pf)->display_it || !HE(pf)->rich_header) && HE(pf)->realaddr && (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){ HE(pf)->start_here = 1; break; } } #ifdef _WINDOWS mswin_setwindowmenu (MENU_COMPOSER); #endif cancel_busy_cue(-1); flush_status_messages(1); /* turn off user input timeout when in composer */ saved_user_timeout = ps_global->hours_to_timeout; ps_global->hours_to_timeout = 0; dprint((1, "\n ---- COMPOSER ----\n")); editor_result = pico(pbf); dprint((4, "... composer returns (0x%x)\n", editor_result)); ps_global->hours_to_timeout = saved_user_timeout; #ifdef _WINDOWS mswin_setwindowmenu (MENU_DEFAULT); #endif fix_windsize(ps_global); /* * Only reinitialize signals if we didn't receive an interesting * one while in pico, since pico's return is part of processing that * signal and it should continue to be ignored. */ if(!(editor_result & COMP_GOTHUP)) init_signals(); /* Pico has it's own signal stuff */ /* * We're going to save in DEADLETTER. Dump attachments first. */ if(editor_result & COMP_CANCEL) free_attachment_list(&pbf->attachments); /* Turn strings back into structures */ strings2outgoing(&header, body, pbf->attachments, flowing_requested); /* Make newsgroups NULL if it is "" (so won't show up in headers) */ if(outgoing->newsgroups){ sqzspaces(outgoing->newsgroups); if(!outgoing->newsgroups[0]) fs_give((void **)&(outgoing->newsgroups)); } /* Make subject NULL if it is "" (so won't show up in headers) */ if(outgoing->subject && !outgoing->subject[0]) fs_give((void **)&(outgoing->subject)); /* remove custom fields that are empty */ for(pf = header.local; pf && pf->name; pf = pf->next){ if(pf->type == FreeText && pf->textbuf){ if(pf->textbuf[0] == '\0'){ fs_give((void **)&pf->textbuf); pf->text = NULL; } } } removing_trailing_white_space(fcc); /*-------- Stamp it with a current date -------*/ if(outgoing->date) /* update old date */ fs_give((void **)&(outgoing->date)); if(F_ON(F_QUELL_TIMEZONE, ps_global)) mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE); rfc822_date(tmp_20k_buf); /* format and copy new date */ if(F_ON(F_QUELL_TIMEZONE, ps_global)) mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE); outgoing->date = (unsigned char *) cpystr(tmp_20k_buf); /* Set return_path based on From which is going to be used */ if(outgoing->return_path) mail_free_address(&outgoing->return_path); outgoing->return_path = rfc822_cpy_adr(outgoing->from); /* * Don't ever believe the sender that is there. * If From doesn't look quite right, generate our own sender. */ if(outgoing->sender) mail_free_address(&outgoing->sender); /* * If the LHS of the address doesn't match, or the RHS * doesn't match one of localdomain or hostname, * then add a sender line (really X-X-Sender). * * Don't add a personal_name since the user can change that. */ if(F_OFF(F_DISABLE_SENDER, ps_global) && (!outgoing->from || !outgoing->from->mailbox || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0 || !outgoing->from->host || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){ outgoing->sender = mail_newaddr(); outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID); outgoing->sender->host = cpystr(ps_global->hostname); } if(ps_global->newthread){ if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to); if(outgoing->references) fs_give((void **)&outgoing->references); } /*----- Message is edited, now decide what to do with it ----*/ if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){ /*=========== Postpone or Interrupted message ============*/ CONTEXT_S *fcc_cntxt = NULL; char folder[MAXPATH+1]; int fcc_result = 0; char label[50]; dprint((4, "pine_send:%s handling\n", (editor_result & COMP_SUSPEND) ? "SUSPEND" : (editor_result & COMP_GOTHUP) ? "HUP" : (editor_result & COMP_CANCEL) ? "CANCEL" : "HUH?")); if((editor_result & COMP_CANCEL) && (F_ON(F_QUELL_DEAD_LETTER, ps_global) || ps_global->deadlets == 0)){ q_status_message(SM_ORDER, 0, 3, "Message cancelled"); break; } /* * The idea here is to use the Fcc: writing facility * to append to the special postponed message folder... * * NOTE: the strategy now is to write the message and * all attachments as they exist at composition time. * In other words, attachments are postponed by value * and not reference. This may change later, but we'll * need a local "message/external-body" type that * outgoing2strings knows how to properly set up for * the composer. Maybe later... */ label[0] = '\0'; if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global) && (editor_result & COMP_SUSPEND) && (check_addresses(&header) == CA_BAD)){ /*--- Addresses didn't check out---*/ q_status_message(SM_ORDER, 7, 7, _("Not allowed to postpone message until addresses are qualified")); continue; } /* * Build the local message copy so. * * In the HUP case, we'll write the bezerk delimiter by * hand and output the message directly into the folder. * It's not only faster, we don't have to worry about * c-client reentrance and less hands paw over the data so * there's less chance of a problem. * * In the Postpone case, just create it if the user wants to * and create a temporary storage object to write into. */ fake_hup: fcc_result = 0; lmc.all_written = lmc.text_only = lmc.text_written = 0; if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){ int newfile = 1; time_t now = time((time_t *)0); #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); folder[0] = '\0'; if(lc != NULL){ strncpy(folder,ps_global->pinerc, MIN(lc-ps_global->pinerc,sizeof(folder)-1)); folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0'; } strncat(folder, (editor_result & COMP_GOTHUP) ? INTERRUPTED_MAIL : DEADLETTER, sizeof(folder)-strlen(folder)-1); } else #endif build_path(folder, ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR : ps_global->home_dir, (editor_result & COMP_GOTHUP) ? INTERRUPTED_MAIL : DEADLETTER, sizeof(folder)); if(editor_result & COMP_CANCEL){ char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5]; if(strlen(folder) + 1 < sizeof(filename)) for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){ strncpy(filename, folder, sizeof(filename)); filename[sizeof(filename)-1] = '\0'; strncpy(newfname, filename, sizeof(newfname)); newfname[sizeof(newfname)-1] = '\0'; if(i > 1){ snprintf(nbuf, sizeof(nbuf), "%d", i); nbuf[sizeof(nbuf)-1] = '\0'; strncat(filename, nbuf, sizeof(filename)-strlen(filename)-1); filename[sizeof(filename)-1] = '\0'; } snprintf(nbuf, sizeof(nbuf), "%d", i+1); nbuf[sizeof(nbuf)-1] = '\0'; strncat(newfname, nbuf, sizeof(newfname)-strlen(newfname)-1); newfname[sizeof(newfname)-1] = '\0'; (void) rename_file(filename, newfname); } our_unlink(folder); } else newfile = can_access(folder, ACCESS_EXISTS); if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){ if (outgoing->from){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012", newfile ? "" : "\015\012", outgoing->from->mailbox, outgoing->from->host, ctime(&now)); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; if(!so_puts(lmc.so, tmp_20k_buf)){ if(editor_result & COMP_CANCEL) q_status_message2(SM_ORDER | SM_DING, 3, 3, "Can't write \"%s\": %s", folder, error_description(errno)); else dprint((1, "* * * CAN'T WRITE %s: %s\n", folder ? folder : "?", error_description(errno))); } } } } else{ /* Must be COMP_SUSPEND */ if(!ps_global->VAR_POSTPONED_FOLDER || !ps_global->VAR_POSTPONED_FOLDER[0]){ q_status_message(SM_ORDER | SM_DING, 3, 3, _("No postponed file defined")); continue; } /* * Store the cursor position * * First find the header entry with the start_here * bit set, if any. This means the editor is telling * us to start on this header field next time. */ start_here_name = NULL; for(pf = header.local; pf && pf->name; pf = pf->next) if(HE(pf) && HE(pf)->start_here){ start_here_name = pf->name; break; } /* If there wasn't one, ":" means we start in the body. */ if(!start_here_name || !*start_here_name) start_here_name = ":"; if(ps_global->VAR_FORM_FOLDER && ps_global->VAR_FORM_FOLDER[0] && postpone_prompt() == 'f'){ strncpy(folder, ps_global->VAR_FORM_FOLDER, sizeof(folder)-1); folder[sizeof(folder)-1] = '\0'; strncpy(label, "form letter", sizeof(label)); label[sizeof(label)-1] = '\0'; } else{ strncpy(folder, ps_global->VAR_POSTPONED_FOLDER, sizeof(folder)-1); folder[sizeof(folder)-1] = '\0'; strncpy(label, "postponed message", sizeof(label)); label[sizeof(label)-1] = '\0'; } lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL); } if(lmc.so){ size_t sz; char *lmq = NULL; /* copy fcc line to postponed or interrupted folder */ if(pf_fcc) pf_fcc->localcopy = 1; /* plug error into header for later display to user */ if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){ pf_err->writehdr = 1; pf_err->localcopy = 1; pf_err->textbuf = lmq; } /* * if reply, write (UID)folder header field so we can * later flag the replied-to message \\ANSWERED * DON'T save MSGNO's. */ if(reply && reply->uid){ char uidbuf[MAILTMPLEN], *p; long i; for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){ if(i) sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf)); sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf)); } tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; pf_uid->writehdr = 1; pf_uid->localcopy = 1; snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s", reply->prefix ? int2string(strlen(reply->prefix)) : (reply->forwarded) ? "": "0 ", reply->prefix ? " " : "", reply->prefix ? reply->prefix : "", i, reply->data.uid.validity, tmp_20k_buf, reply->mailbox); uidbuf[sizeof(uidbuf)-1] = '\0'; pf_uid->textbuf = cpystr(uidbuf); /* * Logically, this ought to be part of pf_uid, but this * was added later and so had to be in a separate header * for backwards compatibility. */ pf_mbox->writehdr = 1; pf_mbox->localcopy = 1; pf_mbox->textbuf = cpystr(reply->origmbox ? reply->origmbox : reply->mailbox); } /* Save cursor position */ if(start_here_name && *start_here_name){ char curposbuf[MAILTMPLEN]; pf_curpos->writehdr = 1; pf_curpos->localcopy = 1; snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name, pbf->edit_offset); curposbuf[sizeof(curposbuf)-1] = '\0'; pf_curpos->textbuf = cpystr(curposbuf); } /* * Work around c-client reply-to bug. C-client will * return a reply_to in an envelope even if there is * no reply-to header field. We want to note here whether * the reply-to is real or not. */ if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){ pf_ourrep->writehdr = 1; pf_ourrep->localcopy = 1; if(outgoing->reply_to) pf_ourrep->textbuf = cpystr("Full"); else pf_ourrep->textbuf = cpystr("Empty"); } /* Save the role-specific smtp server */ if(role && role->smtp && role->smtp[0]){ char *q, *smtp = NULL; char **lp; size_t len = 0; /* * Turn the list of smtp servers into a space- * delimited list in a single string. */ for(lp = role->smtp; (q = *lp) != NULL; lp++) len += (strlen(q) + 1); if(len){ smtp = (char *) fs_get(len * sizeof(char)); smtp[0] = '\0'; for(lp = role->smtp; (q = *lp) != NULL; lp++){ if(lp != role->smtp) strncat(smtp, " ", len-strlen(smtp)-1); strncat(smtp, q, len-strlen(smtp)-1); } smtp[len-1] = '\0'; } pf_smtp_server->writehdr = 1; pf_smtp_server->localcopy = 1; if(smtp) pf_smtp_server->textbuf = smtp; else pf_smtp_server->textbuf = cpystr(""); } /* Save the role-specific nntp server */ if(suggested_nntp_server || (role && role->nntp && role->nntp[0])){ char *q, *nntp = NULL; char **lp; size_t len = 0; if(role && role->nntp && role->nntp[0]){ /* * Turn the list of nntp servers into a space- * delimited list in a single string. */ for(lp = role->nntp; (q = *lp) != NULL; lp++) len += (strlen(q) + 1); if(len){ nntp = (char *) fs_get(len * sizeof(char)); nntp[0] = '\0'; for(lp = role->nntp; (q = *lp) != NULL; lp++){ if(lp != role->nntp) strncat(nntp, " ", len-strlen(nntp)-1); strncat(nntp, q, len-strlen(nntp)-1); } nntp[len-1] = '\0'; } } else nntp = cpystr(suggested_nntp_server); pf_nntp_server->writehdr = 1; pf_nntp_server->localcopy = 1; if(nntp) pf_nntp_server->textbuf = nntp; else pf_nntp_server->textbuf = cpystr(""); } /* * Write the list of custom headers to the * X-Our-Headers header so that we can recover the * list in redraft. */ sz = 0; for(pf = header.custom; pf && pf->name; pf = pf->next) sz += strlen(pf->name) + 1; if(sz){ char *q; pf_ourhdrs->writehdr = 1; pf_ourhdrs->localcopy = 1; pf_ourhdrs->textbuf = (char *)fs_get(sz); memset(pf_ourhdrs->textbuf, 0, sz); q = pf_ourhdrs->textbuf; for(pf = header.custom; pf && pf->name; pf = pf->next){ if(pf != header.custom) sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf)); sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf)); } pf_ourhdrs->textbuf[sz-1] = '\0';; } /* * We need to make sure any header values that got cleared * get written to the postponed message (they won't if * pf->text is NULL). Otherwise, we can't tell previously * non-existent custom headers or default values from * custom (or other) headers that got blanked in the * composer... */ for(pf = header.local; pf && pf->name; pf = pf->next) if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr)) *(HE(pf)->realaddr) = cpystr(""); /* * We're saving the message for use later. It may be that the * characters in the message are not all convertible to the * user's posting_charmap. We'll save it as UTF-8 instead * and worry about that the next time they try to send it. * Use a different save pointer just to be sure we don't * mess up the other stuff. We should probably make the * charset an argument. * * We also need to fix the charset of the body part * the user is editing so that we can read it back * successfully when we resume the composition. */ ps_global->post_utf8 = 1; { PARAMETER *pm; BODY *bp; if((*body)->type == TYPEMULTIPART) bp = &(*body)->nested.part->body; else bp = *body; for(pm = bp->parameter; pm && strucmp(pm->attribute, "charset") != 0; pm = pm->next) ; if(pm){ if(pm->value) fs_give((void **) &pm->value); pm->value = cpystr("UTF-8"); } } if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){ if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){ char *err; STORE_S *hup_so; gf_io_t gc, pc; int we_cancel = 0; if(editor_result & COMP_CANCEL){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Saving to \"%s\"", folder); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1); } if((hup_so = so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){ gf_set_so_readc(&gc, lmc.so); gf_set_so_writec(&pc, hup_so); so_seek(lmc.so, 0L, 0); /* read msg copy and */ so_seek(hup_so, 0L, 2); /* append to folder */ gf_filter_init(); gf_link_filter(gf_nvtnl_local, NULL); if(!(fcc_result = !(err = gf_pipe(gc, pc)))) dprint((1, "*** PIPE FAILED: %s\n", err ? err : "?")); gf_clear_so_readc(lmc.so); gf_clear_so_writec(hup_so); so_give(&hup_so); } else dprint((1, "*** CAN'T CREATE %s: %s\n", folder ? folder : "?", error_description(errno))); if(we_cancel) cancel_busy_cue(-1); } else fcc_result = write_fcc(folder, fcc_cntxt, lmc.so, NULL, label, NULL); } /* discontinue coerced UTF-8 posting */ ps_global->post_utf8 = 0; so_give(&lmc.so); } else dprint((1, "***CAN'T ALLOCATE temp store: %s ", error_description(errno))); if(editor_result & COMP_GOTHUP){ /* * Special Hack #291: if any hi-byte bits are set in * editor's result, we put them there. */ if(editor_result & 0xff00) exit(editor_result >> 8); dprint((1, "Save composition on HUP %sED\n", fcc_result ? "SUCCEED" : "FAIL")); hup_signal(); /* Do what we normally do on SIGHUP */ } else if((editor_result & COMP_SUSPEND) && fcc_result){ if(ps_global->VAR_FORM_FOLDER && ps_global->VAR_FORM_FOLDER[0] && !strcmp(folder, ps_global->VAR_FORM_FOLDER)) q_status_message(SM_ORDER, 0, 3, _("Composition saved to Form Letter Folder. Select Compose to send.")); else q_status_message(SM_ORDER, 0, 3, _("Composition postponed. Select Compose to resume.")); break; /* postpone went OK, get out of here */ } else if(editor_result & COMP_CANCEL){ char *lc = NULL; if(fcc_result && folder) lc = last_cmpnt(folder); q_status_message3(SM_ORDER, 0, 3, _("Message cancelled%s%s%s"), (lc && *lc) ? " and copied to \"" : "", (lc && *lc) ? lc : "", (lc && *lc) ? "\" file" : ""); break; } else{ q_status_message(SM_ORDER, 0, 4, _("Continuing composition. Message not postponed or sent")); body_start = 1; continue; /* postpone failed, jump back in to composer */ } } else{ /*------ Must be sending mail or posting ! -----*/ int result, valid_addr, continue_with_only_fcc = 0; CONTEXT_S *fcc_cntxt = NULL; result = 0; dprint((4, "=== sending: ")); /* --- If posting, confirm with user ----*/ if(outgoing->newsgroups && *outgoing->newsgroups && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global) && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){ q_status_message(SM_ORDER, 0, 3, _("Message not posted")); dprint((4, "no post, continuing\n")); continue; } if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr || outgoing->newsgroups)){ if(fcc && fcc[0]){ if(F_OFF(F_AUTO_FCC_ONLY, ps_global) && want_to(_("No recipients, really copy only to Fcc "), 'n', 'n', h_send_fcc_only, WT_NORM) != 'y') continue; continue_with_only_fcc++; } else{ q_status_message(SM_ORDER, 3, 4, _("No recipients specified!")); dprint((4, "no recip, continuing\n")); continue; } } if((valid_addr = check_addresses(&header)) == CA_BAD){ /*--- Addresses didn't check out---*/ dprint((4, "addrs failed, continuing\n")); continue; } if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global) && !continue_with_only_fcc && !(outgoing->to || outgoing->cc || lcc_addr || outgoing->newsgroups) && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "), 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){ dprint((4, "No To or CC or Newsgroup, continuing\n")); if(local_redraft_pos && local_redraft_pos != redraft_pos) free_redraft_pos(&local_redraft_pos); local_redraft_pos = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos)); memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos)); local_redraft_pos->hdrname = cpystr(TONAME); continue; } if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global) && check_for_subject(&header) == CF_MISSING){ dprint((4, "No subject, continuing\n")); if(local_redraft_pos && local_redraft_pos != redraft_pos) free_redraft_pos(&local_redraft_pos); local_redraft_pos = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos)); memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos)); local_redraft_pos->hdrname = cpystr(SUBJNAME); continue; } if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global) && check_for_fcc(fcc) == CF_MISSING){ dprint((4, "No fcc, continuing\n")); if(local_redraft_pos && local_redraft_pos != redraft_pos) free_redraft_pos(&local_redraft_pos); local_redraft_pos = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos)); memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos)); local_redraft_pos->hdrname = cpystr("Fcc"); continue; } set_last_fcc(fcc); /*---- Check out fcc -----*/ if(fcc && *fcc){ /* * If special name "inbox" then replace it with the * real inbox path. */ if(ps_global->VAR_INBOX_PATH && strucmp(fcc, ps_global->inbox_name) == 0){ char *replace_fcc; replace_fcc = cpystr(ps_global->VAR_INBOX_PATH); fs_give((void **)&fcc); fcc = replace_fcc; } lmc.all_written = lmc.text_written = 0; /* lmc.text_only set on command line */ if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){ /* ---- Open or allocation of fcc failed ----- */ dprint((4,"can't open/allocate fcc, cont'g\n")); /* * Find field entry associated with fcc, and start * composer on it... */ for(pf = header.local; pf && pf->name; pf = pf->next) if(pf->type == Fcc && HE(pf)) HE(pf)->start_here = 1; continue; } else so_truncate(lmc.so, fcc_size_guess(*body) + 2048); } else lmc.so = NULL; /*---- Take care of any requested prefiltering ----*/ if(sending_filter_requested && !filter_message_text(sending_filter_requested, outgoing, *body, &orig_so, &header)){ q_status_message1(SM_ORDER, 3, 3, _("Problem filtering! Nothing sent%s."), fcc ? " or saved to fcc" : ""); continue; } /*------ Actually post -------*/ if(outgoing->newsgroups){ char **alt_nntp = NULL, *alt_nntp_p[2]; if(((role && role->nntp) || suggested_nntp_server)){ if(ps_global->FIX_NNTP_SERVER && ps_global->FIX_NNTP_SERVER[0]) q_status_message(SM_ORDER | SM_DING, 5, 5, "Using nntp-server that is administratively fixed"); else if(role && role->nntp) alt_nntp = role->nntp; else{ alt_nntp_p[0] = suggested_nntp_server; alt_nntp_p[1] = NULL; alt_nntp = alt_nntp_p; } } if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){ dprint((1, "Post failed, continuing\n")); if(outgoing->message_id) fs_give((void **) &outgoing->message_id); outgoing->message_id = generate_message_id(role); continue; } else result |= P_NEWS_WIN; } /* * BUG: IF we've posted the message *and* an fcc was specified * then we've already got a neatly formatted message in the * lmc.so. It'd be nice not to have to re-encode everything * to insert it into the smtp slot... */ /* * Turn on "undisclosed recipients" header if no To or cc. */ if(!(outgoing->to || outgoing->cc) && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){ pf_nobody->writehdr = 1; pf_nobody->localcopy = 1; } if(priority_requested){ (void) set_priority_header(&header, priority_requested); fs_give((void **) &priority_requested); } #if defined(BACKGROUND_POST) && defined(SIGCHLD) /* * If requested, launch background posting... */ if(background_requested && !(call_mailer_flags & CM_VERBOSE)){ ps_global->post = (POST_S *)fs_get(sizeof(POST_S)); memset(ps_global->post, 0, sizeof(POST_S)); if(fcc) ps_global->post->fcc = cpystr(fcc); if((ps_global->post->pid = fork()) == 0){ /* * Put us in new process group... */ setpgrp(0, ps_global->post->pid); /* BUG: should fix argv[0] to indicate what we're up to */ /* * If there are any live streams, pretend we never * knew them. Problem is two processes writing * same server process. * This is not clean but we're just going to exit * right away anyway. We just want to be sure to leave * the stuff that the parent is going to use alone. * The next three lines will disable the re-use of the * existing streams and cause us to open a new one if * needed. */ ps_global->mail_stream = NULL; ps_global->s_pool.streams = NULL; ps_global->s_pool.nstream = 0; /* quell any display output */ ps_global->in_init_seq = 1; /*------- Actually mail the message ------*/ if(valid_addr == CA_OK && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){ char **alt_smtp = NULL; if(role && role->smtp){ if(ps_global->FIX_SMTP_SERVER && ps_global->FIX_SMTP_SERVER[0]) q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited"); else alt_smtp = role->smtp; } result |= (call_mailer(&header, *body, alt_smtp, call_mailer_flags, call_mailer_file_result, pipe_callback) > 0) ? P_MAIL_WIN : P_MAIL_LOSE; if(result & P_MAIL_LOSE) mark_address_failure_for_pico(&header); } /*----- Was there an fcc involved? -----*/ if(lmc.so){ /*------ Write it if at least something worked ------*/ if((result & (P_MAIL_WIN | P_NEWS_WIN)) || (!(result & (P_MAIL_BITS | P_NEWS_BITS)) && pine_rfc822_output(&header, *body, NULL, NULL))){ char label[50]; strncpy(label, "Fcc", sizeof(label)); label[sizeof(label)-1] = '\0'; if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){ snprintf(label + 3, sizeof(label)-3, " to %s", fcc); label[sizeof(label)-1] = '\0'; } /*-- Now actually copy to fcc folder and close --*/ result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label, F_ON(F_MARK_FCC_SEEN, ps_global) ? "\\SEEN" : NULL)) ? P_FCC_WIN : P_FCC_LOSE; } else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){ q_status_message(SM_ORDER, 3, 5, _("Fcc Failed!. No message saved.")); dprint((1, "explicit fcc write failed!\n")); result |= P_FCC_LOSE; } so_give(&lmc.so); } if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){ /* * Encode child's result in hi-byte of * editor's result */ editor_result = ((result << 8) | COMP_GOTHUP); goto fake_hup; } exit(result); } if(ps_global->post->pid > 0){ q_status_message(SM_ORDER, 3, 3, _("Message handed off for posting")); break; /* up to our child now */ } else{ q_status_message1(SM_ORDER | SM_DING, 3, 3, "Can't fork for send: %s", error_description(errno)); if(ps_global->post->fcc) fs_give((void **) &ps_global->post->fcc); fs_give((void **) &ps_global->post); } if(lmc.so) /* throw away unused store obj */ so_give(&lmc.so); if(outgoing->message_id) fs_give((void **) &outgoing->message_id); outgoing->message_id = generate_message_id(role); continue; /* if we got here, there was a prob */ } #endif /* BACKGROUND_POST */ /*------- Actually mail the message ------*/ if(valid_addr == CA_OK && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){ char **alt_smtp = NULL; if(role && role->smtp){ if(ps_global->FIX_SMTP_SERVER && ps_global->FIX_SMTP_SERVER[0]) q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited"); else alt_smtp = role->smtp; } result |= (call_mailer(&header, *body, alt_smtp, call_mailer_flags, call_mailer_file_result, pipe_callback) > 0) ? P_MAIL_WIN : P_MAIL_LOSE; if(result & P_MAIL_LOSE) mark_address_failure_for_pico(&header); } /*----- Was there an fcc involved? -----*/ if(lmc.so){ /*------ Write it if at least something worked ------*/ if((result & (P_MAIL_WIN | P_NEWS_WIN)) || (!(result & (P_MAIL_BITS | P_NEWS_BITS)) && pine_rfc822_output(&header, *body, NULL, NULL))){ char label[50]; strncpy(label, "Fcc", sizeof(label)); label[sizeof(label)-1] = '\0'; if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){ snprintf(label + 3, sizeof(label)-3, " to %s", fcc); label[sizeof(label)-1] = '\0'; } /*-- Now actually copy to fcc folder and close --*/ result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label, F_ON(F_MARK_FCC_SEEN, ps_global) ? "\\SEEN" : NULL)) ? P_FCC_WIN : P_FCC_LOSE; } else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){ q_status_message(SM_ORDER,3,5, _("Fcc Failed!. No message saved.")); dprint((1, "explicit fcc write failed!\n")); result |= P_FCC_LOSE; } so_give(&lmc.so); } /*----- Mail Post FAILED, back to composer -----*/ if(result & (P_MAIL_LOSE | P_FCC_LOSE)){ dprint((1, "Send failed, continuing\n")); if(result & P_FCC_LOSE){ /* * Find field entry associated with fcc, and start * composer on it... */ for(pf = header.local; pf && pf->name; pf = pf->next) if(pf->type == Fcc && HE(pf)) HE(pf)->start_here = 1; q_status_message(SM_ORDER | SM_DING, 3, 3, pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL)); } if(outgoing->message_id) fs_give((void **) &outgoing->message_id); outgoing->message_id = generate_message_id(role); continue; } /* * If message sent *completely* successfully, there's a * reply struct AND we're allowed to write back state, do it. * But also protect against shifted message numbers due * to new mail arrival. Since the number passed is based * on the real imap msg no, AND we're sure no expunge has * been done, just fix up the sorted number... */ update_answered_flags(reply); /*----- Signed, sealed, delivered! ------*/ q_status_message(SM_ORDER, 0, 3, pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL)); break; /* All's well, pop out of here */ } } if(orig_so) so_give(&orig_so); if(fcc) fs_give((void **)&fcc); free_body_particulars(bp); free_attachment_list(&pbf->attachments); standard_picobuf_teardown(pbf); for(i=0; i < fixed_cnt; i++){ if(pfields[i].textbuf) fs_give((void **)&pfields[i].textbuf); fs_give((void **)&pfields[i].name); } if(lcc_addr) mail_free_address(&lcc_addr); if(nobody_addr) mail_free_address(&nobody_addr); free_prompts(header.custom); free_customs(header.custom); fs_give((void **)&pfields); free_headents(&headents); fs_give((void **)&sending_order); if(suggested_nntp_server) fs_give((void **)&suggested_nntp_server); if(title) fs_give((void **)&title); if(local_redraft_pos && local_redraft_pos != redraft_pos) free_redraft_pos(&local_redraft_pos); pbf = save_previous_pbuf; g_rolenick = NULL; dprint((4, "=== send returning ===\n")); } /* * Check for subject in outgoing message. * * Asks user whether to proceed with no subject. */ int check_for_subject(METAENV *header) { PINEFIELD *pf; int rv = CF_OK; for(pf = header->local; pf && pf->name; pf = pf->next) if(pf->type == Subject){ if(pf->text && *pf->text && **pf->text) rv = CF_OK; else{ if(want_to("No Subject, send anyway ", 'n', 'n', h_send_check_subj, WT_NORM) == 'y') rv = CF_OK; else rv = CF_MISSING; } break; } return(rv); } /* * Check for fcc in outgoing message. * * Asks user whether to proceed with no fcc. */ int check_for_fcc(char *fcc) { int rv = CF_OK; if(fcc && *fcc) rv = CF_OK; else{ if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y') rv = CF_OK; else rv = CF_MISSING; } return(rv); } /* * Confirm that the user wants to send to MAILER-DAEMON */ int confirm_daemon_send(void) { return(want_to("Really send this message to the MAILER-DAEMON", 'n', 'n', NO_HELP, WT_NORM) == 'y'); } void free_prompts(PINEFIELD *head) { PINEFIELD *pf; for(pf = head; pf && pf->name; pf = pf->next){ if(HE(pf) && HE(pf)->prompt) fs_give((void **)& HE(pf)->prompt); } } int postpone_prompt(void) { static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")}, {'f', 'f', "F", N_("Form Letter Folder")}, {-1, 0, NULL, NULL} }; return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global), pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN)); } /* * call__mailer_file_result - some results from call_mailer might be in a file. * dislplay that file. */ void call_mailer_file_result(char *filename, int style) { if(filename){ if(style & CM_BR_VERBOSE){ display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF); } else{ display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY); } } } void mark_address_failure_for_pico(METAENV *header) { PINEFIELD *pf; ADDRESS *a; int error_count = 0; struct headerentry *last_he = NULL; for(pf = header->local; pf && pf->name; pf = pf->next) if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr) for(a = *pf->addr; a != NULL; a = a->next) if(a->error != NULL && error_count++ < MAX_ADDR_ERROR && (HE(pf))){ if(last_he) /* start last reported err */ last_he->start_here = 0; (last_he = HE(pf))->start_here = 1; } } /* * This is specialized routine. It assumes that the only things that we * care about restoring are the body type, subtype, encoding and the * state of the charset parameter. It also assumes that if the charset * parameter exists when we save it, it won't be removed later. */ BODY_PARTICULARS_S * save_body_particulars(struct mail_bodystruct *body) { BODY_PARTICULARS_S *bp; PARAMETER *pm; bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S)); bp->type = body->type; bp->encoding = body->encoding; bp->subtype = body->subtype ? cpystr(body->subtype) : NULL; bp->parameter = body->parameter; for(pm = bp->parameter; pm && strucmp(pm->attribute, "charset") != 0; pm = pm->next) ;/* searching for possible charset parameter */ if(pm){ /* found one */ bp->had_csp = 1; /* saved body had charset parameter */ bp->charset = pm->value ? cpystr(pm->value) : NULL; } else{ bp->had_csp = 0; bp->charset = NULL; } return(bp); } void reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body) { body->type = bp->type; body->encoding = bp->encoding; if(body->subtype) fs_give((void **)&body->subtype); body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL; if(bp->parameter){ PARAMETER *pm, *pm_prev = NULL; for(pm = body->parameter; pm && strucmp(pm->attribute, "charset") != 0; pm = pm->next) pm_prev = pm; if(pm){ /* body has charset parameter */ if(bp->had_csp){ /* reset to what it used to be */ if(pm->value) fs_give((void **)&pm->value); pm->value = bp->charset ? cpystr(bp->charset) : NULL; } else{ /* remove charset parameter */ if(pm_prev) pm_prev->next = pm->next; else body->parameter = pm->next; mail_free_body_parameter(&pm); } } else{ if(bp->had_csp){ /* * This can't happen because grope never removes * the charset parameter. */ q_status_message(SM_ORDER | SM_DING, 5, 5, "Programmer error: saved charset but no current charset param in pine_send"); } /* else{ ok, still no parameter } */ } } else{ if(body->parameter) mail_free_body_parameter(&body->parameter); body->parameter = NULL; } } void free_body_particulars(BODY_PARTICULARS_S *bp) { if(bp){ if(bp->subtype) fs_give((void **)&bp->subtype); if(bp->charset) fs_give((void **)&bp->charset); fs_give((void **)&bp); } } /*---------------------------------------------------------------------- Build a status message suitable for framing Returns: pointer to resulting buffer ---*/ char * pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad) { int avail = ps_global->ttyo->screen_cols - 2; int fixedneed, need, lenfcc; char *part1, *part2, *part3, *part4, *part5; char fbuf[MAILTMPLEN+1]; part1 = (result & P_NEWS_WIN) ? "posted" : (result & P_NEWS_LOSE) ? "NOT POSTED" : ""; part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS) && (result & P_FCC_BITS)) ? ", " : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS))) ? " and " : ""; part3 = (result & P_MAIL_WIN) ? "sent" : (result & P_MAIL_LOSE) ? "NOT SENT" : ""; part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS)) ? " and " : ""; part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN))) ? "ONLY copied to " : (result & P_FCC_WIN) ? "copied to " : (result & P_FCC_LOSE) ? "NOT copied to " : ""; lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0); fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) + strlen(part4) + strlen(part5); need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc; if(need > avail && fixedneed + 3 >= avail){ /* dots on end of fixed, no fcc */ snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ", part1, part2, part3, part4, part5); short_str(fbuf, buf, buflen, avail, EndDots); } else if(need > avail){ /* include whole fixed part, quotes and dots at end of fcc name */ if(lenfcc > 0) lenfcc = MAX(1, lenfcc-(need-avail)); snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.", part1, part2, part3, part4, part5, (result & P_FCC_BITS) ? "\"" : "", short_str((result & P_FCC_BITS) ? fcc_name : "", fbuf, sizeof(fbuf), lenfcc, FrontDots), (result & P_FCC_BITS) ? "\"" : ""); } else{ /* whole thing */ snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.", part1, part2, part3, part4, part5, (result & P_FCC_BITS) ? "\"" : "", (result & P_FCC_BITS) ? fcc_name : "", (result & P_FCC_BITS) ? "\"" : ""); } if(goodorbad) *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0; return(buf); } /* Callback from Pico to set the conditions for Alpine to start a new thread */ void new_thread_on_blank_subject(void) { ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global); } /*---------------------------------------------------------------------- Call back for pico to insert the specified message's text Args: n -- message number to format f -- function to use to output the formatted message Returns: returns msg number formatted on success, zero on error. ----*/ long message_format_for_pico(long int n, int (*f) (int)) { ENVELOPE *e; BODY *b; char *old_quote = NULL; long rv = n; if(!(n > 0L && n <= mn_get_total(ps_global->msgmap) && (e = pine_mail_fetchstructure(ps_global->mail_stream, mn_m2raw(ps_global->msgmap, n), &b)))){ q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message"); flush_status_messages(0); return(0L); } /* temporarily assign a new quote string */ old_quote = pbf->quote_str; pbf->quote_str = reply_quote_str(e); /* build separator line */ reply_delimiter(e, NULL, f); /* actually write message text */ if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL, FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){ q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message"); flush_status_messages(0); rv = 0L; } fs_give((void **)&pbf->quote_str); pbf->quote_str = old_quote; return(rv); } /*---------------------------------------------------------------------- Call back for pico to prompt the user for exit confirmation Args: dflt -- default answer for confirmation prompt Returns: either NULL if the user accepts exit, or string containing reason why the user declined. ----*/ int send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed, char **result) { int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend; int dsn_label = 0, fcc_label = 0, lparen; int flowing_label = 0, double_rad; char *rstr = NULL, *p, *lc, *optp; char dsn_string[30]; void (*redraw)(void) = ps_global->redrawer; ESCKEY_S opts[18]; struct filters { char *filter; int index; struct filters *prev, *next; } *filters = NULL, *fp; sending_filter_requested = NULL; call_mailer_flags = 0; background_requested = 0; flowing_requested = allow_flowed ? 1 : 0; lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0; if(priority_requested) fs_give((void **) &priority_requested); if(background_posting(FALSE)){ if(result) *result = "Can't send while background posting. Use postpone."; return(1); } if(F_ON(F_SEND_WO_CONFIRM, ps_global)){ if(result) *result = NULL; return(0); } ps_global->redrawer = redraw_pico; if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0) (void) F_SET(F_CAN_SUSPEND, ps_global, 0); /* * Build list of available filters... */ for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){ for(p = ps_global->VAR_SEND_FILTER[i]; *p && !isspace((unsigned char)*p); p++) ; c = *p; *p = '\0'; if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i]) && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){ *p = c; continue; } fp = (struct filters *)fs_get(sizeof(struct filters)); fp->index = i; if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){ fp->filter = cpystr(lc); } else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; fp->filter = cpystr(tmp_20k_buf); } else fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]); *p = c; if(filters){ fp->next = filters; fp->prev = filters->prev; fp->prev->next = filters->prev = fp; } else{ filters = (struct filters *)fs_get(sizeof(struct filters)); filters->index = -1; filters->filter = NULL; filters->next = filters->prev = fp; fp->next = fp->prev = filters; } } i = 0; opts[i].ch = 'y'; opts[i].rval = 'y'; opts[i].name = "Y"; opts[i++].label = N_("Yes"); opts[i].ch = 'n'; opts[i].rval = 'n'; opts[i].name = "N"; opts[i++].label = N_("No"); if(filters){ /* set global_filter_pointer to desired filter or NULL if none */ /* prepare two keymenu slots for selecting filter */ opts[i].ch = ctrl('P'); opts[i].rval = 10; opts[i].name = "^P"; opts[i++].label = N_("Prev Filter"); opts[i].ch = ctrl('N'); opts[i].rval = 11; opts[i].name = "^N"; opts[i++].label = N_("Next Filter"); if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global)) filters = filters->next; } if(F_ON(F_VERBOSE_POST, ps_global)){ /* setup keymenu slot to toggle verbose mode */ opts[i].ch = ctrl('W'); opts[i].rval = 12; opts[i].name = "^W"; verbose_label = i++; } if(allow_flowed){ /* setup keymenu slot to toggle flowed mode */ opts[i].ch = ctrl('V'); opts[i].rval = 22; opts[i].name = "^V"; flowing_label = i++; flowing_requested = 1; } if(F_ON(F_NO_FCC_ATTACH, ps_global)){ /* setup keymenu slot to toggle attacment on fcc */ opts[i].ch = ctrl('F'); opts[i].rval = 21; opts[i].name = "^F"; fcc_label = i++; } #if defined(BACKGROUND_POST) && defined(SIGCHLD) if(F_ON(F_BACKGROUND_POST, ps_global)){ opts[i].ch = ctrl('R'); opts[i].rval = 15; opts[i].name = "^R"; bg_label = i++; } #endif opts[i].ch = 'p'; opts[i].rval = 'p'; opts[i].name = "P"; opts[i++].label = N_("Priority"); #ifdef SMIME if(F_OFF(F_DONT_DO_SMIME, ps_global)){ opts[i].ch = 'e'; opts[i].rval = 'e'; opts[i].name = "E"; opts[i++].label = "Encrypt"; opts[i].ch = 'g'; opts[i].rval = 'g'; opts[i].name = "G"; opts[i++].label = "Sign"; if(ps_global->smime){ ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global); ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global); } } #endif double_rad = i; if(F_ON(F_DSN, ps_global)){ /* setup keymenu slots to toggle dsn bits */ opts[i].ch = 'd'; opts[i].rval = 'd'; opts[i].name = "D"; opts[i].label = N_("DSNOpts"); dsn_label = i++; opts[i].ch = -2; opts[i].rval = 's'; opts[i].name = "S"; opts[i++].label = ""; opts[i].ch = -2; opts[i].rval = 'x'; opts[i].name = "X"; opts[i++].label = ""; opts[i].ch = -2; opts[i].rval = 'h'; opts[i].name = "H"; opts[i++].label = ""; } if(filters){ opts[i].ch = KEY_UP; opts[i].rval = 10; opts[i].name = ""; opts[i++].label = ""; opts[i].ch = KEY_DOWN; opts[i].rval = 11; opts[i].name = ""; opts[i++].label = ""; } opts[i].ch = -1; fix_windsize(ps_global); while(1){ if(filters && filters->filter && (p = strindex(filters->filter, ' '))) *p = '\0'; else p = NULL; lparen = 0; strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; optp = tmp_20k_buf + strlen(tmp_20k_buf); if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only) sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf)); if(allow_flowed && !flowing_requested){ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){ if(!lparen){ *optp++ = ' '; *optp++ = '('; lparen++; } else *optp++ = ' '; } sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf)); } if(filters){ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){ if(!lparen){ *optp++ = ' '; *optp++ = '('; lparen++; } else{ *optp++ = ','; *optp++ = ' '; } } if(filters->filter){ sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf)); sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf)); if((optp-tmp_20k_buf) < SIZEOF_20KBUF) *optp++ = '\"'; } else sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf)); } if((call_mailer_flags & CM_VERBOSE) || background_requested){ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){ if(!lparen){ *optp++ = ' '; *optp++ = '('; lparen++; } else *optp++ = ' '; } sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf)); if(call_mailer_flags & CM_VERBOSE) sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf)); if(background_requested) sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf)); sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf)); } if(g_rolenick && !(he && he[N_FROM].dirty)){ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){ if(!lparen){ *optp++ = ' '; *optp++ = '('; lparen++; } else *optp++ = ' '; } sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf)); sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf)); sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf)); } if(call_mailer_flags & CM_DSN_SHOW){ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){ if(!lparen){ *optp++ = ' '; *optp++ = '('; lparen++; } else{ *optp++ = ','; *optp++ = ' '; } } sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf)); } #ifdef SMIME if(ps_global->smime && ps_global->smime->do_encrypt){ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){ if(!lparen){ *optp++ = ' '; *optp++ = '('; lparen++; } else{ *optp++ = ','; *optp++ = ' '; } } sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf)); } if(ps_global->smime && ps_global->smime->do_sign){ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){ if(!lparen){ *optp++ = ' '; *optp++ = '('; lparen++; } else{ *optp++ = ','; *optp++ = ' '; } } sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf)); } #endif if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF) *optp++ = ')'; sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf)); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; if(p) *p = ' '; if(flowing_label) opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow"); if(verbose_label) opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose"); if(bg_label) opts[bg_label].label = background_requested ? N_("Foreground") : N_("Background"); if(fcc_label) opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts") : N_("No Fcc Atmts "); if(F_ON(F_DSN, ps_global)){ if(call_mailer_flags & CM_DSN_SHOW){ opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY) ? N_("NoDelay") : N_("Delay"); opts[dsn_label+1].ch = 's'; opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS) ? N_("NoSuccess") : N_("Success"); opts[dsn_label+2].ch = 'x'; opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER) ? N_("ErrRets") : N_("NoErrRets"); opts[dsn_label+3].ch = 'h'; opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL) ? N_("RetHdrs") : N_("RetFull"); } } if(double_rad + ((call_mailer_flags & CM_DSN_SHOW) ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11) rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts, 'y', 'z', (F_ON(F_DSN, ps_global) && allow_flowed) ? h_send_prompt_dsn_flowed : F_ON(F_DSN, ps_global) ? h_send_prompt_dsn : allow_flowed ? h_send_prompt_flowed : h_send_prompt, RB_NORM); else rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts, 'y', 'z', (double_rad + ((call_mailer_flags & CM_DSN_SHOW) ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11) ? NO_HELP : (F_ON(F_DSN, ps_global) && allow_flowed) ? h_send_prompt_dsn_flowed : F_ON(F_DSN, ps_global) ? h_send_prompt_dsn : allow_flowed ? h_send_prompt_flowed : h_send_prompt, RB_NORM); if(rv == 'y'){ /* user ACCEPTS! */ break; } else if(rv == 'n'){ /* Declined! */ rstr = _("No Message Sent"); break; } else if(rv == 'z'){ /* Cancelled! */ rstr = _("Send Cancelled"); break; } else if(rv == 10){ /* PREVIOUS filter */ filters = filters->prev; } else if(rv == 11){ /* NEXT filter */ filters = filters->next; } else if(rv == 21){ lmc.text_only = !lmc.text_only; } else if(rv == 12){ /* flip verbose bit */ if(call_mailer_flags & CM_VERBOSE) call_mailer_flags &= ~CM_VERBOSE; else call_mailer_flags |= CM_VERBOSE; if((call_mailer_flags & CM_VERBOSE) && background_requested) background_requested = 0; } else if(rv == 22){ /* flip flowing bit */ flowing_requested = !flowing_requested; } else if(rv == 15){ if((background_requested = !background_requested) && (call_mailer_flags & CM_VERBOSE)) call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */ } else if(call_mailer_flags & CM_DSN_SHOW){ if(rv == 's'){ /* flip success bit */ call_mailer_flags ^= CM_DSN_SUCCESS; /* turn off related bits */ if(call_mailer_flags & CM_DSN_SUCCESS) call_mailer_flags &= ~(CM_DSN_NEVER); } else if(rv == 'd'){ /* flip delay bit */ call_mailer_flags ^= CM_DSN_DELAY; /* turn off related bits */ if(call_mailer_flags & CM_DSN_DELAY) call_mailer_flags &= ~(CM_DSN_NEVER); } else if(rv == 'x'){ /* flip never bit */ call_mailer_flags ^= CM_DSN_NEVER; /* turn off related bits */ if(call_mailer_flags & CM_DSN_NEVER) call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY); } else if(rv == 'h'){ /* flip full bit */ call_mailer_flags ^= CM_DSN_FULL; } } else if(rv == 'd'){ /* show dsn options */ /* * When you turn on DSN, the default is to notify on * failure, success, or delay; and to return the whole * body on failure. */ call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL); } else if(rv == 'p'){ /* choose X-Priority */ char *prio; prio = choose_a_priority(priority_requested); if((ps_global->redrawer = redraw_pico) != NULL){ (*ps_global->redrawer)(); fix_windsize(ps_global); } if(prio){ if(priority_requested) fs_give((void **) &priority_requested); if(prio[0]) priority_requested = prio; else fs_give((void **) &prio); } } #ifdef SMIME else if(rv=='e'){ if(ps_global->smime) ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt; } else if(rv=='g'){ if(ps_global->smime) ps_global->smime->do_sign = !ps_global->smime->do_sign; } #endif snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]", (call_mailer_flags & CM_DSN_NEVER) ? "Never" : "F", (call_mailer_flags & CM_DSN_DELAY) ? "D" : "", (call_mailer_flags & CM_DSN_SUCCESS) ? "S" : "", (call_mailer_flags & CM_DSN_NEVER) ? "" : (call_mailer_flags & CM_DSN_FULL) ? "-Full" : "-Hdrs"); dsn_string[sizeof(dsn_string)-1] = '\0'; } /* remember selection */ if(filters && filters->index > -1) sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index]; if(filters){ filters->prev->next = NULL; /* tie off list */ while(filters){ /* then free it */ fp = filters->next; if(filters->filter) fs_give((void **)&filters->filter); fs_give((void **)&filters); filters = fp; } } if(old_suspend) (void) F_SET(F_CAN_SUSPEND, ps_global, 1); if(result) *result = rstr; ps_global->redrawer = redraw; return((rstr == NULL) ? 0 : 1); } /* * Allow user to choose a priority for sending. * * Returns an allocated priority on success, NULL otherwise. */ char * choose_a_priority(char *default_val) { char *choice = NULL; char **priority_list, **lp; char *starting_val = NULL; char *none; int i, cnt; PRIORITY_S *p; for(cnt = 0, p = priorities; p && p->desc; p++) cnt++; cnt++; /* for NONE entry */ lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list)); memset(priority_list, 0, (cnt+1) * sizeof(*priority_list)); for(i = 0, p = priorities; p && p->desc; p++){ *lp = cpystr(p->desc); if(default_val && !strcmp(default_val, p->desc)) starting_val = (*lp); lp++; } none = _("NONE - No X-Priority header included"); *lp = cpystr(none); if(!starting_val) starting_val = (*lp); /* TRANSLATORS: SELECT A PRIORITY is a screen title TRANSLATORS: Print something1 using something2. "priorities" is something1 */ choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"), _("priorities"), h_select_priority_screen, _("HELP FOR SELECTING A PRIORITY"), starting_val); if(!choice) q_status_message(SM_ORDER, 1, 4, _("No change")); else if(!strcmp(choice, none)) choice[0] = '\0'; free_list_array(&priority_list); return(choice); } int dont_flow_this_time(void) { return(flowing_requested ? 0 : 1); } /*---------------------------------------------------------------------- Call back for pico to display mime type of attachment Args: file -- filename being attached Returns: returns 1 on success (message queued), zero otherwise (don't know type so nothing queued). ----*/ int mime_type_for_pico(char *file) { BODY *body; int rv; void *file_contents; body = mail_newbody(); body->type = TYPEOTHER; body->encoding = ENCOTHER; /* don't know where the cursor's been, reset it */ clear_cursor_pos(); if(!set_mime_type_by_extension(body, file)){ if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){ body->contents.text.data = file_contents; set_mime_type_by_grope(body); } } if(body->type != TYPEOTHER){ rv = 1; q_status_message3(SM_ORDER, 0, 3, _("File %s attached as type %s/%s"), file, body_types[body->type], body->subtype ? body->subtype : rfc822_default_subtype(body->type)); } else rv = 0; pine_free_body(&body); return(rv); } /*---------------------------------------------------------------------- Call back for pico to receive an uploaded message Args: fname -- name for uploaded file (empty if they want us to assign it) size -- pointer to long to hold the attachment's size Notes: the attachment is uploaded to a temp file, and Returns: TRUE on success, FALSE otherwise ----*/ int upload_msg_to_pico(char *fname, size_t fnlen, long int *size) { char cmd[MAXPATH+1], *fnp = NULL; char *locale_name = NULL; long l; PIPE_S *syspipe; dprint((1, "Upload cmd called to xfer \"%s\"\n", fname ? fname : "")); if(!fname) /* no place for file name */ return(0); if(!*fname){ /* caller wants temp file */ if((fnp = temp_nam(NULL, "pu")) != NULL){ strncpy(fname, fnp, fnlen); fname[fnlen-1] = '\0'; our_unlink(fnp); fs_give((void **)&fnp); } } else locale_name = convert_to_locale(fname); build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX, ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname); if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET, 0, pipe_callback, pipe_report_error)) != NULL){ (void) close_system_pipe(&syspipe, NULL, pipe_callback); if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){ q_status_message2(SM_ORDER | SM_DING, 3, 4, "Error determining size of %s: %s", fname, fnp = error_description(errno)); dprint((1, "!!! Upload cmd \"%s\" failed for \"%s\": %s\n", cmd ? cmd : "?", fname ? fname : "?", fnp ? fnp : "?")); } else if(size) *size = l; if(locale_name) fs_give((void **) &locale_name); return(l >= 0); } else q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe")); if(locale_name) fs_give((void **) &locale_name); return(0); } char * cancel_for_pico(void (*redraw_pico)(void)) { int rv; char *rstr = NULL; char *prompt = _("Cancel message (answering \"Confirm\" will abandon your mail message) ? "); void (*redraw)(void) = ps_global->redrawer; static ESCKEY_S opts[] = { {'c', 'c', "C", N_("Confirm")}, {'n', 'n', "N", N_("No")}, {'y', 'y', "", ""}, {-1, 0, NULL, NULL} }; ps_global->redrawer = redraw_pico; fix_windsize(ps_global); while(1){ rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts, 'n', 'x', h_confirm_cancel, RB_NORM); if(rv == 'c'){ /* user ACCEPTS! */ rstr = ""; break; } else if(rv == 'y'){ q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message ")); display_message('x'); } else break; } ps_global->redrawer = redraw; return(rstr); } /*---------------------------------------------------------------------- Pass the first text segment of the message thru the "send filter" Args: body pointer and address for storage object of old data Returns: returns 1 on success, zero on error. ----*/ int filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body, STORE_S **old, METAENV *header) { char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL; int key = 0, include_hdrs = 0; gf_io_t gc, pc; STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART) ? &body->nested.part->body.contents.text.data : &body->contents.text.data), *tmp_so = NULL, *tmpf_so, *save_local_so, *readthis_so = NULL, *our_tmpf_so = NULL; #define DO_HEADERS 1 if(fcmd && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf, &key, &include_hdrs, NULL))){ if(tmpf){ /* * We need WRITE_TO_LOCALE here because the user is going to * be operating on tmpf. We need to change it back after they * are done. */ if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){ if(key){ so_puts(tmpf_so, filter_session_key()); so_puts(tmpf_so, NEWLINE); } /* * If the headers are wanted for filtering, we can just * stick them in the tmpf file that is already there before * putting the body in. */ if(include_hdrs){ save_local_so = lmc.so; lmc.so = tmpf_so; /* write it to tmpf_so */ lmc.all_written = lmc.text_written = lmc.text_only = 0; pine_rfc822_header(header, body, NULL, NULL); lmc.so = save_local_so; } so_seek(*so, 0L, 0); gf_set_so_readc(&gc, *so); gf_set_so_writec(&pc, tmpf_so); gf_filter_init(); errstr = gf_pipe(gc, pc); gf_clear_so_readc(*so); gf_clear_so_writec(tmpf_so); so_give(&tmpf_so); } else errstr = "Can't create space for filter temporary file."; } else if(include_hdrs){ /* * Gf_filter wants a single storage object to read from. * If headers are wanted for filtering we'll have to put them * and the body into a temp file first and then use that * as the storage object for gf_filter. * We don't use WRITE_TO_LOCALE in this case because gf_filter * takes care of that. */ if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){ /* put headers in our_tmpf_so */ save_local_so = lmc.so; lmc.so = our_tmpf_so; /* write it to our_tmpf_so */ lmc.all_written = lmc.text_written = lmc.text_only = 0; pine_rfc822_header(header, body, NULL, NULL); lmc.so = save_local_so; /* put body in our_tmpf_so */ so_seek(*so, 0L, 0); gf_set_so_readc(&gc, *so); gf_set_so_writec(&pc, our_tmpf_so); gf_filter_init(); errstr = gf_pipe(gc, pc); gf_clear_so_readc(*so); gf_clear_so_writec(our_tmpf_so); /* tell gf_filter to read from our_tmpf_so instead of *so */ readthis_so = our_tmpf_so; } else errstr = "Can't create space for temporary file."; } else readthis_so = *so; if(!errstr){ if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){ gf_set_so_writec(&pc, tmp_so); ps_global->mangled_screen = 1; suspend_busy_cue(); ClearScreen(); fflush(stdout); if(tmpf){ PIPE_S *fpipe; if((fpipe = open_system_pipe(cmd, NULL, NULL, PIPE_NOSHELL | PIPE_RESET, 0, pipe_callback, pipe_report_error)) != NULL){ if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){ /* now we undo the WRITE_FROM_LOCALE change in tmpf */ if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){ gf_set_so_readc(&gc, tmpf_so); gf_filter_init(); errstr = gf_pipe(gc, pc); gf_clear_so_readc(tmpf_so); so_give(&tmpf_so); } else errstr = "Can't open temp file filter wrote."; } else errstr = "Filter command returned error."; } else errstr = "Can't exec filter text."; } else errstr = gf_filter(cmd, key ? filter_session_key() : NULL, readthis_so, pc, NULL, 0, 0, pipe_callback); if(our_tmpf_so) so_give(&our_tmpf_so); gf_clear_so_writec(tmp_so); #ifdef _WINDOWS if(!errstr){ /* * Can't really be using stdout, so don't print message that * gets printed in the else. Ideally the program being called * will wait after showing the message, we might want to look * into doing the waiting in console based apps... or not. */ #else if(errstr){ unsigned long ch; fprintf(stdout, "\r\n%s Hit return to continue.", errstr); fflush(stdout); while((ch = read_char(300)) != ctrl('M') && ch != NO_OP_IDLE) putchar(BELL); } else{ #endif /* _WINDOWS */ BODY *b = (body->type == TYPEMULTIPART) ? &body->nested.part->body : body; *old = *so; /* save old so */ *so = tmp_so; /* return new one */ (*so)->attr = copy_parameters((*old)->attr); /* * If the command said it would return new MIME * mime type data, check it out... */ if(mtf){ char buf[MAILTMPLEN], *s; FILE *fp; if((fp = our_fopen(mtf, "rb")) != NULL){ if(fgets(buf, sizeof(buf), fp) && !struncmp(buf, "content-", 8) && (s = strchr(buf+8, ':'))){ BODY *nb = mail_newbody(); for(*s++ = '\0'; *s == ' '; s++) ; rfc822_parse_content_header(nb, (char *) ucase((unsigned char *) buf+8),s); if(nb->type == TYPETEXT && nb->subtype && (!b->subtype || strucmp(b->subtype, nb->subtype))){ if(b->subtype) fs_give((void **) &b->subtype); b->subtype = nb->subtype; nb->subtype = NULL; mail_free_body_parameter(&b->parameter); b->parameter = nb->parameter; nb->parameter = NULL; mail_free_body_parameter(&nb->parameter); } mail_free_body(&nb); } fclose(fp); } } /* * Reevaluate the encoding in case form's changed... */ b->encoding = ENCOTHER; set_mime_type_by_grope(b); } ClearScreen(); resume_busy_cue(0); } else errstr = "Can't create space for filtered text."; } fs_give((void **)&cmd); } else return(0); if(tmpf){ our_unlink(tmpf); fs_give((void **)&tmpf); } if(mtf){ our_unlink(mtf); fs_give((void **) &mtf); } if(resultf){ if(name_file_size(resultf) > 0L) display_output_file(resultf, "Filter", NULL, DOF_BRIEF); our_unlink(resultf); fs_give((void **)&resultf); } else if(errstr){ if(tmp_so) so_give(&tmp_so); q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"), errstr); dprint((1, "Filter FAILED: %s\n", errstr ? errstr : "?")); } return(errstr == NULL); } /*---------------------------------------------------------------------- Copy the newsgroup name of the given mailbox into the given buffer Args: Returns: ----*/ void pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len) { NETMBX mb; if(*mailbox == '#'){ /* Strip the leading "#news." */ strncpy(group_name, mailbox + 6, len-1); group_name[len-1] = '\0'; } else if(mail_valid_net_parse(mailbox, &mb)){ pine_send_newsgroup_name(mb.mailbox, group_name, len); } else *group_name = '\0'; } /*---------------------------------------------------------------------- Set up fields for passing to pico. Assumes first text part is intended to be passed along for editing, and is in the form of of a storage object brought into existence sometime before pico_send(). -----*/ void outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text, PATMT **pico_a, int from_bounce) { PINEFIELD *pf; /* * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object * is guaranteed to be of type PicoText! */ if(bod->type == TYPETEXT){ *text = so_text((STORE_S *) bod->contents.text.data); /* mark storage object as user edited */ if(!from_bounce) (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1"); } else if(bod->type == TYPEMULTIPART){ PART *part; PATMT **ppa; char *type, *name, *p; int name_l; /* * We used to jump out the window if the first part wasn't text, * but that may not be the case when bouncing a message with * a leading non-text segment. So, IT'S UNDERSTOOD that the * contents of the first part to send is still ALWAYS in a * PicoText storage object, *AND* if that object doesn't contain * data of type text, then it must contain THE ENCODED NON-TEXT * DATA of the piece being sent. * * It's up to the programmer to make sure that such a message is * sent via pine_simple_send and never get to the composer via * pine_send. * * Make sense? */ *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data); /* mark storage object as user edited */ if(!from_bounce) (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1"); /* * If we already had a list, blast it now, so we can build a new * attachment list that reflects what's really there... */ if(pico_a){ free_attachment_list(pico_a); /* Simplifyihg assumption #28e. (see cross reference) All parts in the body passed in here that are not already in the attachments list are added to the end of the attachments list. Attachment items not in the body list will be taken care of in strings2outgoing, but they are unlikely to occur */ for(part = bod->nested.part->next; part != NULL; part = part->next) { /* Already in list? */ for(ppa = pico_a; *ppa && strcmp((*ppa)->id, part->body.id); ppa = &(*ppa)->next) ; if(!*ppa){ /* Not in the list! append it... */ *ppa = (PATMT *)fs_get(sizeof(PATMT)); if(part->body.description){ char *p; size_t len; len = 4*strlen(part->body.description)+1; p = (char *)fs_get(len*sizeof(char)); if(rfc1522_decode_to_utf8((unsigned char *)p, len, part->body.description) == (unsigned char *) p){ (*ppa)->description = p; } else{ fs_give((void **)&p); (*ppa)->description = cpystr(part->body.description); } } else (*ppa)->description = cpystr(""); type = type_desc(part->body.type, part->body.subtype, part->body.parameter, NULL, 0); /* * If we can find a "name" parm, display that too... */ if((name = parameter_val(part->body.parameter, "name")) != NULL){ /* Convert any [ or ]'s the name contained */ for(p = name; *p ; p++) if(*p == '[') *p = '('; else if(*p == ']') *p = ')'; name_l = p - name; } else name_l = 0; (*ppa)->filename = fs_get(strlen(type) + name_l + 5); snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type, name ? ": " : "", name ? name : ""); (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0'; if(name) fs_give((void **) &name); (*ppa)->flags = A_FLIT; (*ppa)->size = cpystr(byte_string( send_body_size(&part->body))); if(!part->body.id) part->body.id = generate_message_id(NULL); (*ppa)->id = cpystr(part->body.id); (*ppa)->next = NULL; } } } } /*------------------------------------------------------------------ Malloc strings to pass to composer editor because it expects such strings so it can realloc them -----------------------------------------------------------------*/ /* * turn any address fields into text strings */ /* * SIMPLIFYING ASSUMPTION #116: all header strings are understood * NOT to be RFC1522 decoded. Said differently, they're understood * to be RFC1522 ENCODED as necessary. The intent is to preserve * original charset tagging as far into the compose/send pipe as * we can. */ for(pf = header->local; pf && pf->name; pf = pf->next) if(pf->canedit) switch(pf->type){ case Address : if(pf->addr){ char *p, *t, *u; long l; pf->scratch = addr_list_string(*pf->addr, NULL, 1); /* * Scan for and fix-up patently bogus fields. * * NOTE: collaboration with this code and what's done in * reply.c:reply_cp_addr to package up the bogus stuff * is required. */ for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); ) for(t = p; ; t--) if(*t == '&'){ /* find "leading" token */ int replacelen; /* * Rfc822_cat has been changed so that it now quotes * this sometimes. So we have to look out for quotes * which confuse the decoder. It was only quoting * because we were putting \r \n in the input, I think. */ if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"') t[-1] = p[-1] = ' '; *t++ = ' '; /* replace token */ *p = '\0'; /* tie off string */ u = rfc822_base64((unsigned char *) t, (unsigned long) strlen(t), (unsigned long *) &l); if(!u) u = ""; replacelen = strlen(t); *p = '@'; /* restore 'p' */ rplstr(p, strlen(p), 12, ""); /* clear special token */ rplstr(t, strlen(u)-replacelen+1, replacelen, u); if(u) fs_give((void **) &u); if(HE(pf)) HE(pf)->start_here = 1; break; } else if(t == pf->scratch) break; removing_leading_white_space(pf->scratch); if(pf->scratch){ size_t l; /* * Replace control characters with ^C notation, unless * some conditions are met (see istrncpy). * If user doesn't edit, then we switch back to the * original version. If user does edit, then all bets * are off. */ iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){ fs_give((void **)&pf->scratch); pf->scratch = (char *)fs_get((l+1) * sizeof(char)); } strncpy(pf->scratch, (char *)tmp_20k_buf, l+1); } } break; case Subject : if(pf->text){ char *p, *src; size_t len; src = pf->scratch ? pf->scratch : (*pf->text) ? *pf->text : ""; len = 4*strlen(src)+1; p = (char *)fs_get(len * sizeof(char)); if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){ if(pf->scratch) fs_give((void **)&pf->scratch); pf->scratch = p; } else{ fs_give((void **)&p); if(!pf->scratch) pf->scratch = cpystr(src); } if(pf->scratch){ size_t l; /* * Replace control characters with ^C notation, unless * some conditions are met (see istrncpy). * If user doesn't edit, then we switch back to the * original version. If user does edit, then all bets * are off. */ iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1); tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){ fs_give((void **)&pf->scratch); pf->scratch = (char *)fs_get((l+1) * sizeof(char)); } strncpy(pf->scratch, (char *)tmp_20k_buf, l+1); } } break; default : break; } } /*---------------------------------------------------------------------- Restore fields returned from pico to form useful to sending routines. -----*/ void strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it) { PINEFIELD *pf; int we_cancel = 0; we_cancel = busy_cue(NULL, NULL, 1); /* * turn any local address strings into address lists */ for(pf = header->local; pf && pf->name; pf = pf->next) if(pf->scratch){ char *the_address = NULL; switch(pf->type){ case Address : removing_trailing_white_space(pf->scratch); if((the_address || *pf->scratch) && pf->addr){ ADDRESS *new_addr = NULL; static char *fakedomain = "@"; if(!the_address) the_address = pf->scratch; rfc822_parse_adrlist(&new_addr, the_address, (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)) ? fakedomain : ps_global->maildomain); mail_free_address(pf->addr); /* free old addrs */ *pf->addr = new_addr; /* assign new addr */ } else if(pf->addr) mail_free_address(pf->addr); /* free old addrs */ break; case Subject : if(*pf->text) fs_give((void **)pf->text); if(*pf->scratch){ *pf->text = cpystr(pf->scratch); } break; default : break; } fs_give((void **)&pf->scratch); /* free now useless text */ } create_message_body(bod, attach, flow_it); if(we_cancel) cancel_busy_cue(-1); } /*---------------------------------------------------------------------- The head of the body list here is always either TEXT or MULTIPART. It may be changed from TEXT to MULTIPART if there are attachments to be added and it is not already multipart. ----*/ void create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it) { PART *p, **pp; PATMT *pa; BODY *tmp_body, *text_body = NULL; void *file_contents; PARAMETER **parmp; char *lc; TIME_STAMP("create_body start.", 1); /* * if conditions are met short circuit MIME wrapping */ if((*b)->type != TYPEMULTIPART && !attach){ /* only override assigned encoding if it might need upgrading */ if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT) (*b)->encoding = ENCOTHER; create_message_body_text(*b, flow_it); if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global) || !((*b)->encoding == ENC8BIT || (*b)->encoding == ENCBINARY)){ TIME_STAMP("create_body end.", 1); return; } else /* protect 8bit in multipart */ text_body = *b; } if((*b)->type == TYPETEXT) { /*-- Current type is text, but there are attachments to add --*/ /*-- Upgrade to a TYPEMULTIPART --*/ tmp_body = (BODY *)mail_newbody(); tmp_body->type = TYPEMULTIPART; tmp_body->nested.part = mail_newbody_part(); if(text_body){ /* * Why do we do this? * The problem is that base64 or quoted-printable encoding is * sensitive to having random data appended to it's end. If * we use a single part TEXT message and something in between * us and the end appends advertising without adjusting for * the encoding, the message is screwed up. So we wrap the * text part inside a multipart and then the appended data * will come after the boundary. * * We wish we could do this on the way out the door in a * child of post_rfc822_output because at that point we know * the character set and the encoding being used. For example, * iso-2022-jp is an encoding that is not sensitive to data * appended to the end, so it wouldn't need to be wrapped. * We could conceivably have post_rfc822_body inspect the * body and change it before doing the output. It would work * but would be very fragile. We'd be passed a body from * c-client to output and instead of just doing the output * we'd change the body and then output it. Not worth it * since the multipart wrapping is completely correct for * MIME-aware mailers. */ (void) copy_body(&(tmp_body->nested.part->body), *b); /* move contents which were NOT copied */ tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data; (*b)->contents.text.data = NULL; } else{ tmp_body->nested.part->body = **b; (*b)->subtype = (*b)->id = (*b)->description = NULL; (*b)->parameter = NULL; (*b)->contents.text.data = NULL; } pine_free_body(b); *b = tmp_body; } if(!text_body){ /*-- Now type must be MULTIPART with first part text --*/ (*b)->nested.part->body.encoding = ENCOTHER; create_message_body_text(&((*b)->nested.part->body), flow_it); } /*------ Go through the parts list remove those to be deleted -----*/ for(pp = &(*b)->nested.part->next; *pp;){ for(pa = attach; pa && (*pp)->body.id; pa = pa->next) /* already existed? */ if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){ char *orig_descp = NULL, *cs = NULL; /* * decode original to see if it matches what was decoded * when we sent it in. */ if((*pp)->body.description) orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, (*pp)->body.description); if(!(*pp)->body.description /* update description? */ || (pa->description && strcmp(pa->description, orig_descp))){ if((*pp)->body.description) fs_give((void **) &(*pp)->body.description); /* encoding happens as msg text is written */ (*pp)->body.description = cpystr(pa->description); } if(cs) fs_give((void **) &cs); break; } if(pa == NULL){ p = *pp; /* prepare to zap *pp */ *pp = p->next; /* pull next one in list up */ p->next = NULL; /* tie off removed node */ pine_free_body_data(&p->body); /* clean up contained data */ mail_free_body_part(&p); /* free up the part */ } else pp = &(*pp)->next; } /*---------- Now add any new attachments ---------*/ for(p = (*b)->nested.part ; p->next != NULL; p = p->next); for(pa = attach; pa != NULL; pa = pa->next) { if(pa->id != NULL) continue; /* Has an ID, it's old */ /* * the idea is handle ALL attachments as open FILE *'s. Actual * encoding and such is handled at the time the message * is shoved into the mail slot or written to disk... * * Also, we never unlink a file, so it's up to whoever opens * it to deal with tmpfile issues. */ if((file_contents = (void *)so_get(FileStar, pa->filename, READ_ACCESS)) == NULL){ q_status_message2(SM_ORDER | SM_DING, 3, 4, _("Error \"%s\", couldn't attach file \"%s\""), error_description(errno), pa->filename); display_message('x'); continue; } p->next = mail_newbody_part(); p = p->next; p->body.id = generate_message_id(NULL); p->body.contents.text.data = file_contents; /* * Set type to unknown and let set_mime_type_by_* figure it out. * Always encode attachments we add as BINARY. */ p->body.type = TYPEOTHER; p->body.encoding = ENCBINARY; p->body.size.bytes = name_file_size(pa->filename); if(!set_mime_type_by_extension(&p->body, pa->filename)){ set_mime_type_by_grope(&p->body); set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap); } so_release((STORE_S *)p->body.contents.text.data); if(pa->description) /* encoding happens when msg written */ p->body.description = cpystr(pa->description); /* Add name attribute for backward compatibility */ for(parmp = &p->body.parameter; *parmp; ) if(!struncmp((*parmp)->attribute, "name", 4) && (!*((*parmp)->attribute + 4) || *((*parmp)->attribute + 4) == '*')){ PARAMETER *free_me = *parmp; *parmp = (*parmp)->next; free_me->next = NULL; mail_free_body_parameter(&free_me); } else parmp = &(*parmp)->next; set_parameter(parmp, "name", pa->filename ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename) : NULL); /* Then set the Content-Disposition ala RFC1806 */ if(!p->body.disposition.type){ p->body.disposition.type = cpystr("attachment"); for(parmp = &p->body.disposition.parameter; *parmp; ) if(!struncmp((*parmp)->attribute, "filename", 4) && (!*((*parmp)->attribute + 4) || *((*parmp)->attribute + 4) == '*')){ PARAMETER *free_me = *parmp; *parmp = (*parmp)->next; free_me->next = NULL; mail_free_body_parameter(&free_me); } else parmp = &(*parmp)->next; set_parameter(parmp, "filename", pa->filename ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename) : NULL); } p->next = NULL; pa->id = cpystr(p->body.id); } /* * Now, if this multipart has but one text piece (that is, no * attachments), then downgrade from a composite type to a discrete * text/plain message if CTE is not 8bit. */ if(!(*b)->nested.part->next && (*b)->nested.part->body.type == TYPETEXT && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global) || !((*b)->nested.part->body.encoding == ENC8BIT || (*b)->nested.part->body.encoding == ENCBINARY))){ /* Clone the interesting body part */ tmp_body = mail_newbody(); *tmp_body = (*b)->nested.part->body; /* and rub out what we don't want cleaned up when it's free'd */ mail_initbody(&(*b)->nested.part->body); mail_free_body(b); *b = tmp_body; } TIME_STAMP("create_body end.", 1); } /* * Fill in text BODY part's structure * */ void create_message_body_text(struct mail_bodystruct *b, int flow_it) { set_mime_type_by_grope(b); if(b != NULL){ remove_parameter(&b->parameter, "format"); /* we will set it up below */ remove_parameter(&b->parameter, "delsp"); /* we never set this up */ } if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global) && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global) && flow_it) set_parameter(b ? &b->parameter : NULL, "format", "flowed"); set_body_size(b); } /* * free_attachment_list - free attachments in given list */ void free_attachment_list(PATMT **alist) { PATMT *leading; while(alist && *alist){ /* pointer pointing to something */ leading = (*alist)->next; if((*alist)->description) fs_give((void **)&(*alist)->description); if((*alist)->filename){ if((*alist)->flags & A_TMP) if(our_unlink((*alist)->filename) < 0) dprint((1, "-- Can't unlink(%s): %s\n", (*alist)->filename ? (*alist)->filename : "?", error_description(errno))); fs_give((void **)&(*alist)->filename); } if((*alist)->size) fs_give((void **)&(*alist)->size); if((*alist)->id) fs_give((void **)&(*alist)->id); fs_give((void **)alist); *alist = leading; } } void set_body_size(struct mail_bodystruct *b) { unsigned char c; int we_cancel = 0; we_cancel = busy_cue(NULL, NULL, 1); so_seek((STORE_S *)b->contents.text.data, 0L, 0); b->size.bytes = 0L; while(so_readc(&c, (STORE_S *)b->contents.text.data)) b->size.bytes++; if(we_cancel) cancel_busy_cue(-1); } /* * view_as_rich - set the rich_header flag * * name - name of the header field * deflt - default value to return if user didn't set it * * Note: if the user tries to turn them all off with "", then * we take that to mean default, since otherwise there is no * way to get to the headers. */ int view_as_rich(char *name, int deflt) { char **p; char *q; p = ps_global->VAR_COMP_HDRS; if(p && *p && **p){ for(; (q = *p) != NULL; p++){ if(!struncmp(q, name, strlen(name))) return 0; /* 0 means we *do* view it by default */ } return 1; /* 1 means it starts out hidden */ } return(deflt); } /* * background_posting - return whether or not we're already in the process * of posting */ int background_posting(int gripe) { if(ps_global->post){ if(gripe) q_status_message(SM_ORDER|SM_DING, 3, 3, _("Can't post while posting!")); return(1); } return(0); } /*---------------------------------------------------------------------- Validate the given subject relative to any news groups. Args: none Returns: always returns 1, but also returns error if ----*/ int valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled) { struct headerentry *hp; if(expanded) *expanded = cpystr(given); if(error){ /* * Now look for any header entry we passed to pico that has to do * with news. If there's no subject, gripe. */ for(hp = pbf->headents; hp->prompt; hp++) if(hp->help == h_composer_news){ if(hp->hd_text->text[0] && !*given) *error = cpystr( _("News postings MUST have a subject! Please add one!")); break; } } return(0); } /* * This is the build_address used by the composer to check for an address * in the addrbook. * * Args: to -- the passed in line to parse * full_to -- Address of a pointer to return the full address in. * This will be allocated here and freed by the caller. * error -- Address of a pointer to return an error message in. * This will be allocated here and freed by the caller. * barg -- Address of a pointer to return the fcc in is in * fcc->tptr. It will have already been allocated by the * caller but we may free it and reallocate if we wish. * Caller will free it. * * Result: 0 is returned if address was OK, * -1 if address wasn't OK. * * Side effect: Can flush addrbook entry cache entries so they need to be * re-fetched afterwords. */ int build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled) { char *p; int ret_val, no_repo = 0, *save_nesting_level; BuildTo bldto; PrivateTop *pt = NULL; PrivateAffector *af = NULL; char *fcc_local = NULL; jmp_buf save_jmp_buf; dprint((5, "- build_address - (%s)\n", to ? to : "nul")); /* check to see if to string is empty to avoid work */ for(p = to; p && *p && isspace((unsigned char)*p); p++) ;/* do nothing */ if(!p || !*p){ if(full_to) *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */ return 0; } if(full_to != NULL) *full_to = (char *)NULL; if(error != NULL) *error = (char *)NULL; /* No guarantee cursor or status line is how we saved it */ clear_cursor_pos(); mark_status_unknown(); if(ps_global->remote_abook_validity > 0 && adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled) *mangled |= BUILDER_SCREEN_MANGLED; /* * If we end up jumping back here because somebody else changed one of * our addrbooks out from underneath us, we may well leak some memory. * That's probably ok since this will be very rare. * * The reason for the memcpy of the jmp_buf is that we may actually * be indirectly calling this function from within the address book. * For example, we may be in the address book screen and then run * the ComposeTo command which puts us in the composer, then we call * build_address from there which resets addrbook_changed_unexpectedly. * Once we leave build_address we need to reset addrbook_changed_un... * because this position on the stack will no longer be valid. * Same is true of the other setjmp's in this file which are wrapped * in memcpy calls. */ save_nesting_level = cpyint(ab_nesting_level); memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf)); if(setjmp(addrbook_changed_unexpectedly)){ no_repo = 0; pt = NULL; af = NULL; fcc_local = NULL; if(error != NULL) *error = (char *)NULL; if(full_to && *full_to) fs_give((void **)full_to); q_status_message(SM_ORDER, 3, 5, "Resetting address book..."); dprint((1, "RESETTING address book... build_address(%s)!\n", to ? to : "?")); addrbook_reset(); ab_nesting_level = *save_nesting_level; } ab_nesting_level++; bldto.type = Str; bldto.arg.str = to; ret_val = build_address_internal(bldto, full_to, error, barg ? &fcc_local : NULL, &no_repo, NULL, save_and_restore, 0, mangled); ab_nesting_level--; if(save_nesting_level) fs_give((void **)&save_nesting_level); /* * Have to rfc1522_decode the full_to string before sending it back. */ if(full_to && *full_to ){ char *q; size_t len; len = 4*strlen(*full_to)+1; q = (char *)fs_get(len * sizeof(char)); p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to); /* p == q means that decoding happened, p is decoded *full_to */ if(p == q){ fs_give((void **)full_to); *full_to = p; } else fs_give((void **)&q); } if(fcc_local){ unsigned long csum; /* Pt will point to headents[Fcc].bldr_private */ pt = NULL; if(barg && barg->aff) pt = (PrivateTop *)(*barg->aff); /* * If *barg->aff is set, that means fcc was set from a list * during some previous builder call. * If the current To line contains the old expansion as a prefix, then * we should leave things as they are. In order to decide that, * we look at a hash value computed from the strings. */ if(pt && (af=pt->affector) && af->who == BP_To){ int len; len = strlen(to); if(len >= af->cksumlen){ int save; save = to[af->cksumlen]; to[af->cksumlen] = '\0'; csum = line_hash(to); to[af->cksumlen] = save; } else csum = af->cksumval + 1; /* something not equal to cksumval */ } if(!pt || !pt->affector || (pt->affector->who == BP_To && csum != pt->affector->cksumval)){ /* replace fcc value */ if(barg->tptr) fs_give((void **)&barg->tptr); barg->tptr = fcc_local; if(barg->aff){ if(!pt){ *barg->aff = (void *)fs_get(sizeof(PrivateTop)); pt = (PrivateTop *)(*barg->aff); memset((void *)pt, 0, sizeof(PrivateTop)); } if(no_repo){ if(!pt->affector) pt->affector = (PrivateAffector *)fs_get(sizeof(PrivateAffector)); af = pt->affector; af->who = BP_To; af->cksumlen = strlen(((full_to && *full_to) ? *full_to : "")); af->cksumval = line_hash(((full_to && *full_to) ? *full_to : "")); } else{ /* * If result is reproducible, we don't keep track here. */ if(pt->affector) fs_give((void **)&pt->affector); } } } else fs_give((void **)&fcc_local); /* unused in this case */ } /* This is so pico will erase the old message */ if(error != NULL && *error == NULL) *error = cpystr(""); memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf)); flush_status_messages(1); return(ret_val); } /* * This is the builder used by the composer for the Lcc line. * * Args: lcc -- the passed in Lcc line to parse * full_lcc -- Address of a pointer to return the full address in. * This will be allocated here and freed by the caller. * error -- Address of a pointer to return an error message in. * This is not allocated so should not be freed by the caller. * barg -- This is a pointer to text for affected entries which * we may be changing. The first one in the list is the * To entry. We may put the name of the list in empty * group syntax form there (like List Name: ;). * The second one in the list is the fcc field. * The tptr members already point to text allocated in the * caller. We may free and reallocate here, caller will * free the result in any case. * * Result: 0 is returned if address was OK, * -1 if address wasn't OK. * * Side effect: Can flush addrbook entry cache entries so they need to be * re-fetched afterwords. */ int build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled) { int ret_val, no_repo = 0; /* fcc or lcc not reproducible */ int *save_nesting_level; BuildTo bldlcc; PrivateTop *pt = NULL; PrivateAffector *af = NULL; char *p, *fcc_local = NULL, *to = NULL, *dummy; jmp_buf save_jmp_buf; dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul")); /* check to see if to string is empty to avoid work */ for(p = lcc; p && *p && isspace((unsigned char)*p); p++) ;/* do nothing */ if(!p || !*p){ if(full_lcc) *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */ return 0; } if(error != NULL) *error = (char *)NULL; if(ps_global->remote_abook_validity > 0 && adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled) *mangled |= BUILDER_SCREEN_MANGLED; /* * If we end up jumping back here because somebody else changed one of * our addrbooks out from underneath us, we may well leak some memory. * That's probably ok since this will be very rare. */ save_nesting_level = cpyint(ab_nesting_level); memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf)); if(setjmp(addrbook_changed_unexpectedly)){ no_repo = 0; pt = NULL; af = NULL; fcc_local = NULL; to = NULL; if(error != NULL) *error = (char *)NULL; if(full_lcc && *full_lcc) fs_give((void **)full_lcc); q_status_message(SM_ORDER, 3, 5, "Resetting address book..."); dprint((1, "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?")); addrbook_reset(); ab_nesting_level = *save_nesting_level; } ab_nesting_level++; bldlcc.type = Str; bldlcc.arg.str = lcc; /* * To is first affected_entry and Fcc is second. * The conditional stuff for the fcc argument says to only change the * fcc if the fcc pointer is passed in non-null, and the To pointer * is also non-null. If they are null, that means they've already been * entered (are sticky). We don't affect fcc if either fcc or To has * been typed in. */ ret_val = build_address_internal(bldlcc, full_lcc, error, (barg && barg->next && barg->next->tptr && barg->tptr) ? &fcc_local : NULL, &no_repo, (barg && barg->tptr) ? &to : NULL, save_and_restore, 0, mangled); ab_nesting_level--; if(save_nesting_level) fs_give((void **)&save_nesting_level); /* full_lcc is what ends up in the Lcc: line */ if(full_lcc && *full_lcc){ size_t len; /* * Have to rfc1522_decode the full_lcc string before sending it back. */ len = 4*strlen(*full_lcc)+1; p = (char *)fs_get(len * sizeof(char)); if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){ fs_give((void **)full_lcc); *full_lcc = p; } else fs_give((void **)&p); } /* to is what ends up in the To: line */ if(to && *to){ unsigned long csum; size_t len; /* * Have to rfc1522_decode the full_to string before sending it back. */ len = 4*strlen(to)+1; p = (char *)fs_get(len * sizeof(char)); dummy = NULL; if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){ /* * If the caller wants us to try to preserve the charset * information (they set aff) we copy it into encoded->etext. * We don't have to worry about pasting together pieces of * etext like we do in build_address because whenever the * Lcc line is setting the To line it will be setting the * whole line, not modifying it. * Pt will point to headents[To].bldr_private. */ if(barg && barg->aff){ pt = (PrivateTop *)(*barg->aff); if(!pt){ *barg->aff = (void *)fs_get(sizeof(PrivateTop)); pt = (PrivateTop *)(*barg->aff); memset((void *)pt, 0, sizeof(PrivateTop)); } } fs_give((void **)&to); to = p; } else fs_give((void **)&p); if(dummy) fs_give((void **)&dummy); /* * This part is recording the fact that the To line was set to * what it is by entering something on the Lcc line. In particular, * if a list alias was entered here then the fullname of the list * goes in the To line. We save this affector information so that * we can tell it shouldn't be modified if we call build_addr_lcc * again unless we actually modified what's in the Lcc line so that * it doesn't start with the same thing. The problem we're solving * is that the contents of the Lcc line no longer look like the * list they were derived from. * Pt will point to headents[To].bldr_private. */ if(barg && barg->aff) pt = (PrivateTop *)(*barg->aff); if(pt && (af=pt->affector) && af->who == BP_Lcc){ int len; len = strlen(lcc); if(len >= af->cksumlen){ int save; save = lcc[af->cksumlen]; lcc[af->cksumlen] = '\0'; csum = line_hash(lcc); lcc[af->cksumlen] = save; } else csum = af->cksumval + 1; /* so they aren't equal */ } if(!pt || !pt->affector || pt->affector->who != BP_Lcc || (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){ /* replace to value */ if(barg->tptr && barg->tptr[0]){ size_t l; char *t; l = strlen(barg->tptr) + strlen(to ? to : "") + 2; t = (char *)fs_get((l+1) * sizeof(char)); snprintf(t, l+1, "%s%s%s", barg->tptr, (to && *to) ? ", " : "", (to && *to) ? to : ""); fs_give((void **)&barg->tptr); if(to) fs_give((void **)&to); barg->tptr = t; } else{ if(barg->tptr) fs_give((void **)&barg->tptr); barg->tptr = to; } if(barg->aff){ if(!pt){ *barg->aff = (void *)fs_get(sizeof(PrivateTop)); pt = (PrivateTop *)(*barg->aff); memset((void *)pt, 0, sizeof(PrivateTop)); } if(no_repo){ if(!pt->affector) pt->affector = (PrivateAffector *)fs_get(sizeof(PrivateAffector)); af = pt->affector; af->who = BP_Lcc; af->cksumlen = strlen(((full_lcc && *full_lcc) ? *full_lcc : "")); af->cksumval = line_hash(((full_lcc && *full_lcc) ? *full_lcc : "")); } else{ /* * If result is reproducible, we don't keep track here. */ if(pt->affector) fs_give((void **)&pt->affector); } } } else fs_give((void **)&to); /* unused in this case */ } if(fcc_local){ unsigned long csum; /* * If *barg->next->aff is set, that means fcc was set from a list * during some previous builder call. If the current Lcc line * contains the old expansion as a prefix, then we should leave * things as they are. In order to decide that we look at a hash * value computed from the strings. * Pt will point to headents[Fcc].bldr_private */ pt = NULL; if(barg && barg->next && barg->next->aff) pt = (PrivateTop *)(*barg->next->aff); if(pt && (af=pt->affector) && af->who == BP_Lcc){ int len; len = strlen(lcc); if(len >= af->cksumlen){ int save; save = lcc[af->cksumlen]; lcc[af->cksumlen] = '\0'; csum = line_hash(lcc); lcc[af->cksumlen] = save; } else csum = af->cksumval + 1; /* something not equal to cksumval */ } if(!pt || !pt->affector || pt->affector->who != BP_Lcc || (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){ /* replace fcc value */ if(barg->next->tptr) fs_give((void **)&barg->next->tptr); barg->next->tptr = fcc_local; if(barg->next->aff){ if(!pt){ *barg->next->aff = (void *)fs_get(sizeof(PrivateTop)); pt = (PrivateTop *)(*barg->next->aff); memset((void *)pt, 0, sizeof(PrivateTop)); } if(no_repo){ if(!pt->affector) pt->affector = (PrivateAffector *)fs_get(sizeof(PrivateAffector)); af = pt->affector; af->who = BP_Lcc; af->cksumlen = strlen(((full_lcc && *full_lcc) ? *full_lcc : "")); af->cksumval = line_hash(((full_lcc && *full_lcc) ? *full_lcc : "")); } else{ /* * If result is reproducible, we don't keep track here. */ if(pt->affector) fs_give((void **)&pt->affector); } } } else fs_give((void **)&fcc_local); /* unused in this case */ } if(error != NULL && *error == NULL) *error = cpystr(""); memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf)); flush_status_messages(0); return(ret_val); } /*---------------------------------------------------------------------- Verify and canonicalize news groups names. Called from the message composer Args: given_group -- List of groups typed by user expanded_group -- pointer to point to expanded list, which will be allocated here and freed in caller. If this is NULL, don't attempt to validate. error -- pointer to store error message fcc -- pointer to point to fcc, which will be allocated here and freed in caller Returns: 0 if all is OK -1 if addresses weren't valid Test the given list of newstroups against those recognized by our nntp servers. Testing by actually trying to open the list is much cheaper, both in bandwidth and memory, than yanking the whole list across the wire. ----*/ int news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled) { int rv; char *fccptr = NULL; if(fcc && fcc->tptr) fccptr = cpystr(fcc->tptr); clear_cursor_pos(); rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy); /* assign any new fcc to the BUILDER_ARG */ if(fccptr){ if(fcc){ /* it changed */ if(fcc->tptr && strcmp(fcc->tptr, fccptr)){ fs_give((void **) &fcc->tptr); fcc->tptr = fccptr; fccptr = NULL; } } if(fccptr) fs_give((void **) &fccptr); } /* deal with any busy indicator */ if(news_busy_cue){ news_busy_cue = 0; cancel_busy_cue(0); mark_status_dirty(); display_message('x'); if(mangled) *mangled |= BUILDER_MESSAGE_DISPLAYED; } return(rv); } void news_build_busy(void) { news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0); } #if defined(DOS) || defined(OS2) /*---------------------------------------------------------------------- Verify that the necessary pieces are around to allow for message sending under DOS Args: strict -- tells us if a remote stream is required before sending is permitted. The idea is to make sure pine knows enough to put together a valid from line. The things we MUST know are a user-id, user-domain and smtp server to dump the message off on. Typically these are provided in pine's configuration file, but if not, the user is queried here. ----*/ int dos_valid_from() { char prompt[100], answer[80]; int rc, i, flags; HelpType help; /* * query for user name portion of address, use IMAP login * name as default */ if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){ NETMBX mb; int no_prompt_user_id = 0; if(ps_global->mail_stream && ps_global->mail_stream->mailbox && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb) && *mb.user){ strncpy(answer, mb.user, sizeof(answer)-1); answer[sizeof(answer)-1] = '\0'; } else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){ /* no user-id prompting if set */ no_prompt_user_id = 1; rc = 0; if(!ps_global->mail_stream) do_broach_folder(ps_global->inbox_name, ps_global->context_list, NULL, DB_INBOXWOCNTXT); if(ps_global->mail_stream && ps_global->mail_stream->mailbox && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb) && *mb.user){ strncpy(answer, mb.user, sizeof(answer)-1); answer[sizeof(answer)-1] = '\0'; } else answer[0] = '\0'; } else answer[0] = '\0'; if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){ /* No prompt, just assume mailbox login is user-id */ no_prompt_user_id = 1; rc = 0; } snprintf(prompt,sizeof(prompt),_("User-id for From address : ")); prompt[sizeof(prompt)-1] = '\0'; help = NO_HELP; while(!no_prompt_user_id) { flags = OE_APPEND_CURRENT; rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0, sizeof(answer),prompt,NULL,help,&flags); if(rc == 2) continue; if(rc == 3){ help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP; continue; } if(rc != 4) break; } if(rc == 1 || (rc == 0 && !answer[0])) { q_status_message(SM_ORDER, 3, 4, _("Send cancelled (User-id must be provided before sending)")); return(0); } /* save the name */ snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"), sizeof(prompt)-50, answer); prompt[sizeof(prompt)-1] = '\0'; if(ps_global->blank_user_id && !no_prompt_user_id && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){ set_variable(V_USER_ID, answer, 1, 1, Main); } else{ fs_give((void **)&(ps_global->VAR_USER_ID)); ps_global->VAR_USER_ID = cpystr(answer); } } /* query for personal name */ if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0' && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){ answer[0] = '\0'; snprintf(prompt, sizeof(prompt), _("Personal name for From address : ")); prompt[sizeof(prompt)-1] = '\0'; help = NO_HELP; while(1) { flags = OE_APPEND_CURRENT; rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0, sizeof(answer),prompt,NULL,help,&flags); if(rc == 2) continue; if(rc == 3){ help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP; continue; } if(rc != 4) break; } if(rc == 0 && answer){ /* save the name */ snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"), sizeof(prompt)-50, answer); prompt[sizeof(prompt)-1] = '\0'; if(ps_global->blank_personal_name && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){ set_variable(V_PERSONAL_NAME, answer, 1, 1, Main); } else{ fs_give((void **)&(ps_global->VAR_PERSONAL_NAME)); ps_global->VAR_PERSONAL_NAME = cpystr(answer); } } } /* * query for host/domain portion of address, using IMAP * host as default */ if(ps_global->blank_user_domain || ps_global->maildomain == ps_global->localdomain || ps_global->maildomain == ps_global->hostname){ if(ps_global->inbox_name[0] == '{'){ for(i=0; i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++) answer[i] = ps_global->inbox_name[i+1]; answer[i] = '\0'; } else answer[0] = '\0'; snprintf(prompt,sizeof(prompt),_("Host/domain for From address : ")); prompt[sizeof(prompt)-1] = '\0'; help = NO_HELP; while(1) { flags = OE_APPEND_CURRENT; rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0, sizeof(answer),prompt,NULL,help,&flags); if(rc == 2) continue; if(rc == 3){ help = (help == NO_HELP) ? h_sticky_domain : NO_HELP; continue; } if(rc != 4) break; } if(rc == 1 || (rc == 0 && !answer[0])) { q_status_message(SM_ORDER, 3, 4, _("Send cancelled (Host/domain name must be provided before sending)")); return(0); } /* save the name */ snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"), sizeof(prompt)-50, answer); prompt[sizeof(prompt)-1] = '\0'; if(!ps_global->userdomain && !ps_global->blank_user_domain && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){ set_variable(V_USER_DOMAIN, answer, 1, 1, Main); fs_give((void **)&(ps_global->maildomain)); /* blast old val */ ps_global->userdomain = cpystr(answer); ps_global->maildomain = ps_global->userdomain; } else{ fs_give((void **)&(ps_global->maildomain)); ps_global->userdomain = cpystr(answer); ps_global->maildomain = ps_global->userdomain; } } /* check for smtp server */ if(!ps_global->VAR_SMTP_SERVER || !ps_global->VAR_SMTP_SERVER[0] || !ps_global->VAR_SMTP_SERVER[0][0]){ char **list; if(ps_global->inbox_name[0] == '{'){ for(i=0; i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++) answer[i] = ps_global->inbox_name[i+1]; answer[i] = '\0'; } else answer[0] = '\0'; snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : ")); prompt[sizeof(prompt)-1] = '\0'; help = NO_HELP; while(1) { flags = OE_APPEND_CURRENT; rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0, sizeof(answer),prompt,NULL,help,&flags); if(rc == 2) continue; if(rc == 3){ help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP; continue; } if(rc != 4) break; } if(rc == 1 || (rc == 0 && answer[0] == '\0')) { q_status_message(SM_ORDER, 3, 4, _("Send cancelled (SMTP server must be provided before sending)")); return(0); } /* save the name */ list = (char **) fs_get(2 * sizeof(char *)); list[0] = cpystr(answer); list[1] = NULL; set_variable_list(V_SMTP_SERVER, list, TRUE, Main); fs_give((void *)&list[0]); fs_give((void *)list); } return(1); } #endif /* defined(DOS) || defined(OS2) */