* Update to packages/alpine.spec to account for the new location of man
[alpine.git] / alpine / send.c
blobef9cdf49e349132d658be12dfb4c2906ba2e46bd
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: send.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2017 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 Functions for composing and sending mail
22 ====*/
25 #include "headers.h"
26 #include "send.h"
27 #include "status.h"
28 #include "mailview.h"
29 #include "mailindx.h"
30 #include "dispfilt.h"
31 #include "keymenu.h"
32 #include "folder.h"
33 #include "radio.h"
34 #include "addrbook.h"
35 #include "reply.h"
36 #include "titlebar.h"
37 #include "signal.h"
38 #include "mailcmd.h"
39 #include "roleconf.h"
40 #include "adrbkcmd.h"
41 #include "busy.h"
42 #include "../pith/debug.h"
43 #include "../pith/state.h"
44 #include "../pith/conf.h"
45 #include "../pith/flag.h"
46 #include "../pith/bldaddr.h"
47 #include "../pith/copyaddr.h"
48 #include "../pith/detach.h"
49 #include "../pith/mimedesc.h"
50 #include "../pith/pipe.h"
51 #include "../pith/addrstring.h"
52 #include "../pith/news.h"
53 #include "../pith/detoken.h"
54 #include "../pith/util.h"
55 #include "../pith/init.h"
56 #include "../pith/mailcmd.h"
57 #include "../pith/ablookup.h"
58 #include "../pith/reply.h"
59 #include "../pith/hist.h"
60 #include "../pith/list.h"
61 #include "../pith/icache.h"
62 #include "../pith/busy.h"
63 #include "../pith/mimetype.h"
64 #include "../pith/send.h"
65 #include "../pith/smime.h"
68 typedef struct body_particulars {
69 unsigned short type, encoding, had_csp;
70 char *subtype, *charset;
71 PARAMETER *parameter;
72 } BODY_PARTICULARS_S;
74 #define PHONE_HOME_VERSION ".count"
76 #define PHONE_HOME_HOST "vfemail.net"
79 * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook
81 #define HE(PF) ((struct headerentry *)((PF)->extdata))
85 * Internal Prototypes
87 int redraft(MAILSTREAM **, ENVELOPE **, BODY **, char **, char **, REPLY_S **,
88 REDRAFT_POS_S **, PINEFIELD **, ACTION_S **, int);
89 int redraft_prompt(char *, char *, int);
90 int check_for_subject(METAENV *);
91 int check_for_fcc(char *);
92 void free_prompts(PINEFIELD *);
93 int postpone_prompt(void);
94 METAENV *pine_simple_send_header(ENVELOPE *, char **, char ***);
95 void call_mailer_file_result(char *, int);
96 void mark_address_failure_for_pico(METAENV *);
97 BODY_PARTICULARS_S
98 *save_body_particulars(BODY *);
99 void reset_body_particulars(BODY_PARTICULARS_S *, BODY *);
100 void free_body_particulars(BODY_PARTICULARS_S *);
101 long message_format_for_pico(long, int (*)(int));
102 int send_exit_for_pico(struct headerentry *, void (*)(void), int, char **);
103 void new_thread_on_blank_subject(void);
104 char *choose_a_priority(char *);
105 int dont_flow_this_time(void);
106 int mime_type_for_pico(char *);
107 char *cancel_for_pico(void (*)(void));
108 int filter_message_text(char *, ENVELOPE *, BODY *, STORE_S **, METAENV *);
109 void pine_send_newsgroup_name(char *, char*, size_t);
110 void outgoing2strings(METAENV *, BODY *, void **, PATMT **, int);
111 void strings2outgoing(METAENV *, BODY **, PATMT *, int);
112 void create_message_body_text(BODY *, int);
113 void set_body_size(BODY *);
114 int view_as_rich(char *, int);
115 int background_posting(int);
116 int valid_subject(char *, char **, char **,BUILDER_ARG *,int *);
117 int build_addr_lcc(char *, char **, char **, BUILDER_ARG *, int *);
118 int news_build(char *, char **, char **, BUILDER_ARG *, int *);
119 void news_build_busy(void);
120 #if defined(DOS) || defined(OS2)
121 int dos_valid_from(void);
122 #endif /* defined(DOS) || defined(OS2) */
126 * Pointer to buffer to hold pointers into pine data that's needed by pico.
128 static PICO *pbf;
131 static char *g_rolenick = NULL;
134 static char *sending_filter_requested;
135 static char background_requested, flowing_requested;
136 static unsigned call_mailer_flags;
137 static char *priority_requested;
139 /* local global to save busy_cue state */
140 static int news_busy_cue = 0;
144 * Various useful strings
146 #define INTRPT_PMT \
147 _("Continue INTERRUPTED composition (answering \"n\" won't erase it)")
148 #define PSTPND_PMT \
149 _("Continue postponed composition (answering \"No\" won't erase it)")
150 #define FORM_PMT \
151 _("Start composition from Form Letter Folder")
152 #define PSTPN_FORM_PMT \
153 _("Save to Postponed or Form letter folder? ")
154 #define POST_PMT \
155 _("Posted message may go to thousands of readers. Really post")
156 #define INTR_DEL_PMT \
157 _("Deleted messages will be removed from folder after use. Proceed")
161 * Macros to help sort out posting results
163 #define P_MAIL_WIN 0x01
164 #define P_MAIL_LOSE 0x02
165 #define P_MAIL_BITS 0x03
166 #define P_NEWS_WIN 0x04
167 #define P_NEWS_LOSE 0x08
168 #define P_NEWS_BITS 0x0C
169 #define P_FCC_WIN 0x10
170 #define P_FCC_LOSE 0x20
171 #define P_FCC_BITS 0x30
174 #define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE"
178 * For check_for_subject and check_for_fcc
180 #define CF_OK 0x1
181 #define CF_MISSING 0x2
184 /*----------------------------------------------------------------------
185 Compose screen (not forward or reply). Set up envelope, call composer
187 Args: pine_state -- The usual pine structure
189 Little front end for the compose screen
190 ---*/
191 void
192 compose_screen(struct pine *pine_state)
194 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
195 (*redraw)(void) = pine_state->redrawer;
197 pine_state->redrawer = NULL;
198 ps_global->next_screen = SCREEN_FUN_NULL;
199 mailcap_free(); /* free resources we won't be using for a while */
200 compose_mail(NULL, NULL, NULL, NULL, NULL);
201 pine_state->next_screen = prev_screen;
202 pine_state->redrawer = redraw;
206 /*----------------------------------------------------------------------
207 Alternate compose screen. Set up role and call regular compose.
209 Args: pine_state -- The usual pine structure
210 ---*/
211 void
212 alt_compose_screen(struct pine *pine_state)
214 ACTION_S *role = NULL;
215 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
216 (*redraw)(void) = pine_state->redrawer;
218 pine_state->redrawer = NULL;
219 ps_global->next_screen = SCREEN_FUN_NULL;
220 mailcap_free(); /* free resources we won't be using for a while */
222 /* Setup role */
223 if(role_select_screen(pine_state, &role, MC_COMPOSE) < 0){
224 cmd_cancelled("Composition");
225 pine_state->next_screen = prev_screen;
226 pine_state->redrawer = redraw;
227 return;
231 * If default role was selected (NULL) we need to make up a role which
232 * won't do anything, but will cause compose_mail to think there's
233 * already a role so that it won't try to confirm the default.
235 if(role)
236 role = combine_inherited_role(role);
237 else{
238 role = (ACTION_S *)fs_get(sizeof(*role));
239 memset((void *)role, 0, sizeof(*role));
240 role->nick = cpystr("Default Role");
243 pine_state->redrawer = NULL;
244 compose_mail(NULL, NULL, role, NULL, NULL);
245 free_action(&role);
246 pine_state->next_screen = prev_screen;
247 pine_state->redrawer = redraw;
251 /*----------------------------------------------------------------------
252 Format envelope for outgoing message and call editor
254 Args: given_to -- An address to send mail to (usually from command line
255 invocation)
256 fcc_arg -- The fcc that goes with this address.
258 If a "To" line is given format that into the envelope and get ready to call
259 the composer
260 If there's a message postponed, offer to continue it, and set it up,
261 otherwise just fill in the outgoing envelope as blank.
263 NOTE: we ignore postponed and interrupted messages in nr mode
264 ----*/
265 void
266 compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
267 PATMT *attach, gf_io_t inc_text_getc)
269 BODY *body = NULL;
270 ENVELOPE *outgoing = NULL;
271 PINEFIELD *custom = NULL;
272 REPLY_S *reply = NULL;
273 REDRAFT_POS_S *redraft_pos = NULL;
274 ACTION_S *role = NULL;
275 MAILSTREAM *stream;
276 char *fcc_to_free,
277 *fcc = NULL,
278 *lcc = NULL,
279 *sig = NULL;
280 int fcc_is_sticky = 0,
281 to_is_sticky = 0,
282 intrptd = 0,
283 postponed = 0,
284 form = 0;
286 dprint((1,
287 "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n"));
289 /*-- Check for INTERRUPTED mail --*/
290 if(!role_arg && !(given_to || attach)){
291 char file_path[MAXPATH+1];
293 /* build filename and see if it exists. build_path creates
294 * an explicit local path name, so all c-client access is thru
295 * local drivers.
297 file_path[0] = '\0';
298 build_path(file_path,
299 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
300 : ps_global->home_dir,
301 INTERRUPTED_MAIL, sizeof(file_path));
303 /* check to see if the folder exists, the user wants to continue
304 * and that we can actually read something in...
306 if(folder_exists(NULL, file_path) & FEX_ISFILE)
307 intrptd = 1;
310 /*-- Check for postponed mail --*/
311 if(!role_arg
312 && !outgoing /* not replying/forwarding */
313 && !(given_to || attach) /* not command line send */
314 && ps_global->VAR_POSTPONED_FOLDER /* folder to look in */
315 && ps_global->VAR_POSTPONED_FOLDER[0])
316 postponed = 1;
318 /*-- Check for form letter folder --*/
319 if(!role_arg
320 && !outgoing /* not replying/forwarding */
321 && !(given_to || attach) /* not command line send */
322 && ps_global->VAR_FORM_FOLDER /* folder to look in */
323 && ps_global->VAR_FORM_FOLDER[0])
324 form = 1;
326 if(!outgoing && !(given_to || attach)
327 && !role_arg && F_ON(F_ALT_COMPOSE_MENU, ps_global)){
328 char prompt[80];
329 char letters[30];
330 char chosen_task;
331 char *new = "New";
332 char *intrpt = "Interrupted";
333 char *postpnd = "Postponed";
334 char *formltr = "FormLetter";
335 char *roles = "setRole";
336 HelpType help = h_composer_browse;
337 ESCKEY_S compose_style[6];
338 unsigned which_help;
339 int ekey_num;
341 ekey_num = 0;
342 compose_style[ekey_num].ch = 'n';
343 compose_style[ekey_num].rval = 'n';
344 compose_style[ekey_num].name = "N";
345 compose_style[ekey_num++].label = new;
347 if(intrptd){
348 compose_style[ekey_num].ch = 'i';
349 compose_style[ekey_num].rval = 'i';
350 compose_style[ekey_num].name = "I";
351 compose_style[ekey_num++].label = intrpt;
354 if(postponed){
355 compose_style[ekey_num].ch = 'p';
356 compose_style[ekey_num].rval = 'p';
357 compose_style[ekey_num].name = "P";
358 compose_style[ekey_num++].label = postpnd;
361 if(form){
362 compose_style[ekey_num].ch = 'f';
363 compose_style[ekey_num].rval = 'f';
364 compose_style[ekey_num].name = "F";
365 compose_style[ekey_num++].label = formltr;
368 compose_style[ekey_num].ch = 'r';
369 compose_style[ekey_num].rval = 'r';
370 compose_style[ekey_num].name = "R";
371 compose_style[ekey_num++].label = roles;
373 compose_style[ekey_num].ch = -1;
375 if(F_ON(F_BLANK_KEYMENU,ps_global)){
376 char *p;
378 p = letters;
379 *p = '\0';
380 for(ekey_num = 0; compose_style[ekey_num].ch != -1; ekey_num++){
381 if(p - letters < sizeof(letters))
382 *p++ = (char) compose_style[ekey_num].ch;
384 if(compose_style[ekey_num + 1].ch != -1 && p - letters < sizeof(letters))
385 *p++ = ',';
388 if(p - letters < sizeof(letters))
389 *p = '\0';
392 which_help = intrptd + 2 * postponed + 4 * form;
393 switch(which_help){
394 case 1:
395 help = h_compose_intrptd;
396 break;
397 case 2:
398 help = h_compose_postponed;
399 break;
400 case 3:
401 help = h_compose_intrptd_postponed;
402 break;
403 case 4:
404 help = h_compose_form;
405 break;
406 case 5:
407 help = h_compose_intrptd_form;
408 break;
409 case 6:
410 help = h_compose_postponed_form;
411 break;
412 case 7:
413 help = h_compose_intrptd_postponed_form;
414 break;
415 default:
416 help = h_compose_default;
417 break;
420 snprintf(prompt, sizeof(prompt),
421 "Choose a compose method from %s : ",
422 F_ON(F_BLANK_KEYMENU,ps_global) ? letters : "the menu below");
423 prompt[sizeof(prompt)-1] = '\0';
425 chosen_task = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
426 compose_style, 'n', 'x', help, RB_NORM);
427 intrptd = postponed = form = 0;
429 switch(chosen_task){
430 case 'i':
431 intrptd = 1;
432 break;
433 case 'p':
434 postponed = 1;
435 break;
436 case 'r':
438 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
439 (*redraw)(void) = ps_global->redrawer;
441 ps_global->redrawer = NULL;
442 ps_global->next_screen = SCREEN_FUN_NULL;
443 if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
444 cmd_cancelled("Composition");
445 ps_global->next_screen = prev_screen;
446 ps_global->redrawer = redraw;
447 return;
450 ps_global->next_screen = prev_screen;
451 ps_global->redrawer = redraw;
452 if(role)
453 role = combine_inherited_role(role);
455 break;
457 case 'f':
458 form = 1;
459 break;
461 case 'x':
462 q_status_message(SM_ORDER, 0, 3,
463 "Composition cancelled");
464 return;
465 break;
467 default:
468 break;
472 if(intrptd && !outgoing){
473 char file_path[MAXPATH+1];
474 int ret = 'n';
476 file_path[0] = '\0';
477 build_path(file_path,
478 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
479 : ps_global->home_dir,
480 INTERRUPTED_MAIL, sizeof(file_path));
481 if(folder_exists(NULL, file_path) & FEX_ISFILE){
482 if((stream = pine_mail_open(NULL, file_path,
483 SP_USEPOOL|SP_TEMPUSE, NULL))
484 && !stream->halfopen){
486 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
487 (ret = redraft_prompt("Interrupted",INTRPT_PMT,'n')) =='y'){
488 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
489 &redraft_pos, &custom, &role, REDRAFT_DEL)){
490 if(stream)
491 pine_mail_close(stream);
493 return;
496 to_is_sticky++;
498 /* redraft() may or may not have closed stream */
499 if(stream)
500 pine_mail_close(stream);
502 postponed = form = 0;
504 else{
505 pine_mail_close(stream);
506 if(ret == 'x'){
507 q_status_message(SM_ORDER, 0, 3,
508 _("Composition cancelled"));
509 return;
513 else{
514 q_status_message1(SM_ORDER | SM_DING, 3, 3,
515 _("Can't open Interrupted mailbox: %s"),
516 file_path);
517 if(stream)
518 pine_mail_close(stream);
523 if(postponed && !outgoing){
524 int ret = 'n', done = 0;
525 int exists;
527 if((exists=postponed_stream(&stream,
528 ps_global->VAR_POSTPONED_FOLDER,
529 "Postponed", 0)) & FEX_ISFILE){
530 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
531 (ret = redraft_prompt("Postponed",PSTPND_PMT,'n')) == 'y'){
532 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
533 &redraft_pos, &custom, &role,
534 REDRAFT_DEL | REDRAFT_PPND))
535 done++;
537 /* stream may or may not be closed in redraft() */
538 if(stream && (stream != ps_global->mail_stream))
539 pine_mail_close(stream);
541 to_is_sticky++;
542 intrptd = form = 0;
544 else{
545 if(stream != ps_global->mail_stream)
546 pine_mail_close(stream);
548 if(ret == 'x'){
549 q_status_message(SM_ORDER, 0, 3,
550 _("Composition cancelled"));
551 done++;
555 else if(F_ON(F_ALT_COMPOSE_MENU, ps_global))
556 done++;
558 if(done)
559 return;
562 if(form && !outgoing){
563 int ret = 'n', done = 0;
564 int exists;
566 if((exists=postponed_stream(&stream,
567 ps_global->VAR_FORM_FOLDER,
568 "Form letter", 1)) & FEX_ISFILE){
569 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
570 (ret = want_to(FORM_PMT,'y','x',NO_HELP,WT_NORM))=='y'){
571 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
572 &redraft_pos, &custom, &role, REDRAFT_NONE))
573 done++;
575 /* stream may or may not be closed in redraft() */
576 if(stream && (stream != ps_global->mail_stream))
577 pine_mail_close(stream);
579 to_is_sticky++;
580 intrptd = postponed = 0;
582 else{
583 if(stream != ps_global->mail_stream)
584 pine_mail_close(stream);
586 if(ret == 'x'){
587 q_status_message(SM_ORDER, 0, 3,
588 _("Composition cancelled"));
589 done++;
593 else{
594 if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
595 q_status_message(SM_ORDER | SM_DING, 3, 3,
596 _("Form letter folder doesn't exist!"));
597 return;
601 if(done)
602 return;
605 /*-- normal composition --*/
606 if(!outgoing){
607 int impl, template_len = 0;
608 long rflags = ROLE_COMPOSE;
609 PAT_STATE dummy;
611 /*================= Compose new message ===============*/
612 body = mail_newbody();
613 outgoing = mail_newenvelope();
615 if(given_to)
616 rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
618 outgoing->message_id = generate_message_id();
621 * Setup possible role
623 if(role_arg)
624 role = copy_action(role_arg);
626 if(!role){
627 /* Setup possible compose role */
628 if(nonempty_patterns(rflags, &dummy)){
630 * setup default role
631 * Msgno = -1 means there is no msg.
632 * This will match roles which have the Compose Use turned
633 * on, and have no patterns set, and match the Current
634 * Folder Type.
636 role = set_role_from_msg(ps_global, rflags, -1L, NULL);
638 if(confirm_role(rflags, &role))
639 role = combine_inherited_role(role);
640 else{ /* cancel reply */
641 role = NULL;
642 cmd_cancelled("Composition");
643 return;
648 if(role)
649 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
650 role->nick);
653 * The type of storage object allocated below is vitally
654 * important. See SIMPLIFYING ASSUMPTION #37
656 if((body->contents.text.data = (void *) so_get(PicoText,
657 NULL, EDIT_ACCESS)) != NULL){
658 char ch;
660 if(inc_text_getc){
661 while((*inc_text_getc)(&ch))
662 if(!so_writec(ch, (STORE_S *)body->contents.text.data)){
663 break;
667 else{
668 q_status_message(SM_ORDER | SM_DING, 3, 4,
669 _("Problem creating space for message text."));
670 return;
673 if(role && role->template){
674 char *filtered;
676 impl = 1; /* leave cursor in header if not explicit */
677 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
678 if(filtered){
679 if(*filtered){
680 so_puts((STORE_S *)body->contents.text.data, filtered);
681 if(impl == 1)
682 template_len = strlen(filtered);
685 fs_give((void **)&filtered);
688 else
689 impl = 1;
691 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
692 if(impl == 2)
693 redraft_pos->offset += template_len;
695 if(*sig)
696 so_puts((STORE_S *)body->contents.text.data, sig);
698 fs_give((void **)&sig);
701 body->type = TYPETEXT;
703 if(attach)
704 create_message_body(&body, attach, 0);
707 ps_global->prev_screen = compose_screen;
708 if(!(fcc_to_free = fcc) && !(role && role->fcc))
709 fcc = fcc_arg; /* Didn't pick up fcc, use given */
712 * check whether a build_address-produced fcc is different from
713 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
715 if(fcc){
716 char *tmp_fcc = NULL;
718 if(outgoing->to){
719 tmp_fcc = get_fcc_based_on_to(outgoing->to);
720 if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
721 fcc_is_sticky++; /* cause sticky bit to get set */
724 else if((tmp_fcc = get_fcc(NULL)) != NULL &&
725 !strcmp(fcc, tmp_fcc)){
726 /* not sticky */
728 else
729 fcc_is_sticky++;
731 if(tmp_fcc)
732 fs_give((void **)&tmp_fcc);
735 pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc,
736 reply, redraft_pos, lcc, custom,
737 (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0));
739 if(reply){
740 if(reply->mailbox)
741 fs_give((void **) &reply->mailbox);
742 if(reply->origmbox)
743 fs_give((void **) &reply->origmbox);
744 if(reply->prefix)
745 fs_give((void **) &reply->prefix);
746 if(reply->data.uid.msgs)
747 fs_give((void **) &reply->data.uid.msgs);
748 fs_give((void **) &reply);
751 if(fcc_to_free)
752 fs_give((void **)&fcc_to_free);
754 if(lcc)
755 fs_give((void **)&lcc);
757 mail_free_envelope(&outgoing);
758 pine_free_body(&body);
759 free_redraft_pos(&redraft_pos);
760 free_action(&role);
764 /*----------------------------------------------------------------------
765 Args: stream -- This is where we get the postponed messages from
766 We'll expunge and close it here unless it is mail_stream.
768 These are all return values:
769 ================
770 outgoing --
771 body --
772 fcc --
773 lcc --
774 reply --
775 redraft_pos --
776 custom --
777 role --
778 ================
780 flags --
782 ----*/
784 redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body,
785 char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos,
786 PINEFIELD **custom, ACTION_S **role, int flags)
788 MAILSTREAM *stream;
789 long cont_msg = 1L;
790 STORE_S *so;
792 if(!(streamp && *streamp))
793 return(0);
795 stream = *streamp;
798 * If we're manipulating the current folder, don't bother
799 * with index
801 if(!stream->nmsgs){
802 if(REDRAFT_PPND&flags)
803 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!"));
804 else
805 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!"));
807 return(redraft_cleanup(streamp, FALSE, flags));
809 else if(stream == ps_global->mail_stream
810 && ps_global->prev_screen == mail_index_screen){
812 * Since the user's got this folder already opened and they're
813 * on a selected message, pick that one rather than rebuild
814 * another index screen...
816 cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
818 else if(stream->nmsgs > 1L){ /* offer browser ? */
819 int rv;
821 if(REDRAFT_PPND&flags){ /* set to last message postponed */
822 mn_set_cur(sp_msgmap(stream),
823 mn_get_revsort(sp_msgmap(stream))
824 ? 1L : mn_get_total(sp_msgmap(stream)));
826 else{ /* set to top form letter */
827 mn_set_cur(sp_msgmap(stream), 1L);
830 clear_index_cache(stream, 0);
831 while(1){
832 void *ti;
834 ti = stop_threading_temporarily();
835 rv = index_lister(ps_global, NULL, stream->mailbox,
836 stream, sp_msgmap(stream));
837 restore_threading(&ti);
839 cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream)));
840 if(count_flagged(stream, F_DEL)
841 && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){
842 if(REDRAFT_PPND&flags)
843 q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
844 else
845 q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
847 continue;
850 break;
853 clear_index_cache(stream, 0);
855 if(rv){
856 q_status_message(SM_ORDER, 0, 3, _("Composition cancelled"));
857 (void) redraft_cleanup(streamp, FALSE, flags);
859 if(!*streamp && !ps_global->mail_stream){
860 q_status_message2(SM_ORDER, 3, 7,
861 "No more %.200s, returning to \"%.200s\"",
862 (REDRAFT_PPND&flags) ? "postponed messages"
863 : "form letters",
864 ps_global->inbox_name);
865 if(ps_global && ps_global->ttyo){
866 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
867 ps_global->mangled_footer = 1;
870 do_broach_folder(ps_global->inbox_name,
871 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
873 ps_global->next_screen = mail_index_screen;
876 return(0); /* special case */
880 if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL)
881 return(redraft_work(streamp, cont_msg, outgoing, body,
882 fcc, lcc, reply, redraft_pos, custom,
883 role, flags, so));
884 else
885 return(0);
890 redraft_prompt(char *type, char *prompt, int failure)
892 if(background_posting(FALSE)){
893 q_status_message1(SM_ORDER, 0, 3,
894 _("%s folder unavailable while background posting"),
895 type);
896 return(failure);
899 return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM));
903 /* this is for initializing the fixed header elements in pine_send() */
905 prompt::name::help::prwid::maxlen::realaddr::
906 builder::affected_entry::next_affected::selector::key_label::fileedit::
907 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
908 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
910 static struct headerentry he_template[]={
911 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
912 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
913 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
914 {"From : ", "From", h_composer_from, 10, 0, NULL,
915 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
916 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
917 {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
918 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
919 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
920 {"To : ", "To", h_composer_to, 10, 0, NULL,
921 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
922 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK},
923 {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL,
924 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
925 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
926 {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL,
927 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
928 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
929 {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL,
930 news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL,
931 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
932 {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL,
933 NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, fcc_tab_complete,
934 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE},
935 {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL,
936 build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete,
937 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
938 {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL,
939 NULL, NULL, NULL, NULL, "To Files", NULL, NULL,
940 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE},
941 {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL,
942 valid_subject, NULL, NULL, NULL, NULL, NULL, NULL,
943 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
944 {"", "References", NO_HELP, 10, 0, NULL,
945 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
946 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
947 {"", "Date", NO_HELP, 10, 0, NULL,
948 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
949 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
950 {"", "In-Reply-To", NO_HELP, 10, 0, NULL,
951 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
952 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
953 {"", "Message-ID", NO_HELP, 10, 0, NULL,
954 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
955 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
956 {"", "X-Priority", NO_HELP, 10, 0, NULL,
957 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
958 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
959 {"", "User-Agent", NO_HELP, 10, 0, NULL,
960 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
961 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
962 {"", "To", NO_HELP, 10, 0, NULL,
963 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
964 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
965 {"", "X-Post-Error",NO_HELP, 10, 0, NULL,
966 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
967 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
968 {"", "X-Reply-UID", NO_HELP, 10, 0, NULL,
969 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
970 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
971 {"", "X-Reply-Mbox", NO_HELP, 10, 0, NULL,
972 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
973 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
974 {"", "X-SMTP-Server", NO_HELP, 10, 0, NULL,
975 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
976 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
977 {"", "X-Cursor-Pos", NO_HELP, 10, 0, NULL,
978 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
979 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
980 {"", "X-Our-ReplyTo", NO_HELP, 10, 0, NULL,
981 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
982 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
983 {"", OUR_HDRS_LIST, NO_HELP, 10, 0, NULL,
984 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
985 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
986 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
987 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
988 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
989 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
990 {"", "Sender", NO_HELP, 10, 0, NULL,
991 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
992 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
993 #endif
997 static struct headerentry he_custom_addr_templ={
998 NULL, NULL, h_composer_custom_addr,10, 0, NULL,
999 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
1000 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK};
1002 static struct headerentry he_custom_free_templ={
1003 NULL, NULL, h_composer_custom_free,10, 0, NULL,
1004 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1005 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE};
1008 /*----------------------------------------------------------------------
1009 Get addressee for message, then post message
1011 Args: outgoing -- Partially formatted outgoing ENVELOPE
1012 body -- Body of outgoing message
1013 prmpt_who -- Optional prompt for optionally_enter call
1014 prmpt_cnf -- Optional prompt for confirmation call
1015 used_tobufval -- The string that the to was eventually set equal to.
1016 This gets passed back if non-NULL on entry.
1017 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1018 SS_NULLRP - Use null return-path so we'll send an
1019 SMTP MAIL FROM: <>
1021 Result: message "To: " field is provided and message is sent or cancelled.
1023 Fields:
1024 remail -
1025 return_path -
1026 date added here
1027 from added here
1028 sender -
1029 reply_to -
1030 subject passed in, NOT edited but maybe canonized here
1031 to possibly passed in, edited and canonized here
1032 cc -
1033 bcc -
1034 in_reply_to -
1035 message_id -
1037 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1038 with the first part TYPETEXT! All newlines in the text here also end with
1039 CRLF.
1041 Returns 0 on success, -1 on failure.
1042 ----*/
1044 pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */
1045 struct mail_bodystruct **body,
1046 ACTION_S **rolep,
1047 char *prmpt_who,
1048 char *prmpt_cnf,
1049 char **used_tobufval,
1050 int flagsarg)
1052 char **tobufp, *p, tmp[MAILTMPLEN];
1053 void *messagebuf;
1054 int done = 0, retval = 0, x;
1055 int lastrc, rc = 0, ku, i, resize_len, result, fcc_result;
1056 int og2s_done = 0;
1057 HelpType help;
1058 static HISTORY_S *history = NULL;
1059 ESCKEY_S ekey[5];
1060 BUILDER_ARG ba_fcc;
1061 METAENV *header;
1062 ACTION_S *role = rolep ? *rolep : NULL;
1063 PAT_STATE pstate;
1065 dprint((1,"\n === simple send called === \n"));
1067 memset(&ba_fcc, 0, sizeof(BUILDER_ARG));
1069 init_hist(&history, HISTSIZE);
1071 header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp);
1073 /*----- Fill in a few general parts of the envelope ----*/
1074 if(!outgoing->date){
1075 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1076 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
1078 rfc822_date(tmp_20k_buf); /* format and copy new date */
1079 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1080 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
1082 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
1085 if(!outgoing->from){
1086 if(role && role->from){
1087 if(ps_global->never_allow_changing_from)
1088 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1089 else
1090 outgoing->from = copyaddrlist(role->from);
1092 else
1093 outgoing->from = generate_from();
1096 if(!(flagsarg & SS_NULLRP))
1097 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1099 ekey[i = 0].ch = ctrl('T');
1100 ekey[i].rval = 2;
1101 ekey[i].name = "^T";
1102 ekey[i++].label = N_("To AddrBk");
1104 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1105 ekey[i].ch = ctrl('I');
1106 ekey[i].rval = 11;
1107 ekey[i].name = "TAB";
1108 ekey[i++].label = N_("Complete");
1111 if(nonempty_patterns(ROLE_DO_ROLES, &pstate) && first_pattern(&pstate)){
1112 ekey[i].ch = ctrl('R');
1113 ekey[i].rval = 15;
1114 ekey[i].name = "^R";
1115 ekey[i++].label = "Set Role";
1118 ekey[i].ch = KEY_UP;
1119 ekey[i].rval = 30;
1120 ekey[i].name = "";
1121 ku = i;
1122 ekey[i++].label = "";
1124 ekey[i].ch = KEY_DOWN;
1125 ekey[i].rval = 31;
1126 ekey[i].name = "";
1127 ekey[i++].label = "";
1129 ekey[i].ch = -1;
1131 if(outgoing->remail == NULL)
1132 strcpy(tmp, _("FORWARD (as e-mail) to : "));
1134 /*----------------------------------------------------------------------
1135 Loop editing the "To: " field until everything goes well
1136 ----*/
1137 help = NO_HELP;
1139 while(!done){
1140 int flags;
1142 if(outgoing->remail){
1143 if(role)
1144 snprintf(tmp, sizeof(tmp), _("BOUNCE (redirect) message using role \"%s\" to : "), role->nick);
1145 else
1146 strncpy(tmp, _("BOUNCE (redirect) message to : "), sizeof(tmp));
1147 tmp[sizeof(tmp)-1] = '\0';
1150 if(!og2s_done){
1151 og2s_done++;
1152 outgoing2strings(header, *body, &messagebuf, NULL, 1);
1155 lastrc = rc;
1156 if(flagsarg & SS_PROMPTFORTO){
1157 if(!*tobufp)
1158 *tobufp = cpystr("");
1160 resize_len = MAX(MAXPATH, strlen(*tobufp));
1161 fs_resize((void **) tobufp, resize_len+1);
1163 if(items_in_hist(history) > 0){
1164 ekey[ku].name = HISTORY_UP_KEYNAME;
1165 ekey[ku].label = HISTORY_KEYLABEL;
1166 ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
1167 ekey[ku+1].label = HISTORY_KEYLABEL;
1169 else{
1170 ekey[ku].name = "";
1171 ekey[ku].label = "";
1172 ekey[ku+1].name = "";
1173 ekey[ku+1].label = "";
1176 flags = OE_APPEND_CURRENT;
1178 rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
1179 0, resize_len,
1180 prmpt_who
1181 ? prmpt_who
1182 : tmp,
1183 ekey, help, &flags);
1185 else
1186 rc = 0;
1188 switch(rc){
1189 case -1:
1190 q_status_message(SM_ORDER | SM_DING, 3, 4,
1191 "Internal problem encountered");
1192 retval = -1;
1193 done++;
1194 break;
1196 case 15 : /* set a role */
1197 {void (*prev_screen)(struct pine *) = NULL, (*redraw)(void) = NULL;
1199 redraw = ps_global->redrawer;
1200 ps_global->redrawer = NULL;
1201 prev_screen = ps_global->prev_screen;
1202 role = NULL;
1203 ps_global->next_screen = SCREEN_FUN_NULL;
1205 if(role_select_screen(ps_global, &role,
1206 outgoing->remail ? MC_BOUNCE : MC_FORWARD) < 0)
1207 cmd_cancelled(_("Set Role"));
1208 else{
1209 if(role)
1210 role = combine_inherited_role(role);
1211 else{
1212 role = (ACTION_S *) fs_get(sizeof(*role));
1213 memset((void *) role, 0, sizeof(*role));
1214 role->nick = cpystr("Default Role");
1218 if(redraw)
1219 (*redraw)();
1221 ps_global->next_screen = prev_screen;
1222 ps_global->redrawer = redraw;
1223 ps_global->mangled_screen = 1;
1225 if(role && role->from && !ps_global->never_allow_changing_from){
1226 mail_free_address (&outgoing->from);
1227 outgoing->from = copyaddrlist(role->from);
1229 if(rolep) *rolep = role;
1231 break;
1233 case 30 :
1234 if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){
1235 strncpy(*tobufp, p, resize_len);
1236 (*tobufp)[resize_len-1] = '\0';
1238 else
1239 Writechar(BELL, 0);
1241 break;
1243 case 31 :
1244 if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){
1245 strncpy(*tobufp, p, resize_len);
1246 (*tobufp)[resize_len-1] = '\0';
1248 else
1249 Writechar(BELL, 0);
1251 break;
1253 case 2: /* ^T */
1254 case 0:
1255 {void (*redraw) (void) = ps_global->redrawer;
1256 char *returned_addr = NULL;
1257 int len, l;
1259 if(rc == 2){
1260 int got_something = 0;
1262 push_titlebar_state();
1263 returned_addr = addr_book_bounce();
1266 * Just make it look like user typed this list in.
1268 if(returned_addr){
1269 got_something++;
1270 if((l=resize_len) < (len = strlen(returned_addr)) + 1){
1271 l = len;
1272 fs_resize((void **) tobufp, (size_t) (l+1));
1275 strncpy(*tobufp, returned_addr, l);
1276 (*tobufp)[l] = '\0';
1277 fs_give((void **)&returned_addr);
1280 ClearScreen();
1281 pop_titlebar_state();
1282 redraw_titlebar();
1283 if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
1284 (*ps_global->redrawer)();
1286 if(!got_something)
1287 continue;
1290 if(*tobufp && **tobufp != '\0'){
1291 char *errbuf, *addr;
1292 int tolen;
1294 save_hist(history, *tobufp, 0, NULL);
1296 errbuf = NULL;
1299 * If role has an fcc, use it instead of what build_address
1300 * tells us.
1302 if(role && role->fcc){
1303 if(ba_fcc.tptr)
1304 fs_give((void **) &ba_fcc.tptr);
1306 ba_fcc.tptr = cpystr(role->fcc);
1309 if(build_address(*tobufp, &addr, &errbuf,
1310 (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){
1311 int sendit = 0;
1313 if(errbuf)
1314 fs_give((void **)&errbuf);
1316 if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){
1317 l = tolen;
1318 fs_resize((void **) tobufp, (size_t) (l+1));
1321 strncpy(*tobufp, addr, l);
1322 (*tobufp)[l] = '\0';
1323 if(used_tobufval)
1324 *used_tobufval = cpystr(addr);
1326 /* confirm address */
1327 if(flagsarg & SS_PROMPTFORTO){
1328 char dsn_string[30];
1329 int dsn_label = 0, dsn_show, i;
1330 int verbose_label = 0;
1331 ESCKEY_S opts[13];
1333 strings2outgoing(header, body, NULL, 0);
1335 if((flagsarg & SS_PROMPTFORTO)
1336 && ((x = check_addresses(header)) == CA_BAD
1337 || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE,
1338 ps_global))))
1339 /*--- Addresses didn't check out---*/
1340 continue;
1342 i = 0;
1343 opts[i].ch = 'y';
1344 opts[i].rval = 'y';
1345 opts[i].name = "Y";
1346 opts[i++].label = N_("Yes");
1348 opts[i].ch = 'n';
1349 opts[i].rval = 'n';
1350 opts[i].name = "N";
1351 opts[i++].label = N_("No");
1353 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
1354 if(F_ON(F_VERBOSE_POST, ps_global)){
1355 /* setup keymenu slot to toggle verbose mode */
1356 opts[i].ch = ctrl('W');
1357 opts[i].rval = 12;
1358 opts[i].name = "^W";
1359 verbose_label = i++;
1360 if(F_ON(F_DSN, ps_global)){
1361 opts[i].ch = 0;
1362 opts[i].rval = 0;
1363 opts[i].name = "";
1364 opts[i++].label = "";
1368 /* clear DSN flags */
1369 call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL);
1370 if(F_ON(F_DSN, ps_global)){
1371 /* setup keymenu slots to toggle dsn bits */
1372 opts[i].ch = 'd';
1373 opts[i].rval = 'd';
1374 opts[i].name = "D";
1375 opts[i].label = "DSNOpts";
1376 dsn_label = i++;
1377 opts[i].ch = -2;
1378 opts[i].rval = 's';
1379 opts[i].name = "S";
1380 opts[i++].label = "";
1381 opts[i].ch = -2;
1382 opts[i].rval = 'x';
1383 opts[i].name = "X";
1384 opts[i++].label = "";
1385 opts[i].ch = -2;
1386 opts[i].rval = 'h';
1387 opts[i].name = "H";
1388 opts[i++].label = "";
1391 opts[i].ch = -1;
1393 while(1){
1394 int rv;
1396 dsn_show = (call_mailer_flags & CM_DSN_SHOW);
1397 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
1398 "%s%s%s%s%s%sto \"%s\" ? ",
1399 prmpt_cnf ? prmpt_cnf : "Send message ",
1400 ((call_mailer_flags & CM_VERBOSE)
1401 || (dsn_show))
1402 ? "(" : "",
1403 (call_mailer_flags & CM_VERBOSE)
1404 ? "in verbose mode" : "",
1405 (dsn_show && (call_mailer_flags & CM_VERBOSE))
1406 ? ", " : "",
1407 (dsn_show) ? dsn_string : "",
1408 ((call_mailer_flags & CM_VERBOSE) || dsn_show)
1409 ? ") " : "",
1410 (addr && *addr)
1411 ? addr
1412 : (F_ON(F_FCC_ON_BOUNCE, ps_global)
1413 && ba_fcc.tptr && ba_fcc.tptr[0])
1414 ? ba_fcc.tptr
1415 : "");
1416 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1418 if((strlen(tmp_20k_buf) >
1419 ps_global->ttyo->screen_cols - 2) &&
1420 ps_global->ttyo->screen_cols >= 7)
1421 strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7,
1422 "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7);
1424 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1426 if(verbose_label)
1427 opts[verbose_label].label =
1428 /* TRANSLATORS: several possible key labels follow */
1429 (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
1431 if(F_ON(F_DSN, ps_global)){
1432 if(call_mailer_flags & CM_DSN_SHOW){
1433 opts[dsn_label].label =
1434 (call_mailer_flags & CM_DSN_DELAY)
1435 ? N_("NoDelay") : N_("Delay");
1436 opts[dsn_label+1].ch = 's';
1437 opts[dsn_label+1].label =
1438 (call_mailer_flags & CM_DSN_SUCCESS)
1439 ? N_("NoSuccess") : N_("Success");
1440 opts[dsn_label+2].ch = 'x';
1441 opts[dsn_label+2].label =
1442 (call_mailer_flags & CM_DSN_NEVER)
1443 ? N_("ErrRets") : N_("NoErrRets");
1444 opts[dsn_label+3].ch = 'h';
1445 opts[dsn_label+3].label =
1446 (call_mailer_flags & CM_DSN_FULL)
1447 ? N_("RetHdrs") : N_("RetFull");
1451 rv = radio_buttons(tmp_20k_buf,
1452 -FOOTER_ROWS(ps_global), opts,
1453 'y', 'z', NO_HELP, RB_NORM);
1454 if(rv == 'y'){ /* user ACCEPTS! */
1455 sendit = 1;
1456 break;
1458 else if(rv == 'n'){ /* Declined! */
1459 break;
1461 else if(rv == 'z'){ /* Cancelled! */
1462 break;
1464 else if(rv == 12){ /* flip verbose bit */
1465 if(call_mailer_flags & CM_VERBOSE)
1466 call_mailer_flags &= ~CM_VERBOSE;
1467 else
1468 call_mailer_flags |= CM_VERBOSE;
1470 else if(call_mailer_flags & CM_DSN_SHOW){
1471 if(rv == 's'){ /* flip success bit */
1472 call_mailer_flags ^= CM_DSN_SUCCESS;
1473 /* turn off related bits */
1474 if(call_mailer_flags & CM_DSN_SUCCESS)
1475 call_mailer_flags &= ~(CM_DSN_NEVER);
1477 else if(rv == 'd'){ /* flip delay bit */
1478 call_mailer_flags ^= CM_DSN_DELAY;
1479 /* turn off related bits */
1480 if(call_mailer_flags & CM_DSN_DELAY)
1481 call_mailer_flags &= ~(CM_DSN_NEVER);
1483 else if(rv == 'x'){ /* flip never bit */
1484 call_mailer_flags ^= CM_DSN_NEVER;
1485 /* turn off related bits */
1486 if(call_mailer_flags & CM_DSN_NEVER)
1487 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
1489 else if(rv == 'h'){ /* flip full bit */
1490 call_mailer_flags ^= CM_DSN_FULL;
1493 else if(rv == 'd'){ /* show dsn options */
1494 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
1497 snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"),
1498 (call_mailer_flags & CM_DSN_NEVER)
1499 ? _("Never") : "F",
1500 (call_mailer_flags & CM_DSN_DELAY)
1501 ? "D" : "",
1502 (call_mailer_flags & CM_DSN_SUCCESS)
1503 ? "S" : "",
1504 (call_mailer_flags & CM_DSN_NEVER)
1505 ? ""
1506 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
1507 : "-Hdrs");
1508 dsn_string[sizeof(dsn_string)-1] = '\0';
1512 if(addr)
1513 fs_give((void **)&addr);
1515 if(!(flagsarg & SS_PROMPTFORTO) || sendit){
1516 char *fcc = NULL;
1517 CONTEXT_S *fcc_cntxt = NULL;
1519 if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
1520 if(ba_fcc.tptr)
1521 fcc = cpystr(ba_fcc.tptr);
1523 set_last_fcc(fcc);
1526 * If special name "inbox" then replace it with the
1527 * real inbox path.
1529 if(ps_global->VAR_INBOX_PATH
1530 && strucmp(fcc, ps_global->inbox_name) == 0){
1531 char *replace_fcc;
1533 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
1534 fs_give((void **) &fcc);
1535 fcc = replace_fcc;
1539 /*---- Check out fcc -----*/
1540 if(fcc && *fcc){
1541 (void) commence_fcc(fcc, &fcc_cntxt, FALSE);
1542 if(!lmc.so){
1543 dprint((4,"can't open fcc, cont\n"));
1544 if(!(flagsarg & SS_PROMPTFORTO)){
1545 retval = -1;
1546 fs_give((void **)&fcc);
1547 fcc = NULL;
1548 goto finish;
1550 else
1551 continue;
1553 else
1554 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
1556 else
1557 lmc.so = NULL;
1559 if(!(outgoing->to || outgoing->cc || outgoing->bcc
1560 || lmc.so)){
1561 q_status_message(SM_ORDER, 3, 5, _("No recipients specified!"));
1562 continue;
1565 if(outgoing->to || outgoing->cc || outgoing->bcc){
1566 char **alt_smtp = NULL;
1568 if(role && role->smtp){
1569 if(ps_global->FIX_SMTP_SERVER
1570 && ps_global->FIX_SMTP_SERVER[0])
1571 q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1572 else
1573 alt_smtp = role->smtp;
1576 result = call_mailer(header, *body, alt_smtp,
1577 call_mailer_flags,
1578 call_mailer_file_result,
1579 pipe_callback);
1580 mark_address_failure_for_pico(header);
1582 else
1583 result = 0;
1585 if(result == 1 && !lmc.so)
1586 q_status_message(SM_ORDER, 0, 3, _("Message sent"));
1588 /*----- Was there an fcc involved? -----*/
1589 if(lmc.so){
1590 if(result == 1
1591 || (result == 0
1592 && pine_rfc822_output(header, *body, NULL, NULL))){
1593 char label[50];
1595 strncpy(label, "Fcc", sizeof(label));
1596 label[sizeof(label)-1] = '\0';
1597 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
1598 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
1599 label[sizeof(label)-1] = '\0';
1602 /* Now actually copy to fcc folder and close */
1603 fcc_result =
1604 write_fcc(fcc, fcc_cntxt, lmc.so, NULL,
1605 label,
1606 F_ON(F_MARK_FCC_SEEN, ps_global)
1607 ? "\\SEEN" : NULL);
1609 else if(result == 0){
1610 q_status_message(SM_ORDER,3,5,
1611 _("Fcc Failed!. No message saved."));
1612 retval = -1;
1613 dprint((1, "explicit fcc write failed!\n"));
1616 so_give(&lmc.so);
1619 if(result < 0){
1620 dprint((1, "Bounce failed\n"));
1621 if(!(flagsarg & SS_PROMPTFORTO))
1622 retval = -1;
1623 else
1624 continue;
1626 else if(result == 1){
1627 if(!fcc)
1628 q_status_message(SM_ORDER, 0, 3,
1629 _("Message sent"));
1630 else{
1631 int avail = ps_global->ttyo->screen_cols-2;
1632 int need, fcclen;
1633 char *part1 = "Message sent and ";
1634 char *part2 = fcc_result ? "" : "NOT ";
1635 char *part3 = "copied to ";
1636 fcclen = strlen(fcc);
1638 need = 2 + strlen(part1) + strlen(part2) +
1639 strlen(part3) + fcclen;
1641 if(need > avail && fcclen > 6)
1642 fcclen -= MIN(fcclen-6, need-avail);
1644 q_status_message4(SM_ORDER, 0, 3,
1645 "%s%s%s\"%s\"",
1646 part1, part2, part3,
1647 short_str(fcc,
1648 (char *)tmp_20k_buf,
1649 SIZEOF_20KBUF,
1650 fcclen, FrontDots));
1654 if(fcc)
1655 fs_give((void **)&fcc);
1657 else{
1658 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1659 retval = -1;
1662 else{
1663 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1664 _("Error in address: %s"), errbuf);
1665 if(errbuf)
1666 fs_give((void **)&errbuf);
1668 if(!(flagsarg & SS_PROMPTFORTO))
1669 retval = -1;
1670 else
1671 continue;
1675 else{
1676 q_status_message(SM_ORDER | SM_DING, 3, 5,
1677 _("No addressee! No e-mail sent."));
1678 retval = -1;
1682 done++;
1683 break;
1685 case 1:
1686 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1687 done++;
1688 retval = -1;
1689 break;
1691 case 3:
1692 help = (help == NO_HELP)
1693 ? (outgoing->remail == NULL
1694 ? h_anon_forward
1695 : h_bounce)
1696 : NO_HELP;
1697 break;
1699 case 11:
1700 if(**tobufp){
1701 char *new_nickname = NULL;
1702 int l;
1703 int ambiguity;
1705 ambiguity = abook_nickname_complete(*tobufp, &new_nickname,
1706 (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA);
1707 if(new_nickname){
1708 if(*new_nickname){
1709 if((l=strlen(new_nickname)) > resize_len){
1710 resize_len = l;
1711 fs_resize((void **) tobufp, resize_len+1);
1714 strncpy(*tobufp, new_nickname, l);
1715 (*tobufp)[l] = '\0';
1718 fs_give((void **) &new_nickname);
1721 if(ambiguity != 2)
1722 Writechar(BELL, 0);
1725 break;
1727 case 4: /* can't suspend */
1728 default:
1729 break;
1733 finish:
1734 if(ba_fcc.tptr)
1735 fs_give((void **)&ba_fcc.tptr);
1737 pine_free_env(&header);
1739 return(retval);
1744 * pine_simple_send_header - generate header suitable for simple_sending
1746 METAENV *
1747 pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp)
1749 METAENV *header;
1750 PINEFIELD *pf;
1751 static struct headerentry he_dummy;
1753 header = pine_new_env(outgoing, fccp, tobufpp, NULL);
1755 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1756 for(pf = header->local; pf && pf->name; pf = pf->next)
1757 if(pf->type == Address && !strucmp(pf->name, "to")){
1758 memset((void *) &he_dummy, 0, sizeof(he_dummy));
1759 pf->extdata = (void *) &he_dummy;
1760 HE(pf)->dirty = 1;
1761 break;
1764 return(header);
1769 /*----------------------------------------------------------------------
1770 Prepare data structures for pico, call pico, then post message
1772 Args: outgoing -- Partially formatted outgoing ENVELOPE
1773 body -- Body of outgoing message
1774 editor_title -- Title for anchor line in composer
1775 fcc_arg -- The file carbon copy field
1776 reply -- Struct describing set of msgs being replied-to
1777 lcc_arg --
1778 custom -- custom header list.
1779 sticky_fcc --
1781 Result: message is edited, then postponed, cancelled or sent.
1783 Fields:
1784 remail -
1785 return_path -
1786 date added here
1787 from added here
1788 sender -
1789 reply_to -
1790 subject passed in, edited and cannonized here
1791 to possibly passed in, edited and cannonized here
1792 cc possibly passed in, edited and cannonized here
1793 bcc edited and cannonized here
1794 in_reply_to generated in reply() and passed in
1795 message_id -
1797 Storage for these fields comes from anywhere outside. It is remalloced
1798 here so the composer can realloc them if needed. The copies here are also
1799 freed here.
1801 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1802 with the first part TYPETEXT! All newlines in the text here also end with
1803 CRLF.
1805 There's a further assumption that the text in the TYPETEXT part is
1806 stored in a storage object (see filter.c).
1807 ----*/
1808 void
1809 pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
1810 char *editor_title, ACTION_S *role, char *fcc_arg,
1811 REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg,
1812 PINEFIELD *custom, int flags)
1814 int i, fixed_cnt, total_cnt, index,
1815 editor_result = 0, body_start = 0, use_news_order = 0;
1816 char *p, *addr, *fcc, *fcc_to_free = NULL;
1817 char *start_here_name = NULL;
1818 char *suggested_nntp_server = NULL;
1819 char *title = NULL;
1820 struct headerentry *he, *headents, *he_to, *he_fcc, *he_news = NULL, *he_lcc = NULL,
1821 *he_from = NULL;
1822 PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL,
1823 *pf_smtp_server, *pf_nntp_server,
1824 *pf_fcc = NULL, *pf_err, *pf_uid, *pf_mbox, *pf_curpos,
1825 *pf_ourrep, *pf_ourhdrs, **sending_order;
1826 METAENV header;
1827 ADDRESS *lcc_addr = NULL;
1828 ADDRESS *nobody_addr = NULL;
1829 BODY_PARTICULARS_S *bp;
1830 STORE_S *orig_so = NULL;
1831 PICO pbuf1, *save_previous_pbuf;
1832 CustomType ct;
1833 REDRAFT_POS_S *local_redraft_pos = NULL;
1835 dprint((1,"\n=== send called ===\n"));
1837 save_previous_pbuf = pbf;
1838 pbf = &pbuf1;
1839 standard_picobuf_setup(pbf);
1842 * Cancel any pending initial commands since pico uses a different
1843 * input routine. If we didn't cancel them, they would happen after
1844 * we returned from the editor, which would be confusing.
1846 if(ps_global->in_init_seq){
1847 ps_global->in_init_seq = 0;
1848 ps_global->save_in_init_seq = 0;
1849 clear_cursor_pos();
1850 if(ps_global->initial_cmds){
1851 if(ps_global->free_initial_cmds)
1852 fs_give((void **)&(ps_global->free_initial_cmds));
1854 ps_global->initial_cmds = 0;
1857 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1860 #if defined(DOS) || defined(OS2)
1861 if(!dos_valid_from()){
1862 pbf = save_previous_pbuf;
1863 return;
1866 pbf->upload = NULL;
1867 #else
1868 pbf->upload = (ps_global->VAR_UPLOAD_CMD
1869 && ps_global->VAR_UPLOAD_CMD[0])
1870 ? upload_msg_to_pico : NULL;
1871 #endif
1873 pbf->msgntext = message_format_for_pico;
1874 pbf->mimetype = mime_type_for_pico;
1875 pbf->exittest = send_exit_for_pico;
1876 pbf->user_says_noflow = dont_flow_this_time;
1877 pbf->newthread = new_thread_on_blank_subject;
1878 ps_global->newthread = 0; /* reset this value */
1879 if(F_OFF(F_CANCEL_CONFIRM, ps_global))
1880 pbf->canceltest = cancel_for_pico;
1881 #ifdef _WINDOWS
1882 pbf->dict = (ps_global->VAR_DICTIONARY
1883 && ps_global->VAR_DICTIONARY[0]
1884 && ps_global->VAR_DICTIONARY[0][0])
1885 ? ps_global->VAR_DICTIONARY : NULL;
1886 pbf->chosen_dict = -1; /* not chosen yet */
1887 #endif /* _WINDOWS */
1888 pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] &&
1889 ps_global->VAR_EDITOR[0][0])
1890 ? ps_global->VAR_EDITOR : NULL;
1891 pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0])
1892 ? ps_global->VAR_SPELLER : NULL;
1893 pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
1894 pbf->quote_str = reply && reply->prefix ? reply->prefix : "> ";
1895 /* We actually want to set this only if message we're sending is flowed */
1896 pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
1897 pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
1898 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
1899 && (strcmp(pbf->quote_str, "> ") == 0
1900 || strcmp(pbf->quote_str, ">") == 0));
1901 pbf->edit_offset = 0;
1902 title = cpystr(set_titlebar(editor_title,
1903 ps_global->mail_stream,
1904 ps_global->context_current,
1905 ps_global->cur_folder,ps_global->msgmap,
1906 0, FolderName, 0, 0, NULL));
1907 pbf->pine_anchor = title;
1909 #if defined(DOS) || defined(OS2)
1910 if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){
1911 pbf->oper_dir = ps_global->VAR_FILE_DIR;
1913 #endif
1915 if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE))
1916 pbf->pine_flags |= P_CHKPTNOW;
1918 /* NOTE: initial cursor position set below */
1920 dprint((9, "flags: %x\n", pbf->pine_flags));
1923 * When user runs compose and the current folder is a newsgroup,
1924 * offer to post to the current newsgroup.
1926 if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups))
1927 && IS_NEWS(ps_global->mail_stream)){
1928 char prompt[200], news_group[MAILTMPLEN];
1930 pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group,
1931 sizeof(news_group));
1934 * Replies don't get this far because To or Newsgroups will already
1935 * be filled in. So must be either ordinary compose or forward.
1936 * Forward sets subject, so use that to tell the difference.
1938 if(news_group[0] && !outgoing->subject){
1939 int ch = 'y';
1940 int ret_val;
1941 char *errmsg = NULL;
1942 BUILDER_ARG *fcc_build = NULL;
1944 if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
1945 snprintf(prompt, sizeof(prompt),
1946 _("Post to current newsgroup (%s)"), news_group);
1947 prompt[sizeof(prompt)-1] = '\0';
1948 ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM);
1951 switch(ch){
1952 case 'y':
1953 if(outgoing->newsgroups)
1954 fs_give((void **)&outgoing->newsgroups);
1956 if(!fcc_arg && !(role && role->fcc)){
1957 fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
1958 memset((void *)fcc_build, 0, sizeof(BUILDER_ARG));
1959 fcc_build->tptr = fcc_to_free;
1962 ret_val = news_build(news_group, &outgoing->newsgroups,
1963 &errmsg, fcc_build, NULL);
1965 if(ret_val == -1){
1966 if(outgoing->newsgroups)
1967 fs_give((void **)&outgoing->newsgroups);
1969 outgoing->newsgroups = cpystr(news_group);
1972 if(!fcc_arg && !(role && role->fcc)){
1973 fcc_arg = fcc_to_free = fcc_build->tptr;
1974 fs_give((void **)&fcc_build);
1977 if(errmsg){
1978 if(*errmsg){
1979 q_status_message(SM_ORDER, 3, 3, errmsg);
1980 display_message(NO_OP_COMMAND);
1983 fs_give((void **)&errmsg);
1986 break;
1988 case 'x': /* ^C */
1989 q_status_message(SM_ORDER, 0, 3, _("Message cancelled"));
1990 dprint((4, "=== send: cancelled\n"));
1991 pbf = save_previous_pbuf;
1992 return;
1994 case 'n':
1995 break;
1997 default:
1998 break;
2002 if(F_ON(F_PREDICT_NNTP_SERVER, ps_global)
2003 && outgoing->newsgroups && *outgoing->newsgroups
2004 && IS_NEWS(ps_global->mail_stream)){
2005 NETMBX news_mb;
2007 if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox,
2008 &news_mb))
2009 if(!strucmp(news_mb.service, "nntp")){
2010 if(*ps_global->mail_stream->original_mailbox == '{'){
2011 char *svcp = NULL, *psvcp;
2013 suggested_nntp_server =
2014 cpystr(ps_global->mail_stream->original_mailbox + 1);
2015 if((p = strindex(suggested_nntp_server, '}')) != NULL)
2016 *p = '\0';
2017 for(p = strindex(suggested_nntp_server, '/'); p && *p;
2018 p = strindex(p, '/')){
2019 /* take out /nntp, which gets added in nntp_open */
2020 if(!struncmp(p, "/nntp", 5))
2021 svcp = p + 5;
2022 else if(!struncmp(p, "/service=nntp", 13))
2023 svcp = p + 13;
2024 else if(!struncmp(p, "/service=\"nntp\"", 15))
2025 svcp = p + 15;
2026 else
2027 p++;
2028 if(svcp){
2029 if(*svcp == '\0')
2030 *p = '\0';
2031 else if(*svcp == '/' || *svcp == ':'){
2032 for(psvcp = p; *svcp; svcp++, psvcp++)
2033 *psvcp = *svcp;
2034 *psvcp = '\0';
2036 svcp = NULL;
2040 else
2041 suggested_nntp_server = cpystr(news_mb.orighost);
2046 * If we don't already have custom headers set and the role has custom
2047 * headers, then incorporate those custom headers into "custom".
2049 if(!custom){
2050 PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL;
2052 dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
2054 * If we allow the Combine argument here, we're saying that we want to
2055 * combine the values from the envelope and the role for the fields To,
2056 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
2057 * we'll have a To in the envelope because we're replying. If our role also
2058 * has a To action, then Combine would combine those two and offer both
2059 * to the user. We've decided against doing this. Instead, we always use
2060 * Replace, and the role's header value replaces the value from the
2061 * envelope. It might also make sense in some cases to do the opposite,
2062 * which would be treating the role headers as defaults, just like
2063 * customized-hdrs.
2065 #ifdef WANT_TO_COMBINE_ADDRESSES
2066 if(role && role->cstm)
2067 rolehdrs = parse_custom_hdrs(role->cstm, Combine);
2068 #else
2069 if(role && role->cstm)
2070 rolehdrs = parse_custom_hdrs(role->cstm, Replace);
2071 #endif
2073 if(rolehdrs){
2074 custom = combine_custom_headers(dflthdrs, rolehdrs);
2075 if(dflthdrs){
2076 free_prompts(dflthdrs);
2077 free_customs(dflthdrs);
2080 if(rolehdrs){
2081 free_prompts(rolehdrs);
2082 free_customs(rolehdrs);
2085 else
2086 custom = dflthdrs;
2089 g_rolenick = role ? role->nick : NULL;
2091 /* how many fixed fields are there? */
2092 for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++)
2095 total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1);
2097 /* the fixed part of the PINEFIELDs */
2098 i = fixed_cnt * sizeof(PINEFIELD);
2099 pfields = (PINEFIELD *)fs_get((size_t) i);
2100 memset(pfields, 0, (size_t) i);
2102 /* temporary headerentry array for pico */
2103 i = (total_cnt + 1) * sizeof(struct headerentry);
2104 headents = (struct headerentry *)fs_get((size_t) i);
2105 memset(headents, 0, (size_t) i);
2107 i = total_cnt * sizeof(PINEFIELD *);
2108 sending_order = (PINEFIELD **)fs_get((size_t) i);
2109 memset(sending_order, 0, (size_t) i);
2111 pbf->headents = headents;
2112 header.env = outgoing;
2113 header.local = pfields;
2114 header.sending_order = sending_order;
2116 /* custom part of PINEFIELDs */
2117 header.custom = custom;
2119 he = headents;
2120 pf = pfields;
2123 * For Address types, pf->addr points to an ADDRESS *.
2124 * If that address is in the "outgoing" envelope, it will
2125 * be freed by the caller, otherwise, it should be freed here.
2126 * Pf->textbuf for an Address is used a little to set up a default,
2127 * but then is freed right away below. Pf->scratch is used for a
2128 * pointer to some alloced space for pico to edit in. Addresses in
2129 * the custom area are freed by free_customs().
2131 * For FreeText types, pf->addr is not used. Pf->text points to a
2132 * pointer that points to the text. Pf->textbuf points to a copy of
2133 * the text that must be freed before we leave, otherwise, it is
2134 * probably a pointer into the envelope and that gets freed by the
2135 * caller.
2137 * He->realaddr is the pointer to the text that pico actually edits.
2140 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2141 # define NN 4
2142 #else
2143 # define NN 3
2144 #endif
2146 if(outgoing->newsgroups && *outgoing->newsgroups)
2147 use_news_order++;
2149 /* initialize the fixed header elements of the two temp arrays */
2150 for(i=0; i < fixed_cnt; i++, pf++){
2151 static int news_order[] = {
2152 N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC,
2153 N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY,
2154 N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX,
2155 N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS
2156 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2157 , N_SENDER
2158 #endif
2161 index = i;
2162 /* slightly different editing order if sending to news */
2163 if(use_news_order &&
2164 index >= 0 && index < sizeof(news_order)/sizeof(news_order[0]))
2165 index = news_order[i];
2167 /* copy the templates */
2168 *he = he_template[index];
2170 pf->name = cpystr(pf_template[index].name);
2171 if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
2172 /* slide string over so it is Sender instead of X-X-Sender */
2173 for(p=pf->name; *(p+1); p++)
2174 *p = *(p+4);
2176 pf->type = pf_template[index].type;
2177 pf->canedit = pf_template[index].canedit;
2178 pf->rcptto = pf_template[index].rcptto;
2179 pf->writehdr = pf_template[index].writehdr;
2180 pf->localcopy = pf_template[index].localcopy;
2181 pf->extdata = he;
2182 pf->next = pf + 1;
2184 he->rich_header = view_as_rich(pf->name, he->rich_header);
2185 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2186 he->nickcmpl = NULL;
2188 switch(pf->type){
2189 case FreeText: /* realaddr points to c-client env */
2190 if(index == N_NEWS){
2191 sending_order[1] = pf;
2192 he->realaddr = &outgoing->newsgroups;
2193 he_news = he;
2195 switch(set_default_hdrval(pf, custom)){
2196 case Replace:
2197 if(*he->realaddr)
2198 fs_give((void **)he->realaddr);
2200 *he->realaddr = pf->textbuf;
2201 pf->textbuf = NULL;
2202 he->sticky = 1;
2203 break;
2205 case Combine:
2206 if(*he->realaddr){ /* combine values */
2207 if(pf->textbuf && *pf->textbuf){
2208 char *combined_hdr;
2209 size_t l;
2211 l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
2212 combined_hdr = (char *) fs_get((l+1) * sizeof(char));
2213 strncpy(combined_hdr, *he->realaddr, l);
2214 combined_hdr[l] = '\0';
2215 strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
2216 combined_hdr[l] = '\0';
2217 strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
2218 combined_hdr[l] = '\0';
2220 fs_give((void **)he->realaddr);
2221 *he->realaddr = combined_hdr;
2222 q_status_message(SM_ORDER, 3, 3,
2223 "Adding newsgroup from role");
2224 he->sticky = 1;
2227 else{
2228 *he->realaddr = pf->textbuf;
2229 pf->textbuf = NULL;
2232 break;
2234 case UseAsDef:
2235 /* if no value, use default */
2236 if(!*he->realaddr){
2237 *he->realaddr = pf->textbuf;
2238 pf->textbuf = NULL;
2241 break;
2243 case NoMatch:
2244 break;
2247 /* If there is a newsgroup, we'd better show it */
2248 if(outgoing->newsgroups && *outgoing->newsgroups)
2249 he->rich_header = 0; /* force on by default */
2251 if(pf->textbuf)
2252 fs_give((void **)&pf->textbuf);
2254 pf->text = he->realaddr;
2256 else if(index == N_DATE){
2257 sending_order[2] = pf;
2258 pf->text = (char **) &outgoing->date;
2259 pf->extdata = NULL;
2261 else if(index == N_INREPLY){
2262 sending_order[NN+9] = pf;
2263 pf->text = &outgoing->in_reply_to;
2264 pf->extdata = NULL;
2266 else if(index == N_MSGID){
2267 sending_order[NN+10] = pf;
2268 pf->text = &outgoing->message_id;
2269 pf->extdata = NULL;
2271 else if(index == N_REF){
2272 sending_order[NN+11] = pf;
2273 pf->text = &outgoing->references;
2274 pf->extdata = NULL;
2276 else if(index == N_PRIORITY){
2277 sending_order[NN+12] = pf;
2278 pf->text = &pf->textbuf;
2279 pf->extdata = NULL;
2281 else if(index == N_USERAGENT){
2282 sending_order[NN+13] = pf;
2283 pf->text = &pf->textbuf;
2284 pf->textbuf = generate_user_agent();
2285 pf->extdata = NULL;
2287 else if(index == N_POSTERR){
2288 sending_order[NN+14] = pf;
2289 pf_err = pf;
2290 pf->text = &pf->textbuf;
2291 pf->extdata = NULL;
2293 else if(index == N_RPLUID){
2294 sending_order[NN+15] = pf;
2295 pf_uid = pf;
2296 pf->text = &pf->textbuf;
2297 pf->extdata = NULL;
2299 else if(index == N_RPLMBOX){
2300 sending_order[NN+16] = pf;
2301 pf_mbox = pf;
2302 pf->text = &pf->textbuf;
2303 pf->extdata = NULL;
2305 else if(index == N_SMTP){
2306 sending_order[NN+17] = pf;
2307 pf_smtp_server = pf;
2308 pf->text = &pf->textbuf;
2309 pf->extdata = NULL;
2311 else if(index == N_NNTP){
2312 sending_order[NN+18] = pf;
2313 pf_nntp_server = pf;
2314 pf->text = &pf->textbuf;
2315 pf->extdata = NULL;
2317 else if(index == N_CURPOS){
2318 sending_order[NN+19] = pf;
2319 pf_curpos = pf;
2320 pf->text = &pf->textbuf;
2321 pf->extdata = NULL;
2323 else if(index == N_OURREPLYTO){
2324 sending_order[NN+20] = pf;
2325 pf_ourrep = pf;
2326 pf->text = &pf->textbuf;
2327 pf->extdata = NULL;
2329 else if(index == N_OURHDRS){
2330 sending_order[NN+21] = pf;
2331 pf_ourhdrs = pf;
2332 pf->text = &pf->textbuf;
2333 pf->extdata = NULL;
2335 else if(index == N_AUTHRCVD){
2336 sending_order[0] = pf;
2337 pf_ourhdrs = pf;
2338 pf->text = &pf->textbuf;
2339 pf->extdata = NULL;
2341 else{
2342 q_status_message(SM_ORDER | SM_DING, 3, 7,
2343 "Botched: Unmatched FreeText header in pine_send");
2346 break;
2348 /* can't do a default for this one */
2349 case Attachment:
2350 /* If there is an attachment already, we'd better show them */
2351 if(body && *body && (*body)->type != TYPETEXT)
2352 he->rich_header = 0; /* force on by default */
2354 break;
2356 case Address:
2357 switch(index){
2358 case N_FROM:
2359 sending_order[3] = pf;
2360 pf->addr = &outgoing->from;
2361 if(role && role->from){
2362 if(ps_global->never_allow_changing_from)
2363 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2364 else{
2365 outgoing->from = copyaddrlist(role->from);
2366 he->display_it = 1; /* show it */
2367 he->rich_header = 0;
2371 he_from = he;
2372 break;
2374 case N_TO:
2375 sending_order[NN+2] = pf;
2376 pf->addr = &outgoing->to;
2377 /* If already set, make it act like we typed it in */
2378 if(outgoing->to
2379 && outgoing->to->mailbox
2380 && outgoing->to->mailbox[0]
2381 && flags & PS_STICKY_TO)
2382 he->sticky = 1;
2384 he_to = he;
2385 pf_to = pf;
2386 break;
2388 case N_NOBODY:
2389 sending_order[NN+5] = pf;
2390 pf_nobody = pf;
2391 if(ps_global->VAR_EMPTY_HDR_MSG
2392 && !ps_global->VAR_EMPTY_HDR_MSG[0]){
2393 pf->addr = NULL;
2395 else{
2396 nobody_addr = mail_newaddr();
2397 nobody_addr->next = mail_newaddr();
2398 nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
2399 SIZEOF_20KBUF,
2400 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
2401 ? ps_global->VAR_EMPTY_HDR_MSG
2402 : "undisclosed-recipients"),
2403 ps_global->posting_charmap));
2404 pf->addr = &nobody_addr;
2407 break;
2409 case N_CC:
2410 sending_order[NN+3] = pf;
2411 pf->addr = &outgoing->cc;
2412 break;
2414 case N_BCC:
2415 sending_order[NN+4] = pf;
2416 pf->addr = &outgoing->bcc;
2417 /* if bcc exists, make sure it's exposed so nothing's
2418 * sent by mistake...
2420 if(outgoing->bcc)
2421 he->display_it = 1;
2423 break;
2425 case N_REPLYTO:
2426 sending_order[NN+1] = pf;
2427 pf->addr = &outgoing->reply_to;
2428 if(role && role->replyto){
2429 if(outgoing->reply_to)
2430 mail_free_address(&outgoing->reply_to);
2432 outgoing->reply_to = copyaddrlist(role->replyto);
2433 he->display_it = 1; /* show it */
2434 he->rich_header = 0;
2437 break;
2439 case N_LCC:
2440 sending_order[NN+7] = pf;
2441 pf->addr = &lcc_addr;
2442 he_lcc = he;
2443 if(lcc_arg){
2444 build_address(lcc_arg, &addr, NULL, NULL, NULL);
2445 rfc822_parse_adrlist(&lcc_addr, addr,
2446 ps_global->maildomain);
2447 fs_give((void **)&addr);
2448 he->display_it = 1;
2451 break;
2453 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2454 case N_SENDER:
2455 sending_order[4] = pf;
2456 pf->addr = &outgoing->sender;
2457 break;
2458 #endif
2460 default:
2461 q_status_message1(SM_ORDER,3,7,
2462 "Internal error: Address header %s", comatose(index));
2463 break;
2467 * If this is a reply to news, don't show the regular email
2468 * recipient headers (unless they are non-empty).
2470 if((outgoing->newsgroups && *outgoing->newsgroups)
2471 && (index == N_TO || index == N_CC
2472 || index == N_BCC || index == N_LCC)
2473 && (pf->addr && !*pf->addr)){
2474 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2475 pf->textbuf && *pf->textbuf){
2476 removing_trailing_white_space(pf->textbuf);
2477 (void)removing_double_quotes(pf->textbuf);
2478 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2479 rfc822_parse_adrlist(pf->addr, addr,
2480 ps_global->maildomain);
2481 fs_give((void **)&addr);
2482 if(ct > UseAsDef)
2483 he->sticky = 1;
2485 else
2486 he->rich_header = 1; /* hide */
2490 * If this address doesn't already have a value, then we check
2491 * for a default value assigned by the user.
2493 else if(pf->addr && !*pf->addr){
2494 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2495 (index != N_FROM ||
2496 (!ps_global->never_allow_changing_from &&
2497 F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
2498 pf->textbuf && *pf->textbuf){
2500 removing_trailing_white_space(pf->textbuf);
2501 (void)removing_double_quotes(pf->textbuf);
2504 * Try to set To based on Lcc. Don't attempt Fcc.
2506 if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
2507 BUILDER_ARG *barg = NULL;
2508 char *ppp = NULL;
2510 if(*pf_to->addr)
2511 ppp = addr_list_string(*pf_to->addr, NULL, 1);
2513 if(!ppp)
2514 ppp = cpystr("");
2516 barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
2517 memset(barg, 0, sizeof(*barg));
2518 barg->me = &(he->bldr_private);
2519 barg->aff = &(he_to->bldr_private);
2520 barg->tptr = cpystr(ppp);
2522 build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
2523 he->display_it = 1;
2525 rfc822_parse_adrlist(pf->addr, addr,
2526 ps_global->maildomain);
2527 if(addr)
2528 fs_give((void **) &addr);
2530 if(ct > UseAsDef)
2531 he->sticky = 1;
2533 if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
2534 ADDRESS *a = NULL;
2536 rfc822_parse_adrlist(&a, barg->tptr,
2537 ps_global->maildomain);
2538 if(a){
2539 if(pf_to->addr)
2540 mail_free_address(pf_to->addr);
2542 *pf_to->addr = a;
2546 if(barg){
2547 if(barg->tptr)
2548 fs_give((void **) &barg->tptr);
2550 fs_give((void **) &barg);
2553 if(ppp)
2554 fs_give((void **) &ppp);
2556 else{
2557 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2558 rfc822_parse_adrlist(pf->addr, addr,
2559 ps_global->maildomain);
2560 if(addr)
2561 fs_give((void **) &addr);
2563 if(ct > UseAsDef)
2564 he->sticky = 1;
2568 /* if we still don't have a from */
2569 if(index == N_FROM && !*pf->addr)
2570 *pf->addr = generate_from();
2574 * Addr is already set in the rest of the cases.
2576 else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
2577 ADDRESS *adr = NULL;
2580 * We get to this case of the ifelse if the from or reply-to
2581 * addr was set by a role above.
2584 /* figure out the default value */
2585 (void)set_default_hdrval(pf, custom);
2586 if(pf->textbuf && *pf->textbuf){
2587 removing_trailing_white_space(pf->textbuf);
2588 (void)removing_double_quotes(pf->textbuf);
2589 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2590 rfc822_parse_adrlist(&adr, addr,
2591 ps_global->maildomain);
2592 fs_give((void **)&addr);
2595 /* if value set by role is different from default, show it */
2596 if(adr && !address_is_same(*pf->addr, adr))
2597 he->display_it = 1; /* start this off showing */
2599 /* malformed */
2600 if(!(*pf->addr)->mailbox){
2601 fs_give((void **)pf->addr);
2602 he->display_it = 1;
2605 if(adr)
2606 mail_free_address(&adr);
2608 else if((index == N_TO || index == N_CC || index == N_BCC)
2609 && pf->addr){
2610 ADDRESS *a = NULL, **tail;
2613 * These three are different from the others because we
2614 * might add the addresses to what is already there instead
2615 * of replacing.
2618 switch(set_default_hdrval(pf, custom)){
2619 case Replace:
2620 if(*pf->addr)
2621 mail_free_address(pf->addr);
2623 removing_trailing_white_space(pf->textbuf);
2624 (void)removing_double_quotes(pf->textbuf);
2625 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2626 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2627 fs_give((void **)&addr);
2628 he->sticky = 1;
2629 break;
2631 case Combine:
2632 removing_trailing_white_space(pf->textbuf);
2633 (void)removing_double_quotes(pf->textbuf);
2634 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2635 rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
2636 fs_give((void **)&addr);
2637 he->sticky = 1;
2638 if(a){
2639 for(tail = pf->addr; *tail; tail = &(*tail)->next)
2641 *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
2642 *pf->addr, NULL, a, RCA_ALL);
2643 q_status_message(SM_ORDER, 3, 3,
2644 "Adding addresses from role");
2645 mail_free_address(&a);
2648 break;
2650 case UseAsDef:
2651 case NoMatch:
2652 break;
2655 he->display_it = 1; /* start this off showing */
2657 else if(pf->addr){
2658 switch(set_default_hdrval(pf, custom)){
2659 case Replace:
2660 case Combine:
2661 if(*pf->addr)
2662 mail_free_address(pf->addr);
2664 removing_trailing_white_space(pf->textbuf);
2665 (void)removing_double_quotes(pf->textbuf);
2666 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2667 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2668 fs_give((void **)&addr);
2669 he->sticky = 1;
2670 break;
2672 case UseAsDef:
2673 case NoMatch:
2674 break;
2677 he->display_it = 1;
2680 if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
2681 mail_free_address(pf->addr);
2682 he->display_it = 1; /* start this off showing */
2685 if(pf->textbuf) /* free default value in any case */
2686 fs_give((void **)&pf->textbuf);
2688 /* outgoing2strings will alloc the string pf->scratch below */
2689 he->realaddr = &pf->scratch;
2690 break;
2692 case Fcc:
2693 sending_order[NN+8] = pf;
2694 pf_fcc = pf;
2695 if(role && role->fcc)
2696 fcc = role->fcc;
2697 else
2698 fcc = get_fcc(fcc_arg);
2700 if(fcc_to_free){
2701 fs_give((void **)&fcc_to_free);
2702 fcc_arg = NULL;
2705 if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
2706 he->sticky = 1;
2708 if(role)
2709 role->fcc = NULL;
2711 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
2712 he->display_it = 1; /* start this off showing */
2714 he->realaddr = &fcc;
2715 pf->text = &fcc;
2716 he_fcc = he;
2717 break;
2719 case Subject :
2720 sending_order[NN+6] = pf;
2722 switch(set_default_hdrval(pf, custom)){
2723 case Replace:
2724 case Combine:
2725 pf->scratch = pf->textbuf;
2726 pf->textbuf = NULL;
2727 he->sticky = 1;
2728 if(outgoing->subject)
2729 fs_give((void **)&outgoing->subject);
2731 break;
2733 case UseAsDef:
2734 case NoMatch:
2735 /* if no value, use default */
2736 if(outgoing->subject){
2737 pf->scratch = cpystr(outgoing->subject);
2739 else{
2740 pf->scratch = pf->textbuf;
2741 pf->textbuf = NULL;
2744 break;
2747 he->realaddr = &pf->scratch;
2748 pf->text = &outgoing->subject;
2749 break;
2751 default:
2752 q_status_message1(SM_ORDER,3,7,
2753 "Unknown header type %d in pine_send",
2754 (void *)pf->type);
2755 break;
2759 * We may or may not want to give the user the chance to edit
2760 * the From and Reply-To lines. If they are listed in either
2761 * Default-composer-hdrs or Customized-hdrs, then they can edit
2762 * them, else no.
2763 * If canedit is not set, that means that this header is not in
2764 * the user's customized-hdrs. If rich_header is set, that
2765 * means that this header is not in the user's
2766 * default-composer-hdrs (since From and Reply-To are rich
2767 * by default). So, don't give it an he to edit with in that case.
2769 * For other types, just not setting canedit will cause it to be
2770 * uneditable, regardless of what the user does.
2772 switch(index){
2773 case N_FROM:
2774 /* to allow it, we let this fall through to the reply-to case below */
2775 if(ps_global->never_allow_changing_from ||
2776 (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
2777 !(role && role->from))){
2778 if(pf->canedit || !he->rich_header)
2779 q_status_message(SM_ORDER, 3, 3,
2780 _("Not allowed to change header \"From\""));
2782 memset(he, 0, (size_t)sizeof(*he));
2783 pf->extdata = NULL;
2784 break;
2787 case N_REPLYTO:
2788 if(!pf->canedit && he->rich_header){
2789 memset(he, 0, (size_t)sizeof(*he));
2790 pf->extdata = NULL;
2792 else{
2793 pf->canedit = 1;
2794 he++;
2797 break;
2799 default:
2800 if(!pf->canedit){
2801 memset(he, 0, (size_t)sizeof(*he));
2802 pf->extdata = NULL;
2804 else
2805 he++;
2807 break;
2812 * This is so the builder can tell the composer to fill the affected
2813 * field based on the value in the field on the left.
2815 * Note that this mechanism isn't completely general. Each entry has
2816 * only a single next_affected, so if some other entry points an
2817 * affected entry at an entry with a next_affected, they all inherit
2818 * that next_affected. Since this isn't used much a careful ordering
2819 * of the affected fields should make it a sufficient mechanism.
2821 he_to->affected_entry = he_fcc;
2822 he_news->affected_entry = he_fcc;
2823 he_lcc->affected_entry = he_to;
2824 he_to->next_affected = he_fcc;
2826 (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
2828 i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2830 * Set up headerentries for custom fields.
2831 * NOTE: "i" is assumed to now index first custom field in sending
2832 * order.
2834 for(pf = pf->next; pf && pf->name; pf = pf->next){
2835 char *addr;
2837 if(pf->standard)
2838 continue;
2840 pf->extdata = he;
2841 pf->canedit = 1;
2842 pf->rcptto = 0;
2843 pf->writehdr = 1;
2844 pf->localcopy = 1;
2846 switch(pf->type){
2847 case Address:
2848 if(pf->addr){ /* better be set */
2849 sending_order[i++] = pf;
2850 *he = he_custom_addr_templ;
2851 /* change default text into an ADDRESS */
2852 /* strip quotes around whole default */
2853 removing_trailing_white_space(pf->textbuf);
2854 (void)removing_double_quotes(pf->textbuf);
2855 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2856 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2857 fs_give((void **)&addr);
2858 if(pf->textbuf)
2859 fs_give((void **)&pf->textbuf);
2861 he->realaddr = &pf->scratch;
2862 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2863 he->nickcmpl = NULL;
2866 break;
2868 case FreeText:
2869 sending_order[i++] = pf;
2870 *he = he_custom_free_templ;
2871 he->realaddr = &pf->textbuf;
2872 pf->text = &pf->textbuf;
2873 if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
2874 (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
2875 he->display_it = 1; /* show it */
2877 break;
2879 default:
2880 q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
2881 (void *)pf->type);
2882 break;
2885 he->name = pf->name;
2887 /* use first 8 characters for prompt */
2888 he->prompt = cpystr(" : ");
2889 strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
2891 he->rich_header = view_as_rich(he->name, he->rich_header);
2892 he++;
2896 * Make sure at least *one* field is displayable...
2898 for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
2899 if(HE(pf) && !HE(pf)->rich_header){
2900 index = i;
2901 break;
2905 * None displayable!!! Warn and display defaults.
2907 if(index == -1){
2908 q_status_message(SM_ORDER,0,5,
2909 "No default-composer-hdrs matched, displaying defaults");
2910 for(i = 0, pf = header.local; pf; pf = pf->next, i++)
2911 if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
2912 && HE(pf))
2913 HE(pf)->rich_header = 0;
2917 * Save information about body which set_mime_type_by_grope might change.
2918 * Then, if we get an error sending, we reset these things so that
2919 * grope can do it's thing again after we edit some more.
2921 if ((*body)->type == TYPEMULTIPART)
2922 bp = save_body_particulars(&(*body)->nested.part->body);
2923 else
2924 bp = save_body_particulars(*body);
2927 local_redraft_pos = redraft_pos;
2929 /*----------------------------------------------------------------------
2930 Loop calling the editor until everything goes well
2931 ----*/
2932 while(1){
2933 int saved_user_timeout;
2935 /* Reset body to what it was when we started. */
2936 if ((*body)->type == TYPEMULTIPART)
2937 reset_body_particulars(bp, &(*body)->nested.part->body);
2938 else
2939 reset_body_particulars(bp,*body);
2941 * set initial cursor position based on how many times we've been
2942 * thru the loop...
2944 if(reply && reply->pseudo){
2945 pbf->pine_flags |= reply->data.pico_flags;
2947 else if(body_start){
2948 pbf->pine_flags |= P_BODY;
2949 body_start = 0; /* maybe not next time */
2951 else if(local_redraft_pos){
2952 pbf->edit_offset = local_redraft_pos->offset;
2953 /* set the start_here bit in correct header */
2954 for(pf = header.local; pf && pf->name; pf = pf->next)
2955 if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
2956 && HE(pf)){
2957 HE(pf)->start_here = 1;
2958 break;
2961 /* If didn't find it, we start in body. */
2962 if(!pf || !pf->name)
2963 pbf->pine_flags |= P_BODY;
2965 else if(reply && (!reply->forw && !reply->forwarded)){
2966 pbf->pine_flags |= P_BODY;
2969 /* in case these were turned on in previous pass through loop */
2970 if(pf_nobody){
2971 pf_nobody->writehdr = 0;
2972 pf_nobody->localcopy = 0;
2975 if(pf_fcc)
2976 pf_fcc->localcopy = 0;
2979 * If a sending attempt failed after we passed the message text
2980 * thru a user-defined filter, "orig_so" points to the original
2981 * text. Replace the body's encoded data with the original...
2983 if(orig_so){
2984 STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
2985 ? &(*body)->nested.part->body.contents.text.data
2986 : &(*body)->contents.text.data);
2987 so_give(so);
2988 *so = orig_so;
2989 orig_so = NULL;
2993 * Convert the envelope and body to the string format that
2994 * pico can edit
2996 outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
2998 for(pf = header.local; pf && pf->name; pf = pf->next){
3000 * If this isn't the first time through this loop, we may have
3001 * freed some of the FreeText headers below so that they wouldn't
3002 * show up as empty headers in the finished message. Need to
3003 * alloc them again here so they can be edited.
3005 if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
3006 *HE(pf)->realaddr = cpystr("");
3008 if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
3009 HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
3013 * If From is exposed, probably by a role, then start the cursor
3014 * on the first line which isn't filled in. If it isn't, then we
3015 * don't move the cursor, mostly for back-compat.
3017 if((!reply || reply->forw || reply->forwarded) &&
3018 !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
3019 (he_from->display_it || !he_from->rich_header)){
3020 for(pf = header.local; pf && pf->name; pf = pf->next)
3021 if(HE(pf) &&
3022 (HE(pf)->display_it || !HE(pf)->rich_header) &&
3023 HE(pf)->realaddr &&
3024 (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
3025 HE(pf)->start_here = 1;
3026 break;
3030 #ifdef _WINDOWS
3031 mswin_setwindowmenu (MENU_COMPOSER);
3032 #endif
3034 cancel_busy_cue(-1);
3035 flush_status_messages(1);
3037 /* turn off user input timeout when in composer */
3038 saved_user_timeout = ps_global->hours_to_timeout;
3039 ps_global->hours_to_timeout = 0;
3040 dprint((1, "\n ---- COMPOSER ----\n"));
3041 editor_result = pico(pbf);
3042 dprint((4, "... composer returns (0x%x)\n", editor_result));
3043 ps_global->hours_to_timeout = saved_user_timeout;
3045 #ifdef _WINDOWS
3046 mswin_setwindowmenu (MENU_DEFAULT);
3047 #endif
3048 fix_windsize(ps_global);
3051 * Only reinitialize signals if we didn't receive an interesting
3052 * one while in pico, since pico's return is part of processing that
3053 * signal and it should continue to be ignored.
3055 if(!(editor_result & COMP_GOTHUP))
3056 init_signals(); /* Pico has it's own signal stuff */
3059 * We're going to save in DEADLETTER. Dump attachments first.
3061 if(editor_result & COMP_CANCEL)
3062 free_attachment_list(&pbf->attachments);
3064 /* Turn strings back into structures */
3065 strings2outgoing(&header, body, pbf->attachments, flowing_requested);
3067 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3068 if(outgoing->newsgroups){
3069 sqzspaces(outgoing->newsgroups);
3070 if(!outgoing->newsgroups[0])
3071 fs_give((void **)&(outgoing->newsgroups));
3074 /* Make subject NULL if it is "" (so won't show up in headers) */
3075 if(outgoing->subject && !outgoing->subject[0])
3076 fs_give((void **)&(outgoing->subject));
3078 /* remove custom fields that are empty */
3079 for(pf = header.local; pf && pf->name; pf = pf->next){
3080 if(pf->type == FreeText && pf->textbuf){
3081 if(pf->textbuf[0] == '\0'){
3082 fs_give((void **)&pf->textbuf);
3083 pf->text = NULL;
3088 removing_trailing_white_space(fcc);
3090 /*-------- Stamp it with a current date -------*/
3091 if(outgoing->date) /* update old date */
3092 fs_give((void **)&(outgoing->date));
3094 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3095 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
3097 rfc822_date(tmp_20k_buf); /* format and copy new date */
3098 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3099 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
3101 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
3103 /* Set return_path based on From which is going to be used */
3104 if(outgoing->return_path)
3105 mail_free_address(&outgoing->return_path);
3107 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
3110 * Don't ever believe the sender that is there.
3111 * If From doesn't look quite right, generate our own sender.
3113 if(outgoing->sender)
3114 mail_free_address(&outgoing->sender);
3117 * If the LHS of the address doesn't match, or the RHS
3118 * doesn't match one of localdomain or hostname,
3119 * then add a sender line (really X-X-Sender).
3121 * Don't add a personal_name since the user can change that.
3123 if(F_OFF(F_DISABLE_SENDER, ps_global)
3125 (!outgoing->from
3126 || !outgoing->from->mailbox
3127 || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
3128 || !outgoing->from->host
3129 || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
3130 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
3132 outgoing->sender = mail_newaddr();
3133 outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
3134 outgoing->sender->host = cpystr(ps_global->hostname);
3137 if(ps_global->newthread){
3138 if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
3139 if(outgoing->references) fs_give((void **)&outgoing->references);
3142 /*----- Message is edited, now decide what to do with it ----*/
3143 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
3144 /*=========== Postpone or Interrupted message ============*/
3145 CONTEXT_S *fcc_cntxt = NULL;
3146 char folder[MAXPATH+1];
3147 int fcc_result = 0;
3148 char label[50];
3150 dprint((4, "pine_send:%s handling\n",
3151 (editor_result & COMP_SUSPEND)
3152 ? "SUSPEND"
3153 : (editor_result & COMP_GOTHUP)
3154 ? "HUP"
3155 : (editor_result & COMP_CANCEL)
3156 ? "CANCEL" : "HUH?"));
3157 if((editor_result & COMP_CANCEL)
3158 && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
3159 || ps_global->deadlets == 0)){
3160 q_status_message(SM_ORDER, 0, 3, "Message cancelled");
3161 break;
3165 * The idea here is to use the Fcc: writing facility
3166 * to append to the special postponed message folder...
3168 * NOTE: the strategy now is to write the message and
3169 * all attachments as they exist at composition time.
3170 * In other words, attachments are postponed by value
3171 * and not reference. This may change later, but we'll
3172 * need a local "message/external-body" type that
3173 * outgoing2strings knows how to properly set up for
3174 * the composer. Maybe later...
3177 label[0] = '\0';
3179 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
3180 && (editor_result & COMP_SUSPEND)
3181 && (check_addresses(&header) == CA_BAD)){
3182 /*--- Addresses didn't check out---*/
3183 q_status_message(SM_ORDER, 7, 7,
3184 _("Not allowed to postpone message until addresses are qualified"));
3185 continue;
3189 * Build the local message copy so.
3191 * In the HUP case, we'll write the bezerk delimiter by
3192 * hand and output the message directly into the folder.
3193 * It's not only faster, we don't have to worry about
3194 * c-client reentrance and less hands paw over the data so
3195 * there's less chance of a problem.
3197 * In the Postpone case, just create it if the user wants to
3198 * and create a temporary storage object to write into. */
3199 fake_hup:
3200 lmc.all_written = lmc.text_only = lmc.text_written = 0;
3201 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3202 int newfile = 1;
3203 time_t now = time((time_t *)0);
3205 #if defined(DOS) || defined(OS2)
3207 * we can't assume anything about root or home dirs, so
3208 * just plunk it down in the same place as the pinerc
3210 if(!getenv("HOME")){
3211 char *lc = last_cmpnt(ps_global->pinerc);
3212 folder[0] = '\0';
3213 if(lc != NULL){
3214 strncpy(folder,ps_global->pinerc,
3215 MIN(lc-ps_global->pinerc,sizeof(folder)-1));
3216 folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
3219 strncat(folder, (editor_result & COMP_GOTHUP)
3220 ? INTERRUPTED_MAIL : DEADLETTER,
3221 sizeof(folder)-strlen(folder)-1);
3223 else
3224 #endif
3225 build_path(folder,
3226 ps_global->VAR_OPER_DIR
3227 ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
3228 (editor_result & COMP_GOTHUP)
3229 ? INTERRUPTED_MAIL : DEADLETTER,
3230 sizeof(folder));
3232 if(editor_result & COMP_CANCEL){
3233 char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
3235 if(strlen(folder) + 1 < sizeof(filename))
3236 for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
3237 strncpy(filename, folder, sizeof(filename));
3238 filename[sizeof(filename)-1] = '\0';
3239 strncpy(newfname, filename, sizeof(newfname));
3240 newfname[sizeof(newfname)-1] = '\0';
3242 if(i > 1){
3243 snprintf(nbuf, sizeof(nbuf), "%d", i);
3244 nbuf[sizeof(nbuf)-1] = '\0';
3245 strncat(filename, nbuf,
3246 sizeof(filename)-strlen(filename)-1);
3247 filename[sizeof(filename)-1] = '\0';
3250 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
3251 nbuf[sizeof(nbuf)-1] = '\0';
3252 strncat(newfname, nbuf,
3253 sizeof(newfname)-strlen(newfname)-1);
3254 newfname[sizeof(newfname)-1] = '\0';
3255 (void) rename_file(filename, newfname);
3258 our_unlink(folder);
3260 else
3261 newfile = can_access(folder, ACCESS_EXISTS);
3263 if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
3264 if (outgoing->from){
3265 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
3266 newfile ? "" : "\015\012",
3267 outgoing->from->mailbox,
3268 outgoing->from->host, ctime(&now));
3269 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3270 if(!so_puts(lmc.so, tmp_20k_buf)){
3271 if(editor_result & COMP_CANCEL)
3272 q_status_message2(SM_ORDER | SM_DING, 3, 3,
3273 "Can't write \"%s\": %s",
3274 folder, error_description(errno));
3275 else
3276 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3277 folder ? folder : "?",
3278 error_description(errno)));
3283 else{ /* Must be COMP_SUSPEND */
3284 if(!ps_global->VAR_POSTPONED_FOLDER
3285 || !ps_global->VAR_POSTPONED_FOLDER[0]){
3286 q_status_message(SM_ORDER | SM_DING, 3, 3,
3287 _("No postponed file defined"));
3288 continue;
3292 * Store the cursor position
3294 * First find the header entry with the start_here
3295 * bit set, if any. This means the editor is telling
3296 * us to start on this header field next time.
3298 start_here_name = NULL;
3299 for(pf = header.local; pf && pf->name; pf = pf->next)
3300 if(HE(pf) && HE(pf)->start_here){
3301 start_here_name = pf->name;
3302 break;
3305 /* If there wasn't one, ":" means we start in the body. */
3306 if(!start_here_name || !*start_here_name)
3307 start_here_name = ":";
3309 if(ps_global->VAR_FORM_FOLDER
3310 && ps_global->VAR_FORM_FOLDER[0]
3311 && postpone_prompt() == 'f'){
3312 strncpy(folder, ps_global->VAR_FORM_FOLDER,
3313 sizeof(folder)-1);
3314 folder[sizeof(folder)-1] = '\0';
3315 strncpy(label, "form letter", sizeof(label));
3316 label[sizeof(label)-1] = '\0';
3318 else{
3319 strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
3320 sizeof(folder)-1);
3321 folder[sizeof(folder)-1] = '\0';
3322 strncpy(label, "postponed message", sizeof(label));
3323 label[sizeof(label)-1] = '\0';
3326 lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
3329 if(lmc.so){
3330 size_t sz;
3331 char *lmq = NULL;
3333 /* copy fcc line to postponed or interrupted folder */
3334 if(pf_fcc)
3335 pf_fcc->localcopy = 1;
3337 /* plug error into header for later display to user */
3338 if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
3339 pf_err->writehdr = 1;
3340 pf_err->localcopy = 1;
3341 pf_err->textbuf = lmq;
3345 * if reply, write (UID)folder header field so we can
3346 * later flag the replied-to message \\ANSWERED
3347 * DON'T save MSGNO's.
3349 if(reply && reply->uid){
3350 char uidbuf[MAILTMPLEN], *p;
3351 long i;
3353 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
3354 if(i)
3355 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
3357 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
3360 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3362 pf_uid->writehdr = 1;
3363 pf_uid->localcopy = 1;
3364 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
3365 reply->prefix ? int2string(strlen(reply->prefix))
3366 : (reply->forwarded) ? "": "0 ",
3367 reply->prefix ? " " : "",
3368 reply->prefix ? reply->prefix : "",
3369 i, reply->data.uid.validity,
3370 tmp_20k_buf, reply->mailbox);
3371 uidbuf[sizeof(uidbuf)-1] = '\0';
3372 pf_uid->textbuf = cpystr(uidbuf);
3375 * Logically, this ought to be part of pf_uid, but this
3376 * was added later and so had to be in a separate header
3377 * for backwards compatibility.
3379 pf_mbox->writehdr = 1;
3380 pf_mbox->localcopy = 1;
3381 pf_mbox->textbuf = cpystr(reply->origmbox
3382 ? reply->origmbox
3383 : reply->mailbox);
3386 /* Save cursor position */
3387 if(start_here_name && *start_here_name){
3388 char curposbuf[MAILTMPLEN];
3390 pf_curpos->writehdr = 1;
3391 pf_curpos->localcopy = 1;
3392 snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
3393 pbf->edit_offset);
3394 curposbuf[sizeof(curposbuf)-1] = '\0';
3395 pf_curpos->textbuf = cpystr(curposbuf);
3399 * Work around c-client reply-to bug. C-client will
3400 * return a reply_to in an envelope even if there is
3401 * no reply-to header field. We want to note here whether
3402 * the reply-to is real or not.
3404 if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
3405 pf_ourrep->writehdr = 1;
3406 pf_ourrep->localcopy = 1;
3407 if(outgoing->reply_to)
3408 pf_ourrep->textbuf = cpystr("Full");
3409 else
3410 pf_ourrep->textbuf = cpystr("Empty");
3413 /* Save the role-specific smtp server */
3414 if(role && role->smtp && role->smtp[0]){
3415 char *q, *smtp = NULL;
3416 char **lp;
3417 size_t len = 0;
3420 * Turn the list of smtp servers into a space-
3421 * delimited list in a single string.
3423 for(lp = role->smtp; (q = *lp) != NULL; lp++)
3424 len += (strlen(q) + 1);
3426 if(len){
3427 smtp = (char *) fs_get(len * sizeof(char));
3428 smtp[0] = '\0';
3429 for(lp = role->smtp; (q = *lp) != NULL; lp++){
3430 if(lp != role->smtp)
3431 strncat(smtp, " ", len-strlen(smtp)-1);
3433 strncat(smtp, q, len-strlen(smtp)-1);
3436 smtp[len-1] = '\0';
3439 pf_smtp_server->writehdr = 1;
3440 pf_smtp_server->localcopy = 1;
3441 if(smtp)
3442 pf_smtp_server->textbuf = smtp;
3443 else
3444 pf_smtp_server->textbuf = cpystr("");
3447 /* Save the role-specific nntp server */
3448 if(suggested_nntp_server ||
3449 (role && role->nntp && role->nntp[0])){
3450 char *q, *nntp = NULL;
3451 char **lp;
3452 size_t len = 0;
3454 if(role && role->nntp && role->nntp[0]){
3456 * Turn the list of nntp servers into a space-
3457 * delimited list in a single string.
3459 for(lp = role->nntp; (q = *lp) != NULL; lp++)
3460 len += (strlen(q) + 1);
3462 if(len){
3463 nntp = (char *) fs_get(len * sizeof(char));
3464 nntp[0] = '\0';
3465 for(lp = role->nntp; (q = *lp) != NULL; lp++){
3466 if(lp != role->nntp)
3467 strncat(nntp, " ", len-strlen(nntp)-1);
3469 strncat(nntp, q, len-strlen(nntp)-1);
3472 nntp[len-1] = '\0';
3475 else
3476 nntp = cpystr(suggested_nntp_server);
3478 pf_nntp_server->writehdr = 1;
3479 pf_nntp_server->localcopy = 1;
3480 if(nntp)
3481 pf_nntp_server->textbuf = nntp;
3482 else
3483 pf_nntp_server->textbuf = cpystr("");
3487 * Write the list of custom headers to the
3488 * X-Our-Headers header so that we can recover the
3489 * list in redraft.
3491 sz = 0;
3492 for(pf = header.custom; pf && pf->name; pf = pf->next)
3493 sz += strlen(pf->name) + 1;
3495 if(sz){
3496 char *q;
3498 pf_ourhdrs->writehdr = 1;
3499 pf_ourhdrs->localcopy = 1;
3500 pf_ourhdrs->textbuf = (char *)fs_get(sz);
3501 memset(pf_ourhdrs->textbuf, 0, sz);
3502 q = pf_ourhdrs->textbuf;
3503 for(pf = header.custom; pf && pf->name; pf = pf->next){
3504 if(pf != header.custom)
3505 sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
3507 sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
3510 pf_ourhdrs->textbuf[sz-1] = '\0';;
3514 * We need to make sure any header values that got cleared
3515 * get written to the postponed message (they won't if
3516 * pf->text is NULL). Otherwise, we can't tell previously
3517 * non-existent custom headers or default values from
3518 * custom (or other) headers that got blanked in the
3519 * composer...
3521 for(pf = header.local; pf && pf->name; pf = pf->next)
3522 if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
3523 *(HE(pf)->realaddr) = cpystr("");
3526 * We're saving the message for use later. It may be that the
3527 * characters in the message are not all convertible to the
3528 * user's posting_charmap. We'll save it as UTF-8 instead
3529 * and worry about that the next time they try to send it.
3530 * Use a different save pointer just to be sure we don't
3531 * mess up the other stuff. We should probably make the
3532 * charset an argument.
3534 * We also need to fix the charset of the body part
3535 * the user is editing so that we can read it back
3536 * successfully when we resume the composition.
3538 ps_global->post_utf8 = 1;
3541 PARAMETER *pm;
3542 BODY *bp;
3543 if((*body)->type == TYPEMULTIPART)
3544 bp = &(*body)->nested.part->body;
3545 else
3546 bp = *body;
3548 for(pm = bp->parameter;
3549 pm && strucmp(pm->attribute, "charset") != 0;
3550 pm = pm->next)
3553 if(pm){
3554 if(pm->value)
3555 fs_give((void **) &pm->value);
3557 pm->value = cpystr("UTF-8");
3561 if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
3562 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3563 char *err;
3564 STORE_S *hup_so;
3565 gf_io_t gc, pc;
3566 int we_cancel = 0;
3568 if(editor_result & COMP_CANCEL){
3569 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3570 "Saving to \"%s\"", folder);
3571 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3572 we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
3575 if((hup_so =
3576 so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
3577 gf_set_so_readc(&gc, lmc.so);
3578 gf_set_so_writec(&pc, hup_so);
3579 so_seek(lmc.so, 0L, 0); /* read msg copy and */
3580 so_seek(hup_so, 0L, 2); /* append to folder */
3581 gf_filter_init();
3582 gf_link_filter(gf_nvtnl_local, NULL);
3583 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
3584 dprint((1, "*** PIPE FAILED: %s\n",
3585 err ? err : "?"));
3587 gf_clear_so_readc(lmc.so);
3588 gf_clear_so_writec(hup_so);
3589 so_give(&hup_so);
3591 else
3592 dprint((1, "*** CAN'T CREATE %s: %s\n",
3593 folder ? folder : "?",
3594 error_description(errno)));
3596 if(we_cancel)
3597 cancel_busy_cue(-1);
3599 else
3600 fcc_result = write_fcc(folder, fcc_cntxt,
3601 lmc.so, NULL, label, NULL);
3604 /* discontinue coerced UTF-8 posting */
3605 ps_global->post_utf8 = 0;
3607 so_give(&lmc.so);
3609 else
3610 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3611 error_description(errno)));
3613 if(editor_result & COMP_GOTHUP){
3615 * Special Hack #291: if any hi-byte bits are set in
3616 * editor's result, we put them there.
3618 if(editor_result & 0xff00)
3619 exit(editor_result >> 8);
3621 dprint((1, "Save composition on HUP %sED\n",
3622 fcc_result ? "SUCCEED" : "FAIL"));
3623 hup_signal(); /* Do what we normally do on SIGHUP */
3625 else if((editor_result & COMP_SUSPEND) && fcc_result){
3626 if(ps_global->VAR_FORM_FOLDER
3627 && ps_global->VAR_FORM_FOLDER[0]
3628 && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
3629 q_status_message(SM_ORDER, 0, 3,
3630 _("Composition saved to Form Letter Folder. Select Compose to send."));
3631 else
3632 q_status_message(SM_ORDER, 0, 3,
3633 _("Composition postponed. Select Compose to resume."));
3635 break; /* postpone went OK, get out of here */
3637 else if(editor_result & COMP_CANCEL){
3638 char *lc = NULL;
3640 if(fcc_result && folder)
3641 lc = last_cmpnt(folder);
3643 q_status_message3(SM_ORDER, 0, 3,
3644 _("Message cancelled%s%s%s"),
3645 (lc && *lc) ? " and copied to \"" : "",
3646 (lc && *lc) ? lc : "",
3647 (lc && *lc) ? "\" file" : "");
3648 break;
3650 else{
3651 q_status_message(SM_ORDER, 0, 4,
3652 _("Continuing composition. Message not postponed or sent"));
3653 body_start = 1;
3654 continue; /* postpone failed, jump back in to composer */
3657 else{
3658 /*------ Must be sending mail or posting ! -----*/
3659 int result, valid_addr, continue_with_only_fcc = 0;
3660 CONTEXT_S *fcc_cntxt = NULL;
3662 result = 0;
3663 dprint((4, "=== sending: "));
3665 /* --- If posting, confirm with user ----*/
3666 if(outgoing->newsgroups && *outgoing->newsgroups
3667 && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
3668 && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
3669 q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
3670 dprint((4, "no post, continuing\n"));
3671 continue;
3674 if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
3675 || outgoing->newsgroups)){
3676 if(fcc && fcc[0]){
3677 if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
3678 want_to(_("No recipients, really copy only to Fcc "),
3679 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
3680 continue;
3682 continue_with_only_fcc++;
3684 else{
3685 q_status_message(SM_ORDER, 3, 4,
3686 _("No recipients specified!"));
3687 dprint((4, "no recip, continuing\n"));
3688 continue;
3692 if((valid_addr = check_addresses(&header)) == CA_BAD){
3693 /*--- Addresses didn't check out---*/
3694 dprint((4, "addrs failed, continuing\n"));
3695 continue;
3698 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
3699 && !continue_with_only_fcc
3700 && !(outgoing->to || outgoing->cc || lcc_addr
3701 || outgoing->newsgroups)
3702 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3703 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
3704 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3705 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3706 free_redraft_pos(&local_redraft_pos);
3708 local_redraft_pos
3709 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3710 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3711 local_redraft_pos->hdrname = cpystr(TONAME);
3712 continue;
3715 if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
3716 && check_for_subject(&header) == CF_MISSING){
3717 dprint((4, "No subject, continuing\n"));
3718 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3719 free_redraft_pos(&local_redraft_pos);
3721 local_redraft_pos
3722 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3723 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3724 local_redraft_pos->hdrname = cpystr(SUBJNAME);
3725 continue;
3728 if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
3729 && check_for_fcc(fcc) == CF_MISSING){
3730 dprint((4, "No fcc, continuing\n"));
3731 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3732 free_redraft_pos(&local_redraft_pos);
3734 local_redraft_pos
3735 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3736 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3737 local_redraft_pos->hdrname = cpystr("Fcc");
3738 continue;
3741 set_last_fcc(fcc);
3743 /*---- Check out fcc -----*/
3744 if(fcc && *fcc){
3746 * If special name "inbox" then replace it with the
3747 * real inbox path.
3749 if(ps_global->VAR_INBOX_PATH
3750 && strucmp(fcc, ps_global->inbox_name) == 0){
3751 char *replace_fcc;
3753 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
3754 fs_give((void **)&fcc);
3755 fcc = replace_fcc;
3758 lmc.all_written = lmc.text_written = 0;
3759 /* lmc.text_only set on command line */
3760 if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
3761 /* ---- Open or allocation of fcc failed ----- */
3762 dprint((4,"can't open/allocate fcc, cont'g\n"));
3765 * Find field entry associated with fcc, and start
3766 * composer on it...
3768 for(pf = header.local; pf && pf->name; pf = pf->next)
3769 if(pf->type == Fcc && HE(pf))
3770 HE(pf)->start_here = 1;
3772 continue;
3774 else
3775 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
3777 else
3778 lmc.so = NULL;
3780 /*---- Take care of any requested prefiltering ----*/
3781 if(sending_filter_requested
3782 && !filter_message_text(sending_filter_requested, outgoing,
3783 *body, &orig_so, &header)){
3784 q_status_message1(SM_ORDER, 3, 3,
3785 _("Problem filtering! Nothing sent%s."),
3786 fcc ? " or saved to fcc" : "");
3787 continue;
3790 /*------ Actually post -------*/
3791 if(outgoing->newsgroups){
3792 char **alt_nntp = NULL, *alt_nntp_p[2];
3793 if(((role && role->nntp)
3794 || suggested_nntp_server)){
3795 if(ps_global->FIX_NNTP_SERVER
3796 && ps_global->FIX_NNTP_SERVER[0])
3797 q_status_message(SM_ORDER | SM_DING, 5, 5,
3798 "Using nntp-server that is administratively fixed");
3799 else if(role && role->nntp)
3800 alt_nntp = role->nntp;
3801 else{
3802 alt_nntp_p[0] = suggested_nntp_server;
3803 alt_nntp_p[1] = NULL;
3804 alt_nntp = alt_nntp_p;
3807 if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
3808 dprint((1, "Post failed, continuing\n"));
3809 if(outgoing->message_id)
3810 fs_give((void **) &outgoing->message_id);
3812 outgoing->message_id = generate_message_id();
3814 continue;
3816 else
3817 result |= P_NEWS_WIN;
3821 * BUG: IF we've posted the message *and* an fcc was specified
3822 * then we've already got a neatly formatted message in the
3823 * lmc.so. It'd be nice not to have to re-encode everything
3824 * to insert it into the smtp slot...
3828 * Turn on "undisclosed recipients" header if no To or cc.
3830 if(!(outgoing->to || outgoing->cc)
3831 && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
3832 pf_nobody->writehdr = 1;
3833 pf_nobody->localcopy = 1;
3836 if(priority_requested){
3837 (void) set_priority_header(&header, priority_requested);
3838 fs_give((void **) &priority_requested);
3841 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3843 * If requested, launch backgroud posting...
3845 if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
3846 ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
3847 memset(ps_global->post, 0, sizeof(POST_S));
3848 if(fcc)
3849 ps_global->post->fcc = cpystr(fcc);
3851 if((ps_global->post->pid = fork()) == 0){
3853 * Put us in new process group...
3855 setpgrp(0, ps_global->post->pid);
3857 /* BUG: should fix argv[0] to indicate what we're up to */
3860 * If there are any live streams, pretend we never
3861 * knew them. Problem is two processes writing
3862 * same server process.
3863 * This is not clean but we're just going to exit
3864 * right away anyway. We just want to be sure to leave
3865 * the stuff that the parent is going to use alone.
3866 * The next three lines will disable the re-use of the
3867 * existing streams and cause us to open a new one if
3868 * needed.
3870 ps_global->mail_stream = NULL;
3871 ps_global->s_pool.streams = NULL;
3872 ps_global->s_pool.nstream = 0;
3874 /* quell any display output */
3875 ps_global->in_init_seq = 1;
3877 /*------- Actually mail the message ------*/
3878 if(valid_addr == CA_OK
3879 && (outgoing->to || outgoing->cc
3880 || outgoing->bcc || lcc_addr)){
3881 char **alt_smtp = NULL;
3883 if(role && role->smtp){
3884 if(ps_global->FIX_SMTP_SERVER
3885 && ps_global->FIX_SMTP_SERVER[0])
3886 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3887 else
3888 alt_smtp = role->smtp;
3891 result |= (call_mailer(&header, *body, alt_smtp,
3892 call_mailer_flags,
3893 call_mailer_file_result,
3894 pipe_callback) > 0)
3895 ? P_MAIL_WIN : P_MAIL_LOSE;
3897 if(result & P_MAIL_LOSE)
3898 mark_address_failure_for_pico(&header);
3901 /*----- Was there an fcc involved? -----*/
3902 if(lmc.so){
3903 /*------ Write it if at least something worked ------*/
3904 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3905 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3906 && pine_rfc822_output(&header, *body,
3907 NULL, NULL))){
3908 char label[50];
3910 strncpy(label, "Fcc", sizeof(label));
3911 label[sizeof(label)-1] = '\0';
3912 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3913 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3914 label[sizeof(label)-1] = '\0';
3917 /*-- Now actually copy to fcc folder and close --*/
3918 result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
3919 NULL, label,
3920 F_ON(F_MARK_FCC_SEEN, ps_global)
3921 ? "\\SEEN" : NULL))
3922 ? P_FCC_WIN : P_FCC_LOSE;
3924 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3925 q_status_message(SM_ORDER, 3, 5,
3926 _("Fcc Failed!. No message saved."));
3927 dprint((1,
3928 "explicit fcc write failed!\n"));
3929 result |= P_FCC_LOSE;
3932 so_give(&lmc.so);
3935 if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
3937 * Encode child's result in hi-byte of
3938 * editor's result
3940 editor_result = ((result << 8) | COMP_GOTHUP);
3941 goto fake_hup;
3944 exit(result);
3947 if(ps_global->post->pid > 0){
3948 q_status_message(SM_ORDER, 3, 3,
3949 _("Message handed off for posting"));
3950 break; /* up to our child now */
3952 else{
3953 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3954 "Can't fork for send: %s",
3955 error_description(errno));
3956 if(ps_global->post->fcc)
3957 fs_give((void **) &ps_global->post->fcc);
3959 fs_give((void **) &ps_global->post);
3962 if(lmc.so) /* throw away unused store obj */
3963 so_give(&lmc.so);
3965 if(outgoing->message_id)
3966 fs_give((void **) &outgoing->message_id);
3968 outgoing->message_id = generate_message_id();
3970 continue; /* if we got here, there was a prob */
3972 #endif /* BACKGROUND_POST */
3974 /*------- Actually mail the message ------*/
3975 if(valid_addr == CA_OK
3976 && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
3977 char **alt_smtp = NULL;
3979 if(role && role->smtp){
3980 if(ps_global->FIX_SMTP_SERVER
3981 && ps_global->FIX_SMTP_SERVER[0])
3982 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3983 else
3984 alt_smtp = role->smtp;
3987 result |= (call_mailer(&header, *body, alt_smtp,
3988 call_mailer_flags,
3989 call_mailer_file_result,
3990 pipe_callback) > 0)
3991 ? P_MAIL_WIN : P_MAIL_LOSE;
3993 if(result & P_MAIL_LOSE)
3994 mark_address_failure_for_pico(&header);
3997 /*----- Was there an fcc involved? -----*/
3998 if(lmc.so){
3999 /*------ Write it if at least something worked ------*/
4000 if((result & (P_MAIL_WIN | P_NEWS_WIN))
4001 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
4002 && pine_rfc822_output(&header, *body, NULL, NULL))){
4003 char label[50];
4005 strncpy(label, "Fcc", sizeof(label));
4006 label[sizeof(label)-1] = '\0';
4007 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
4008 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
4009 label[sizeof(label)-1] = '\0';
4012 /*-- Now actually copy to fcc folder and close --*/
4013 result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
4014 F_ON(F_MARK_FCC_SEEN, ps_global)
4015 ? "\\SEEN" : NULL))
4016 ? P_FCC_WIN : P_FCC_LOSE;
4018 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
4019 q_status_message(SM_ORDER,3,5,
4020 _("Fcc Failed!. No message saved."));
4021 dprint((1, "explicit fcc write failed!\n"));
4022 result |= P_FCC_LOSE;
4025 so_give(&lmc.so);
4028 /*----- Mail Post FAILED, back to composer -----*/
4029 if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
4030 dprint((1, "Send failed, continuing\n"));
4032 if(result & P_FCC_LOSE){
4034 * Find field entry associated with fcc, and start
4035 * composer on it...
4037 for(pf = header.local; pf && pf->name; pf = pf->next)
4038 if(pf->type == Fcc && HE(pf))
4039 HE(pf)->start_here = 1;
4041 q_status_message(SM_ORDER | SM_DING, 3, 3,
4042 pine_send_status(result, fcc,
4043 tmp_20k_buf, SIZEOF_20KBUF, NULL));
4046 if(outgoing->message_id)
4047 fs_give((void **) &outgoing->message_id);
4049 outgoing->message_id = generate_message_id();
4051 continue;
4055 * If message sent *completely* successfully, there's a
4056 * reply struct AND we're allowed to write back state, do it.
4057 * But also protect against shifted message numbers due
4058 * to new mail arrival. Since the number passed is based
4059 * on the real imap msg no, AND we're sure no expunge has
4060 * been done, just fix up the sorted number...
4062 update_answered_flags(reply);
4064 /*----- Signed, sealed, delivered! ------*/
4065 q_status_message(SM_ORDER, 0, 3,
4066 pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
4068 break; /* All's well, pop out of here */
4072 if(orig_so)
4073 so_give(&orig_so);
4075 if(fcc)
4076 fs_give((void **)&fcc);
4078 free_body_particulars(bp);
4080 free_attachment_list(&pbf->attachments);
4082 standard_picobuf_teardown(pbf);
4084 for(i=0; i < fixed_cnt; i++){
4085 if(pfields[i].textbuf)
4086 fs_give((void **)&pfields[i].textbuf);
4088 fs_give((void **)&pfields[i].name);
4091 if(lcc_addr)
4092 mail_free_address(&lcc_addr);
4094 if(nobody_addr)
4095 mail_free_address(&nobody_addr);
4097 free_prompts(header.custom);
4098 free_customs(header.custom);
4099 fs_give((void **)&pfields);
4100 free_headents(&headents);
4101 fs_give((void **)&sending_order);
4102 if(suggested_nntp_server)
4103 fs_give((void **)&suggested_nntp_server);
4104 if(title)
4105 fs_give((void **)&title);
4107 if(local_redraft_pos && local_redraft_pos != redraft_pos)
4108 free_redraft_pos(&local_redraft_pos);
4110 pbf = save_previous_pbuf;
4111 g_rolenick = NULL;
4113 dprint((4, "=== send returning ===\n"));
4118 * Check for subject in outgoing message.
4120 * Asks user whether to proceed with no subject.
4123 check_for_subject(METAENV *header)
4125 PINEFIELD *pf;
4126 int rv = CF_OK;
4128 for(pf = header->local; pf && pf->name; pf = pf->next)
4129 if(pf->type == Subject){
4130 if(pf->text && *pf->text && **pf->text)
4131 rv = CF_OK;
4132 else{
4133 if(want_to("No Subject, send anyway ",
4134 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
4135 rv = CF_OK;
4136 else
4137 rv = CF_MISSING;
4140 break;
4144 return(rv);
4149 * Check for fcc in outgoing message.
4151 * Asks user whether to proceed with no fcc.
4154 check_for_fcc(char *fcc)
4156 int rv = CF_OK;
4158 if(fcc && *fcc)
4159 rv = CF_OK;
4160 else{
4161 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
4162 rv = CF_OK;
4163 else
4164 rv = CF_MISSING;
4167 return(rv);
4172 * Confirm that the user wants to send to MAILER-DAEMON
4175 confirm_daemon_send(void)
4177 return(want_to("Really send this message to the MAILER-DAEMON",
4178 'n', 'n', NO_HELP, WT_NORM) == 'y');
4182 void
4183 free_prompts(PINEFIELD *head)
4185 PINEFIELD *pf;
4187 for(pf = head; pf && pf->name; pf = pf->next){
4188 if(HE(pf) && HE(pf)->prompt)
4189 fs_give((void **)& HE(pf)->prompt);
4195 postpone_prompt(void)
4197 static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
4198 {'f', 'f', "F", N_("Form Letter Folder")},
4199 {-1, 0, NULL, NULL} };
4201 return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
4202 pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
4207 * call__mailer_file_result - some results from call_mailer might be in a file.
4208 * dislplay that file.
4210 void
4211 call_mailer_file_result(char *filename, int style)
4213 if(filename){
4214 if(style & CM_BR_VERBOSE){
4215 display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
4217 else{
4218 display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
4223 void
4224 mark_address_failure_for_pico(METAENV *header)
4226 PINEFIELD *pf;
4227 ADDRESS *a;
4228 int error_count = 0;
4229 struct headerentry *last_he = NULL;
4231 for(pf = header->local; pf && pf->name; pf = pf->next)
4232 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
4233 for(a = *pf->addr; a != NULL; a = a->next)
4234 if(a->error != NULL
4235 && error_count++ < MAX_ADDR_ERROR
4236 && (HE(pf))){
4237 if(last_he) /* start last reported err */
4238 last_he->start_here = 0;
4240 (last_he = HE(pf))->start_here = 1;
4247 * This is specialized routine. It assumes that the only things that we
4248 * care about restoring are the body type, subtype, encoding and the
4249 * state of the charset parameter. It also assumes that if the charset
4250 * parameter exists when we save it, it won't be removed later.
4252 BODY_PARTICULARS_S *
4253 save_body_particulars(struct mail_bodystruct *body)
4255 BODY_PARTICULARS_S *bp;
4256 PARAMETER *pm;
4258 bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
4260 bp->type = body->type;
4261 bp->encoding = body->encoding;
4262 bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
4263 bp->parameter = body->parameter;
4264 for(pm = bp->parameter;
4265 pm && strucmp(pm->attribute, "charset") != 0;
4266 pm = pm->next)
4267 ;/* searching for possible charset parameter */
4269 if(pm){ /* found one */
4270 bp->had_csp = 1; /* saved body had charset parameter */
4271 bp->charset = pm->value ? cpystr(pm->value) : NULL;
4273 else{
4274 bp->had_csp = 0;
4275 bp->charset = NULL;
4278 return(bp);
4282 void
4283 reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
4285 body->type = bp->type;
4286 body->encoding = bp->encoding;
4287 if(body->subtype)
4288 fs_give((void **)&body->subtype);
4290 body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
4292 if(bp->parameter){
4293 PARAMETER *pm, *pm_prev = NULL;
4295 for(pm = body->parameter;
4296 pm && strucmp(pm->attribute, "charset") != 0;
4297 pm = pm->next)
4298 pm_prev = pm;
4300 if(pm){ /* body has charset parameter */
4301 if(bp->had_csp){ /* reset to what it used to be */
4302 if(pm->value)
4303 fs_give((void **)&pm->value);
4305 pm->value = bp->charset ? cpystr(bp->charset) : NULL;
4307 else{ /* remove charset parameter */
4308 if(pm_prev)
4309 pm_prev->next = pm->next;
4310 else
4311 body->parameter = pm->next;
4313 mail_free_body_parameter(&pm);
4316 else{
4317 if(bp->had_csp){
4319 * This can't happen because grope never removes
4320 * the charset parameter.
4322 q_status_message(SM_ORDER | SM_DING, 5, 5,
4323 "Programmer error: saved charset but no current charset param in pine_send");
4326 else{
4327 ok, still no parameter
4332 else{
4333 if(body->parameter)
4334 mail_free_body_parameter(&body->parameter);
4336 body->parameter = NULL;
4341 void
4342 free_body_particulars(BODY_PARTICULARS_S *bp)
4344 if(bp){
4345 if(bp->subtype)
4346 fs_give((void **)&bp->subtype);
4348 if(bp->charset)
4349 fs_give((void **)&bp->charset);
4351 fs_give((void **)&bp);
4356 /*----------------------------------------------------------------------
4357 Build a status message suitable for framing
4359 Returns: pointer to resulting buffer
4360 ---*/
4361 char *
4362 pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
4364 int avail = ps_global->ttyo->screen_cols - 2;
4365 int fixedneed, need, lenfcc;
4366 char *part1, *part2, *part3, *part4, *part5;
4367 char fbuf[MAILTMPLEN+1];
4369 part1 = (result & P_NEWS_WIN)
4370 ? "posted"
4371 : (result & P_NEWS_LOSE)
4372 ? "NOT POSTED"
4373 : "";
4374 part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
4375 && (result & P_FCC_BITS))
4376 ? ", "
4377 : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
4378 ? " and "
4379 : "";
4380 part3 = (result & P_MAIL_WIN)
4381 ? "sent"
4382 : (result & P_MAIL_LOSE)
4383 ? "NOT SENT"
4384 : "";
4385 part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
4386 ? " and "
4387 : "";
4388 part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
4389 ? "ONLY copied to "
4390 : (result & P_FCC_WIN)
4391 ? "copied to "
4392 : (result & P_FCC_LOSE)
4393 ? "NOT copied to "
4394 : "";
4395 lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
4397 fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
4398 strlen(part4) + strlen(part5);
4399 need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
4401 if(need > avail && fixedneed + 3 >= avail){
4402 /* dots on end of fixed, no fcc */
4403 snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
4404 part1, part2, part3, part4, part5);
4405 short_str(fbuf, buf, buflen, avail, EndDots);
4407 else if(need > avail){
4408 /* include whole fixed part, quotes and dots at end of fcc name */
4409 if(lenfcc > 0)
4410 lenfcc = MAX(1, lenfcc-(need-avail));
4412 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4413 part1, part2, part3, part4, part5,
4414 (result & P_FCC_BITS) ? "\"" : "",
4415 short_str((result & P_FCC_BITS) ? fcc_name : "",
4416 fbuf, sizeof(fbuf), lenfcc, FrontDots),
4417 (result & P_FCC_BITS) ? "\"" : "");
4419 else{
4420 /* whole thing */
4421 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4422 part1, part2, part3, part4, part5,
4423 (result & P_FCC_BITS) ? "\"" : "",
4424 (result & P_FCC_BITS) ? fcc_name : "",
4425 (result & P_FCC_BITS) ? "\"" : "");
4428 if(goodorbad)
4429 *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
4431 return(buf);
4434 /* Callback from Pico to set the conditions for Alpine to start a new thread
4437 void
4438 new_thread_on_blank_subject(void)
4440 ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
4445 /*----------------------------------------------------------------------
4446 Call back for pico to insert the specified message's text
4448 Args: n -- message number to format
4449 f -- function to use to output the formatted message
4452 Returns: returns msg number formatted on success, zero on error.
4453 ----*/
4454 long
4455 message_format_for_pico(long int n, int (*f) (int))
4457 ENVELOPE *e;
4458 BODY *b;
4459 char *old_quote = NULL;
4460 long rv = n;
4462 if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
4463 && (e = pine_mail_fetchstructure(ps_global->mail_stream,
4464 mn_m2raw(ps_global->msgmap, n), &b)))){
4465 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4466 flush_status_messages(0);
4467 return(0L);
4470 /* temporarily assign a new quote string */
4471 old_quote = pbf->quote_str;
4472 pbf->quote_str = reply_quote_str(e);
4474 /* build separator line */
4475 reply_delimiter(e, NULL, f);
4477 /* actually write message text */
4478 if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
4479 FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
4480 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4481 flush_status_messages(0);
4482 rv = 0L;
4485 fs_give((void **)&pbf->quote_str);
4486 pbf->quote_str = old_quote;
4487 return(rv);
4491 /*----------------------------------------------------------------------
4492 Call back for pico to prompt the user for exit confirmation
4494 Args: dflt -- default answer for confirmation prompt
4496 Returns: either NULL if the user accepts exit, or string containing
4497 reason why the user declined.
4498 ----*/
4500 send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
4501 char **result)
4503 int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
4504 int dsn_label = 0, fcc_label = 0, lparen;
4505 int flowing_label = 0, double_rad;
4506 char *rstr = NULL, *p, *lc, *optp;
4507 char dsn_string[30];
4508 void (*redraw)(void) = ps_global->redrawer;
4509 ESCKEY_S opts[18];
4510 struct filters {
4511 char *filter;
4512 int index;
4513 struct filters *prev, *next;
4514 } *filters = NULL, *fp;
4516 sending_filter_requested = NULL;
4517 call_mailer_flags = 0;
4518 background_requested = 0;
4519 flowing_requested = allow_flowed ? 1 : 0;
4520 lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
4521 if(priority_requested)
4522 fs_give((void **) &priority_requested);
4524 if(background_posting(FALSE)){
4525 if(result)
4526 *result = "Can't send while background posting. Use postpone.";
4528 return(1);
4531 if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
4532 if(result)
4533 *result = NULL;
4535 return(0);
4538 ps_global->redrawer = redraw_pico;
4540 if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
4541 (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
4544 * Build list of available filters...
4546 for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
4547 for(p = ps_global->VAR_SEND_FILTER[i];
4548 *p && !isspace((unsigned char)*p); p++)
4551 c = *p;
4552 *p = '\0';
4553 if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
4554 && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
4555 *p = c;
4556 continue;
4559 fp = (struct filters *)fs_get(sizeof(struct filters));
4560 fp->index = i;
4561 if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
4562 fp->filter = cpystr(lc);
4564 else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
4565 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
4566 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4567 fp->filter = cpystr(tmp_20k_buf);
4569 else
4570 fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
4572 *p = c;
4574 if(filters){
4575 fp->next = filters;
4576 fp->prev = filters->prev;
4577 fp->prev->next = filters->prev = fp;
4579 else{
4580 filters = (struct filters *)fs_get(sizeof(struct filters));
4581 filters->index = -1;
4582 filters->filter = NULL;
4583 filters->next = filters->prev = fp;
4584 fp->next = fp->prev = filters;
4588 i = 0;
4589 opts[i].ch = 'y';
4590 opts[i].rval = 'y';
4591 opts[i].name = "Y";
4592 opts[i++].label = N_("Yes");
4594 opts[i].ch = 'n';
4595 opts[i].rval = 'n';
4596 opts[i].name = "N";
4597 opts[i++].label = N_("No");
4599 if(filters){
4600 /* set global_filter_pointer to desired filter or NULL if none */
4601 /* prepare two keymenu slots for selecting filter */
4602 opts[i].ch = ctrl('P');
4603 opts[i].rval = 10;
4604 opts[i].name = "^P";
4605 opts[i++].label = N_("Prev Filter");
4607 opts[i].ch = ctrl('N');
4608 opts[i].rval = 11;
4609 opts[i].name = "^N";
4610 opts[i++].label = N_("Next Filter");
4612 if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
4613 filters = filters->next;
4616 if(F_ON(F_VERBOSE_POST, ps_global)){
4617 /* setup keymenu slot to toggle verbose mode */
4618 opts[i].ch = ctrl('W');
4619 opts[i].rval = 12;
4620 opts[i].name = "^W";
4621 verbose_label = i++;
4624 if(allow_flowed){
4625 /* setup keymenu slot to toggle flowed mode */
4626 opts[i].ch = ctrl('V');
4627 opts[i].rval = 22;
4628 opts[i].name = "^V";
4629 flowing_label = i++;
4630 flowing_requested = 1;
4633 if(F_ON(F_NO_FCC_ATTACH, ps_global)){
4634 /* setup keymenu slot to toggle attacment on fcc */
4635 opts[i].ch = ctrl('F');
4636 opts[i].rval = 21;
4637 opts[i].name = "^F";
4638 fcc_label = i++;
4641 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4642 if(F_ON(F_BACKGROUND_POST, ps_global)){
4643 opts[i].ch = ctrl('R');
4644 opts[i].rval = 15;
4645 opts[i].name = "^R";
4646 bg_label = i++;
4648 #endif
4650 opts[i].ch = 'p';
4651 opts[i].rval = 'p';
4652 opts[i].name = "P";
4653 opts[i++].label = N_("Priority");
4655 #ifdef SMIME
4656 if(F_OFF(F_DONT_DO_SMIME, ps_global)){
4657 opts[i].ch = 'e';
4658 opts[i].rval = 'e';
4659 opts[i].name = "E";
4660 opts[i++].label = "Encrypt";
4662 opts[i].ch = 'g';
4663 opts[i].rval = 'g';
4664 opts[i].name = "G";
4665 opts[i++].label = "Sign";
4667 if(ps_global->smime){
4668 ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
4669 ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
4672 #endif
4674 double_rad = i;
4676 if(F_ON(F_DSN, ps_global)){
4677 /* setup keymenu slots to toggle dsn bits */
4678 opts[i].ch = 'd';
4679 opts[i].rval = 'd';
4680 opts[i].name = "D";
4681 opts[i].label = N_("DSNOpts");
4682 dsn_label = i++;
4683 opts[i].ch = -2;
4684 opts[i].rval = 's';
4685 opts[i].name = "S";
4686 opts[i++].label = "";
4687 opts[i].ch = -2;
4688 opts[i].rval = 'x';
4689 opts[i].name = "X";
4690 opts[i++].label = "";
4691 opts[i].ch = -2;
4692 opts[i].rval = 'h';
4693 opts[i].name = "H";
4694 opts[i++].label = "";
4697 if(filters){
4698 opts[i].ch = KEY_UP;
4699 opts[i].rval = 10;
4700 opts[i].name = "";
4701 opts[i++].label = "";
4703 opts[i].ch = KEY_DOWN;
4704 opts[i].rval = 11;
4705 opts[i].name = "";
4706 opts[i++].label = "";
4709 opts[i].ch = -1;
4711 fix_windsize(ps_global);
4713 while(1){
4714 if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
4715 *p = '\0';
4716 else
4717 p = NULL;
4719 lparen = 0;
4720 strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
4721 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4722 optp = tmp_20k_buf + strlen(tmp_20k_buf);
4724 if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
4725 sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4727 if(allow_flowed && !flowing_requested){
4728 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4729 if(!lparen){
4730 *optp++ = ' ';
4731 *optp++ = '(';
4732 lparen++;
4734 else
4735 *optp++ = ' ';
4738 sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4741 if(filters){
4742 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4743 if(!lparen){
4744 *optp++ = ' ';
4745 *optp++ = '(';
4746 lparen++;
4748 else{
4749 *optp++ = ',';
4750 *optp++ = ' ';
4754 if(filters->filter){
4755 sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4756 sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4757 if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
4758 *optp++ = '\"';
4760 else
4761 sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4764 if((call_mailer_flags & CM_VERBOSE) || background_requested){
4765 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4766 if(!lparen){
4767 *optp++ = ' ';
4768 *optp++ = '(';
4769 lparen++;
4771 else
4772 *optp++ = ' ';
4775 sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4776 if(call_mailer_flags & CM_VERBOSE)
4777 sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4779 if(background_requested)
4780 sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4782 sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4785 if(g_rolenick && !(he && he[N_FROM].dirty)){
4786 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4787 if(!lparen){
4788 *optp++ = ' ';
4789 *optp++ = '(';
4790 lparen++;
4792 else
4793 *optp++ = ' ';
4796 sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4797 sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4798 sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4801 if(call_mailer_flags & CM_DSN_SHOW){
4802 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4803 if(!lparen){
4804 *optp++ = ' ';
4805 *optp++ = '(';
4806 lparen++;
4808 else{
4809 *optp++ = ',';
4810 *optp++ = ' ';
4814 sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4817 #ifdef SMIME
4818 if(ps_global->smime && ps_global->smime->do_encrypt){
4819 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4820 if(!lparen){
4821 *optp++ = ' ';
4822 *optp++ = '(';
4823 lparen++;
4825 else{
4826 *optp++ = ',';
4827 *optp++ = ' ';
4831 sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4834 if(ps_global->smime && ps_global->smime->do_sign){
4835 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4836 if(!lparen){
4837 *optp++ = ' ';
4838 *optp++ = '(';
4839 lparen++;
4841 else{
4842 *optp++ = ',';
4843 *optp++ = ' ';
4847 sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4849 #endif
4851 if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
4852 *optp++ = ')';
4854 sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4855 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4857 if(p)
4858 *p = ' ';
4860 if(flowing_label)
4861 opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
4863 if(verbose_label)
4864 opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
4866 if(bg_label)
4867 opts[bg_label].label = background_requested
4868 ? N_("Foreground") : N_("Background");
4870 if(fcc_label)
4871 opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
4872 : N_("No Fcc Atmts ");
4874 if(F_ON(F_DSN, ps_global)){
4875 if(call_mailer_flags & CM_DSN_SHOW){
4876 opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
4877 ? N_("NoDelay") : N_("Delay");
4878 opts[dsn_label+1].ch = 's';
4879 opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
4880 ? N_("NoSuccess") : N_("Success");
4881 opts[dsn_label+2].ch = 'x';
4882 opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
4883 ? N_("ErrRets") : N_("NoErrRets");
4884 opts[dsn_label+3].ch = 'h';
4885 opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
4886 ? N_("RetHdrs") : N_("RetFull");
4890 if(double_rad +
4891 ((call_mailer_flags & CM_DSN_SHOW)
4892 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
4893 rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4894 'y', 'z',
4895 (F_ON(F_DSN, ps_global) && allow_flowed)
4896 ? h_send_prompt_dsn_flowed :
4897 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4898 allow_flowed ? h_send_prompt_flowed :
4899 h_send_prompt,
4900 RB_NORM);
4901 else
4902 rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4903 'y', 'z',
4904 (double_rad +
4905 ((call_mailer_flags & CM_DSN_SHOW)
4906 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
4907 ? NO_HELP :
4908 (F_ON(F_DSN, ps_global) && allow_flowed)
4909 ? h_send_prompt_dsn_flowed :
4910 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4911 allow_flowed ? h_send_prompt_flowed :
4912 h_send_prompt,
4913 RB_NORM);
4915 if(rv == 'y'){ /* user ACCEPTS! */
4916 break;
4918 else if(rv == 'n'){ /* Declined! */
4919 rstr = _("No Message Sent");
4920 break;
4922 else if(rv == 'z'){ /* Cancelled! */
4923 rstr = _("Send Cancelled");
4924 break;
4926 else if(rv == 10){ /* PREVIOUS filter */
4927 filters = filters->prev;
4929 else if(rv == 11){ /* NEXT filter */
4930 filters = filters->next;
4932 else if(rv == 21){
4933 lmc.text_only = !lmc.text_only;
4935 else if(rv == 12){ /* flip verbose bit */
4936 if(call_mailer_flags & CM_VERBOSE)
4937 call_mailer_flags &= ~CM_VERBOSE;
4938 else
4939 call_mailer_flags |= CM_VERBOSE;
4941 if((call_mailer_flags & CM_VERBOSE) && background_requested)
4942 background_requested = 0;
4944 else if(rv == 22){ /* flip flowing bit */
4945 flowing_requested = !flowing_requested;
4947 else if(rv == 15){
4948 if((background_requested = !background_requested)
4949 && (call_mailer_flags & CM_VERBOSE))
4950 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
4952 else if(call_mailer_flags & CM_DSN_SHOW){
4953 if(rv == 's'){ /* flip success bit */
4954 call_mailer_flags ^= CM_DSN_SUCCESS;
4955 /* turn off related bits */
4956 if(call_mailer_flags & CM_DSN_SUCCESS)
4957 call_mailer_flags &= ~(CM_DSN_NEVER);
4959 else if(rv == 'd'){ /* flip delay bit */
4960 call_mailer_flags ^= CM_DSN_DELAY;
4961 /* turn off related bits */
4962 if(call_mailer_flags & CM_DSN_DELAY)
4963 call_mailer_flags &= ~(CM_DSN_NEVER);
4965 else if(rv == 'x'){ /* flip never bit */
4966 call_mailer_flags ^= CM_DSN_NEVER;
4967 /* turn off related bits */
4968 if(call_mailer_flags & CM_DSN_NEVER)
4969 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
4971 else if(rv == 'h'){ /* flip full bit */
4972 call_mailer_flags ^= CM_DSN_FULL;
4975 else if(rv == 'd'){ /* show dsn options */
4977 * When you turn on DSN, the default is to notify on
4978 * failure, success, or delay; and to return the whole
4979 * body on failure.
4981 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
4983 else if(rv == 'p'){ /* choose X-Priority */
4984 char *prio;
4986 prio = choose_a_priority(priority_requested);
4987 if((ps_global->redrawer = redraw_pico) != NULL){
4988 (*ps_global->redrawer)();
4989 fix_windsize(ps_global);
4992 if(prio){
4993 if(priority_requested)
4994 fs_give((void **) &priority_requested);
4996 if(prio[0])
4997 priority_requested = prio;
4998 else
4999 fs_give((void **) &prio);
5002 #ifdef SMIME
5003 else if(rv=='e'){
5004 if(ps_global->smime)
5005 ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
5007 else if(rv=='g'){
5008 if(ps_global->smime)
5009 ps_global->smime->do_sign = !ps_global->smime->do_sign;
5011 #endif
5013 snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
5014 (call_mailer_flags & CM_DSN_NEVER)
5015 ? "Never" : "F",
5016 (call_mailer_flags & CM_DSN_DELAY)
5017 ? "D" : "",
5018 (call_mailer_flags & CM_DSN_SUCCESS)
5019 ? "S" : "",
5020 (call_mailer_flags & CM_DSN_NEVER)
5021 ? ""
5022 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
5023 : "-Hdrs");
5024 dsn_string[sizeof(dsn_string)-1] = '\0';
5027 /* remember selection */
5028 if(filters && filters->index > -1)
5029 sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
5031 if(filters){
5032 filters->prev->next = NULL; /* tie off list */
5033 while(filters){ /* then free it */
5034 fp = filters->next;
5035 if(filters->filter)
5036 fs_give((void **)&filters->filter);
5038 fs_give((void **)&filters);
5039 filters = fp;
5043 if(old_suspend)
5044 (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
5046 if(result)
5047 *result = rstr;
5049 ps_global->redrawer = redraw;
5051 return((rstr == NULL) ? 0 : 1);
5056 * Allow user to choose a priority for sending.
5058 * Returns an allocated priority on success, NULL otherwise.
5060 char *
5061 choose_a_priority(char *default_val)
5063 char *choice = NULL;
5064 char **priority_list, **lp;
5065 char *starting_val = NULL;
5066 char *none;
5067 int i, cnt;
5068 PRIORITY_S *p;
5070 for(cnt = 0, p = priorities; p && p->desc; p++)
5071 cnt++;
5073 cnt++; /* for NONE entry */
5074 lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
5075 memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
5077 for(i = 0, p = priorities; p && p->desc; p++){
5078 *lp = cpystr(p->desc);
5079 if(default_val && !strcmp(default_val, p->desc))
5080 starting_val = (*lp);
5082 lp++;
5085 none = _("NONE - No X-Priority header included");
5086 *lp = cpystr(none);
5087 if(!starting_val)
5088 starting_val = (*lp);
5090 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5091 TRANSLATORS: Print something1 using something2.
5092 "priorities" is something1 */
5093 choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
5094 _("priorities"), h_select_priority_screen,
5095 _("HELP FOR SELECTING A PRIORITY"),
5096 starting_val);
5098 if(!choice)
5099 q_status_message(SM_ORDER, 1, 4, _("No change"));
5100 else if(!strcmp(choice, none))
5101 choice[0] = '\0';
5103 free_list_array(&priority_list);
5105 return(choice);
5110 dont_flow_this_time(void)
5112 return(flowing_requested ? 0 : 1);
5116 /*----------------------------------------------------------------------
5117 Call back for pico to display mime type of attachment
5119 Args: file -- filename being attached
5121 Returns: returns 1 on success (message queued), zero otherwise (don't know
5122 type so nothing queued).
5123 ----*/
5125 mime_type_for_pico(char *file)
5127 BODY *body;
5128 int rv;
5129 void *file_contents;
5131 body = mail_newbody();
5132 body->type = TYPEOTHER;
5133 body->encoding = ENCOTHER;
5135 /* don't know where the cursor's been, reset it */
5136 clear_cursor_pos();
5137 if(!set_mime_type_by_extension(body, file)){
5138 if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5139 body->contents.text.data = file_contents;
5140 set_mime_type_by_grope(body);
5144 if(body->type != TYPEOTHER){
5145 rv = 1;
5146 q_status_message3(SM_ORDER, 0, 3,
5147 _("File %s attached as type %s/%s"), file,
5148 body_types[body->type],
5149 body->subtype ? body->subtype : rfc822_default_subtype(body->type));
5151 else
5152 rv = 0;
5154 pine_free_body(&body);
5155 return(rv);
5159 /*----------------------------------------------------------------------
5160 Call back for pico to receive an uploaded message
5162 Args: fname -- name for uploaded file (empty if they want us to assign it)
5163 size -- pointer to long to hold the attachment's size
5165 Notes: the attachment is uploaded to a temp file, and
5167 Returns: TRUE on success, FALSE otherwise
5168 ----*/
5170 upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
5172 char cmd[MAXPATH+1], *fnp = NULL;
5173 char *locale_name = NULL;
5174 long l;
5175 PIPE_S *syspipe;
5177 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5178 fname ? fname : "<NO FILE>"));
5180 if(!fname) /* no place for file name */
5181 return(0);
5183 if(!*fname){ /* caller wants temp file */
5184 if((fnp = temp_nam(NULL, "pu")) != NULL){
5185 strncpy(fname, fnp, fnlen);
5186 fname[fnlen-1] = '\0';
5187 our_unlink(fnp);
5188 fs_give((void **)&fnp);
5191 else
5192 locale_name = convert_to_locale(fname);
5194 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
5195 ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
5196 if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
5197 0, pipe_callback, pipe_report_error)) != NULL){
5198 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
5199 if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
5200 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5201 "Error determining size of %s: %s", fname,
5202 fnp = error_description(errno));
5203 dprint((1,
5204 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5205 cmd ? cmd : "?",
5206 fname ? fname : "?",
5207 fnp ? fnp : "?"));
5209 else if(size)
5210 *size = l;
5212 if(locale_name)
5213 fs_give((void **) &locale_name);
5215 return(l >= 0);
5217 else
5218 q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
5220 if(locale_name)
5221 fs_give((void **) &locale_name);
5223 return(0);
5227 char *
5228 cancel_for_pico(void (*redraw_pico)(void))
5230 int rv;
5231 char *rstr = NULL;
5232 char *prompt =
5233 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5234 void (*redraw)(void) = ps_global->redrawer;
5235 static ESCKEY_S opts[] = {
5236 {'c', 'c', "C", N_("Confirm")},
5237 {'n', 'n', "N", N_("No")},
5238 {'y', 'y', "", ""},
5239 {-1, 0, NULL, NULL}
5242 ps_global->redrawer = redraw_pico;
5243 fix_windsize(ps_global);
5245 while(1){
5246 rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
5247 'n', 'x', h_confirm_cancel, RB_NORM);
5248 if(rv == 'c'){ /* user ACCEPTS! */
5249 rstr = "";
5250 break;
5252 else if(rv == 'y'){
5253 q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
5254 display_message('x');
5256 else
5257 break;
5260 ps_global->redrawer = redraw;
5261 return(rstr);
5265 /*----------------------------------------------------------------------
5266 Pass the first text segment of the message thru the "send filter"
5268 Args: body pointer and address for storage object of old data
5270 Returns: returns 1 on success, zero on error.
5271 ----*/
5273 filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
5274 STORE_S **old, METAENV *header)
5276 char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
5277 int key = 0, include_hdrs = 0;
5278 gf_io_t gc, pc;
5279 STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
5280 ? &body->nested.part->body.contents.text.data
5281 : &body->contents.text.data),
5282 *tmp_so = NULL, *tmpf_so,
5283 *save_local_so, *readthis_so, *our_tmpf_so = NULL;
5284 #define DO_HEADERS 1
5286 if(fcmd
5287 && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
5288 &key, &include_hdrs, NULL))){
5289 if(tmpf){
5291 * We need WRITE_TO_LOCALE here because the user is going to
5292 * be operating on tmpf. We need to change it back after they
5293 * are done.
5295 if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
5296 if(key){
5297 so_puts(tmpf_so, filter_session_key());
5298 so_puts(tmpf_so, NEWLINE);
5302 * If the headers are wanted for filtering, we can just
5303 * stick them in the tmpf file that is already there before
5304 * putting the body in.
5306 if(include_hdrs){
5307 save_local_so = lmc.so;
5308 lmc.so = tmpf_so; /* write it to tmpf_so */
5309 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5310 pine_rfc822_header(header, body, NULL, NULL);
5311 lmc.so = save_local_so;
5314 so_seek(*so, 0L, 0);
5315 gf_set_so_readc(&gc, *so);
5316 gf_set_so_writec(&pc, tmpf_so);
5317 gf_filter_init();
5318 errstr = gf_pipe(gc, pc);
5319 gf_clear_so_readc(*so);
5320 gf_clear_so_writec(tmpf_so);
5321 so_give(&tmpf_so);
5323 else
5324 errstr = "Can't create space for filter temporary file.";
5326 else if(include_hdrs){
5328 * Gf_filter wants a single storage object to read from.
5329 * If headers are wanted for filtering we'll have to put them
5330 * and the body into a temp file first and then use that
5331 * as the storage object for gf_filter.
5332 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5333 * takes care of that.
5335 if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
5336 /* put headers in our_tmpf_so */
5337 save_local_so = lmc.so;
5338 lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
5339 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5340 pine_rfc822_header(header, body, NULL, NULL);
5341 lmc.so = save_local_so;
5343 /* put body in our_tmpf_so */
5344 so_seek(*so, 0L, 0);
5345 gf_set_so_readc(&gc, *so);
5346 gf_set_so_writec(&pc, our_tmpf_so);
5347 gf_filter_init();
5348 errstr = gf_pipe(gc, pc);
5349 gf_clear_so_readc(*so);
5350 gf_clear_so_writec(our_tmpf_so);
5352 /* tell gf_filter to read from our_tmpf_so instead of *so */
5353 readthis_so = our_tmpf_so;
5355 else
5356 errstr = "Can't create space for temporary file.";
5358 else
5359 readthis_so = *so;
5361 if(!errstr){
5362 if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
5363 gf_set_so_writec(&pc, tmp_so);
5364 ps_global->mangled_screen = 1;
5365 suspend_busy_cue();
5366 ClearScreen();
5367 fflush(stdout);
5368 if(tmpf){
5369 PIPE_S *fpipe;
5371 if((fpipe = open_system_pipe(cmd, NULL, NULL,
5372 PIPE_NOSHELL | PIPE_RESET,
5373 0, pipe_callback, pipe_report_error)) != NULL){
5374 if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
5376 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5377 if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5378 gf_set_so_readc(&gc, tmpf_so);
5379 gf_filter_init();
5380 errstr = gf_pipe(gc, pc);
5381 gf_clear_so_readc(tmpf_so);
5382 so_give(&tmpf_so);
5384 else
5385 errstr = "Can't open temp file filter wrote.";
5387 else
5388 errstr = "Filter command returned error.";
5390 else
5391 errstr = "Can't exec filter text.";
5393 else
5394 errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
5395 readthis_so, pc, NULL, 0, 0,
5396 pipe_callback);
5398 if(our_tmpf_so)
5399 so_give(&our_tmpf_so);
5401 gf_clear_so_writec(tmp_so);
5403 #ifdef _WINDOWS
5404 if(!errstr){
5406 * Can't really be using stdout, so don't print message that
5407 * gets printed in the else. Ideally the program being called
5408 * will wait after showing the message, we might want to look
5409 * into doing the waiting in console based apps... or not.
5411 #else
5412 if(errstr){
5413 unsigned long ch;
5415 fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
5416 fflush(stdout);
5417 while((ch = read_char(300)) != ctrl('M')
5418 && ch != NO_OP_IDLE)
5419 putchar(BELL);
5421 else{
5422 #endif /* _WINDOWS */
5423 BODY *b = (body->type == TYPEMULTIPART)
5424 ? &body->nested.part->body : body;
5426 *old = *so; /* save old so */
5427 *so = tmp_so; /* return new one */
5428 (*so)->attr = copy_parameters((*old)->attr);
5431 * If the command said it would return new MIME
5432 * mime type data, check it out...
5434 if(mtf){
5435 char buf[MAILTMPLEN], *s;
5436 FILE *fp;
5438 if((fp = our_fopen(mtf, "rb")) != NULL){
5439 if(fgets(buf, sizeof(buf), fp)
5440 && !struncmp(buf, "content-", 8)
5441 && (s = strchr(buf+8, ':'))){
5442 BODY *nb = mail_newbody();
5444 for(*s++ = '\0'; *s == ' '; s++)
5447 rfc822_parse_content_header(nb,
5448 (char *) ucase((unsigned char *) buf+8),s);
5449 if(nb->type == TYPETEXT
5450 && nb->subtype
5451 && (!b->subtype
5452 || strucmp(b->subtype, nb->subtype))){
5453 if(b->subtype)
5454 fs_give((void **) &b->subtype);
5456 b->subtype = nb->subtype;
5457 nb->subtype = NULL;
5459 mail_free_body_parameter(&b->parameter);
5460 b->parameter = nb->parameter;
5461 nb->parameter = NULL;
5462 mail_free_body_parameter(&nb->parameter);
5465 mail_free_body(&nb);
5468 fclose(fp);
5473 * Reevaluate the encoding in case form's changed...
5475 b->encoding = ENCOTHER;
5476 set_mime_type_by_grope(b);
5479 ClearScreen();
5480 resume_busy_cue(0);
5482 else
5483 errstr = "Can't create space for filtered text.";
5486 fs_give((void **)&cmd);
5488 else
5489 return(0);
5491 if(tmpf){
5492 our_unlink(tmpf);
5493 fs_give((void **)&tmpf);
5496 if(mtf){
5497 our_unlink(mtf);
5498 fs_give((void **) &mtf);
5501 if(resultf){
5502 if(name_file_size(resultf) > 0L)
5503 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
5504 our_unlink(resultf);
5505 fs_give((void **)&resultf);
5507 else if(errstr){
5508 if(tmp_so)
5509 so_give(&tmp_so);
5511 q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
5512 errstr);
5513 dprint((1, "Filter FAILED: %s\n",
5514 errstr ? errstr : "?"));
5517 return(errstr == NULL);
5521 /*----------------------------------------------------------------------
5522 Copy the newsgroup name of the given mailbox into the given buffer
5524 Args:
5526 Returns:
5527 ----*/
5528 void
5529 pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
5531 NETMBX mb;
5533 if(*mailbox == '#'){ /* Strip the leading "#news." */
5534 strncpy(group_name, mailbox + 6, len-1);
5535 group_name[len-1] = '\0';
5537 else if(mail_valid_net_parse(mailbox, &mb)){
5538 pine_send_newsgroup_name(mb.mailbox, group_name, len);
5540 else
5541 *group_name = '\0';
5545 /*----------------------------------------------------------------------
5546 Generate and send a message back to the pine development team
5548 Args: none
5550 Returns: none
5551 ----*/
5552 void
5553 phone_home(char *addr)
5555 char tmp[MAX_ADDRESS];
5556 ENVELOPE *outgoing;
5557 BODY *body;
5559 outgoing = mail_newenvelope();
5560 if(!addr || !strindex(addr, '@')){
5561 snprintf(addr = tmp, sizeof(tmp), "alpine%s@%s", PHONE_HOME_VERSION, PHONE_HOME_HOST);
5562 tmp[sizeof(tmp)-1] = '\0';
5565 rfc822_parse_adrlist(&outgoing->to, addr, ps_global->maildomain);
5567 outgoing->message_id = generate_message_id();
5568 outgoing->subject = cpystr("Document Request");
5569 outgoing->from = phone_home_from();
5571 body = mail_newbody();
5572 body->type = TYPETEXT;
5574 if((body->contents.text.data = (void *)so_get(PicoText,NULL,EDIT_ACCESS)) != NULL){
5575 so_puts((STORE_S *)body->contents.text.data, "Document request: ");
5576 so_puts((STORE_S *)body->contents.text.data, "Alpine-");
5577 so_puts((STORE_S *)body->contents.text.data, ALPINE_VERSION);
5578 if(ps_global->first_time_user)
5579 so_puts((STORE_S *)body->contents.text.data, " for New Users");
5581 if(ps_global->VAR_INBOX_PATH && ps_global->VAR_INBOX_PATH[0] == '{')
5582 so_puts((STORE_S *)body->contents.text.data, " and IMAP");
5584 if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
5585 && ps_global->VAR_NNTP_SERVER[0][0])
5586 so_puts((STORE_S *)body->contents.text.data, " and NNTP");
5588 (void) pine_simple_send(outgoing, &body, NULL,NULL,NULL,NULL, SS_NULLRP);
5590 q_status_message(SM_ORDER, 1, 3, "Thank you for being counted!");
5592 else
5593 q_status_message(SM_ORDER | SM_DING, 3, 4,
5594 "Problem creating space for message text.");
5596 mail_free_envelope(&outgoing);
5597 pine_free_body(&body);
5602 /*----------------------------------------------------------------------
5603 Set up fields for passing to pico. Assumes first text part is
5604 intended to be passed along for editing, and is in the form of
5605 of a storage object brought into existence sometime before pico_send().
5606 -----*/
5607 void
5608 outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
5609 PATMT **pico_a, int from_bounce)
5611 PINEFIELD *pf;
5614 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5615 * is guaranteed to be of type PicoText!
5617 if(bod->type == TYPETEXT){
5618 *text = so_text((STORE_S *) bod->contents.text.data);
5620 /* mark storage object as user edited */
5621 if(!from_bounce)
5622 (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
5624 else if(bod->type == TYPEMULTIPART){
5625 PART *part;
5626 PATMT **ppa;
5627 char *type, *name, *p;
5628 int name_l;
5631 * We used to jump out the window if the first part wasn't text,
5632 * but that may not be the case when bouncing a message with
5633 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5634 * contents of the first part to send is still ALWAYS in a
5635 * PicoText storage object, *AND* if that object doesn't contain
5636 * data of type text, then it must contain THE ENCODED NON-TEXT
5637 * DATA of the piece being sent.
5639 * It's up to the programmer to make sure that such a message is
5640 * sent via pine_simple_send and never get to the composer via
5641 * pine_send.
5643 * Make sense?
5645 *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
5647 /* mark storage object as user edited */
5648 if(!from_bounce)
5649 (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
5652 * If we already had a list, blast it now, so we can build a new
5653 * attachment list that reflects what's really there...
5655 if(pico_a){
5656 free_attachment_list(pico_a);
5659 /* Simplifyihg assumption #28e. (see cross reference)
5660 All parts in the body passed in here that are not already
5661 in the attachments list are added to the end of the attachments
5662 list. Attachment items not in the body list will be taken care
5663 of in strings2outgoing, but they are unlikey to occur
5666 for(part = bod->nested.part->next; part != NULL; part = part->next) {
5667 /* Already in list? */
5668 for(ppa = pico_a;
5669 *ppa && strcmp((*ppa)->id, part->body.id);
5670 ppa = &(*ppa)->next)
5673 if(!*ppa){ /* Not in the list! append it... */
5674 *ppa = (PATMT *)fs_get(sizeof(PATMT));
5676 if(part->body.description){
5677 char *p;
5678 size_t len;
5680 len = 4*strlen(part->body.description)+1;
5681 p = (char *)fs_get(len*sizeof(char));
5682 if(rfc1522_decode_to_utf8((unsigned char *)p,
5683 len, part->body.description) == (unsigned char *) p){
5684 (*ppa)->description = p;
5686 else{
5687 fs_give((void **)&p);
5688 (*ppa)->description = cpystr(part->body.description);
5691 else
5692 (*ppa)->description = cpystr("");
5694 type = type_desc(part->body.type, part->body.subtype,
5695 part->body.parameter, NULL, 0);
5698 * If we can find a "name" parm, display that too...
5700 if((name = parameter_val(part->body.parameter, "name")) != NULL){
5701 /* Convert any [ or ]'s the name contained */
5702 for(p = name; *p ; p++)
5703 if(*p == '[')
5704 *p = '(';
5705 else if(*p == ']')
5706 *p = ')';
5708 name_l = p - name;
5710 else
5711 name_l = 0;
5713 (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
5715 snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
5716 name ? ": " : "", name ? name : "");
5717 (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
5719 if(name)
5720 fs_give((void **) &name);
5722 (*ppa)->flags = A_FLIT;
5723 (*ppa)->size = cpystr(byte_string(
5724 send_body_size(&part->body)));
5725 if(!part->body.id)
5726 part->body.id = generate_message_id();
5728 (*ppa)->id = cpystr(part->body.id);
5729 (*ppa)->next = NULL;
5736 /*------------------------------------------------------------------
5737 Malloc strings to pass to composer editor because it expects
5738 such strings so it can realloc them
5739 -----------------------------------------------------------------*/
5741 * turn any address fields into text strings
5744 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5745 * NOT to be RFC1522 decoded. Said differently, they're understood
5746 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5747 * original charset tagging as far into the compose/send pipe as
5748 * we can.
5750 for(pf = header->local; pf && pf->name; pf = pf->next)
5751 if(pf->canedit)
5752 switch(pf->type){
5753 case Address :
5754 if(pf->addr){
5755 char *p, *t, *u;
5756 long l;
5758 pf->scratch = addr_list_string(*pf->addr, NULL, 1);
5761 * Scan for and fix-up patently bogus fields.
5763 * NOTE: collaboration with this code and what's done in
5764 * reply.c:reply_cp_addr to package up the bogus stuff
5765 * is required.
5767 for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
5768 for(t = p; ; t--)
5769 if(*t == '&'){ /* find "leading" token */
5770 int replacelen;
5773 * Rfc822_cat has been changed so that it now quotes
5774 * this sometimes. So we have to look out for quotes
5775 * which confuse the decoder. It was only quoting
5776 * because we were putting \r \n in the input, I think.
5778 if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
5779 t[-1] = p[-1] = ' ';
5781 *t++ = ' '; /* replace token */
5782 *p = '\0'; /* tie off string */
5783 u = rfc822_base64((unsigned char *) t,
5784 (unsigned long) strlen(t),
5785 (unsigned long *) &l);
5786 if(!u)
5787 u = "";
5789 replacelen = strlen(t);
5790 *p = '@'; /* restore 'p' */
5791 rplstr(p, strlen(p), 12, ""); /* clear special token */
5792 rplstr(t, strlen(u)-replacelen+1, replacelen, u);
5793 if(u)
5794 fs_give((void **) &u);
5796 if(HE(pf))
5797 HE(pf)->start_here = 1;
5799 break;
5801 else if(t == pf->scratch)
5802 break;
5804 removing_leading_white_space(pf->scratch);
5805 if(pf->scratch){
5806 size_t l;
5809 * Replace control characters with ^C notation, unless
5810 * some conditions are met (see istrncpy).
5811 * If user doesn't edit, then we switch back to the
5812 * original version. If user does edit, then all bets
5813 * are off.
5815 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5816 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5817 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5818 fs_give((void **)&pf->scratch);
5819 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5822 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5826 break;
5828 case Subject :
5829 if(pf->text){
5830 char *p, *src;
5831 size_t len;
5833 src = pf->scratch ? pf->scratch
5834 : (*pf->text) ? *pf->text : "";
5836 len = 4*strlen(src)+1;
5837 p = (char *)fs_get(len * sizeof(char));
5838 if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
5839 if(pf->scratch)
5840 fs_give((void **)&pf->scratch);
5842 pf->scratch = p;
5844 else{
5845 fs_give((void **)&p);
5846 if(!pf->scratch)
5847 pf->scratch = cpystr(src);
5850 if(pf->scratch){
5851 size_t l;
5854 * Replace control characters with ^C notation, unless
5855 * some conditions are met (see istrncpy).
5856 * If user doesn't edit, then we switch back to the
5857 * original version. If user does edit, then all bets
5858 * are off.
5860 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5861 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5862 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5863 fs_give((void **)&pf->scratch);
5864 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5867 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5871 break;
5873 default :
5874 break;
5879 /*----------------------------------------------------------------------
5880 Restore fields returned from pico to form useful to sending
5881 routines.
5882 -----*/
5883 void
5884 strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
5886 PINEFIELD *pf;
5887 int we_cancel = 0;
5889 we_cancel = busy_cue(NULL, NULL, 1);
5892 * turn any local address strings into address lists
5894 for(pf = header->local; pf && pf->name; pf = pf->next)
5895 if(pf->scratch){
5896 char *the_address = NULL;
5898 switch(pf->type){
5899 case Address :
5900 removing_trailing_white_space(pf->scratch);
5902 if((the_address || *pf->scratch) && pf->addr){
5903 ADDRESS *new_addr = NULL;
5904 static char *fakedomain = "@";
5906 if(!the_address)
5907 the_address = pf->scratch;
5909 rfc822_parse_adrlist(&new_addr, the_address,
5910 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
5911 ? fakedomain : ps_global->maildomain);
5912 mail_free_address(pf->addr); /* free old addrs */
5913 *pf->addr = new_addr; /* assign new addr */
5915 else if(pf->addr)
5916 mail_free_address(pf->addr); /* free old addrs */
5918 break;
5920 case Subject :
5921 if(*pf->text)
5922 fs_give((void **)pf->text);
5924 if(*pf->scratch){
5925 *pf->text = cpystr(pf->scratch);
5928 break;
5930 default :
5931 break;
5934 fs_give((void **)&pf->scratch); /* free now useless text */
5937 create_message_body(bod, attach, flow_it);
5939 if(we_cancel)
5940 cancel_busy_cue(-1);
5944 /*----------------------------------------------------------------------
5946 The head of the body list here is always either TEXT or MULTIPART. It may be
5947 changed from TEXT to MULTIPART if there are attachments to be added
5948 and it is not already multipart.
5949 ----*/
5950 void
5951 create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
5953 PART *p, **pp;
5954 PATMT *pa;
5955 BODY *tmp_body, *text_body = NULL;
5956 void *file_contents;
5957 PARAMETER **parmp;
5958 char *lc;
5960 TIME_STAMP("create_body start.", 1);
5962 * if conditions are met short circuit MIME wrapping
5964 if((*b)->type != TYPEMULTIPART && !attach){
5966 /* only override assigned encoding if it might need upgrading */
5967 if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
5968 (*b)->encoding = ENCOTHER;
5970 create_message_body_text(*b, flow_it);
5972 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
5973 || !((*b)->encoding == ENC8BIT
5974 || (*b)->encoding == ENCBINARY)){
5975 TIME_STAMP("create_body end.", 1);
5976 return;
5978 else /* protect 8bit in multipart */
5979 text_body = *b;
5982 if((*b)->type == TYPETEXT) {
5983 /*-- Current type is text, but there are attachments to add --*/
5984 /*-- Upgrade to a TYPEMULTIPART --*/
5985 tmp_body = (BODY *)mail_newbody();
5986 tmp_body->type = TYPEMULTIPART;
5987 tmp_body->nested.part = mail_newbody_part();
5988 if(text_body){
5990 * Why do we do this?
5991 * The problem is that base64 or quoted-printable encoding is
5992 * sensitive to having random data appended to it's end. If
5993 * we use a single part TEXT message and something in between
5994 * us and the end appends advertising without adjusting for
5995 * the encoding, the message is screwed up. So we wrap the
5996 * text part inside a multipart and then the appended data
5997 * will come after the boundary.
5999 * We wish we could do this on the way out the door in a
6000 * child of post_rfc822_output because at that point we know
6001 * the character set and the encoding being used. For example,
6002 * iso-2022-jp is an encoding that is not sensitive to data
6003 * appended to the end, so it wouldn't need to be wrapped.
6004 * We could conceivably have post_rfc822_body inspect the
6005 * body and change it before doing the output. It would work
6006 * but would be very fragile. We'd be passed a body from
6007 * c-client to output and instead of just doing the output
6008 * we'd change the body and then output it. Not worth it
6009 * since the multipart wrapping is completely correct for
6010 * MIME-aware mailers.
6012 (void) copy_body(&(tmp_body->nested.part->body), *b);
6013 /* move contents which were NOT copied */
6014 tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
6015 (*b)->contents.text.data = NULL;
6017 else{
6018 tmp_body->nested.part->body = **b;
6020 (*b)->subtype = (*b)->id = (*b)->description = NULL;
6021 (*b)->parameter = NULL;
6022 (*b)->contents.text.data = NULL;
6025 pine_free_body(b);
6026 *b = tmp_body;
6029 if(!text_body){
6030 /*-- Now type must be MULTIPART with first part text --*/
6031 (*b)->nested.part->body.encoding = ENCOTHER;
6032 create_message_body_text(&((*b)->nested.part->body), flow_it);
6035 /*------ Go through the parts list remove those to be deleted -----*/
6036 for(pp = &(*b)->nested.part->next; *pp;){
6037 for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
6038 /* already existed? */
6039 if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
6040 char *orig_descp = NULL, *cs = NULL;
6043 * decode original to see if it matches what was decoded
6044 * when we sent it in.
6047 if((*pp)->body.description)
6048 orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
6049 SIZEOF_20KBUF, (*pp)->body.description);
6051 if(!(*pp)->body.description /* update description? */
6052 || (pa->description && strcmp(pa->description, orig_descp))){
6053 if((*pp)->body.description)
6054 fs_give((void **) &(*pp)->body.description);
6056 /* encoding happens as msg text is written */
6057 (*pp)->body.description = cpystr(pa->description);
6060 if(cs)
6061 fs_give((void **) &cs);
6063 break;
6066 if(pa == NULL){
6067 p = *pp; /* prepare to zap *pp */
6068 *pp = p->next; /* pull next one in list up */
6069 p->next = NULL; /* tie off removed node */
6071 pine_free_body_data(&p->body); /* clean up contained data */
6072 mail_free_body_part(&p); /* free up the part */
6074 else
6075 pp = &(*pp)->next;
6078 /*---------- Now add any new attachments ---------*/
6079 for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
6080 for(pa = attach; pa != NULL; pa = pa->next) {
6081 if(pa->id != NULL)
6082 continue; /* Has an ID, it's old */
6085 * the idea is handle ALL attachments as open FILE *'s. Actual
6086 * encoding and such is handled at the time the message
6087 * is shoved into the mail slot or written to disk...
6089 * Also, we never unlink a file, so it's up to whoever opens
6090 * it to deal with tmpfile issues.
6092 if((file_contents = (void *)so_get(FileStar, pa->filename,
6093 READ_ACCESS)) == NULL){
6094 q_status_message2(SM_ORDER | SM_DING, 3, 4,
6095 _("Error \"%s\", couldn't attach file \"%s\""),
6096 error_description(errno), pa->filename);
6097 display_message('x');
6098 continue;
6101 p->next = mail_newbody_part();
6102 p = p->next;
6103 p->body.id = generate_message_id();
6104 p->body.contents.text.data = file_contents;
6107 * Set type to unknown and let set_mime_type_by_* figure it out.
6108 * Always encode attachments we add as BINARY.
6110 p->body.type = TYPEOTHER;
6111 p->body.encoding = ENCBINARY;
6112 p->body.size.bytes = name_file_size(pa->filename);
6113 if(!set_mime_type_by_extension(&p->body, pa->filename)){
6114 set_mime_type_by_grope(&p->body);
6115 set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
6118 so_release((STORE_S *)p->body.contents.text.data);
6120 if(pa->description) /* encoding happens when msg written */
6121 p->body.description = cpystr(pa->description);
6123 /* Add name attribute for backward compatibility */
6124 for(parmp = &p->body.parameter; *parmp; )
6125 if(!struncmp((*parmp)->attribute, "name", 4)
6126 && (!*((*parmp)->attribute + 4)
6127 || *((*parmp)->attribute + 4) == '*')){
6128 PARAMETER *free_me = *parmp;
6129 *parmp = (*parmp)->next;
6130 free_me->next = NULL;
6131 mail_free_body_parameter(&free_me);
6133 else
6134 parmp = &(*parmp)->next;
6136 set_parameter(parmp, "name",
6137 pa->filename
6138 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6139 : NULL);
6141 /* Then set the Content-Disposition ala RFC1806 */
6142 if(!p->body.disposition.type){
6143 p->body.disposition.type = cpystr("attachment");
6144 for(parmp = &p->body.disposition.parameter; *parmp; )
6145 if(!struncmp((*parmp)->attribute, "filename", 4)
6146 && (!*((*parmp)->attribute + 4)
6147 || *((*parmp)->attribute + 4) == '*')){
6148 PARAMETER *free_me = *parmp;
6149 *parmp = (*parmp)->next;
6150 free_me->next = NULL;
6151 mail_free_body_parameter(&free_me);
6153 else
6154 parmp = &(*parmp)->next;
6156 set_parameter(parmp, "filename",
6157 pa->filename
6158 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6159 : NULL);
6162 p->next = NULL;
6163 pa->id = cpystr(p->body.id);
6167 * Now, if this multipart has but one text piece (that is, no
6168 * attachments), then downgrade from a composite type to a discrete
6169 * text/plain message if CTE is not 8bit.
6171 if(!(*b)->nested.part->next
6172 && (*b)->nested.part->body.type == TYPETEXT
6173 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
6174 || !((*b)->nested.part->body.encoding == ENC8BIT
6175 || (*b)->nested.part->body.encoding == ENCBINARY))){
6176 /* Clone the interesting body part */
6177 tmp_body = mail_newbody();
6178 *tmp_body = (*b)->nested.part->body;
6179 /* and rub out what we don't want cleaned up when it's free'd */
6180 mail_initbody(&(*b)->nested.part->body);
6181 mail_free_body(b);
6182 *b = tmp_body;
6186 TIME_STAMP("create_body end.", 1);
6191 * Fill in text BODY part's structure
6194 void
6195 create_message_body_text(struct mail_bodystruct *b, int flow_it)
6197 set_mime_type_by_grope(b);
6198 if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
6199 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
6200 && flow_it)
6201 set_parameter(b ? &b->parameter : NULL, "format", "flowed");
6203 set_body_size(b);
6208 * free_attachment_list - free attachments in given list
6210 void
6211 free_attachment_list(PATMT **alist)
6213 PATMT *leading;
6215 while(alist && *alist){ /* pointer pointing to something */
6216 leading = (*alist)->next;
6217 if((*alist)->description)
6218 fs_give((void **)&(*alist)->description);
6220 if((*alist)->filename){
6221 if((*alist)->flags & A_TMP)
6222 if(our_unlink((*alist)->filename) < 0)
6223 dprint((1, "-- Can't unlink(%s): %s\n",
6224 (*alist)->filename ? (*alist)->filename : "?",
6225 error_description(errno)));
6227 fs_give((void **)&(*alist)->filename);
6230 if((*alist)->size)
6231 fs_give((void **)&(*alist)->size);
6233 if((*alist)->id)
6234 fs_give((void **)&(*alist)->id);
6236 fs_give((void **)alist);
6238 *alist = leading;
6243 void
6244 set_body_size(struct mail_bodystruct *b)
6246 unsigned char c;
6247 int we_cancel = 0;
6249 we_cancel = busy_cue(NULL, NULL, 1);
6250 so_seek((STORE_S *)b->contents.text.data, 0L, 0);
6251 b->size.bytes = 0L;
6252 while(so_readc(&c, (STORE_S *)b->contents.text.data))
6253 b->size.bytes++;
6255 if(we_cancel)
6256 cancel_busy_cue(-1);
6261 * view_as_rich - set the rich_header flag
6263 * name - name of the header field
6264 * deflt - default value to return if user didn't set it
6266 * Note: if the user tries to turn them all off with "", then
6267 * we take that to mean default, since otherwise there is no
6268 * way to get to the headers.
6271 view_as_rich(char *name, int deflt)
6273 char **p;
6274 char *q;
6276 p = ps_global->VAR_COMP_HDRS;
6278 if(p && *p && **p){
6279 for(; (q = *p) != NULL; p++){
6280 if(!struncmp(q, name, strlen(name)))
6281 return 0; /* 0 means we *do* view it by default */
6284 return 1; /* 1 means it starts out hidden */
6286 return(deflt);
6291 * background_posting - return whether or not we're already in the process
6292 * of posting
6295 background_posting(int gripe)
6297 if(ps_global->post){
6298 if(gripe)
6299 q_status_message(SM_ORDER|SM_DING, 3, 3,
6300 _("Can't post while posting!"));
6301 return(1);
6304 return(0);
6308 /*----------------------------------------------------------------------
6309 Validate the given subject relative to any news groups.
6311 Args: none
6313 Returns: always returns 1, but also returns error if
6314 ----*/
6316 valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
6318 struct headerentry *hp;
6320 if(expanded)
6321 *expanded = cpystr(given);
6323 if(error){
6325 * Now look for any header entry we passed to pico that has to do
6326 * with news. If there's no subject, gripe.
6328 for(hp = pbf->headents; hp->prompt; hp++)
6329 if(hp->help == h_composer_news){
6330 if(hp->hd_text->text[0] && !*given)
6331 *error = cpystr(
6332 _("News postings MUST have a subject! Please add one!"));
6334 break;
6338 return(0);
6343 * This is the build_address used by the composer to check for an address
6344 * in the addrbook.
6346 * Args: to -- the passed in line to parse
6347 * full_to -- Address of a pointer to return the full address in.
6348 * This will be allocated here and freed by the caller.
6349 * error -- Address of a pointer to return an error message in.
6350 * This will be allocated here and freed by the caller.
6351 * barg -- Address of a pointer to return the fcc in is in
6352 * fcc->tptr. It will have already been allocated by the
6353 * caller but we may free it and reallocate if we wish.
6354 * Caller will free it.
6356 * Result: 0 is returned if address was OK,
6357 * -1 if address wasn't OK.
6359 * Side effect: Can flush addrbook entry cache entries so they need to be
6360 * re-fetched afterwords.
6363 build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
6365 char *p;
6366 int ret_val, no_repo = 0, *save_nesting_level;
6367 BuildTo bldto;
6368 PrivateTop *pt = NULL;
6369 PrivateAffector *af = NULL;
6370 char *fcc_local = NULL;
6371 jmp_buf save_jmp_buf;
6373 dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
6375 /* check to see if to string is empty to avoid work */
6376 for(p = to; p && *p && isspace((unsigned char)*p); p++)
6377 ;/* do nothing */
6379 if(!p || !*p){
6380 if(full_to)
6381 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
6383 return 0;
6386 if(full_to != NULL)
6387 *full_to = (char *)NULL;
6389 if(error != NULL)
6390 *error = (char *)NULL;
6392 /* No guarantee cursor or status line is how we saved it */
6393 clear_cursor_pos();
6394 mark_status_unknown();
6396 if(ps_global->remote_abook_validity > 0 &&
6397 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6398 *mangled |= BUILDER_SCREEN_MANGLED;
6401 * If we end up jumping back here because somebody else changed one of
6402 * our addrbooks out from underneath us, we may well leak some memory.
6403 * That's probably ok since this will be very rare.
6405 * The reason for the memcpy of the jmp_buf is that we may actually
6406 * be indirectly calling this function from within the address book.
6407 * For example, we may be in the address book screen and then run
6408 * the ComposeTo command which puts us in the composer, then we call
6409 * build_address from there which resets addrbook_changed_unexpectedly.
6410 * Once we leave build_address we need to reset addrbook_changed_un...
6411 * because this position on the stack will no longer be valid.
6412 * Same is true of the other setjmp's in this file which are wrapped
6413 * in memcpy calls.
6415 save_nesting_level = cpyint(ab_nesting_level);
6416 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6417 if(setjmp(addrbook_changed_unexpectedly)){
6418 no_repo = 0;
6419 pt = NULL;
6420 af = NULL;
6421 fcc_local = NULL;
6422 if(error != NULL)
6423 *error = (char *)NULL;
6425 if(full_to && *full_to)
6426 fs_give((void **)full_to);
6428 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6429 dprint((1,
6430 "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
6431 addrbook_reset();
6432 ab_nesting_level = *save_nesting_level;
6435 ab_nesting_level++;
6436 bldto.type = Str;
6437 bldto.arg.str = to;
6438 ret_val = build_address_internal(bldto, full_to, error,
6439 barg ? &fcc_local : NULL,
6440 &no_repo, NULL, save_and_restore,
6441 0, mangled);
6442 ab_nesting_level--;
6443 if(save_nesting_level)
6444 fs_give((void **)&save_nesting_level);
6447 * Have to rfc1522_decode the full_to string before sending it back.
6449 if(full_to && *full_to ){
6450 char *q;
6451 size_t len;
6453 len = 4*strlen(*full_to)+1;
6454 q = (char *)fs_get(len * sizeof(char));
6455 p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
6457 /* p == q means that decoding happened, p is decoded *full_to */
6458 if(p == q){
6459 fs_give((void **)full_to);
6460 *full_to = p;
6462 else
6463 fs_give((void **)&q);
6466 if(fcc_local){
6467 unsigned long csum;
6469 /* Pt will point to headents[Fcc].bldr_private */
6470 pt = NULL;
6471 if(barg && barg->aff)
6472 pt = (PrivateTop *)(*barg->aff);
6475 * If *barg->aff is set, that means fcc was set from a list
6476 * during some previous builder call.
6477 * If the current To line contains the old expansion as a prefix, then
6478 * we should leave things as they are. In order to decide that,
6479 * we look at a hash value computed from the strings.
6481 if(pt && (af=pt->affector) && af->who == BP_To){
6482 int len;
6484 len = strlen(to);
6485 if(len >= af->cksumlen){
6486 int save;
6488 save = to[af->cksumlen];
6489 to[af->cksumlen] = '\0';
6490 csum = line_hash(to);
6491 to[af->cksumlen] = save;
6493 else
6494 csum = af->cksumval + 1; /* something not equal to cksumval */
6497 if(!pt ||
6498 !pt->affector ||
6499 (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
6501 /* replace fcc value */
6502 if(barg->tptr)
6503 fs_give((void **)&barg->tptr);
6505 barg->tptr = fcc_local;
6507 if(barg->aff){
6508 if(!pt){
6509 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6510 pt = (PrivateTop *)(*barg->aff);
6511 memset((void *)pt, 0, sizeof(PrivateTop));
6514 if(no_repo){
6515 if(!pt->affector)
6516 pt->affector =
6517 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6519 af = pt->affector;
6520 af->who = BP_To;
6521 af->cksumlen = strlen(((full_to && *full_to)
6522 ? *full_to : ""));
6523 af->cksumval = line_hash(((full_to && *full_to)
6524 ? *full_to : ""));
6526 else{
6528 * If result is reproducible, we don't keep track here.
6530 if(pt->affector)
6531 fs_give((void **)&pt->affector);
6535 else
6536 fs_give((void **)&fcc_local); /* unused in this case */
6539 /* This is so pico will erase the old message */
6540 if(error != NULL && *error == NULL)
6541 *error = cpystr("");
6543 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6544 flush_status_messages(1);
6545 return(ret_val);
6550 * This is the builder used by the composer for the Lcc line.
6552 * Args: lcc -- the passed in Lcc line to parse
6553 * full_lcc -- Address of a pointer to return the full address in.
6554 * This will be allocated here and freed by the caller.
6555 * error -- Address of a pointer to return an error message in.
6556 * This is not allocated so should not be freed by the caller.
6557 * barg -- This is a pointer to text for affected entries which
6558 * we may be changing. The first one in the list is the
6559 * To entry. We may put the name of the list in empty
6560 * group syntax form there (like List Name: ;).
6561 * The second one in the list is the fcc field.
6562 * The tptr members already point to text allocated in the
6563 * caller. We may free and reallocate here, caller will
6564 * free the result in any case.
6566 * Result: 0 is returned if address was OK,
6567 * -1 if address wasn't OK.
6569 * Side effect: Can flush addrbook entry cache entries so they need to be
6570 * re-fetched afterwords.
6573 build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
6575 int ret_val,
6576 no_repo = 0; /* fcc or lcc not reproducible */
6577 int *save_nesting_level;
6578 BuildTo bldlcc;
6579 PrivateTop *pt = NULL;
6580 PrivateAffector *af = NULL;
6581 char *p,
6582 *fcc_local = NULL,
6583 *to = NULL,
6584 *dummy;
6585 jmp_buf save_jmp_buf;
6587 dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
6589 /* check to see if to string is empty to avoid work */
6590 for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
6591 ;/* do nothing */
6593 if(!p || !*p){
6594 if(full_lcc)
6595 *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
6597 return 0;
6600 if(error != NULL)
6601 *error = (char *)NULL;
6603 if(ps_global->remote_abook_validity > 0 &&
6604 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6605 *mangled |= BUILDER_SCREEN_MANGLED;
6608 * If we end up jumping back here because somebody else changed one of
6609 * our addrbooks out from underneath us, we may well leak some memory.
6610 * That's probably ok since this will be very rare.
6612 save_nesting_level = cpyint(ab_nesting_level);
6613 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6614 if(setjmp(addrbook_changed_unexpectedly)){
6615 no_repo = 0;
6616 pt = NULL;
6617 af = NULL;
6618 fcc_local = NULL;
6619 to = NULL;
6620 if(error != NULL)
6621 *error = (char *)NULL;
6623 if(full_lcc && *full_lcc)
6624 fs_give((void **)full_lcc);
6626 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6627 dprint((1,
6628 "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
6629 addrbook_reset();
6630 ab_nesting_level = *save_nesting_level;
6633 ab_nesting_level++;
6634 bldlcc.type = Str;
6635 bldlcc.arg.str = lcc;
6638 * To is first affected_entry and Fcc is second.
6639 * The conditional stuff for the fcc argument says to only change the
6640 * fcc if the fcc pointer is passed in non-null, and the To pointer
6641 * is also non-null. If they are null, that means they've already been
6642 * entered (are sticky). We don't affect fcc if either fcc or To has
6643 * been typed in.
6645 ret_val = build_address_internal(bldlcc,
6646 full_lcc,
6647 error,
6648 (barg && barg->next && barg->next->tptr && barg->tptr)
6649 ? &fcc_local : NULL,
6650 &no_repo,
6651 (barg && barg->tptr) ? &to : NULL,
6652 save_and_restore, 0, mangled);
6654 ab_nesting_level--;
6655 if(save_nesting_level)
6656 fs_give((void **)&save_nesting_level);
6658 /* full_lcc is what ends up in the Lcc: line */
6659 if(full_lcc && *full_lcc){
6660 size_t len;
6663 * Have to rfc1522_decode the full_lcc string before sending it back.
6665 len = 4*strlen(*full_lcc)+1;
6666 p = (char *)fs_get(len * sizeof(char));
6667 if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
6668 fs_give((void **)full_lcc);
6669 *full_lcc = p;
6671 else
6672 fs_give((void **)&p);
6675 /* to is what ends up in the To: line */
6676 if(to && *to){
6677 unsigned long csum;
6678 size_t len;
6681 * Have to rfc1522_decode the full_to string before sending it back.
6683 len = 4*strlen(to)+1;
6684 p = (char *)fs_get(len * sizeof(char));
6685 dummy = NULL;
6686 if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
6688 * If the caller wants us to try to preserve the charset
6689 * information (they set aff) we copy it into encoded->etext.
6690 * We don't have to worry about pasting together pieces of
6691 * etext like we do in build_address because whenever the
6692 * Lcc line is setting the To line it will be setting the
6693 * whole line, not modifying it.
6694 * Pt will point to headents[To].bldr_private.
6696 if(barg && barg->aff){
6697 pt = (PrivateTop *)(*barg->aff);
6699 if(!pt){
6700 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6701 pt = (PrivateTop *)(*barg->aff);
6702 memset((void *)pt, 0, sizeof(PrivateTop));
6706 fs_give((void **)&to);
6707 to = p;
6709 else
6710 fs_give((void **)&p);
6712 if(dummy)
6713 fs_give((void **)&dummy);
6717 * This part is recording the fact that the To line was set to
6718 * what it is by entering something on the Lcc line. In particular,
6719 * if a list alias was entered here then the fullname of the list
6720 * goes in the To line. We save this affector information so that
6721 * we can tell it shouldn't be modified if we call build_addr_lcc
6722 * again unless we actually modified what's in the Lcc line so that
6723 * it doesn't start with the same thing. The problem we're solving
6724 * is that the contents of the Lcc line no longer look like the
6725 * list they were derived from.
6726 * Pt will point to headents[To].bldr_private.
6728 if(barg && barg->aff)
6729 pt = (PrivateTop *)(*barg->aff);
6731 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6732 int len;
6734 len = strlen(lcc);
6735 if(len >= af->cksumlen){
6736 int save;
6738 save = lcc[af->cksumlen];
6739 lcc[af->cksumlen] = '\0';
6740 csum = line_hash(lcc);
6741 lcc[af->cksumlen] = save;
6743 else
6744 csum = af->cksumval + 1; /* so they aren't equal */
6747 if(!pt ||
6748 !pt->affector ||
6749 pt->affector->who != BP_Lcc ||
6750 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6752 /* replace to value */
6753 if(barg->tptr && barg->tptr[0]){
6754 size_t l;
6755 char *t;
6757 l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
6758 t = (char *)fs_get((l+1) * sizeof(char));
6759 snprintf(t, l+1, "%s%s%s",
6760 barg->tptr,
6761 (to && *to) ? ", " : "",
6762 (to && *to) ? to : "");
6763 fs_give((void **)&barg->tptr);
6764 if(to)
6765 fs_give((void **)&to);
6767 barg->tptr = t;
6769 else{
6770 if(barg->tptr)
6771 fs_give((void **)&barg->tptr);
6773 barg->tptr = to;
6776 if(barg->aff){
6777 if(!pt){
6778 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6779 pt = (PrivateTop *)(*barg->aff);
6780 memset((void *)pt, 0, sizeof(PrivateTop));
6783 if(no_repo){
6784 if(!pt->affector)
6785 pt->affector =
6786 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6788 af = pt->affector;
6789 af->who = BP_Lcc;
6790 af->cksumlen = strlen(((full_lcc && *full_lcc)
6791 ? *full_lcc : ""));
6792 af->cksumval = line_hash(((full_lcc && *full_lcc)
6793 ? *full_lcc : ""));
6795 else{
6797 * If result is reproducible, we don't keep track here.
6799 if(pt->affector)
6800 fs_give((void **)&pt->affector);
6804 else
6805 fs_give((void **)&to); /* unused in this case */
6808 if(fcc_local){
6809 unsigned long csum;
6812 * If *barg->next->aff is set, that means fcc was set from a list
6813 * during some previous builder call. If the current Lcc line
6814 * contains the old expansion as a prefix, then we should leave
6815 * things as they are. In order to decide that we look at a hash
6816 * value computed from the strings.
6817 * Pt will point to headents[Fcc].bldr_private
6819 pt = NULL;
6820 if(barg && barg->next && barg->next->aff)
6821 pt = (PrivateTop *)(*barg->next->aff);
6823 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6824 int len;
6826 len = strlen(lcc);
6827 if(len >= af->cksumlen){
6828 int save;
6830 save = lcc[af->cksumlen];
6831 lcc[af->cksumlen] = '\0';
6832 csum = line_hash(lcc);
6833 lcc[af->cksumlen] = save;
6835 else
6836 csum = af->cksumval + 1; /* something not equal to cksumval */
6839 if(!pt ||
6840 !pt->affector ||
6841 pt->affector->who != BP_Lcc ||
6842 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6844 /* replace fcc value */
6845 if(barg->next->tptr)
6846 fs_give((void **)&barg->next->tptr);
6848 barg->next->tptr = fcc_local;
6850 if(barg->next->aff){
6851 if(!pt){
6852 *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
6853 pt = (PrivateTop *)(*barg->next->aff);
6854 memset((void *)pt, 0, sizeof(PrivateTop));
6857 if(no_repo){
6858 if(!pt->affector)
6859 pt->affector =
6860 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6862 af = pt->affector;
6863 af->who = BP_Lcc;
6864 af->cksumlen = strlen(((full_lcc && *full_lcc)
6865 ? *full_lcc : ""));
6866 af->cksumval = line_hash(((full_lcc && *full_lcc)
6867 ? *full_lcc : ""));
6869 else{
6871 * If result is reproducible, we don't keep track here.
6873 if(pt->affector)
6874 fs_give((void **)&pt->affector);
6878 else
6879 fs_give((void **)&fcc_local); /* unused in this case */
6883 if(error != NULL && *error == NULL)
6884 *error = cpystr("");
6886 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6887 flush_status_messages(0);
6888 return(ret_val);
6892 /*----------------------------------------------------------------------
6893 Verify and canonicalize news groups names.
6894 Called from the message composer
6896 Args: given_group -- List of groups typed by user
6897 expanded_group -- pointer to point to expanded list, which will be
6898 allocated here and freed in caller. If this is
6899 NULL, don't attempt to validate.
6900 error -- pointer to store error message
6901 fcc -- pointer to point to fcc, which will be
6902 allocated here and freed in caller
6904 Returns: 0 if all is OK
6905 -1 if addresses weren't valid
6907 Test the given list of newstroups against those recognized by our nntp
6908 servers. Testing by actually trying to open the list is much cheaper, both
6909 in bandwidth and memory, than yanking the whole list across the wire.
6910 ----*/
6912 news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
6914 int rv;
6915 char *fccptr = NULL;
6917 if(fcc && fcc->tptr)
6918 fccptr = cpystr(fcc->tptr);
6920 clear_cursor_pos();
6922 rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
6924 /* assign any new fcc to the BUILDER_ARG */
6925 if(fccptr){
6926 if(fcc){
6927 /* it changed */
6928 if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
6929 fs_give((void **) &fcc->tptr);
6930 fcc->tptr = fccptr;
6931 fccptr = NULL;
6935 if(fccptr)
6936 fs_give((void **) &fccptr);
6939 /* deal with any busy indicator */
6940 if(news_busy_cue){
6941 news_busy_cue = 0;
6942 cancel_busy_cue(0);
6943 mark_status_dirty();
6944 display_message('x');
6945 if(mangled)
6946 *mangled |= BUILDER_MESSAGE_DISPLAYED;
6950 return(rv);
6954 void
6955 news_build_busy(void)
6957 news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
6961 #if defined(DOS) || defined(OS2)
6963 /*----------------------------------------------------------------------
6964 Verify that the necessary pieces are around to allow for
6965 message sending under DOS
6967 Args: strict -- tells us if a remote stream is required before
6968 sending is permitted.
6970 The idea is to make sure pine knows enough to put together a valid
6971 from line. The things we MUST know are a user-id, user-domain and
6972 smtp server to dump the message off on. Typically these are
6973 provided in pine's configuration file, but if not, the user is
6974 queried here.
6975 ----*/
6977 dos_valid_from()
6979 char prompt[100], answer[80];
6980 int rc, i, flags;
6981 HelpType help;
6984 * query for user name portion of address, use IMAP login
6985 * name as default
6987 if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
6988 NETMBX mb;
6989 int no_prompt_user_id = 0;
6991 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6992 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6993 && *mb.user){
6994 strncpy(answer, mb.user, sizeof(answer)-1);
6995 answer[sizeof(answer)-1] = '\0';
6997 else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
6998 /* no user-id prompting if set */
6999 no_prompt_user_id = 1;
7000 rc = 0;
7001 if(!ps_global->mail_stream)
7002 do_broach_folder(ps_global->inbox_name,
7003 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
7004 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
7005 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
7006 && *mb.user){
7007 strncpy(answer, mb.user, sizeof(answer)-1);
7008 answer[sizeof(answer)-1] = '\0';
7010 else
7011 answer[0] = '\0';
7013 else
7014 answer[0] = '\0';
7016 if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
7017 /* No prompt, just assume mailbox login is user-id */
7018 no_prompt_user_id = 1;
7019 rc = 0;
7022 snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
7023 prompt[sizeof(prompt)-1] = '\0';
7025 help = NO_HELP;
7026 while(!no_prompt_user_id) {
7027 flags = OE_APPEND_CURRENT;
7028 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7029 sizeof(answer),prompt,NULL,help,&flags);
7030 if(rc == 2)
7031 continue;
7033 if(rc == 3){
7034 help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
7035 continue;
7038 if(rc != 4)
7039 break;
7042 if(rc == 1 || (rc == 0 && !answer[0])) {
7043 q_status_message(SM_ORDER, 3, 4,
7044 _("Send cancelled (User-id must be provided before sending)"));
7045 return(0);
7048 /* save the name */
7049 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
7050 sizeof(prompt)-50, answer);
7051 prompt[sizeof(prompt)-1] = '\0';
7052 if(ps_global->blank_user_id
7053 && !no_prompt_user_id
7054 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7055 set_variable(V_USER_ID, answer, 1, 1, Main);
7057 else{
7058 fs_give((void **)&(ps_global->VAR_USER_ID));
7059 ps_global->VAR_USER_ID = cpystr(answer);
7063 /* query for personal name */
7064 if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
7065 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
7066 answer[0] = '\0';
7067 snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
7068 prompt[sizeof(prompt)-1] = '\0';
7070 help = NO_HELP;
7071 while(1) {
7072 flags = OE_APPEND_CURRENT;
7073 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7074 sizeof(answer),prompt,NULL,help,&flags);
7075 if(rc == 2)
7076 continue;
7078 if(rc == 3){
7079 help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
7080 continue;
7083 if(rc != 4)
7084 break;
7087 if(rc == 0 && answer){ /* save the name */
7088 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
7089 sizeof(prompt)-50, answer);
7090 prompt[sizeof(prompt)-1] = '\0';
7091 if(ps_global->blank_personal_name
7092 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7093 set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
7095 else{
7096 fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
7097 ps_global->VAR_PERSONAL_NAME = cpystr(answer);
7103 * query for host/domain portion of address, using IMAP
7104 * host as default
7106 if(ps_global->blank_user_domain
7107 || ps_global->maildomain == ps_global->localdomain
7108 || ps_global->maildomain == ps_global->hostname){
7109 if(ps_global->inbox_name[0] == '{'){
7110 for(i=0;
7111 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7112 answer[i] = ps_global->inbox_name[i+1];
7114 answer[i] = '\0';
7116 else
7117 answer[0] = '\0';
7119 snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
7120 prompt[sizeof(prompt)-1] = '\0';
7122 help = NO_HELP;
7123 while(1) {
7124 flags = OE_APPEND_CURRENT;
7125 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7126 sizeof(answer),prompt,NULL,help,&flags);
7127 if(rc == 2)
7128 continue;
7130 if(rc == 3){
7131 help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
7132 continue;
7135 if(rc != 4)
7136 break;
7139 if(rc == 1 || (rc == 0 && !answer[0])) {
7140 q_status_message(SM_ORDER, 3, 4,
7141 _("Send cancelled (Host/domain name must be provided before sending)"));
7142 return(0);
7145 /* save the name */
7146 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
7147 sizeof(prompt)-50, answer);
7148 prompt[sizeof(prompt)-1] = '\0';
7149 if(!ps_global->userdomain && !ps_global->blank_user_domain
7150 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7151 set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
7152 fs_give((void **)&(ps_global->maildomain)); /* blast old val */
7153 ps_global->userdomain = cpystr(answer);
7154 ps_global->maildomain = ps_global->userdomain;
7156 else{
7157 fs_give((void **)&(ps_global->maildomain));
7158 ps_global->userdomain = cpystr(answer);
7159 ps_global->maildomain = ps_global->userdomain;
7163 /* check for smtp server */
7164 if(!ps_global->VAR_SMTP_SERVER ||
7165 !ps_global->VAR_SMTP_SERVER[0] ||
7166 !ps_global->VAR_SMTP_SERVER[0][0]){
7167 char **list;
7169 if(ps_global->inbox_name[0] == '{'){
7170 for(i=0;
7171 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7172 answer[i] = ps_global->inbox_name[i+1];
7174 answer[i] = '\0';
7176 else
7177 answer[0] = '\0';
7179 snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
7180 prompt[sizeof(prompt)-1] = '\0';
7182 help = NO_HELP;
7183 while(1) {
7184 flags = OE_APPEND_CURRENT;
7185 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7186 sizeof(answer),prompt,NULL,help,&flags);
7187 if(rc == 2)
7188 continue;
7190 if(rc == 3){
7191 help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
7192 continue;
7195 if(rc != 4)
7196 break;
7199 if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
7200 q_status_message(SM_ORDER, 3, 4,
7201 _("Send cancelled (SMTP server must be provided before sending)"));
7202 return(0);
7205 /* save the name */
7206 list = (char **) fs_get(2 * sizeof(char *));
7207 list[0] = cpystr(answer);
7208 list[1] = NULL;
7209 set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
7210 fs_give((void *)&list[0]);
7211 fs_give((void *)list);
7214 return(1);
7217 #endif /* defined(DOS) || defined(OS2) */