* For a calendar entry with method PUBLISH, we show all entries in the calendar.
[alpine.git] / alpine / send.c
blob4768a7e55916b1484c9977c445b8380d6a91f27b
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-2018 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[6];
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+4; *p != '\0'; p++)
2174 *(p-4) = *p;
2175 *(p-4) = '\0';
2177 pf->type = pf_template[index].type;
2178 pf->canedit = pf_template[index].canedit;
2179 pf->rcptto = pf_template[index].rcptto;
2180 pf->writehdr = pf_template[index].writehdr;
2181 pf->localcopy = pf_template[index].localcopy;
2182 pf->extdata = he;
2183 pf->next = pf + 1;
2185 he->rich_header = view_as_rich(pf->name, he->rich_header);
2186 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2187 he->nickcmpl = NULL;
2189 switch(pf->type){
2190 case FreeText: /* realaddr points to c-client env */
2191 if(index == N_NEWS){
2192 sending_order[1] = pf;
2193 he->realaddr = &outgoing->newsgroups;
2194 he_news = he;
2196 switch(set_default_hdrval(pf, custom)){
2197 case Replace:
2198 if(*he->realaddr)
2199 fs_give((void **)he->realaddr);
2201 *he->realaddr = pf->textbuf;
2202 pf->textbuf = NULL;
2203 he->sticky = 1;
2204 break;
2206 case Combine:
2207 if(*he->realaddr){ /* combine values */
2208 if(pf->textbuf && *pf->textbuf){
2209 char *combined_hdr;
2210 size_t l;
2212 l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
2213 combined_hdr = (char *) fs_get((l+1) * sizeof(char));
2214 strncpy(combined_hdr, *he->realaddr, l);
2215 combined_hdr[l] = '\0';
2216 strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
2217 combined_hdr[l] = '\0';
2218 strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
2219 combined_hdr[l] = '\0';
2221 fs_give((void **)he->realaddr);
2222 *he->realaddr = combined_hdr;
2223 q_status_message(SM_ORDER, 3, 3,
2224 "Adding newsgroup from role");
2225 he->sticky = 1;
2228 else{
2229 *he->realaddr = pf->textbuf;
2230 pf->textbuf = NULL;
2233 break;
2235 case UseAsDef:
2236 /* if no value, use default */
2237 if(!*he->realaddr){
2238 *he->realaddr = pf->textbuf;
2239 pf->textbuf = NULL;
2242 break;
2244 case NoMatch:
2245 break;
2248 /* If there is a newsgroup, we'd better show it */
2249 if(outgoing->newsgroups && *outgoing->newsgroups)
2250 he->rich_header = 0; /* force on by default */
2252 if(pf->textbuf)
2253 fs_give((void **)&pf->textbuf);
2255 pf->text = he->realaddr;
2257 else if(index == N_DATE){
2258 sending_order[2] = pf;
2259 pf->text = (char **) &outgoing->date;
2260 pf->extdata = NULL;
2262 else if(index == N_INREPLY){
2263 sending_order[NN+9] = pf;
2264 pf->text = &outgoing->in_reply_to;
2265 pf->extdata = NULL;
2267 else if(index == N_MSGID){
2268 sending_order[NN+10] = pf;
2269 pf->text = &outgoing->message_id;
2270 pf->extdata = NULL;
2272 else if(index == N_REF){
2273 sending_order[NN+11] = pf;
2274 pf->text = &outgoing->references;
2275 pf->extdata = NULL;
2277 else if(index == N_PRIORITY){
2278 sending_order[NN+12] = pf;
2279 pf->text = &pf->textbuf;
2280 pf->extdata = NULL;
2282 else if(index == N_USERAGENT){
2283 sending_order[NN+13] = pf;
2284 pf->text = &pf->textbuf;
2285 pf->textbuf = generate_user_agent();
2286 pf->extdata = NULL;
2288 else if(index == N_POSTERR){
2289 sending_order[NN+14] = pf;
2290 pf_err = pf;
2291 pf->text = &pf->textbuf;
2292 pf->extdata = NULL;
2294 else if(index == N_RPLUID){
2295 sending_order[NN+15] = pf;
2296 pf_uid = pf;
2297 pf->text = &pf->textbuf;
2298 pf->extdata = NULL;
2300 else if(index == N_RPLMBOX){
2301 sending_order[NN+16] = pf;
2302 pf_mbox = pf;
2303 pf->text = &pf->textbuf;
2304 pf->extdata = NULL;
2306 else if(index == N_SMTP){
2307 sending_order[NN+17] = pf;
2308 pf_smtp_server = pf;
2309 pf->text = &pf->textbuf;
2310 pf->extdata = NULL;
2312 else if(index == N_NNTP){
2313 sending_order[NN+18] = pf;
2314 pf_nntp_server = pf;
2315 pf->text = &pf->textbuf;
2316 pf->extdata = NULL;
2318 else if(index == N_CURPOS){
2319 sending_order[NN+19] = pf;
2320 pf_curpos = pf;
2321 pf->text = &pf->textbuf;
2322 pf->extdata = NULL;
2324 else if(index == N_OURREPLYTO){
2325 sending_order[NN+20] = pf;
2326 pf_ourrep = pf;
2327 pf->text = &pf->textbuf;
2328 pf->extdata = NULL;
2330 else if(index == N_OURHDRS){
2331 sending_order[NN+21] = pf;
2332 pf_ourhdrs = pf;
2333 pf->text = &pf->textbuf;
2334 pf->extdata = NULL;
2336 else if(index == N_AUTHRCVD){
2337 sending_order[0] = pf;
2338 pf_ourhdrs = pf;
2339 pf->text = &pf->textbuf;
2340 pf->extdata = NULL;
2342 else{
2343 q_status_message(SM_ORDER | SM_DING, 3, 7,
2344 "Botched: Unmatched FreeText header in pine_send");
2347 break;
2349 /* can't do a default for this one */
2350 case Attachment:
2351 /* If there is an attachment already, we'd better show them */
2352 if(body && *body && (*body)->type != TYPETEXT)
2353 he->rich_header = 0; /* force on by default */
2355 break;
2357 case Address:
2358 switch(index){
2359 case N_FROM:
2360 sending_order[3] = pf;
2361 pf->addr = &outgoing->from;
2362 if(role && role->from){
2363 if(ps_global->never_allow_changing_from)
2364 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2365 else{
2366 outgoing->from = copyaddrlist(role->from);
2367 he->display_it = 1; /* show it */
2368 he->rich_header = 0;
2372 he_from = he;
2373 break;
2375 case N_TO:
2376 sending_order[NN+2] = pf;
2377 pf->addr = &outgoing->to;
2378 /* If already set, make it act like we typed it in */
2379 if(outgoing->to
2380 && outgoing->to->mailbox
2381 && outgoing->to->mailbox[0]
2382 && flags & PS_STICKY_TO)
2383 he->sticky = 1;
2385 he_to = he;
2386 pf_to = pf;
2387 break;
2389 case N_NOBODY:
2390 sending_order[NN+5] = pf;
2391 pf_nobody = pf;
2392 if(ps_global->VAR_EMPTY_HDR_MSG
2393 && !ps_global->VAR_EMPTY_HDR_MSG[0]){
2394 pf->addr = NULL;
2396 else{
2397 nobody_addr = mail_newaddr();
2398 nobody_addr->next = mail_newaddr();
2399 nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
2400 SIZEOF_20KBUF,
2401 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
2402 ? ps_global->VAR_EMPTY_HDR_MSG
2403 : "undisclosed-recipients"),
2404 ps_global->posting_charmap));
2405 pf->addr = &nobody_addr;
2408 break;
2410 case N_CC:
2411 sending_order[NN+3] = pf;
2412 pf->addr = &outgoing->cc;
2413 break;
2415 case N_BCC:
2416 sending_order[NN+4] = pf;
2417 pf->addr = &outgoing->bcc;
2418 /* if bcc exists, make sure it's exposed so nothing's
2419 * sent by mistake...
2421 if(outgoing->bcc)
2422 he->display_it = 1;
2424 break;
2426 case N_REPLYTO:
2427 sending_order[NN+1] = pf;
2428 pf->addr = &outgoing->reply_to;
2429 if(role && role->replyto){
2430 if(outgoing->reply_to)
2431 mail_free_address(&outgoing->reply_to);
2433 outgoing->reply_to = copyaddrlist(role->replyto);
2434 he->display_it = 1; /* show it */
2435 he->rich_header = 0;
2438 break;
2440 case N_LCC:
2441 sending_order[NN+7] = pf;
2442 pf->addr = &lcc_addr;
2443 he_lcc = he;
2444 if(lcc_arg){
2445 build_address(lcc_arg, &addr, NULL, NULL, NULL);
2446 rfc822_parse_adrlist(&lcc_addr, addr,
2447 ps_global->maildomain);
2448 fs_give((void **)&addr);
2449 he->display_it = 1;
2452 break;
2454 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2455 case N_SENDER:
2456 sending_order[4] = pf;
2457 pf->addr = &outgoing->sender;
2458 break;
2459 #endif
2461 default:
2462 q_status_message1(SM_ORDER,3,7,
2463 "Internal error: Address header %s", comatose(index));
2464 break;
2468 * If this is a reply to news, don't show the regular email
2469 * recipient headers (unless they are non-empty).
2471 if((outgoing->newsgroups && *outgoing->newsgroups)
2472 && (index == N_TO || index == N_CC
2473 || index == N_BCC || index == N_LCC)
2474 && (pf->addr && !*pf->addr)){
2475 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2476 pf->textbuf && *pf->textbuf){
2477 removing_trailing_white_space(pf->textbuf);
2478 (void)removing_double_quotes(pf->textbuf);
2479 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2480 rfc822_parse_adrlist(pf->addr, addr,
2481 ps_global->maildomain);
2482 fs_give((void **)&addr);
2483 if(ct > UseAsDef)
2484 he->sticky = 1;
2486 else
2487 he->rich_header = 1; /* hide */
2491 * If this address doesn't already have a value, then we check
2492 * for a default value assigned by the user.
2494 else if(pf->addr && !*pf->addr){
2495 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2496 (index != N_FROM ||
2497 (!ps_global->never_allow_changing_from &&
2498 F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
2499 pf->textbuf && *pf->textbuf){
2501 removing_trailing_white_space(pf->textbuf);
2502 (void)removing_double_quotes(pf->textbuf);
2505 * Try to set To based on Lcc. Don't attempt Fcc.
2507 if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
2508 BUILDER_ARG *barg = NULL;
2509 char *ppp = NULL;
2511 if(*pf_to->addr)
2512 ppp = addr_list_string(*pf_to->addr, NULL, 1);
2514 if(!ppp)
2515 ppp = cpystr("");
2517 barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
2518 memset(barg, 0, sizeof(*barg));
2519 barg->me = &(he->bldr_private);
2520 barg->aff = &(he_to->bldr_private);
2521 barg->tptr = cpystr(ppp);
2523 build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
2524 he->display_it = 1;
2526 rfc822_parse_adrlist(pf->addr, addr,
2527 ps_global->maildomain);
2528 if(addr)
2529 fs_give((void **) &addr);
2531 if(ct > UseAsDef)
2532 he->sticky = 1;
2534 if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
2535 ADDRESS *a = NULL;
2537 rfc822_parse_adrlist(&a, barg->tptr,
2538 ps_global->maildomain);
2539 if(a){
2540 if(pf_to->addr)
2541 mail_free_address(pf_to->addr);
2543 *pf_to->addr = a;
2547 if(barg){
2548 if(barg->tptr)
2549 fs_give((void **) &barg->tptr);
2551 fs_give((void **) &barg);
2554 if(ppp)
2555 fs_give((void **) &ppp);
2557 else{
2558 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2559 rfc822_parse_adrlist(pf->addr, addr,
2560 ps_global->maildomain);
2561 if(addr)
2562 fs_give((void **) &addr);
2564 if(ct > UseAsDef)
2565 he->sticky = 1;
2569 /* if we still don't have a from */
2570 if(index == N_FROM && !*pf->addr)
2571 *pf->addr = generate_from();
2575 * Addr is already set in the rest of the cases.
2577 else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
2578 ADDRESS *adr = NULL;
2581 * We get to this case of the ifelse if the from or reply-to
2582 * addr was set by a role above.
2585 /* figure out the default value */
2586 (void)set_default_hdrval(pf, custom);
2587 if(pf->textbuf && *pf->textbuf){
2588 removing_trailing_white_space(pf->textbuf);
2589 (void)removing_double_quotes(pf->textbuf);
2590 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2591 rfc822_parse_adrlist(&adr, addr,
2592 ps_global->maildomain);
2593 fs_give((void **)&addr);
2596 /* if value set by role is different from default, show it */
2597 if(adr && !address_is_same(*pf->addr, adr))
2598 he->display_it = 1; /* start this off showing */
2600 /* malformed */
2601 if(!(*pf->addr)->mailbox){
2602 fs_give((void **)pf->addr);
2603 he->display_it = 1;
2606 if(adr)
2607 mail_free_address(&adr);
2609 else if((index == N_TO || index == N_CC || index == N_BCC)
2610 && pf->addr){
2611 ADDRESS *a = NULL, **tail;
2614 * These three are different from the others because we
2615 * might add the addresses to what is already there instead
2616 * of replacing.
2619 switch(set_default_hdrval(pf, custom)){
2620 case Replace:
2621 if(*pf->addr)
2622 mail_free_address(pf->addr);
2624 removing_trailing_white_space(pf->textbuf);
2625 (void)removing_double_quotes(pf->textbuf);
2626 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2627 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2628 fs_give((void **)&addr);
2629 he->sticky = 1;
2630 break;
2632 case Combine:
2633 removing_trailing_white_space(pf->textbuf);
2634 (void)removing_double_quotes(pf->textbuf);
2635 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2636 rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
2637 fs_give((void **)&addr);
2638 he->sticky = 1;
2639 if(a){
2640 for(tail = pf->addr; *tail; tail = &(*tail)->next)
2642 *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
2643 *pf->addr, NULL, a, RCA_ALL);
2644 q_status_message(SM_ORDER, 3, 3,
2645 "Adding addresses from role");
2646 mail_free_address(&a);
2649 break;
2651 case UseAsDef:
2652 case NoMatch:
2653 break;
2656 he->display_it = 1; /* start this off showing */
2658 else if(pf->addr){
2659 switch(set_default_hdrval(pf, custom)){
2660 case Replace:
2661 case Combine:
2662 if(*pf->addr)
2663 mail_free_address(pf->addr);
2665 removing_trailing_white_space(pf->textbuf);
2666 (void)removing_double_quotes(pf->textbuf);
2667 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2668 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2669 fs_give((void **)&addr);
2670 he->sticky = 1;
2671 break;
2673 case UseAsDef:
2674 case NoMatch:
2675 break;
2678 he->display_it = 1;
2681 if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
2682 mail_free_address(pf->addr);
2683 he->display_it = 1; /* start this off showing */
2686 if(pf->textbuf) /* free default value in any case */
2687 fs_give((void **)&pf->textbuf);
2689 /* outgoing2strings will alloc the string pf->scratch below */
2690 he->realaddr = &pf->scratch;
2691 break;
2693 case Fcc:
2694 sending_order[NN+8] = pf;
2695 pf_fcc = pf;
2696 if(role && role->fcc)
2697 fcc = role->fcc;
2698 else
2699 fcc = get_fcc(fcc_arg);
2701 if(fcc_to_free){
2702 fs_give((void **)&fcc_to_free);
2703 fcc_arg = NULL;
2706 if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
2707 he->sticky = 1;
2709 if(role)
2710 role->fcc = NULL;
2712 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
2713 he->display_it = 1; /* start this off showing */
2715 he->realaddr = &fcc;
2716 pf->text = &fcc;
2717 he_fcc = he;
2718 break;
2720 case Subject :
2721 sending_order[NN+6] = pf;
2723 switch(set_default_hdrval(pf, custom)){
2724 case Replace:
2725 case Combine:
2726 pf->scratch = pf->textbuf;
2727 pf->textbuf = NULL;
2728 he->sticky = 1;
2729 if(outgoing->subject)
2730 fs_give((void **)&outgoing->subject);
2732 break;
2734 case UseAsDef:
2735 case NoMatch:
2736 /* if no value, use default */
2737 if(outgoing->subject){
2738 pf->scratch = cpystr(outgoing->subject);
2740 else{
2741 pf->scratch = pf->textbuf;
2742 pf->textbuf = NULL;
2745 break;
2748 he->realaddr = &pf->scratch;
2749 pf->text = &outgoing->subject;
2750 break;
2752 default:
2753 q_status_message1(SM_ORDER,3,7,
2754 "Unknown header type %d in pine_send",
2755 (void *)pf->type);
2756 break;
2760 * We may or may not want to give the user the chance to edit
2761 * the From and Reply-To lines. If they are listed in either
2762 * Default-composer-hdrs or Customized-hdrs, then they can edit
2763 * them, else no.
2764 * If canedit is not set, that means that this header is not in
2765 * the user's customized-hdrs. If rich_header is set, that
2766 * means that this header is not in the user's
2767 * default-composer-hdrs (since From and Reply-To are rich
2768 * by default). So, don't give it an he to edit with in that case.
2770 * For other types, just not setting canedit will cause it to be
2771 * uneditable, regardless of what the user does.
2773 switch(index){
2774 case N_FROM:
2775 /* to allow it, we let this fall through to the reply-to case below */
2776 if(ps_global->never_allow_changing_from ||
2777 (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
2778 !(role && role->from))){
2779 if(pf->canedit || !he->rich_header)
2780 q_status_message(SM_ORDER, 3, 3,
2781 _("Not allowed to change header \"From\""));
2783 memset(he, 0, (size_t)sizeof(*he));
2784 pf->extdata = NULL;
2785 break;
2788 case N_REPLYTO:
2789 if(!pf->canedit && he->rich_header){
2790 memset(he, 0, (size_t)sizeof(*he));
2791 pf->extdata = NULL;
2793 else{
2794 pf->canedit = 1;
2795 he++;
2798 break;
2800 default:
2801 if(!pf->canedit){
2802 memset(he, 0, (size_t)sizeof(*he));
2803 pf->extdata = NULL;
2805 else
2806 he++;
2808 break;
2813 * This is so the builder can tell the composer to fill the affected
2814 * field based on the value in the field on the left.
2816 * Note that this mechanism isn't completely general. Each entry has
2817 * only a single next_affected, so if some other entry points an
2818 * affected entry at an entry with a next_affected, they all inherit
2819 * that next_affected. Since this isn't used much a careful ordering
2820 * of the affected fields should make it a sufficient mechanism.
2822 he_to->affected_entry = he_fcc;
2823 he_news->affected_entry = he_fcc;
2824 he_lcc->affected_entry = he_to;
2825 he_to->next_affected = he_fcc;
2827 (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
2829 i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2831 * Set up headerentries for custom fields.
2832 * NOTE: "i" is assumed to now index first custom field in sending
2833 * order.
2835 for(pf = pf->next; pf && pf->name; pf = pf->next){
2836 char *addr;
2838 if(pf->standard)
2839 continue;
2841 pf->extdata = he;
2842 pf->canedit = 1;
2843 pf->rcptto = 0;
2844 pf->writehdr = 1;
2845 pf->localcopy = 1;
2847 switch(pf->type){
2848 case Address:
2849 if(pf->addr){ /* better be set */
2850 sending_order[i++] = pf;
2851 *he = he_custom_addr_templ;
2852 /* change default text into an ADDRESS */
2853 /* strip quotes around whole default */
2854 removing_trailing_white_space(pf->textbuf);
2855 (void)removing_double_quotes(pf->textbuf);
2856 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2857 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2858 fs_give((void **)&addr);
2859 if(pf->textbuf)
2860 fs_give((void **)&pf->textbuf);
2862 he->realaddr = &pf->scratch;
2863 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2864 he->nickcmpl = NULL;
2867 break;
2869 case FreeText:
2870 sending_order[i++] = pf;
2871 *he = he_custom_free_templ;
2872 he->realaddr = &pf->textbuf;
2873 pf->text = &pf->textbuf;
2874 if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
2875 (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
2876 he->display_it = 1; /* show it */
2878 break;
2880 default:
2881 q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
2882 (void *)pf->type);
2883 break;
2886 he->name = pf->name;
2888 /* use first 8 characters for prompt */
2889 he->prompt = cpystr(" : ");
2890 strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
2892 he->rich_header = view_as_rich(he->name, he->rich_header);
2893 he++;
2897 * Make sure at least *one* field is displayable...
2899 for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
2900 if(HE(pf) && !HE(pf)->rich_header){
2901 index = i;
2902 break;
2906 * None displayable!!! Warn and display defaults.
2908 if(index == -1){
2909 q_status_message(SM_ORDER,0,5,
2910 "No default-composer-hdrs matched, displaying defaults");
2911 for(i = 0, pf = header.local; pf; pf = pf->next, i++)
2912 if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
2913 && HE(pf))
2914 HE(pf)->rich_header = 0;
2918 * Save information about body which set_mime_type_by_grope might change.
2919 * Then, if we get an error sending, we reset these things so that
2920 * grope can do it's thing again after we edit some more.
2922 if ((*body)->type == TYPEMULTIPART)
2923 bp = save_body_particulars(&(*body)->nested.part->body);
2924 else
2925 bp = save_body_particulars(*body);
2928 local_redraft_pos = redraft_pos;
2930 /*----------------------------------------------------------------------
2931 Loop calling the editor until everything goes well
2932 ----*/
2933 while(1){
2934 int saved_user_timeout;
2936 /* Reset body to what it was when we started. */
2937 if ((*body)->type == TYPEMULTIPART)
2938 reset_body_particulars(bp, &(*body)->nested.part->body);
2939 else
2940 reset_body_particulars(bp,*body);
2942 * set initial cursor position based on how many times we've been
2943 * thru the loop...
2945 if(reply && reply->pseudo){
2946 pbf->pine_flags |= reply->data.pico_flags;
2948 else if(body_start){
2949 pbf->pine_flags |= P_BODY;
2950 body_start = 0; /* maybe not next time */
2952 else if(local_redraft_pos){
2953 pbf->edit_offset = local_redraft_pos->offset;
2954 /* set the start_here bit in correct header */
2955 for(pf = header.local; pf && pf->name; pf = pf->next)
2956 if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
2957 && HE(pf)){
2958 HE(pf)->start_here = 1;
2959 break;
2962 /* If didn't find it, we start in body. */
2963 if(!pf || !pf->name)
2964 pbf->pine_flags |= P_BODY;
2966 else if(reply && (!reply->forw && !reply->forwarded)){
2967 pbf->pine_flags |= P_BODY;
2970 /* in case these were turned on in previous pass through loop */
2971 if(pf_nobody){
2972 pf_nobody->writehdr = 0;
2973 pf_nobody->localcopy = 0;
2976 if(pf_fcc)
2977 pf_fcc->localcopy = 0;
2980 * If a sending attempt failed after we passed the message text
2981 * thru a user-defined filter, "orig_so" points to the original
2982 * text. Replace the body's encoded data with the original...
2984 if(orig_so){
2985 STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
2986 ? &(*body)->nested.part->body.contents.text.data
2987 : &(*body)->contents.text.data);
2988 so_give(so);
2989 *so = orig_so;
2990 orig_so = NULL;
2994 * Convert the envelope and body to the string format that
2995 * pico can edit
2997 outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
2999 for(pf = header.local; pf && pf->name; pf = pf->next){
3001 * If this isn't the first time through this loop, we may have
3002 * freed some of the FreeText headers below so that they wouldn't
3003 * show up as empty headers in the finished message. Need to
3004 * alloc them again here so they can be edited.
3006 if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
3007 *HE(pf)->realaddr = cpystr("");
3009 if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
3010 HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
3014 * If From is exposed, probably by a role, then start the cursor
3015 * on the first line which isn't filled in. If it isn't, then we
3016 * don't move the cursor, mostly for back-compat.
3018 if((!reply || reply->forw || reply->forwarded) &&
3019 !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
3020 (he_from->display_it || !he_from->rich_header)){
3021 for(pf = header.local; pf && pf->name; pf = pf->next)
3022 if(HE(pf) &&
3023 (HE(pf)->display_it || !HE(pf)->rich_header) &&
3024 HE(pf)->realaddr &&
3025 (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
3026 HE(pf)->start_here = 1;
3027 break;
3031 #ifdef _WINDOWS
3032 mswin_setwindowmenu (MENU_COMPOSER);
3033 #endif
3035 cancel_busy_cue(-1);
3036 flush_status_messages(1);
3038 /* turn off user input timeout when in composer */
3039 saved_user_timeout = ps_global->hours_to_timeout;
3040 ps_global->hours_to_timeout = 0;
3041 dprint((1, "\n ---- COMPOSER ----\n"));
3042 editor_result = pico(pbf);
3043 dprint((4, "... composer returns (0x%x)\n", editor_result));
3044 ps_global->hours_to_timeout = saved_user_timeout;
3046 #ifdef _WINDOWS
3047 mswin_setwindowmenu (MENU_DEFAULT);
3048 #endif
3049 fix_windsize(ps_global);
3052 * Only reinitialize signals if we didn't receive an interesting
3053 * one while in pico, since pico's return is part of processing that
3054 * signal and it should continue to be ignored.
3056 if(!(editor_result & COMP_GOTHUP))
3057 init_signals(); /* Pico has it's own signal stuff */
3060 * We're going to save in DEADLETTER. Dump attachments first.
3062 if(editor_result & COMP_CANCEL)
3063 free_attachment_list(&pbf->attachments);
3065 /* Turn strings back into structures */
3066 strings2outgoing(&header, body, pbf->attachments, flowing_requested);
3068 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3069 if(outgoing->newsgroups){
3070 sqzspaces(outgoing->newsgroups);
3071 if(!outgoing->newsgroups[0])
3072 fs_give((void **)&(outgoing->newsgroups));
3075 /* Make subject NULL if it is "" (so won't show up in headers) */
3076 if(outgoing->subject && !outgoing->subject[0])
3077 fs_give((void **)&(outgoing->subject));
3079 /* remove custom fields that are empty */
3080 for(pf = header.local; pf && pf->name; pf = pf->next){
3081 if(pf->type == FreeText && pf->textbuf){
3082 if(pf->textbuf[0] == '\0'){
3083 fs_give((void **)&pf->textbuf);
3084 pf->text = NULL;
3089 removing_trailing_white_space(fcc);
3091 /*-------- Stamp it with a current date -------*/
3092 if(outgoing->date) /* update old date */
3093 fs_give((void **)&(outgoing->date));
3095 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3096 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
3098 rfc822_date(tmp_20k_buf); /* format and copy new date */
3099 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3100 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
3102 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
3104 /* Set return_path based on From which is going to be used */
3105 if(outgoing->return_path)
3106 mail_free_address(&outgoing->return_path);
3108 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
3111 * Don't ever believe the sender that is there.
3112 * If From doesn't look quite right, generate our own sender.
3114 if(outgoing->sender)
3115 mail_free_address(&outgoing->sender);
3118 * If the LHS of the address doesn't match, or the RHS
3119 * doesn't match one of localdomain or hostname,
3120 * then add a sender line (really X-X-Sender).
3122 * Don't add a personal_name since the user can change that.
3124 if(F_OFF(F_DISABLE_SENDER, ps_global)
3126 (!outgoing->from
3127 || !outgoing->from->mailbox
3128 || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
3129 || !outgoing->from->host
3130 || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
3131 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
3133 outgoing->sender = mail_newaddr();
3134 outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
3135 outgoing->sender->host = cpystr(ps_global->hostname);
3138 if(ps_global->newthread){
3139 if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
3140 if(outgoing->references) fs_give((void **)&outgoing->references);
3143 /*----- Message is edited, now decide what to do with it ----*/
3144 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
3145 /*=========== Postpone or Interrupted message ============*/
3146 CONTEXT_S *fcc_cntxt = NULL;
3147 char folder[MAXPATH+1];
3148 int fcc_result = 0;
3149 char label[50];
3151 dprint((4, "pine_send:%s handling\n",
3152 (editor_result & COMP_SUSPEND)
3153 ? "SUSPEND"
3154 : (editor_result & COMP_GOTHUP)
3155 ? "HUP"
3156 : (editor_result & COMP_CANCEL)
3157 ? "CANCEL" : "HUH?"));
3158 if((editor_result & COMP_CANCEL)
3159 && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
3160 || ps_global->deadlets == 0)){
3161 q_status_message(SM_ORDER, 0, 3, "Message cancelled");
3162 break;
3166 * The idea here is to use the Fcc: writing facility
3167 * to append to the special postponed message folder...
3169 * NOTE: the strategy now is to write the message and
3170 * all attachments as they exist at composition time.
3171 * In other words, attachments are postponed by value
3172 * and not reference. This may change later, but we'll
3173 * need a local "message/external-body" type that
3174 * outgoing2strings knows how to properly set up for
3175 * the composer. Maybe later...
3178 label[0] = '\0';
3180 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
3181 && (editor_result & COMP_SUSPEND)
3182 && (check_addresses(&header) == CA_BAD)){
3183 /*--- Addresses didn't check out---*/
3184 q_status_message(SM_ORDER, 7, 7,
3185 _("Not allowed to postpone message until addresses are qualified"));
3186 continue;
3190 * Build the local message copy so.
3192 * In the HUP case, we'll write the bezerk delimiter by
3193 * hand and output the message directly into the folder.
3194 * It's not only faster, we don't have to worry about
3195 * c-client reentrance and less hands paw over the data so
3196 * there's less chance of a problem.
3198 * In the Postpone case, just create it if the user wants to
3199 * and create a temporary storage object to write into. */
3200 fake_hup:
3201 lmc.all_written = lmc.text_only = lmc.text_written = 0;
3202 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3203 int newfile = 1;
3204 time_t now = time((time_t *)0);
3206 #if defined(DOS) || defined(OS2)
3208 * we can't assume anything about root or home dirs, so
3209 * just plunk it down in the same place as the pinerc
3211 if(!getenv("HOME")){
3212 char *lc = last_cmpnt(ps_global->pinerc);
3213 folder[0] = '\0';
3214 if(lc != NULL){
3215 strncpy(folder,ps_global->pinerc,
3216 MIN(lc-ps_global->pinerc,sizeof(folder)-1));
3217 folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
3220 strncat(folder, (editor_result & COMP_GOTHUP)
3221 ? INTERRUPTED_MAIL : DEADLETTER,
3222 sizeof(folder)-strlen(folder)-1);
3224 else
3225 #endif
3226 build_path(folder,
3227 ps_global->VAR_OPER_DIR
3228 ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
3229 (editor_result & COMP_GOTHUP)
3230 ? INTERRUPTED_MAIL : DEADLETTER,
3231 sizeof(folder));
3233 if(editor_result & COMP_CANCEL){
3234 char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
3236 if(strlen(folder) + 1 < sizeof(filename))
3237 for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
3238 strncpy(filename, folder, sizeof(filename));
3239 filename[sizeof(filename)-1] = '\0';
3240 strncpy(newfname, filename, sizeof(newfname));
3241 newfname[sizeof(newfname)-1] = '\0';
3243 if(i > 1){
3244 snprintf(nbuf, sizeof(nbuf), "%d", i);
3245 nbuf[sizeof(nbuf)-1] = '\0';
3246 strncat(filename, nbuf,
3247 sizeof(filename)-strlen(filename)-1);
3248 filename[sizeof(filename)-1] = '\0';
3251 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
3252 nbuf[sizeof(nbuf)-1] = '\0';
3253 strncat(newfname, nbuf,
3254 sizeof(newfname)-strlen(newfname)-1);
3255 newfname[sizeof(newfname)-1] = '\0';
3256 (void) rename_file(filename, newfname);
3259 our_unlink(folder);
3261 else
3262 newfile = can_access(folder, ACCESS_EXISTS);
3264 if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
3265 if (outgoing->from){
3266 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
3267 newfile ? "" : "\015\012",
3268 outgoing->from->mailbox,
3269 outgoing->from->host, ctime(&now));
3270 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3271 if(!so_puts(lmc.so, tmp_20k_buf)){
3272 if(editor_result & COMP_CANCEL)
3273 q_status_message2(SM_ORDER | SM_DING, 3, 3,
3274 "Can't write \"%s\": %s",
3275 folder, error_description(errno));
3276 else
3277 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3278 folder ? folder : "?",
3279 error_description(errno)));
3284 else{ /* Must be COMP_SUSPEND */
3285 if(!ps_global->VAR_POSTPONED_FOLDER
3286 || !ps_global->VAR_POSTPONED_FOLDER[0]){
3287 q_status_message(SM_ORDER | SM_DING, 3, 3,
3288 _("No postponed file defined"));
3289 continue;
3293 * Store the cursor position
3295 * First find the header entry with the start_here
3296 * bit set, if any. This means the editor is telling
3297 * us to start on this header field next time.
3299 start_here_name = NULL;
3300 for(pf = header.local; pf && pf->name; pf = pf->next)
3301 if(HE(pf) && HE(pf)->start_here){
3302 start_here_name = pf->name;
3303 break;
3306 /* If there wasn't one, ":" means we start in the body. */
3307 if(!start_here_name || !*start_here_name)
3308 start_here_name = ":";
3310 if(ps_global->VAR_FORM_FOLDER
3311 && ps_global->VAR_FORM_FOLDER[0]
3312 && postpone_prompt() == 'f'){
3313 strncpy(folder, ps_global->VAR_FORM_FOLDER,
3314 sizeof(folder)-1);
3315 folder[sizeof(folder)-1] = '\0';
3316 strncpy(label, "form letter", sizeof(label));
3317 label[sizeof(label)-1] = '\0';
3319 else{
3320 strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
3321 sizeof(folder)-1);
3322 folder[sizeof(folder)-1] = '\0';
3323 strncpy(label, "postponed message", sizeof(label));
3324 label[sizeof(label)-1] = '\0';
3327 lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
3330 if(lmc.so){
3331 size_t sz;
3332 char *lmq = NULL;
3334 /* copy fcc line to postponed or interrupted folder */
3335 if(pf_fcc)
3336 pf_fcc->localcopy = 1;
3338 /* plug error into header for later display to user */
3339 if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
3340 pf_err->writehdr = 1;
3341 pf_err->localcopy = 1;
3342 pf_err->textbuf = lmq;
3346 * if reply, write (UID)folder header field so we can
3347 * later flag the replied-to message \\ANSWERED
3348 * DON'T save MSGNO's.
3350 if(reply && reply->uid){
3351 char uidbuf[MAILTMPLEN], *p;
3352 long i;
3354 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
3355 if(i)
3356 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
3358 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
3361 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3363 pf_uid->writehdr = 1;
3364 pf_uid->localcopy = 1;
3365 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
3366 reply->prefix ? int2string(strlen(reply->prefix))
3367 : (reply->forwarded) ? "": "0 ",
3368 reply->prefix ? " " : "",
3369 reply->prefix ? reply->prefix : "",
3370 i, reply->data.uid.validity,
3371 tmp_20k_buf, reply->mailbox);
3372 uidbuf[sizeof(uidbuf)-1] = '\0';
3373 pf_uid->textbuf = cpystr(uidbuf);
3376 * Logically, this ought to be part of pf_uid, but this
3377 * was added later and so had to be in a separate header
3378 * for backwards compatibility.
3380 pf_mbox->writehdr = 1;
3381 pf_mbox->localcopy = 1;
3382 pf_mbox->textbuf = cpystr(reply->origmbox
3383 ? reply->origmbox
3384 : reply->mailbox);
3387 /* Save cursor position */
3388 if(start_here_name && *start_here_name){
3389 char curposbuf[MAILTMPLEN];
3391 pf_curpos->writehdr = 1;
3392 pf_curpos->localcopy = 1;
3393 snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
3394 pbf->edit_offset);
3395 curposbuf[sizeof(curposbuf)-1] = '\0';
3396 pf_curpos->textbuf = cpystr(curposbuf);
3400 * Work around c-client reply-to bug. C-client will
3401 * return a reply_to in an envelope even if there is
3402 * no reply-to header field. We want to note here whether
3403 * the reply-to is real or not.
3405 if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
3406 pf_ourrep->writehdr = 1;
3407 pf_ourrep->localcopy = 1;
3408 if(outgoing->reply_to)
3409 pf_ourrep->textbuf = cpystr("Full");
3410 else
3411 pf_ourrep->textbuf = cpystr("Empty");
3414 /* Save the role-specific smtp server */
3415 if(role && role->smtp && role->smtp[0]){
3416 char *q, *smtp = NULL;
3417 char **lp;
3418 size_t len = 0;
3421 * Turn the list of smtp servers into a space-
3422 * delimited list in a single string.
3424 for(lp = role->smtp; (q = *lp) != NULL; lp++)
3425 len += (strlen(q) + 1);
3427 if(len){
3428 smtp = (char *) fs_get(len * sizeof(char));
3429 smtp[0] = '\0';
3430 for(lp = role->smtp; (q = *lp) != NULL; lp++){
3431 if(lp != role->smtp)
3432 strncat(smtp, " ", len-strlen(smtp)-1);
3434 strncat(smtp, q, len-strlen(smtp)-1);
3437 smtp[len-1] = '\0';
3440 pf_smtp_server->writehdr = 1;
3441 pf_smtp_server->localcopy = 1;
3442 if(smtp)
3443 pf_smtp_server->textbuf = smtp;
3444 else
3445 pf_smtp_server->textbuf = cpystr("");
3448 /* Save the role-specific nntp server */
3449 if(suggested_nntp_server ||
3450 (role && role->nntp && role->nntp[0])){
3451 char *q, *nntp = NULL;
3452 char **lp;
3453 size_t len = 0;
3455 if(role && role->nntp && role->nntp[0]){
3457 * Turn the list of nntp servers into a space-
3458 * delimited list in a single string.
3460 for(lp = role->nntp; (q = *lp) != NULL; lp++)
3461 len += (strlen(q) + 1);
3463 if(len){
3464 nntp = (char *) fs_get(len * sizeof(char));
3465 nntp[0] = '\0';
3466 for(lp = role->nntp; (q = *lp) != NULL; lp++){
3467 if(lp != role->nntp)
3468 strncat(nntp, " ", len-strlen(nntp)-1);
3470 strncat(nntp, q, len-strlen(nntp)-1);
3473 nntp[len-1] = '\0';
3476 else
3477 nntp = cpystr(suggested_nntp_server);
3479 pf_nntp_server->writehdr = 1;
3480 pf_nntp_server->localcopy = 1;
3481 if(nntp)
3482 pf_nntp_server->textbuf = nntp;
3483 else
3484 pf_nntp_server->textbuf = cpystr("");
3488 * Write the list of custom headers to the
3489 * X-Our-Headers header so that we can recover the
3490 * list in redraft.
3492 sz = 0;
3493 for(pf = header.custom; pf && pf->name; pf = pf->next)
3494 sz += strlen(pf->name) + 1;
3496 if(sz){
3497 char *q;
3499 pf_ourhdrs->writehdr = 1;
3500 pf_ourhdrs->localcopy = 1;
3501 pf_ourhdrs->textbuf = (char *)fs_get(sz);
3502 memset(pf_ourhdrs->textbuf, 0, sz);
3503 q = pf_ourhdrs->textbuf;
3504 for(pf = header.custom; pf && pf->name; pf = pf->next){
3505 if(pf != header.custom)
3506 sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
3508 sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
3511 pf_ourhdrs->textbuf[sz-1] = '\0';;
3515 * We need to make sure any header values that got cleared
3516 * get written to the postponed message (they won't if
3517 * pf->text is NULL). Otherwise, we can't tell previously
3518 * non-existent custom headers or default values from
3519 * custom (or other) headers that got blanked in the
3520 * composer...
3522 for(pf = header.local; pf && pf->name; pf = pf->next)
3523 if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
3524 *(HE(pf)->realaddr) = cpystr("");
3527 * We're saving the message for use later. It may be that the
3528 * characters in the message are not all convertible to the
3529 * user's posting_charmap. We'll save it as UTF-8 instead
3530 * and worry about that the next time they try to send it.
3531 * Use a different save pointer just to be sure we don't
3532 * mess up the other stuff. We should probably make the
3533 * charset an argument.
3535 * We also need to fix the charset of the body part
3536 * the user is editing so that we can read it back
3537 * successfully when we resume the composition.
3539 ps_global->post_utf8 = 1;
3542 PARAMETER *pm;
3543 BODY *bp;
3544 if((*body)->type == TYPEMULTIPART)
3545 bp = &(*body)->nested.part->body;
3546 else
3547 bp = *body;
3549 for(pm = bp->parameter;
3550 pm && strucmp(pm->attribute, "charset") != 0;
3551 pm = pm->next)
3554 if(pm){
3555 if(pm->value)
3556 fs_give((void **) &pm->value);
3558 pm->value = cpystr("UTF-8");
3562 if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
3563 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3564 char *err;
3565 STORE_S *hup_so;
3566 gf_io_t gc, pc;
3567 int we_cancel = 0;
3569 if(editor_result & COMP_CANCEL){
3570 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3571 "Saving to \"%s\"", folder);
3572 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3573 we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
3576 if((hup_so =
3577 so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
3578 gf_set_so_readc(&gc, lmc.so);
3579 gf_set_so_writec(&pc, hup_so);
3580 so_seek(lmc.so, 0L, 0); /* read msg copy and */
3581 so_seek(hup_so, 0L, 2); /* append to folder */
3582 gf_filter_init();
3583 gf_link_filter(gf_nvtnl_local, NULL);
3584 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
3585 dprint((1, "*** PIPE FAILED: %s\n",
3586 err ? err : "?"));
3588 gf_clear_so_readc(lmc.so);
3589 gf_clear_so_writec(hup_so);
3590 so_give(&hup_so);
3592 else
3593 dprint((1, "*** CAN'T CREATE %s: %s\n",
3594 folder ? folder : "?",
3595 error_description(errno)));
3597 if(we_cancel)
3598 cancel_busy_cue(-1);
3600 else
3601 fcc_result = write_fcc(folder, fcc_cntxt,
3602 lmc.so, NULL, label, NULL);
3605 /* discontinue coerced UTF-8 posting */
3606 ps_global->post_utf8 = 0;
3608 so_give(&lmc.so);
3610 else
3611 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3612 error_description(errno)));
3614 if(editor_result & COMP_GOTHUP){
3616 * Special Hack #291: if any hi-byte bits are set in
3617 * editor's result, we put them there.
3619 if(editor_result & 0xff00)
3620 exit(editor_result >> 8);
3622 dprint((1, "Save composition on HUP %sED\n",
3623 fcc_result ? "SUCCEED" : "FAIL"));
3624 hup_signal(); /* Do what we normally do on SIGHUP */
3626 else if((editor_result & COMP_SUSPEND) && fcc_result){
3627 if(ps_global->VAR_FORM_FOLDER
3628 && ps_global->VAR_FORM_FOLDER[0]
3629 && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
3630 q_status_message(SM_ORDER, 0, 3,
3631 _("Composition saved to Form Letter Folder. Select Compose to send."));
3632 else
3633 q_status_message(SM_ORDER, 0, 3,
3634 _("Composition postponed. Select Compose to resume."));
3636 break; /* postpone went OK, get out of here */
3638 else if(editor_result & COMP_CANCEL){
3639 char *lc = NULL;
3641 if(fcc_result && folder)
3642 lc = last_cmpnt(folder);
3644 q_status_message3(SM_ORDER, 0, 3,
3645 _("Message cancelled%s%s%s"),
3646 (lc && *lc) ? " and copied to \"" : "",
3647 (lc && *lc) ? lc : "",
3648 (lc && *lc) ? "\" file" : "");
3649 break;
3651 else{
3652 q_status_message(SM_ORDER, 0, 4,
3653 _("Continuing composition. Message not postponed or sent"));
3654 body_start = 1;
3655 continue; /* postpone failed, jump back in to composer */
3658 else{
3659 /*------ Must be sending mail or posting ! -----*/
3660 int result, valid_addr, continue_with_only_fcc = 0;
3661 CONTEXT_S *fcc_cntxt = NULL;
3663 result = 0;
3664 dprint((4, "=== sending: "));
3666 /* --- If posting, confirm with user ----*/
3667 if(outgoing->newsgroups && *outgoing->newsgroups
3668 && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
3669 && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
3670 q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
3671 dprint((4, "no post, continuing\n"));
3672 continue;
3675 if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
3676 || outgoing->newsgroups)){
3677 if(fcc && fcc[0]){
3678 if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
3679 want_to(_("No recipients, really copy only to Fcc "),
3680 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
3681 continue;
3683 continue_with_only_fcc++;
3685 else{
3686 q_status_message(SM_ORDER, 3, 4,
3687 _("No recipients specified!"));
3688 dprint((4, "no recip, continuing\n"));
3689 continue;
3693 if((valid_addr = check_addresses(&header)) == CA_BAD){
3694 /*--- Addresses didn't check out---*/
3695 dprint((4, "addrs failed, continuing\n"));
3696 continue;
3699 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
3700 && !continue_with_only_fcc
3701 && !(outgoing->to || outgoing->cc || lcc_addr
3702 || outgoing->newsgroups)
3703 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3704 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
3705 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3706 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3707 free_redraft_pos(&local_redraft_pos);
3709 local_redraft_pos
3710 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3711 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3712 local_redraft_pos->hdrname = cpystr(TONAME);
3713 continue;
3716 if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
3717 && check_for_subject(&header) == CF_MISSING){
3718 dprint((4, "No subject, continuing\n"));
3719 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3720 free_redraft_pos(&local_redraft_pos);
3722 local_redraft_pos
3723 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3724 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3725 local_redraft_pos->hdrname = cpystr(SUBJNAME);
3726 continue;
3729 if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
3730 && check_for_fcc(fcc) == CF_MISSING){
3731 dprint((4, "No fcc, continuing\n"));
3732 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3733 free_redraft_pos(&local_redraft_pos);
3735 local_redraft_pos
3736 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3737 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3738 local_redraft_pos->hdrname = cpystr("Fcc");
3739 continue;
3742 set_last_fcc(fcc);
3744 /*---- Check out fcc -----*/
3745 if(fcc && *fcc){
3747 * If special name "inbox" then replace it with the
3748 * real inbox path.
3750 if(ps_global->VAR_INBOX_PATH
3751 && strucmp(fcc, ps_global->inbox_name) == 0){
3752 char *replace_fcc;
3754 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
3755 fs_give((void **)&fcc);
3756 fcc = replace_fcc;
3759 lmc.all_written = lmc.text_written = 0;
3760 /* lmc.text_only set on command line */
3761 if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
3762 /* ---- Open or allocation of fcc failed ----- */
3763 dprint((4,"can't open/allocate fcc, cont'g\n"));
3766 * Find field entry associated with fcc, and start
3767 * composer on it...
3769 for(pf = header.local; pf && pf->name; pf = pf->next)
3770 if(pf->type == Fcc && HE(pf))
3771 HE(pf)->start_here = 1;
3773 continue;
3775 else
3776 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
3778 else
3779 lmc.so = NULL;
3781 /*---- Take care of any requested prefiltering ----*/
3782 if(sending_filter_requested
3783 && !filter_message_text(sending_filter_requested, outgoing,
3784 *body, &orig_so, &header)){
3785 q_status_message1(SM_ORDER, 3, 3,
3786 _("Problem filtering! Nothing sent%s."),
3787 fcc ? " or saved to fcc" : "");
3788 continue;
3791 /*------ Actually post -------*/
3792 if(outgoing->newsgroups){
3793 char **alt_nntp = NULL, *alt_nntp_p[2];
3794 if(((role && role->nntp)
3795 || suggested_nntp_server)){
3796 if(ps_global->FIX_NNTP_SERVER
3797 && ps_global->FIX_NNTP_SERVER[0])
3798 q_status_message(SM_ORDER | SM_DING, 5, 5,
3799 "Using nntp-server that is administratively fixed");
3800 else if(role && role->nntp)
3801 alt_nntp = role->nntp;
3802 else{
3803 alt_nntp_p[0] = suggested_nntp_server;
3804 alt_nntp_p[1] = NULL;
3805 alt_nntp = alt_nntp_p;
3808 if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
3809 dprint((1, "Post failed, continuing\n"));
3810 if(outgoing->message_id)
3811 fs_give((void **) &outgoing->message_id);
3813 outgoing->message_id = generate_message_id();
3815 continue;
3817 else
3818 result |= P_NEWS_WIN;
3822 * BUG: IF we've posted the message *and* an fcc was specified
3823 * then we've already got a neatly formatted message in the
3824 * lmc.so. It'd be nice not to have to re-encode everything
3825 * to insert it into the smtp slot...
3829 * Turn on "undisclosed recipients" header if no To or cc.
3831 if(!(outgoing->to || outgoing->cc)
3832 && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
3833 pf_nobody->writehdr = 1;
3834 pf_nobody->localcopy = 1;
3837 if(priority_requested){
3838 (void) set_priority_header(&header, priority_requested);
3839 fs_give((void **) &priority_requested);
3842 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3844 * If requested, launch backgroud posting...
3846 if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
3847 ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
3848 memset(ps_global->post, 0, sizeof(POST_S));
3849 if(fcc)
3850 ps_global->post->fcc = cpystr(fcc);
3852 if((ps_global->post->pid = fork()) == 0){
3854 * Put us in new process group...
3856 setpgrp(0, ps_global->post->pid);
3858 /* BUG: should fix argv[0] to indicate what we're up to */
3861 * If there are any live streams, pretend we never
3862 * knew them. Problem is two processes writing
3863 * same server process.
3864 * This is not clean but we're just going to exit
3865 * right away anyway. We just want to be sure to leave
3866 * the stuff that the parent is going to use alone.
3867 * The next three lines will disable the re-use of the
3868 * existing streams and cause us to open a new one if
3869 * needed.
3871 ps_global->mail_stream = NULL;
3872 ps_global->s_pool.streams = NULL;
3873 ps_global->s_pool.nstream = 0;
3875 /* quell any display output */
3876 ps_global->in_init_seq = 1;
3878 /*------- Actually mail the message ------*/
3879 if(valid_addr == CA_OK
3880 && (outgoing->to || outgoing->cc
3881 || outgoing->bcc || lcc_addr)){
3882 char **alt_smtp = NULL;
3884 if(role && role->smtp){
3885 if(ps_global->FIX_SMTP_SERVER
3886 && ps_global->FIX_SMTP_SERVER[0])
3887 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3888 else
3889 alt_smtp = role->smtp;
3892 result |= (call_mailer(&header, *body, alt_smtp,
3893 call_mailer_flags,
3894 call_mailer_file_result,
3895 pipe_callback) > 0)
3896 ? P_MAIL_WIN : P_MAIL_LOSE;
3898 if(result & P_MAIL_LOSE)
3899 mark_address_failure_for_pico(&header);
3902 /*----- Was there an fcc involved? -----*/
3903 if(lmc.so){
3904 /*------ Write it if at least something worked ------*/
3905 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3906 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3907 && pine_rfc822_output(&header, *body,
3908 NULL, NULL))){
3909 char label[50];
3911 strncpy(label, "Fcc", sizeof(label));
3912 label[sizeof(label)-1] = '\0';
3913 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3914 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3915 label[sizeof(label)-1] = '\0';
3918 /*-- Now actually copy to fcc folder and close --*/
3919 result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
3920 NULL, label,
3921 F_ON(F_MARK_FCC_SEEN, ps_global)
3922 ? "\\SEEN" : NULL))
3923 ? P_FCC_WIN : P_FCC_LOSE;
3925 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3926 q_status_message(SM_ORDER, 3, 5,
3927 _("Fcc Failed!. No message saved."));
3928 dprint((1,
3929 "explicit fcc write failed!\n"));
3930 result |= P_FCC_LOSE;
3933 so_give(&lmc.so);
3936 if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
3938 * Encode child's result in hi-byte of
3939 * editor's result
3941 editor_result = ((result << 8) | COMP_GOTHUP);
3942 goto fake_hup;
3945 exit(result);
3948 if(ps_global->post->pid > 0){
3949 q_status_message(SM_ORDER, 3, 3,
3950 _("Message handed off for posting"));
3951 break; /* up to our child now */
3953 else{
3954 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3955 "Can't fork for send: %s",
3956 error_description(errno));
3957 if(ps_global->post->fcc)
3958 fs_give((void **) &ps_global->post->fcc);
3960 fs_give((void **) &ps_global->post);
3963 if(lmc.so) /* throw away unused store obj */
3964 so_give(&lmc.so);
3966 if(outgoing->message_id)
3967 fs_give((void **) &outgoing->message_id);
3969 outgoing->message_id = generate_message_id();
3971 continue; /* if we got here, there was a prob */
3973 #endif /* BACKGROUND_POST */
3975 /*------- Actually mail the message ------*/
3976 if(valid_addr == CA_OK
3977 && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
3978 char **alt_smtp = NULL;
3980 if(role && role->smtp){
3981 if(ps_global->FIX_SMTP_SERVER
3982 && ps_global->FIX_SMTP_SERVER[0])
3983 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3984 else
3985 alt_smtp = role->smtp;
3988 result |= (call_mailer(&header, *body, alt_smtp,
3989 call_mailer_flags,
3990 call_mailer_file_result,
3991 pipe_callback) > 0)
3992 ? P_MAIL_WIN : P_MAIL_LOSE;
3994 if(result & P_MAIL_LOSE)
3995 mark_address_failure_for_pico(&header);
3998 /*----- Was there an fcc involved? -----*/
3999 if(lmc.so){
4000 /*------ Write it if at least something worked ------*/
4001 if((result & (P_MAIL_WIN | P_NEWS_WIN))
4002 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
4003 && pine_rfc822_output(&header, *body, NULL, NULL))){
4004 char label[50];
4006 strncpy(label, "Fcc", sizeof(label));
4007 label[sizeof(label)-1] = '\0';
4008 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
4009 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
4010 label[sizeof(label)-1] = '\0';
4013 /*-- Now actually copy to fcc folder and close --*/
4014 result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
4015 F_ON(F_MARK_FCC_SEEN, ps_global)
4016 ? "\\SEEN" : NULL))
4017 ? P_FCC_WIN : P_FCC_LOSE;
4019 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
4020 q_status_message(SM_ORDER,3,5,
4021 _("Fcc Failed!. No message saved."));
4022 dprint((1, "explicit fcc write failed!\n"));
4023 result |= P_FCC_LOSE;
4026 so_give(&lmc.so);
4029 /*----- Mail Post FAILED, back to composer -----*/
4030 if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
4031 dprint((1, "Send failed, continuing\n"));
4033 if(result & P_FCC_LOSE){
4035 * Find field entry associated with fcc, and start
4036 * composer on it...
4038 for(pf = header.local; pf && pf->name; pf = pf->next)
4039 if(pf->type == Fcc && HE(pf))
4040 HE(pf)->start_here = 1;
4042 q_status_message(SM_ORDER | SM_DING, 3, 3,
4043 pine_send_status(result, fcc,
4044 tmp_20k_buf, SIZEOF_20KBUF, NULL));
4047 if(outgoing->message_id)
4048 fs_give((void **) &outgoing->message_id);
4050 outgoing->message_id = generate_message_id();
4052 continue;
4056 * If message sent *completely* successfully, there's a
4057 * reply struct AND we're allowed to write back state, do it.
4058 * But also protect against shifted message numbers due
4059 * to new mail arrival. Since the number passed is based
4060 * on the real imap msg no, AND we're sure no expunge has
4061 * been done, just fix up the sorted number...
4063 update_answered_flags(reply);
4065 /*----- Signed, sealed, delivered! ------*/
4066 q_status_message(SM_ORDER, 0, 3,
4067 pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
4069 break; /* All's well, pop out of here */
4073 if(orig_so)
4074 so_give(&orig_so);
4076 if(fcc)
4077 fs_give((void **)&fcc);
4079 free_body_particulars(bp);
4081 free_attachment_list(&pbf->attachments);
4083 standard_picobuf_teardown(pbf);
4085 for(i=0; i < fixed_cnt; i++){
4086 if(pfields[i].textbuf)
4087 fs_give((void **)&pfields[i].textbuf);
4089 fs_give((void **)&pfields[i].name);
4092 if(lcc_addr)
4093 mail_free_address(&lcc_addr);
4095 if(nobody_addr)
4096 mail_free_address(&nobody_addr);
4098 free_prompts(header.custom);
4099 free_customs(header.custom);
4100 fs_give((void **)&pfields);
4101 free_headents(&headents);
4102 fs_give((void **)&sending_order);
4103 if(suggested_nntp_server)
4104 fs_give((void **)&suggested_nntp_server);
4105 if(title)
4106 fs_give((void **)&title);
4108 if(local_redraft_pos && local_redraft_pos != redraft_pos)
4109 free_redraft_pos(&local_redraft_pos);
4111 pbf = save_previous_pbuf;
4112 g_rolenick = NULL;
4114 dprint((4, "=== send returning ===\n"));
4119 * Check for subject in outgoing message.
4121 * Asks user whether to proceed with no subject.
4124 check_for_subject(METAENV *header)
4126 PINEFIELD *pf;
4127 int rv = CF_OK;
4129 for(pf = header->local; pf && pf->name; pf = pf->next)
4130 if(pf->type == Subject){
4131 if(pf->text && *pf->text && **pf->text)
4132 rv = CF_OK;
4133 else{
4134 if(want_to("No Subject, send anyway ",
4135 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
4136 rv = CF_OK;
4137 else
4138 rv = CF_MISSING;
4141 break;
4145 return(rv);
4150 * Check for fcc in outgoing message.
4152 * Asks user whether to proceed with no fcc.
4155 check_for_fcc(char *fcc)
4157 int rv = CF_OK;
4159 if(fcc && *fcc)
4160 rv = CF_OK;
4161 else{
4162 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
4163 rv = CF_OK;
4164 else
4165 rv = CF_MISSING;
4168 return(rv);
4173 * Confirm that the user wants to send to MAILER-DAEMON
4176 confirm_daemon_send(void)
4178 return(want_to("Really send this message to the MAILER-DAEMON",
4179 'n', 'n', NO_HELP, WT_NORM) == 'y');
4183 void
4184 free_prompts(PINEFIELD *head)
4186 PINEFIELD *pf;
4188 for(pf = head; pf && pf->name; pf = pf->next){
4189 if(HE(pf) && HE(pf)->prompt)
4190 fs_give((void **)& HE(pf)->prompt);
4196 postpone_prompt(void)
4198 static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
4199 {'f', 'f', "F", N_("Form Letter Folder")},
4200 {-1, 0, NULL, NULL} };
4202 return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
4203 pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
4208 * call__mailer_file_result - some results from call_mailer might be in a file.
4209 * dislplay that file.
4211 void
4212 call_mailer_file_result(char *filename, int style)
4214 if(filename){
4215 if(style & CM_BR_VERBOSE){
4216 display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
4218 else{
4219 display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
4224 void
4225 mark_address_failure_for_pico(METAENV *header)
4227 PINEFIELD *pf;
4228 ADDRESS *a;
4229 int error_count = 0;
4230 struct headerentry *last_he = NULL;
4232 for(pf = header->local; pf && pf->name; pf = pf->next)
4233 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
4234 for(a = *pf->addr; a != NULL; a = a->next)
4235 if(a->error != NULL
4236 && error_count++ < MAX_ADDR_ERROR
4237 && (HE(pf))){
4238 if(last_he) /* start last reported err */
4239 last_he->start_here = 0;
4241 (last_he = HE(pf))->start_here = 1;
4248 * This is specialized routine. It assumes that the only things that we
4249 * care about restoring are the body type, subtype, encoding and the
4250 * state of the charset parameter. It also assumes that if the charset
4251 * parameter exists when we save it, it won't be removed later.
4253 BODY_PARTICULARS_S *
4254 save_body_particulars(struct mail_bodystruct *body)
4256 BODY_PARTICULARS_S *bp;
4257 PARAMETER *pm;
4259 bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
4261 bp->type = body->type;
4262 bp->encoding = body->encoding;
4263 bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
4264 bp->parameter = body->parameter;
4265 for(pm = bp->parameter;
4266 pm && strucmp(pm->attribute, "charset") != 0;
4267 pm = pm->next)
4268 ;/* searching for possible charset parameter */
4270 if(pm){ /* found one */
4271 bp->had_csp = 1; /* saved body had charset parameter */
4272 bp->charset = pm->value ? cpystr(pm->value) : NULL;
4274 else{
4275 bp->had_csp = 0;
4276 bp->charset = NULL;
4279 return(bp);
4283 void
4284 reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
4286 body->type = bp->type;
4287 body->encoding = bp->encoding;
4288 if(body->subtype)
4289 fs_give((void **)&body->subtype);
4291 body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
4293 if(bp->parameter){
4294 PARAMETER *pm, *pm_prev = NULL;
4296 for(pm = body->parameter;
4297 pm && strucmp(pm->attribute, "charset") != 0;
4298 pm = pm->next)
4299 pm_prev = pm;
4301 if(pm){ /* body has charset parameter */
4302 if(bp->had_csp){ /* reset to what it used to be */
4303 if(pm->value)
4304 fs_give((void **)&pm->value);
4306 pm->value = bp->charset ? cpystr(bp->charset) : NULL;
4308 else{ /* remove charset parameter */
4309 if(pm_prev)
4310 pm_prev->next = pm->next;
4311 else
4312 body->parameter = pm->next;
4314 mail_free_body_parameter(&pm);
4317 else{
4318 if(bp->had_csp){
4320 * This can't happen because grope never removes
4321 * the charset parameter.
4323 q_status_message(SM_ORDER | SM_DING, 5, 5,
4324 "Programmer error: saved charset but no current charset param in pine_send");
4327 else{
4328 ok, still no parameter
4333 else{
4334 if(body->parameter)
4335 mail_free_body_parameter(&body->parameter);
4337 body->parameter = NULL;
4342 void
4343 free_body_particulars(BODY_PARTICULARS_S *bp)
4345 if(bp){
4346 if(bp->subtype)
4347 fs_give((void **)&bp->subtype);
4349 if(bp->charset)
4350 fs_give((void **)&bp->charset);
4352 fs_give((void **)&bp);
4357 /*----------------------------------------------------------------------
4358 Build a status message suitable for framing
4360 Returns: pointer to resulting buffer
4361 ---*/
4362 char *
4363 pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
4365 int avail = ps_global->ttyo->screen_cols - 2;
4366 int fixedneed, need, lenfcc;
4367 char *part1, *part2, *part3, *part4, *part5;
4368 char fbuf[MAILTMPLEN+1];
4370 part1 = (result & P_NEWS_WIN)
4371 ? "posted"
4372 : (result & P_NEWS_LOSE)
4373 ? "NOT POSTED"
4374 : "";
4375 part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
4376 && (result & P_FCC_BITS))
4377 ? ", "
4378 : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
4379 ? " and "
4380 : "";
4381 part3 = (result & P_MAIL_WIN)
4382 ? "sent"
4383 : (result & P_MAIL_LOSE)
4384 ? "NOT SENT"
4385 : "";
4386 part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
4387 ? " and "
4388 : "";
4389 part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
4390 ? "ONLY copied to "
4391 : (result & P_FCC_WIN)
4392 ? "copied to "
4393 : (result & P_FCC_LOSE)
4394 ? "NOT copied to "
4395 : "";
4396 lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
4398 fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
4399 strlen(part4) + strlen(part5);
4400 need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
4402 if(need > avail && fixedneed + 3 >= avail){
4403 /* dots on end of fixed, no fcc */
4404 snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
4405 part1, part2, part3, part4, part5);
4406 short_str(fbuf, buf, buflen, avail, EndDots);
4408 else if(need > avail){
4409 /* include whole fixed part, quotes and dots at end of fcc name */
4410 if(lenfcc > 0)
4411 lenfcc = MAX(1, lenfcc-(need-avail));
4413 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4414 part1, part2, part3, part4, part5,
4415 (result & P_FCC_BITS) ? "\"" : "",
4416 short_str((result & P_FCC_BITS) ? fcc_name : "",
4417 fbuf, sizeof(fbuf), lenfcc, FrontDots),
4418 (result & P_FCC_BITS) ? "\"" : "");
4420 else{
4421 /* whole thing */
4422 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4423 part1, part2, part3, part4, part5,
4424 (result & P_FCC_BITS) ? "\"" : "",
4425 (result & P_FCC_BITS) ? fcc_name : "",
4426 (result & P_FCC_BITS) ? "\"" : "");
4429 if(goodorbad)
4430 *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
4432 return(buf);
4435 /* Callback from Pico to set the conditions for Alpine to start a new thread
4438 void
4439 new_thread_on_blank_subject(void)
4441 ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
4446 /*----------------------------------------------------------------------
4447 Call back for pico to insert the specified message's text
4449 Args: n -- message number to format
4450 f -- function to use to output the formatted message
4453 Returns: returns msg number formatted on success, zero on error.
4454 ----*/
4455 long
4456 message_format_for_pico(long int n, int (*f) (int))
4458 ENVELOPE *e;
4459 BODY *b;
4460 char *old_quote = NULL;
4461 long rv = n;
4463 if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
4464 && (e = pine_mail_fetchstructure(ps_global->mail_stream,
4465 mn_m2raw(ps_global->msgmap, n), &b)))){
4466 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4467 flush_status_messages(0);
4468 return(0L);
4471 /* temporarily assign a new quote string */
4472 old_quote = pbf->quote_str;
4473 pbf->quote_str = reply_quote_str(e);
4475 /* build separator line */
4476 reply_delimiter(e, NULL, f);
4478 /* actually write message text */
4479 if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
4480 FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
4481 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4482 flush_status_messages(0);
4483 rv = 0L;
4486 fs_give((void **)&pbf->quote_str);
4487 pbf->quote_str = old_quote;
4488 return(rv);
4492 /*----------------------------------------------------------------------
4493 Call back for pico to prompt the user for exit confirmation
4495 Args: dflt -- default answer for confirmation prompt
4497 Returns: either NULL if the user accepts exit, or string containing
4498 reason why the user declined.
4499 ----*/
4501 send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
4502 char **result)
4504 int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
4505 int dsn_label = 0, fcc_label = 0, lparen;
4506 int flowing_label = 0, double_rad;
4507 char *rstr = NULL, *p, *lc, *optp;
4508 char dsn_string[30];
4509 void (*redraw)(void) = ps_global->redrawer;
4510 ESCKEY_S opts[18];
4511 struct filters {
4512 char *filter;
4513 int index;
4514 struct filters *prev, *next;
4515 } *filters = NULL, *fp;
4517 sending_filter_requested = NULL;
4518 call_mailer_flags = 0;
4519 background_requested = 0;
4520 flowing_requested = allow_flowed ? 1 : 0;
4521 lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
4522 if(priority_requested)
4523 fs_give((void **) &priority_requested);
4525 if(background_posting(FALSE)){
4526 if(result)
4527 *result = "Can't send while background posting. Use postpone.";
4529 return(1);
4532 if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
4533 if(result)
4534 *result = NULL;
4536 return(0);
4539 ps_global->redrawer = redraw_pico;
4541 if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
4542 (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
4545 * Build list of available filters...
4547 for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
4548 for(p = ps_global->VAR_SEND_FILTER[i];
4549 *p && !isspace((unsigned char)*p); p++)
4552 c = *p;
4553 *p = '\0';
4554 if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
4555 && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
4556 *p = c;
4557 continue;
4560 fp = (struct filters *)fs_get(sizeof(struct filters));
4561 fp->index = i;
4562 if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
4563 fp->filter = cpystr(lc);
4565 else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
4566 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
4567 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4568 fp->filter = cpystr(tmp_20k_buf);
4570 else
4571 fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
4573 *p = c;
4575 if(filters){
4576 fp->next = filters;
4577 fp->prev = filters->prev;
4578 fp->prev->next = filters->prev = fp;
4580 else{
4581 filters = (struct filters *)fs_get(sizeof(struct filters));
4582 filters->index = -1;
4583 filters->filter = NULL;
4584 filters->next = filters->prev = fp;
4585 fp->next = fp->prev = filters;
4589 i = 0;
4590 opts[i].ch = 'y';
4591 opts[i].rval = 'y';
4592 opts[i].name = "Y";
4593 opts[i++].label = N_("Yes");
4595 opts[i].ch = 'n';
4596 opts[i].rval = 'n';
4597 opts[i].name = "N";
4598 opts[i++].label = N_("No");
4600 if(filters){
4601 /* set global_filter_pointer to desired filter or NULL if none */
4602 /* prepare two keymenu slots for selecting filter */
4603 opts[i].ch = ctrl('P');
4604 opts[i].rval = 10;
4605 opts[i].name = "^P";
4606 opts[i++].label = N_("Prev Filter");
4608 opts[i].ch = ctrl('N');
4609 opts[i].rval = 11;
4610 opts[i].name = "^N";
4611 opts[i++].label = N_("Next Filter");
4613 if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
4614 filters = filters->next;
4617 if(F_ON(F_VERBOSE_POST, ps_global)){
4618 /* setup keymenu slot to toggle verbose mode */
4619 opts[i].ch = ctrl('W');
4620 opts[i].rval = 12;
4621 opts[i].name = "^W";
4622 verbose_label = i++;
4625 if(allow_flowed){
4626 /* setup keymenu slot to toggle flowed mode */
4627 opts[i].ch = ctrl('V');
4628 opts[i].rval = 22;
4629 opts[i].name = "^V";
4630 flowing_label = i++;
4631 flowing_requested = 1;
4634 if(F_ON(F_NO_FCC_ATTACH, ps_global)){
4635 /* setup keymenu slot to toggle attacment on fcc */
4636 opts[i].ch = ctrl('F');
4637 opts[i].rval = 21;
4638 opts[i].name = "^F";
4639 fcc_label = i++;
4642 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4643 if(F_ON(F_BACKGROUND_POST, ps_global)){
4644 opts[i].ch = ctrl('R');
4645 opts[i].rval = 15;
4646 opts[i].name = "^R";
4647 bg_label = i++;
4649 #endif
4651 opts[i].ch = 'p';
4652 opts[i].rval = 'p';
4653 opts[i].name = "P";
4654 opts[i++].label = N_("Priority");
4656 #ifdef SMIME
4657 if(F_OFF(F_DONT_DO_SMIME, ps_global)){
4658 opts[i].ch = 'e';
4659 opts[i].rval = 'e';
4660 opts[i].name = "E";
4661 opts[i++].label = "Encrypt";
4663 opts[i].ch = 'g';
4664 opts[i].rval = 'g';
4665 opts[i].name = "G";
4666 opts[i++].label = "Sign";
4668 if(ps_global->smime){
4669 ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
4670 ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
4673 #endif
4675 double_rad = i;
4677 if(F_ON(F_DSN, ps_global)){
4678 /* setup keymenu slots to toggle dsn bits */
4679 opts[i].ch = 'd';
4680 opts[i].rval = 'd';
4681 opts[i].name = "D";
4682 opts[i].label = N_("DSNOpts");
4683 dsn_label = i++;
4684 opts[i].ch = -2;
4685 opts[i].rval = 's';
4686 opts[i].name = "S";
4687 opts[i++].label = "";
4688 opts[i].ch = -2;
4689 opts[i].rval = 'x';
4690 opts[i].name = "X";
4691 opts[i++].label = "";
4692 opts[i].ch = -2;
4693 opts[i].rval = 'h';
4694 opts[i].name = "H";
4695 opts[i++].label = "";
4698 if(filters){
4699 opts[i].ch = KEY_UP;
4700 opts[i].rval = 10;
4701 opts[i].name = "";
4702 opts[i++].label = "";
4704 opts[i].ch = KEY_DOWN;
4705 opts[i].rval = 11;
4706 opts[i].name = "";
4707 opts[i++].label = "";
4710 opts[i].ch = -1;
4712 fix_windsize(ps_global);
4714 while(1){
4715 if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
4716 *p = '\0';
4717 else
4718 p = NULL;
4720 lparen = 0;
4721 strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
4722 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4723 optp = tmp_20k_buf + strlen(tmp_20k_buf);
4725 if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
4726 sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4728 if(allow_flowed && !flowing_requested){
4729 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4730 if(!lparen){
4731 *optp++ = ' ';
4732 *optp++ = '(';
4733 lparen++;
4735 else
4736 *optp++ = ' ';
4739 sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4742 if(filters){
4743 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4744 if(!lparen){
4745 *optp++ = ' ';
4746 *optp++ = '(';
4747 lparen++;
4749 else{
4750 *optp++ = ',';
4751 *optp++ = ' ';
4755 if(filters->filter){
4756 sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4757 sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4758 if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
4759 *optp++ = '\"';
4761 else
4762 sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4765 if((call_mailer_flags & CM_VERBOSE) || background_requested){
4766 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4767 if(!lparen){
4768 *optp++ = ' ';
4769 *optp++ = '(';
4770 lparen++;
4772 else
4773 *optp++ = ' ';
4776 sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4777 if(call_mailer_flags & CM_VERBOSE)
4778 sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4780 if(background_requested)
4781 sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4783 sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4786 if(g_rolenick && !(he && he[N_FROM].dirty)){
4787 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4788 if(!lparen){
4789 *optp++ = ' ';
4790 *optp++ = '(';
4791 lparen++;
4793 else
4794 *optp++ = ' ';
4797 sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4798 sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4799 sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4802 if(call_mailer_flags & CM_DSN_SHOW){
4803 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4804 if(!lparen){
4805 *optp++ = ' ';
4806 *optp++ = '(';
4807 lparen++;
4809 else{
4810 *optp++ = ',';
4811 *optp++ = ' ';
4815 sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4818 #ifdef SMIME
4819 if(ps_global->smime && ps_global->smime->do_encrypt){
4820 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4821 if(!lparen){
4822 *optp++ = ' ';
4823 *optp++ = '(';
4824 lparen++;
4826 else{
4827 *optp++ = ',';
4828 *optp++ = ' ';
4832 sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4835 if(ps_global->smime && ps_global->smime->do_sign){
4836 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4837 if(!lparen){
4838 *optp++ = ' ';
4839 *optp++ = '(';
4840 lparen++;
4842 else{
4843 *optp++ = ',';
4844 *optp++ = ' ';
4848 sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4850 #endif
4852 if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
4853 *optp++ = ')';
4855 sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4856 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4858 if(p)
4859 *p = ' ';
4861 if(flowing_label)
4862 opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
4864 if(verbose_label)
4865 opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
4867 if(bg_label)
4868 opts[bg_label].label = background_requested
4869 ? N_("Foreground") : N_("Background");
4871 if(fcc_label)
4872 opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
4873 : N_("No Fcc Atmts ");
4875 if(F_ON(F_DSN, ps_global)){
4876 if(call_mailer_flags & CM_DSN_SHOW){
4877 opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
4878 ? N_("NoDelay") : N_("Delay");
4879 opts[dsn_label+1].ch = 's';
4880 opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
4881 ? N_("NoSuccess") : N_("Success");
4882 opts[dsn_label+2].ch = 'x';
4883 opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
4884 ? N_("ErrRets") : N_("NoErrRets");
4885 opts[dsn_label+3].ch = 'h';
4886 opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
4887 ? N_("RetHdrs") : N_("RetFull");
4891 if(double_rad +
4892 ((call_mailer_flags & CM_DSN_SHOW)
4893 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
4894 rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4895 'y', 'z',
4896 (F_ON(F_DSN, ps_global) && allow_flowed)
4897 ? h_send_prompt_dsn_flowed :
4898 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4899 allow_flowed ? h_send_prompt_flowed :
4900 h_send_prompt,
4901 RB_NORM);
4902 else
4903 rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4904 'y', 'z',
4905 (double_rad +
4906 ((call_mailer_flags & CM_DSN_SHOW)
4907 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
4908 ? NO_HELP :
4909 (F_ON(F_DSN, ps_global) && allow_flowed)
4910 ? h_send_prompt_dsn_flowed :
4911 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4912 allow_flowed ? h_send_prompt_flowed :
4913 h_send_prompt,
4914 RB_NORM);
4916 if(rv == 'y'){ /* user ACCEPTS! */
4917 break;
4919 else if(rv == 'n'){ /* Declined! */
4920 rstr = _("No Message Sent");
4921 break;
4923 else if(rv == 'z'){ /* Cancelled! */
4924 rstr = _("Send Cancelled");
4925 break;
4927 else if(rv == 10){ /* PREVIOUS filter */
4928 filters = filters->prev;
4930 else if(rv == 11){ /* NEXT filter */
4931 filters = filters->next;
4933 else if(rv == 21){
4934 lmc.text_only = !lmc.text_only;
4936 else if(rv == 12){ /* flip verbose bit */
4937 if(call_mailer_flags & CM_VERBOSE)
4938 call_mailer_flags &= ~CM_VERBOSE;
4939 else
4940 call_mailer_flags |= CM_VERBOSE;
4942 if((call_mailer_flags & CM_VERBOSE) && background_requested)
4943 background_requested = 0;
4945 else if(rv == 22){ /* flip flowing bit */
4946 flowing_requested = !flowing_requested;
4948 else if(rv == 15){
4949 if((background_requested = !background_requested)
4950 && (call_mailer_flags & CM_VERBOSE))
4951 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
4953 else if(call_mailer_flags & CM_DSN_SHOW){
4954 if(rv == 's'){ /* flip success bit */
4955 call_mailer_flags ^= CM_DSN_SUCCESS;
4956 /* turn off related bits */
4957 if(call_mailer_flags & CM_DSN_SUCCESS)
4958 call_mailer_flags &= ~(CM_DSN_NEVER);
4960 else if(rv == 'd'){ /* flip delay bit */
4961 call_mailer_flags ^= CM_DSN_DELAY;
4962 /* turn off related bits */
4963 if(call_mailer_flags & CM_DSN_DELAY)
4964 call_mailer_flags &= ~(CM_DSN_NEVER);
4966 else if(rv == 'x'){ /* flip never bit */
4967 call_mailer_flags ^= CM_DSN_NEVER;
4968 /* turn off related bits */
4969 if(call_mailer_flags & CM_DSN_NEVER)
4970 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
4972 else if(rv == 'h'){ /* flip full bit */
4973 call_mailer_flags ^= CM_DSN_FULL;
4976 else if(rv == 'd'){ /* show dsn options */
4978 * When you turn on DSN, the default is to notify on
4979 * failure, success, or delay; and to return the whole
4980 * body on failure.
4982 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
4984 else if(rv == 'p'){ /* choose X-Priority */
4985 char *prio;
4987 prio = choose_a_priority(priority_requested);
4988 if((ps_global->redrawer = redraw_pico) != NULL){
4989 (*ps_global->redrawer)();
4990 fix_windsize(ps_global);
4993 if(prio){
4994 if(priority_requested)
4995 fs_give((void **) &priority_requested);
4997 if(prio[0])
4998 priority_requested = prio;
4999 else
5000 fs_give((void **) &prio);
5003 #ifdef SMIME
5004 else if(rv=='e'){
5005 if(ps_global->smime)
5006 ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
5008 else if(rv=='g'){
5009 if(ps_global->smime)
5010 ps_global->smime->do_sign = !ps_global->smime->do_sign;
5012 #endif
5014 snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
5015 (call_mailer_flags & CM_DSN_NEVER)
5016 ? "Never" : "F",
5017 (call_mailer_flags & CM_DSN_DELAY)
5018 ? "D" : "",
5019 (call_mailer_flags & CM_DSN_SUCCESS)
5020 ? "S" : "",
5021 (call_mailer_flags & CM_DSN_NEVER)
5022 ? ""
5023 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
5024 : "-Hdrs");
5025 dsn_string[sizeof(dsn_string)-1] = '\0';
5028 /* remember selection */
5029 if(filters && filters->index > -1)
5030 sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
5032 if(filters){
5033 filters->prev->next = NULL; /* tie off list */
5034 while(filters){ /* then free it */
5035 fp = filters->next;
5036 if(filters->filter)
5037 fs_give((void **)&filters->filter);
5039 fs_give((void **)&filters);
5040 filters = fp;
5044 if(old_suspend)
5045 (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
5047 if(result)
5048 *result = rstr;
5050 ps_global->redrawer = redraw;
5052 return((rstr == NULL) ? 0 : 1);
5057 * Allow user to choose a priority for sending.
5059 * Returns an allocated priority on success, NULL otherwise.
5061 char *
5062 choose_a_priority(char *default_val)
5064 char *choice = NULL;
5065 char **priority_list, **lp;
5066 char *starting_val = NULL;
5067 char *none;
5068 int i, cnt;
5069 PRIORITY_S *p;
5071 for(cnt = 0, p = priorities; p && p->desc; p++)
5072 cnt++;
5074 cnt++; /* for NONE entry */
5075 lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
5076 memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
5078 for(i = 0, p = priorities; p && p->desc; p++){
5079 *lp = cpystr(p->desc);
5080 if(default_val && !strcmp(default_val, p->desc))
5081 starting_val = (*lp);
5083 lp++;
5086 none = _("NONE - No X-Priority header included");
5087 *lp = cpystr(none);
5088 if(!starting_val)
5089 starting_val = (*lp);
5091 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5092 TRANSLATORS: Print something1 using something2.
5093 "priorities" is something1 */
5094 choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
5095 _("priorities"), h_select_priority_screen,
5096 _("HELP FOR SELECTING A PRIORITY"),
5097 starting_val);
5099 if(!choice)
5100 q_status_message(SM_ORDER, 1, 4, _("No change"));
5101 else if(!strcmp(choice, none))
5102 choice[0] = '\0';
5104 free_list_array(&priority_list);
5106 return(choice);
5111 dont_flow_this_time(void)
5113 return(flowing_requested ? 0 : 1);
5117 /*----------------------------------------------------------------------
5118 Call back for pico to display mime type of attachment
5120 Args: file -- filename being attached
5122 Returns: returns 1 on success (message queued), zero otherwise (don't know
5123 type so nothing queued).
5124 ----*/
5126 mime_type_for_pico(char *file)
5128 BODY *body;
5129 int rv;
5130 void *file_contents;
5132 body = mail_newbody();
5133 body->type = TYPEOTHER;
5134 body->encoding = ENCOTHER;
5136 /* don't know where the cursor's been, reset it */
5137 clear_cursor_pos();
5138 if(!set_mime_type_by_extension(body, file)){
5139 if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5140 body->contents.text.data = file_contents;
5141 set_mime_type_by_grope(body);
5145 if(body->type != TYPEOTHER){
5146 rv = 1;
5147 q_status_message3(SM_ORDER, 0, 3,
5148 _("File %s attached as type %s/%s"), file,
5149 body_types[body->type],
5150 body->subtype ? body->subtype : rfc822_default_subtype(body->type));
5152 else
5153 rv = 0;
5155 pine_free_body(&body);
5156 return(rv);
5160 /*----------------------------------------------------------------------
5161 Call back for pico to receive an uploaded message
5163 Args: fname -- name for uploaded file (empty if they want us to assign it)
5164 size -- pointer to long to hold the attachment's size
5166 Notes: the attachment is uploaded to a temp file, and
5168 Returns: TRUE on success, FALSE otherwise
5169 ----*/
5171 upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
5173 char cmd[MAXPATH+1], *fnp = NULL;
5174 char *locale_name = NULL;
5175 long l;
5176 PIPE_S *syspipe;
5178 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5179 fname ? fname : "<NO FILE>"));
5181 if(!fname) /* no place for file name */
5182 return(0);
5184 if(!*fname){ /* caller wants temp file */
5185 if((fnp = temp_nam(NULL, "pu")) != NULL){
5186 strncpy(fname, fnp, fnlen);
5187 fname[fnlen-1] = '\0';
5188 our_unlink(fnp);
5189 fs_give((void **)&fnp);
5192 else
5193 locale_name = convert_to_locale(fname);
5195 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
5196 ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
5197 if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
5198 0, pipe_callback, pipe_report_error)) != NULL){
5199 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
5200 if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
5201 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5202 "Error determining size of %s: %s", fname,
5203 fnp = error_description(errno));
5204 dprint((1,
5205 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5206 cmd ? cmd : "?",
5207 fname ? fname : "?",
5208 fnp ? fnp : "?"));
5210 else if(size)
5211 *size = l;
5213 if(locale_name)
5214 fs_give((void **) &locale_name);
5216 return(l >= 0);
5218 else
5219 q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
5221 if(locale_name)
5222 fs_give((void **) &locale_name);
5224 return(0);
5228 char *
5229 cancel_for_pico(void (*redraw_pico)(void))
5231 int rv;
5232 char *rstr = NULL;
5233 char *prompt =
5234 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5235 void (*redraw)(void) = ps_global->redrawer;
5236 static ESCKEY_S opts[] = {
5237 {'c', 'c', "C", N_("Confirm")},
5238 {'n', 'n', "N", N_("No")},
5239 {'y', 'y', "", ""},
5240 {-1, 0, NULL, NULL}
5243 ps_global->redrawer = redraw_pico;
5244 fix_windsize(ps_global);
5246 while(1){
5247 rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
5248 'n', 'x', h_confirm_cancel, RB_NORM);
5249 if(rv == 'c'){ /* user ACCEPTS! */
5250 rstr = "";
5251 break;
5253 else if(rv == 'y'){
5254 q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
5255 display_message('x');
5257 else
5258 break;
5261 ps_global->redrawer = redraw;
5262 return(rstr);
5266 /*----------------------------------------------------------------------
5267 Pass the first text segment of the message thru the "send filter"
5269 Args: body pointer and address for storage object of old data
5271 Returns: returns 1 on success, zero on error.
5272 ----*/
5274 filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
5275 STORE_S **old, METAENV *header)
5277 char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
5278 int key = 0, include_hdrs = 0;
5279 gf_io_t gc, pc;
5280 STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
5281 ? &body->nested.part->body.contents.text.data
5282 : &body->contents.text.data),
5283 *tmp_so = NULL, *tmpf_so,
5284 *save_local_so, *readthis_so, *our_tmpf_so = NULL;
5285 #define DO_HEADERS 1
5287 if(fcmd
5288 && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
5289 &key, &include_hdrs, NULL))){
5290 if(tmpf){
5292 * We need WRITE_TO_LOCALE here because the user is going to
5293 * be operating on tmpf. We need to change it back after they
5294 * are done.
5296 if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
5297 if(key){
5298 so_puts(tmpf_so, filter_session_key());
5299 so_puts(tmpf_so, NEWLINE);
5303 * If the headers are wanted for filtering, we can just
5304 * stick them in the tmpf file that is already there before
5305 * putting the body in.
5307 if(include_hdrs){
5308 save_local_so = lmc.so;
5309 lmc.so = tmpf_so; /* write it to tmpf_so */
5310 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5311 pine_rfc822_header(header, body, NULL, NULL);
5312 lmc.so = save_local_so;
5315 so_seek(*so, 0L, 0);
5316 gf_set_so_readc(&gc, *so);
5317 gf_set_so_writec(&pc, tmpf_so);
5318 gf_filter_init();
5319 errstr = gf_pipe(gc, pc);
5320 gf_clear_so_readc(*so);
5321 gf_clear_so_writec(tmpf_so);
5322 so_give(&tmpf_so);
5324 else
5325 errstr = "Can't create space for filter temporary file.";
5327 else if(include_hdrs){
5329 * Gf_filter wants a single storage object to read from.
5330 * If headers are wanted for filtering we'll have to put them
5331 * and the body into a temp file first and then use that
5332 * as the storage object for gf_filter.
5333 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5334 * takes care of that.
5336 if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
5337 /* put headers in our_tmpf_so */
5338 save_local_so = lmc.so;
5339 lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
5340 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5341 pine_rfc822_header(header, body, NULL, NULL);
5342 lmc.so = save_local_so;
5344 /* put body in our_tmpf_so */
5345 so_seek(*so, 0L, 0);
5346 gf_set_so_readc(&gc, *so);
5347 gf_set_so_writec(&pc, our_tmpf_so);
5348 gf_filter_init();
5349 errstr = gf_pipe(gc, pc);
5350 gf_clear_so_readc(*so);
5351 gf_clear_so_writec(our_tmpf_so);
5353 /* tell gf_filter to read from our_tmpf_so instead of *so */
5354 readthis_so = our_tmpf_so;
5356 else
5357 errstr = "Can't create space for temporary file.";
5359 else
5360 readthis_so = *so;
5362 if(!errstr){
5363 if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
5364 gf_set_so_writec(&pc, tmp_so);
5365 ps_global->mangled_screen = 1;
5366 suspend_busy_cue();
5367 ClearScreen();
5368 fflush(stdout);
5369 if(tmpf){
5370 PIPE_S *fpipe;
5372 if((fpipe = open_system_pipe(cmd, NULL, NULL,
5373 PIPE_NOSHELL | PIPE_RESET,
5374 0, pipe_callback, pipe_report_error)) != NULL){
5375 if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
5377 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5378 if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5379 gf_set_so_readc(&gc, tmpf_so);
5380 gf_filter_init();
5381 errstr = gf_pipe(gc, pc);
5382 gf_clear_so_readc(tmpf_so);
5383 so_give(&tmpf_so);
5385 else
5386 errstr = "Can't open temp file filter wrote.";
5388 else
5389 errstr = "Filter command returned error.";
5391 else
5392 errstr = "Can't exec filter text.";
5394 else
5395 errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
5396 readthis_so, pc, NULL, 0, 0,
5397 pipe_callback);
5399 if(our_tmpf_so)
5400 so_give(&our_tmpf_so);
5402 gf_clear_so_writec(tmp_so);
5404 #ifdef _WINDOWS
5405 if(!errstr){
5407 * Can't really be using stdout, so don't print message that
5408 * gets printed in the else. Ideally the program being called
5409 * will wait after showing the message, we might want to look
5410 * into doing the waiting in console based apps... or not.
5412 #else
5413 if(errstr){
5414 unsigned long ch;
5416 fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
5417 fflush(stdout);
5418 while((ch = read_char(300)) != ctrl('M')
5419 && ch != NO_OP_IDLE)
5420 putchar(BELL);
5422 else{
5423 #endif /* _WINDOWS */
5424 BODY *b = (body->type == TYPEMULTIPART)
5425 ? &body->nested.part->body : body;
5427 *old = *so; /* save old so */
5428 *so = tmp_so; /* return new one */
5429 (*so)->attr = copy_parameters((*old)->attr);
5432 * If the command said it would return new MIME
5433 * mime type data, check it out...
5435 if(mtf){
5436 char buf[MAILTMPLEN], *s;
5437 FILE *fp;
5439 if((fp = our_fopen(mtf, "rb")) != NULL){
5440 if(fgets(buf, sizeof(buf), fp)
5441 && !struncmp(buf, "content-", 8)
5442 && (s = strchr(buf+8, ':'))){
5443 BODY *nb = mail_newbody();
5445 for(*s++ = '\0'; *s == ' '; s++)
5448 rfc822_parse_content_header(nb,
5449 (char *) ucase((unsigned char *) buf+8),s);
5450 if(nb->type == TYPETEXT
5451 && nb->subtype
5452 && (!b->subtype
5453 || strucmp(b->subtype, nb->subtype))){
5454 if(b->subtype)
5455 fs_give((void **) &b->subtype);
5457 b->subtype = nb->subtype;
5458 nb->subtype = NULL;
5460 mail_free_body_parameter(&b->parameter);
5461 b->parameter = nb->parameter;
5462 nb->parameter = NULL;
5463 mail_free_body_parameter(&nb->parameter);
5466 mail_free_body(&nb);
5469 fclose(fp);
5474 * Reevaluate the encoding in case form's changed...
5476 b->encoding = ENCOTHER;
5477 set_mime_type_by_grope(b);
5480 ClearScreen();
5481 resume_busy_cue(0);
5483 else
5484 errstr = "Can't create space for filtered text.";
5487 fs_give((void **)&cmd);
5489 else
5490 return(0);
5492 if(tmpf){
5493 our_unlink(tmpf);
5494 fs_give((void **)&tmpf);
5497 if(mtf){
5498 our_unlink(mtf);
5499 fs_give((void **) &mtf);
5502 if(resultf){
5503 if(name_file_size(resultf) > 0L)
5504 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
5505 our_unlink(resultf);
5506 fs_give((void **)&resultf);
5508 else if(errstr){
5509 if(tmp_so)
5510 so_give(&tmp_so);
5512 q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
5513 errstr);
5514 dprint((1, "Filter FAILED: %s\n",
5515 errstr ? errstr : "?"));
5518 return(errstr == NULL);
5522 /*----------------------------------------------------------------------
5523 Copy the newsgroup name of the given mailbox into the given buffer
5525 Args:
5527 Returns:
5528 ----*/
5529 void
5530 pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
5532 NETMBX mb;
5534 if(*mailbox == '#'){ /* Strip the leading "#news." */
5535 strncpy(group_name, mailbox + 6, len-1);
5536 group_name[len-1] = '\0';
5538 else if(mail_valid_net_parse(mailbox, &mb)){
5539 pine_send_newsgroup_name(mb.mailbox, group_name, len);
5541 else
5542 *group_name = '\0';
5546 /*----------------------------------------------------------------------
5547 Generate and send a message back to the pine development team
5549 Args: none
5551 Returns: none
5552 ----*/
5553 void
5554 phone_home(char *addr)
5556 char tmp[MAX_ADDRESS];
5557 ENVELOPE *outgoing;
5558 BODY *body;
5560 outgoing = mail_newenvelope();
5561 if(!addr || !strindex(addr, '@')){
5562 snprintf(addr = tmp, sizeof(tmp), "alpine%s@%s", PHONE_HOME_VERSION, PHONE_HOME_HOST);
5563 tmp[sizeof(tmp)-1] = '\0';
5566 rfc822_parse_adrlist(&outgoing->to, addr, ps_global->maildomain);
5568 outgoing->message_id = generate_message_id();
5569 outgoing->subject = cpystr("Document Request");
5570 outgoing->from = phone_home_from();
5572 body = mail_newbody();
5573 body->type = TYPETEXT;
5575 if((body->contents.text.data = (void *)so_get(PicoText,NULL,EDIT_ACCESS)) != NULL){
5576 so_puts((STORE_S *)body->contents.text.data, "Document request: ");
5577 so_puts((STORE_S *)body->contents.text.data, "Alpine-");
5578 so_puts((STORE_S *)body->contents.text.data, ALPINE_VERSION);
5579 if(ps_global->first_time_user)
5580 so_puts((STORE_S *)body->contents.text.data, " for New Users");
5582 if(ps_global->VAR_INBOX_PATH && ps_global->VAR_INBOX_PATH[0] == '{')
5583 so_puts((STORE_S *)body->contents.text.data, " and IMAP");
5585 if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
5586 && ps_global->VAR_NNTP_SERVER[0][0])
5587 so_puts((STORE_S *)body->contents.text.data, " and NNTP");
5589 (void) pine_simple_send(outgoing, &body, NULL,NULL,NULL,NULL, SS_NULLRP);
5591 q_status_message(SM_ORDER, 1, 3, "Thank you for being counted!");
5593 else
5594 q_status_message(SM_ORDER | SM_DING, 3, 4,
5595 "Problem creating space for message text.");
5597 mail_free_envelope(&outgoing);
5598 pine_free_body(&body);
5603 /*----------------------------------------------------------------------
5604 Set up fields for passing to pico. Assumes first text part is
5605 intended to be passed along for editing, and is in the form of
5606 of a storage object brought into existence sometime before pico_send().
5607 -----*/
5608 void
5609 outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
5610 PATMT **pico_a, int from_bounce)
5612 PINEFIELD *pf;
5615 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5616 * is guaranteed to be of type PicoText!
5618 if(bod->type == TYPETEXT){
5619 *text = so_text((STORE_S *) bod->contents.text.data);
5621 /* mark storage object as user edited */
5622 if(!from_bounce)
5623 (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
5625 else if(bod->type == TYPEMULTIPART){
5626 PART *part;
5627 PATMT **ppa;
5628 char *type, *name, *p;
5629 int name_l;
5632 * We used to jump out the window if the first part wasn't text,
5633 * but that may not be the case when bouncing a message with
5634 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5635 * contents of the first part to send is still ALWAYS in a
5636 * PicoText storage object, *AND* if that object doesn't contain
5637 * data of type text, then it must contain THE ENCODED NON-TEXT
5638 * DATA of the piece being sent.
5640 * It's up to the programmer to make sure that such a message is
5641 * sent via pine_simple_send and never get to the composer via
5642 * pine_send.
5644 * Make sense?
5646 *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
5648 /* mark storage object as user edited */
5649 if(!from_bounce)
5650 (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
5653 * If we already had a list, blast it now, so we can build a new
5654 * attachment list that reflects what's really there...
5656 if(pico_a){
5657 free_attachment_list(pico_a);
5660 /* Simplifyihg assumption #28e. (see cross reference)
5661 All parts in the body passed in here that are not already
5662 in the attachments list are added to the end of the attachments
5663 list. Attachment items not in the body list will be taken care
5664 of in strings2outgoing, but they are unlikey to occur
5667 for(part = bod->nested.part->next; part != NULL; part = part->next) {
5668 /* Already in list? */
5669 for(ppa = pico_a;
5670 *ppa && strcmp((*ppa)->id, part->body.id);
5671 ppa = &(*ppa)->next)
5674 if(!*ppa){ /* Not in the list! append it... */
5675 *ppa = (PATMT *)fs_get(sizeof(PATMT));
5677 if(part->body.description){
5678 char *p;
5679 size_t len;
5681 len = 4*strlen(part->body.description)+1;
5682 p = (char *)fs_get(len*sizeof(char));
5683 if(rfc1522_decode_to_utf8((unsigned char *)p,
5684 len, part->body.description) == (unsigned char *) p){
5685 (*ppa)->description = p;
5687 else{
5688 fs_give((void **)&p);
5689 (*ppa)->description = cpystr(part->body.description);
5692 else
5693 (*ppa)->description = cpystr("");
5695 type = type_desc(part->body.type, part->body.subtype,
5696 part->body.parameter, NULL, 0);
5699 * If we can find a "name" parm, display that too...
5701 if((name = parameter_val(part->body.parameter, "name")) != NULL){
5702 /* Convert any [ or ]'s the name contained */
5703 for(p = name; *p ; p++)
5704 if(*p == '[')
5705 *p = '(';
5706 else if(*p == ']')
5707 *p = ')';
5709 name_l = p - name;
5711 else
5712 name_l = 0;
5714 (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
5716 snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
5717 name ? ": " : "", name ? name : "");
5718 (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
5720 if(name)
5721 fs_give((void **) &name);
5723 (*ppa)->flags = A_FLIT;
5724 (*ppa)->size = cpystr(byte_string(
5725 send_body_size(&part->body)));
5726 if(!part->body.id)
5727 part->body.id = generate_message_id();
5729 (*ppa)->id = cpystr(part->body.id);
5730 (*ppa)->next = NULL;
5737 /*------------------------------------------------------------------
5738 Malloc strings to pass to composer editor because it expects
5739 such strings so it can realloc them
5740 -----------------------------------------------------------------*/
5742 * turn any address fields into text strings
5745 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5746 * NOT to be RFC1522 decoded. Said differently, they're understood
5747 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5748 * original charset tagging as far into the compose/send pipe as
5749 * we can.
5751 for(pf = header->local; pf && pf->name; pf = pf->next)
5752 if(pf->canedit)
5753 switch(pf->type){
5754 case Address :
5755 if(pf->addr){
5756 char *p, *t, *u;
5757 long l;
5759 pf->scratch = addr_list_string(*pf->addr, NULL, 1);
5762 * Scan for and fix-up patently bogus fields.
5764 * NOTE: collaboration with this code and what's done in
5765 * reply.c:reply_cp_addr to package up the bogus stuff
5766 * is required.
5768 for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
5769 for(t = p; ; t--)
5770 if(*t == '&'){ /* find "leading" token */
5771 int replacelen;
5774 * Rfc822_cat has been changed so that it now quotes
5775 * this sometimes. So we have to look out for quotes
5776 * which confuse the decoder. It was only quoting
5777 * because we were putting \r \n in the input, I think.
5779 if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
5780 t[-1] = p[-1] = ' ';
5782 *t++ = ' '; /* replace token */
5783 *p = '\0'; /* tie off string */
5784 u = rfc822_base64((unsigned char *) t,
5785 (unsigned long) strlen(t),
5786 (unsigned long *) &l);
5787 if(!u)
5788 u = "";
5790 replacelen = strlen(t);
5791 *p = '@'; /* restore 'p' */
5792 rplstr(p, strlen(p), 12, ""); /* clear special token */
5793 rplstr(t, strlen(u)-replacelen+1, replacelen, u);
5794 if(u)
5795 fs_give((void **) &u);
5797 if(HE(pf))
5798 HE(pf)->start_here = 1;
5800 break;
5802 else if(t == pf->scratch)
5803 break;
5805 removing_leading_white_space(pf->scratch);
5806 if(pf->scratch){
5807 size_t l;
5810 * Replace control characters with ^C notation, unless
5811 * some conditions are met (see istrncpy).
5812 * If user doesn't edit, then we switch back to the
5813 * original version. If user does edit, then all bets
5814 * are off.
5816 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5817 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5818 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5819 fs_give((void **)&pf->scratch);
5820 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5823 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5827 break;
5829 case Subject :
5830 if(pf->text){
5831 char *p, *src;
5832 size_t len;
5834 src = pf->scratch ? pf->scratch
5835 : (*pf->text) ? *pf->text : "";
5837 len = 4*strlen(src)+1;
5838 p = (char *)fs_get(len * sizeof(char));
5839 if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
5840 if(pf->scratch)
5841 fs_give((void **)&pf->scratch);
5843 pf->scratch = p;
5845 else{
5846 fs_give((void **)&p);
5847 if(!pf->scratch)
5848 pf->scratch = cpystr(src);
5851 if(pf->scratch){
5852 size_t l;
5855 * Replace control characters with ^C notation, unless
5856 * some conditions are met (see istrncpy).
5857 * If user doesn't edit, then we switch back to the
5858 * original version. If user does edit, then all bets
5859 * are off.
5861 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5862 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5863 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5864 fs_give((void **)&pf->scratch);
5865 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5868 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5872 break;
5874 default :
5875 break;
5880 /*----------------------------------------------------------------------
5881 Restore fields returned from pico to form useful to sending
5882 routines.
5883 -----*/
5884 void
5885 strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
5887 PINEFIELD *pf;
5888 int we_cancel = 0;
5890 we_cancel = busy_cue(NULL, NULL, 1);
5893 * turn any local address strings into address lists
5895 for(pf = header->local; pf && pf->name; pf = pf->next)
5896 if(pf->scratch){
5897 char *the_address = NULL;
5899 switch(pf->type){
5900 case Address :
5901 removing_trailing_white_space(pf->scratch);
5903 if((the_address || *pf->scratch) && pf->addr){
5904 ADDRESS *new_addr = NULL;
5905 static char *fakedomain = "@";
5907 if(!the_address)
5908 the_address = pf->scratch;
5910 rfc822_parse_adrlist(&new_addr, the_address,
5911 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
5912 ? fakedomain : ps_global->maildomain);
5913 mail_free_address(pf->addr); /* free old addrs */
5914 *pf->addr = new_addr; /* assign new addr */
5916 else if(pf->addr)
5917 mail_free_address(pf->addr); /* free old addrs */
5919 break;
5921 case Subject :
5922 if(*pf->text)
5923 fs_give((void **)pf->text);
5925 if(*pf->scratch){
5926 *pf->text = cpystr(pf->scratch);
5929 break;
5931 default :
5932 break;
5935 fs_give((void **)&pf->scratch); /* free now useless text */
5938 create_message_body(bod, attach, flow_it);
5940 if(we_cancel)
5941 cancel_busy_cue(-1);
5945 /*----------------------------------------------------------------------
5947 The head of the body list here is always either TEXT or MULTIPART. It may be
5948 changed from TEXT to MULTIPART if there are attachments to be added
5949 and it is not already multipart.
5950 ----*/
5951 void
5952 create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
5954 PART *p, **pp;
5955 PATMT *pa;
5956 BODY *tmp_body, *text_body = NULL;
5957 void *file_contents;
5958 PARAMETER **parmp;
5959 char *lc;
5961 TIME_STAMP("create_body start.", 1);
5963 * if conditions are met short circuit MIME wrapping
5965 if((*b)->type != TYPEMULTIPART && !attach){
5967 /* only override assigned encoding if it might need upgrading */
5968 if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
5969 (*b)->encoding = ENCOTHER;
5971 create_message_body_text(*b, flow_it);
5973 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
5974 || !((*b)->encoding == ENC8BIT
5975 || (*b)->encoding == ENCBINARY)){
5976 TIME_STAMP("create_body end.", 1);
5977 return;
5979 else /* protect 8bit in multipart */
5980 text_body = *b;
5983 if((*b)->type == TYPETEXT) {
5984 /*-- Current type is text, but there are attachments to add --*/
5985 /*-- Upgrade to a TYPEMULTIPART --*/
5986 tmp_body = (BODY *)mail_newbody();
5987 tmp_body->type = TYPEMULTIPART;
5988 tmp_body->nested.part = mail_newbody_part();
5989 if(text_body){
5991 * Why do we do this?
5992 * The problem is that base64 or quoted-printable encoding is
5993 * sensitive to having random data appended to it's end. If
5994 * we use a single part TEXT message and something in between
5995 * us and the end appends advertising without adjusting for
5996 * the encoding, the message is screwed up. So we wrap the
5997 * text part inside a multipart and then the appended data
5998 * will come after the boundary.
6000 * We wish we could do this on the way out the door in a
6001 * child of post_rfc822_output because at that point we know
6002 * the character set and the encoding being used. For example,
6003 * iso-2022-jp is an encoding that is not sensitive to data
6004 * appended to the end, so it wouldn't need to be wrapped.
6005 * We could conceivably have post_rfc822_body inspect the
6006 * body and change it before doing the output. It would work
6007 * but would be very fragile. We'd be passed a body from
6008 * c-client to output and instead of just doing the output
6009 * we'd change the body and then output it. Not worth it
6010 * since the multipart wrapping is completely correct for
6011 * MIME-aware mailers.
6013 (void) copy_body(&(tmp_body->nested.part->body), *b);
6014 /* move contents which were NOT copied */
6015 tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
6016 (*b)->contents.text.data = NULL;
6018 else{
6019 tmp_body->nested.part->body = **b;
6021 (*b)->subtype = (*b)->id = (*b)->description = NULL;
6022 (*b)->parameter = NULL;
6023 (*b)->contents.text.data = NULL;
6026 pine_free_body(b);
6027 *b = tmp_body;
6030 if(!text_body){
6031 /*-- Now type must be MULTIPART with first part text --*/
6032 (*b)->nested.part->body.encoding = ENCOTHER;
6033 create_message_body_text(&((*b)->nested.part->body), flow_it);
6036 /*------ Go through the parts list remove those to be deleted -----*/
6037 for(pp = &(*b)->nested.part->next; *pp;){
6038 for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
6039 /* already existed? */
6040 if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
6041 char *orig_descp = NULL, *cs = NULL;
6044 * decode original to see if it matches what was decoded
6045 * when we sent it in.
6048 if((*pp)->body.description)
6049 orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
6050 SIZEOF_20KBUF, (*pp)->body.description);
6052 if(!(*pp)->body.description /* update description? */
6053 || (pa->description && strcmp(pa->description, orig_descp))){
6054 if((*pp)->body.description)
6055 fs_give((void **) &(*pp)->body.description);
6057 /* encoding happens as msg text is written */
6058 (*pp)->body.description = cpystr(pa->description);
6061 if(cs)
6062 fs_give((void **) &cs);
6064 break;
6067 if(pa == NULL){
6068 p = *pp; /* prepare to zap *pp */
6069 *pp = p->next; /* pull next one in list up */
6070 p->next = NULL; /* tie off removed node */
6072 pine_free_body_data(&p->body); /* clean up contained data */
6073 mail_free_body_part(&p); /* free up the part */
6075 else
6076 pp = &(*pp)->next;
6079 /*---------- Now add any new attachments ---------*/
6080 for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
6081 for(pa = attach; pa != NULL; pa = pa->next) {
6082 if(pa->id != NULL)
6083 continue; /* Has an ID, it's old */
6086 * the idea is handle ALL attachments as open FILE *'s. Actual
6087 * encoding and such is handled at the time the message
6088 * is shoved into the mail slot or written to disk...
6090 * Also, we never unlink a file, so it's up to whoever opens
6091 * it to deal with tmpfile issues.
6093 if((file_contents = (void *)so_get(FileStar, pa->filename,
6094 READ_ACCESS)) == NULL){
6095 q_status_message2(SM_ORDER | SM_DING, 3, 4,
6096 _("Error \"%s\", couldn't attach file \"%s\""),
6097 error_description(errno), pa->filename);
6098 display_message('x');
6099 continue;
6102 p->next = mail_newbody_part();
6103 p = p->next;
6104 p->body.id = generate_message_id();
6105 p->body.contents.text.data = file_contents;
6108 * Set type to unknown and let set_mime_type_by_* figure it out.
6109 * Always encode attachments we add as BINARY.
6111 p->body.type = TYPEOTHER;
6112 p->body.encoding = ENCBINARY;
6113 p->body.size.bytes = name_file_size(pa->filename);
6114 if(!set_mime_type_by_extension(&p->body, pa->filename)){
6115 set_mime_type_by_grope(&p->body);
6116 set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
6119 so_release((STORE_S *)p->body.contents.text.data);
6121 if(pa->description) /* encoding happens when msg written */
6122 p->body.description = cpystr(pa->description);
6124 /* Add name attribute for backward compatibility */
6125 for(parmp = &p->body.parameter; *parmp; )
6126 if(!struncmp((*parmp)->attribute, "name", 4)
6127 && (!*((*parmp)->attribute + 4)
6128 || *((*parmp)->attribute + 4) == '*')){
6129 PARAMETER *free_me = *parmp;
6130 *parmp = (*parmp)->next;
6131 free_me->next = NULL;
6132 mail_free_body_parameter(&free_me);
6134 else
6135 parmp = &(*parmp)->next;
6137 set_parameter(parmp, "name",
6138 pa->filename
6139 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6140 : NULL);
6142 /* Then set the Content-Disposition ala RFC1806 */
6143 if(!p->body.disposition.type){
6144 p->body.disposition.type = cpystr("attachment");
6145 for(parmp = &p->body.disposition.parameter; *parmp; )
6146 if(!struncmp((*parmp)->attribute, "filename", 4)
6147 && (!*((*parmp)->attribute + 4)
6148 || *((*parmp)->attribute + 4) == '*')){
6149 PARAMETER *free_me = *parmp;
6150 *parmp = (*parmp)->next;
6151 free_me->next = NULL;
6152 mail_free_body_parameter(&free_me);
6154 else
6155 parmp = &(*parmp)->next;
6157 set_parameter(parmp, "filename",
6158 pa->filename
6159 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6160 : NULL);
6163 p->next = NULL;
6164 pa->id = cpystr(p->body.id);
6168 * Now, if this multipart has but one text piece (that is, no
6169 * attachments), then downgrade from a composite type to a discrete
6170 * text/plain message if CTE is not 8bit.
6172 if(!(*b)->nested.part->next
6173 && (*b)->nested.part->body.type == TYPETEXT
6174 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
6175 || !((*b)->nested.part->body.encoding == ENC8BIT
6176 || (*b)->nested.part->body.encoding == ENCBINARY))){
6177 /* Clone the interesting body part */
6178 tmp_body = mail_newbody();
6179 *tmp_body = (*b)->nested.part->body;
6180 /* and rub out what we don't want cleaned up when it's free'd */
6181 mail_initbody(&(*b)->nested.part->body);
6182 mail_free_body(b);
6183 *b = tmp_body;
6187 TIME_STAMP("create_body end.", 1);
6192 * Fill in text BODY part's structure
6195 void
6196 create_message_body_text(struct mail_bodystruct *b, int flow_it)
6198 set_mime_type_by_grope(b);
6199 if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
6200 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
6201 && flow_it)
6202 set_parameter(b ? &b->parameter : NULL, "format", "flowed");
6204 set_body_size(b);
6209 * free_attachment_list - free attachments in given list
6211 void
6212 free_attachment_list(PATMT **alist)
6214 PATMT *leading;
6216 while(alist && *alist){ /* pointer pointing to something */
6217 leading = (*alist)->next;
6218 if((*alist)->description)
6219 fs_give((void **)&(*alist)->description);
6221 if((*alist)->filename){
6222 if((*alist)->flags & A_TMP)
6223 if(our_unlink((*alist)->filename) < 0)
6224 dprint((1, "-- Can't unlink(%s): %s\n",
6225 (*alist)->filename ? (*alist)->filename : "?",
6226 error_description(errno)));
6228 fs_give((void **)&(*alist)->filename);
6231 if((*alist)->size)
6232 fs_give((void **)&(*alist)->size);
6234 if((*alist)->id)
6235 fs_give((void **)&(*alist)->id);
6237 fs_give((void **)alist);
6239 *alist = leading;
6244 void
6245 set_body_size(struct mail_bodystruct *b)
6247 unsigned char c;
6248 int we_cancel = 0;
6250 we_cancel = busy_cue(NULL, NULL, 1);
6251 so_seek((STORE_S *)b->contents.text.data, 0L, 0);
6252 b->size.bytes = 0L;
6253 while(so_readc(&c, (STORE_S *)b->contents.text.data))
6254 b->size.bytes++;
6256 if(we_cancel)
6257 cancel_busy_cue(-1);
6262 * view_as_rich - set the rich_header flag
6264 * name - name of the header field
6265 * deflt - default value to return if user didn't set it
6267 * Note: if the user tries to turn them all off with "", then
6268 * we take that to mean default, since otherwise there is no
6269 * way to get to the headers.
6272 view_as_rich(char *name, int deflt)
6274 char **p;
6275 char *q;
6277 p = ps_global->VAR_COMP_HDRS;
6279 if(p && *p && **p){
6280 for(; (q = *p) != NULL; p++){
6281 if(!struncmp(q, name, strlen(name)))
6282 return 0; /* 0 means we *do* view it by default */
6285 return 1; /* 1 means it starts out hidden */
6287 return(deflt);
6292 * background_posting - return whether or not we're already in the process
6293 * of posting
6296 background_posting(int gripe)
6298 if(ps_global->post){
6299 if(gripe)
6300 q_status_message(SM_ORDER|SM_DING, 3, 3,
6301 _("Can't post while posting!"));
6302 return(1);
6305 return(0);
6309 /*----------------------------------------------------------------------
6310 Validate the given subject relative to any news groups.
6312 Args: none
6314 Returns: always returns 1, but also returns error if
6315 ----*/
6317 valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
6319 struct headerentry *hp;
6321 if(expanded)
6322 *expanded = cpystr(given);
6324 if(error){
6326 * Now look for any header entry we passed to pico that has to do
6327 * with news. If there's no subject, gripe.
6329 for(hp = pbf->headents; hp->prompt; hp++)
6330 if(hp->help == h_composer_news){
6331 if(hp->hd_text->text[0] && !*given)
6332 *error = cpystr(
6333 _("News postings MUST have a subject! Please add one!"));
6335 break;
6339 return(0);
6344 * This is the build_address used by the composer to check for an address
6345 * in the addrbook.
6347 * Args: to -- the passed in line to parse
6348 * full_to -- Address of a pointer to return the full address in.
6349 * This will be allocated here and freed by the caller.
6350 * error -- Address of a pointer to return an error message in.
6351 * This will be allocated here and freed by the caller.
6352 * barg -- Address of a pointer to return the fcc in is in
6353 * fcc->tptr. It will have already been allocated by the
6354 * caller but we may free it and reallocate if we wish.
6355 * Caller will free it.
6357 * Result: 0 is returned if address was OK,
6358 * -1 if address wasn't OK.
6360 * Side effect: Can flush addrbook entry cache entries so they need to be
6361 * re-fetched afterwords.
6364 build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
6366 char *p;
6367 int ret_val, no_repo = 0, *save_nesting_level;
6368 BuildTo bldto;
6369 PrivateTop *pt = NULL;
6370 PrivateAffector *af = NULL;
6371 char *fcc_local = NULL;
6372 jmp_buf save_jmp_buf;
6374 dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
6376 /* check to see if to string is empty to avoid work */
6377 for(p = to; p && *p && isspace((unsigned char)*p); p++)
6378 ;/* do nothing */
6380 if(!p || !*p){
6381 if(full_to)
6382 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
6384 return 0;
6387 if(full_to != NULL)
6388 *full_to = (char *)NULL;
6390 if(error != NULL)
6391 *error = (char *)NULL;
6393 /* No guarantee cursor or status line is how we saved it */
6394 clear_cursor_pos();
6395 mark_status_unknown();
6397 if(ps_global->remote_abook_validity > 0 &&
6398 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6399 *mangled |= BUILDER_SCREEN_MANGLED;
6402 * If we end up jumping back here because somebody else changed one of
6403 * our addrbooks out from underneath us, we may well leak some memory.
6404 * That's probably ok since this will be very rare.
6406 * The reason for the memcpy of the jmp_buf is that we may actually
6407 * be indirectly calling this function from within the address book.
6408 * For example, we may be in the address book screen and then run
6409 * the ComposeTo command which puts us in the composer, then we call
6410 * build_address from there which resets addrbook_changed_unexpectedly.
6411 * Once we leave build_address we need to reset addrbook_changed_un...
6412 * because this position on the stack will no longer be valid.
6413 * Same is true of the other setjmp's in this file which are wrapped
6414 * in memcpy calls.
6416 save_nesting_level = cpyint(ab_nesting_level);
6417 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6418 if(setjmp(addrbook_changed_unexpectedly)){
6419 no_repo = 0;
6420 pt = NULL;
6421 af = NULL;
6422 fcc_local = NULL;
6423 if(error != NULL)
6424 *error = (char *)NULL;
6426 if(full_to && *full_to)
6427 fs_give((void **)full_to);
6429 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6430 dprint((1,
6431 "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
6432 addrbook_reset();
6433 ab_nesting_level = *save_nesting_level;
6436 ab_nesting_level++;
6437 bldto.type = Str;
6438 bldto.arg.str = to;
6439 ret_val = build_address_internal(bldto, full_to, error,
6440 barg ? &fcc_local : NULL,
6441 &no_repo, NULL, save_and_restore,
6442 0, mangled);
6443 ab_nesting_level--;
6444 if(save_nesting_level)
6445 fs_give((void **)&save_nesting_level);
6448 * Have to rfc1522_decode the full_to string before sending it back.
6450 if(full_to && *full_to ){
6451 char *q;
6452 size_t len;
6454 len = 4*strlen(*full_to)+1;
6455 q = (char *)fs_get(len * sizeof(char));
6456 p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
6458 /* p == q means that decoding happened, p is decoded *full_to */
6459 if(p == q){
6460 fs_give((void **)full_to);
6461 *full_to = p;
6463 else
6464 fs_give((void **)&q);
6467 if(fcc_local){
6468 unsigned long csum;
6470 /* Pt will point to headents[Fcc].bldr_private */
6471 pt = NULL;
6472 if(barg && barg->aff)
6473 pt = (PrivateTop *)(*barg->aff);
6476 * If *barg->aff is set, that means fcc was set from a list
6477 * during some previous builder call.
6478 * If the current To line contains the old expansion as a prefix, then
6479 * we should leave things as they are. In order to decide that,
6480 * we look at a hash value computed from the strings.
6482 if(pt && (af=pt->affector) && af->who == BP_To){
6483 int len;
6485 len = strlen(to);
6486 if(len >= af->cksumlen){
6487 int save;
6489 save = to[af->cksumlen];
6490 to[af->cksumlen] = '\0';
6491 csum = line_hash(to);
6492 to[af->cksumlen] = save;
6494 else
6495 csum = af->cksumval + 1; /* something not equal to cksumval */
6498 if(!pt ||
6499 !pt->affector ||
6500 (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
6502 /* replace fcc value */
6503 if(barg->tptr)
6504 fs_give((void **)&barg->tptr);
6506 barg->tptr = fcc_local;
6508 if(barg->aff){
6509 if(!pt){
6510 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6511 pt = (PrivateTop *)(*barg->aff);
6512 memset((void *)pt, 0, sizeof(PrivateTop));
6515 if(no_repo){
6516 if(!pt->affector)
6517 pt->affector =
6518 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6520 af = pt->affector;
6521 af->who = BP_To;
6522 af->cksumlen = strlen(((full_to && *full_to)
6523 ? *full_to : ""));
6524 af->cksumval = line_hash(((full_to && *full_to)
6525 ? *full_to : ""));
6527 else{
6529 * If result is reproducible, we don't keep track here.
6531 if(pt->affector)
6532 fs_give((void **)&pt->affector);
6536 else
6537 fs_give((void **)&fcc_local); /* unused in this case */
6540 /* This is so pico will erase the old message */
6541 if(error != NULL && *error == NULL)
6542 *error = cpystr("");
6544 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6545 flush_status_messages(1);
6546 return(ret_val);
6551 * This is the builder used by the composer for the Lcc line.
6553 * Args: lcc -- the passed in Lcc line to parse
6554 * full_lcc -- Address of a pointer to return the full address in.
6555 * This will be allocated here and freed by the caller.
6556 * error -- Address of a pointer to return an error message in.
6557 * This is not allocated so should not be freed by the caller.
6558 * barg -- This is a pointer to text for affected entries which
6559 * we may be changing. The first one in the list is the
6560 * To entry. We may put the name of the list in empty
6561 * group syntax form there (like List Name: ;).
6562 * The second one in the list is the fcc field.
6563 * The tptr members already point to text allocated in the
6564 * caller. We may free and reallocate here, caller will
6565 * free the result in any case.
6567 * Result: 0 is returned if address was OK,
6568 * -1 if address wasn't OK.
6570 * Side effect: Can flush addrbook entry cache entries so they need to be
6571 * re-fetched afterwords.
6574 build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
6576 int ret_val,
6577 no_repo = 0; /* fcc or lcc not reproducible */
6578 int *save_nesting_level;
6579 BuildTo bldlcc;
6580 PrivateTop *pt = NULL;
6581 PrivateAffector *af = NULL;
6582 char *p,
6583 *fcc_local = NULL,
6584 *to = NULL,
6585 *dummy;
6586 jmp_buf save_jmp_buf;
6588 dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
6590 /* check to see if to string is empty to avoid work */
6591 for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
6592 ;/* do nothing */
6594 if(!p || !*p){
6595 if(full_lcc)
6596 *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
6598 return 0;
6601 if(error != NULL)
6602 *error = (char *)NULL;
6604 if(ps_global->remote_abook_validity > 0 &&
6605 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6606 *mangled |= BUILDER_SCREEN_MANGLED;
6609 * If we end up jumping back here because somebody else changed one of
6610 * our addrbooks out from underneath us, we may well leak some memory.
6611 * That's probably ok since this will be very rare.
6613 save_nesting_level = cpyint(ab_nesting_level);
6614 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6615 if(setjmp(addrbook_changed_unexpectedly)){
6616 no_repo = 0;
6617 pt = NULL;
6618 af = NULL;
6619 fcc_local = NULL;
6620 to = NULL;
6621 if(error != NULL)
6622 *error = (char *)NULL;
6624 if(full_lcc && *full_lcc)
6625 fs_give((void **)full_lcc);
6627 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6628 dprint((1,
6629 "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
6630 addrbook_reset();
6631 ab_nesting_level = *save_nesting_level;
6634 ab_nesting_level++;
6635 bldlcc.type = Str;
6636 bldlcc.arg.str = lcc;
6639 * To is first affected_entry and Fcc is second.
6640 * The conditional stuff for the fcc argument says to only change the
6641 * fcc if the fcc pointer is passed in non-null, and the To pointer
6642 * is also non-null. If they are null, that means they've already been
6643 * entered (are sticky). We don't affect fcc if either fcc or To has
6644 * been typed in.
6646 ret_val = build_address_internal(bldlcc,
6647 full_lcc,
6648 error,
6649 (barg && barg->next && barg->next->tptr && barg->tptr)
6650 ? &fcc_local : NULL,
6651 &no_repo,
6652 (barg && barg->tptr) ? &to : NULL,
6653 save_and_restore, 0, mangled);
6655 ab_nesting_level--;
6656 if(save_nesting_level)
6657 fs_give((void **)&save_nesting_level);
6659 /* full_lcc is what ends up in the Lcc: line */
6660 if(full_lcc && *full_lcc){
6661 size_t len;
6664 * Have to rfc1522_decode the full_lcc string before sending it back.
6666 len = 4*strlen(*full_lcc)+1;
6667 p = (char *)fs_get(len * sizeof(char));
6668 if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
6669 fs_give((void **)full_lcc);
6670 *full_lcc = p;
6672 else
6673 fs_give((void **)&p);
6676 /* to is what ends up in the To: line */
6677 if(to && *to){
6678 unsigned long csum;
6679 size_t len;
6682 * Have to rfc1522_decode the full_to string before sending it back.
6684 len = 4*strlen(to)+1;
6685 p = (char *)fs_get(len * sizeof(char));
6686 dummy = NULL;
6687 if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
6689 * If the caller wants us to try to preserve the charset
6690 * information (they set aff) we copy it into encoded->etext.
6691 * We don't have to worry about pasting together pieces of
6692 * etext like we do in build_address because whenever the
6693 * Lcc line is setting the To line it will be setting the
6694 * whole line, not modifying it.
6695 * Pt will point to headents[To].bldr_private.
6697 if(barg && barg->aff){
6698 pt = (PrivateTop *)(*barg->aff);
6700 if(!pt){
6701 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6702 pt = (PrivateTop *)(*barg->aff);
6703 memset((void *)pt, 0, sizeof(PrivateTop));
6707 fs_give((void **)&to);
6708 to = p;
6710 else
6711 fs_give((void **)&p);
6713 if(dummy)
6714 fs_give((void **)&dummy);
6718 * This part is recording the fact that the To line was set to
6719 * what it is by entering something on the Lcc line. In particular,
6720 * if a list alias was entered here then the fullname of the list
6721 * goes in the To line. We save this affector information so that
6722 * we can tell it shouldn't be modified if we call build_addr_lcc
6723 * again unless we actually modified what's in the Lcc line so that
6724 * it doesn't start with the same thing. The problem we're solving
6725 * is that the contents of the Lcc line no longer look like the
6726 * list they were derived from.
6727 * Pt will point to headents[To].bldr_private.
6729 if(barg && barg->aff)
6730 pt = (PrivateTop *)(*barg->aff);
6732 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6733 int len;
6735 len = strlen(lcc);
6736 if(len >= af->cksumlen){
6737 int save;
6739 save = lcc[af->cksumlen];
6740 lcc[af->cksumlen] = '\0';
6741 csum = line_hash(lcc);
6742 lcc[af->cksumlen] = save;
6744 else
6745 csum = af->cksumval + 1; /* so they aren't equal */
6748 if(!pt ||
6749 !pt->affector ||
6750 pt->affector->who != BP_Lcc ||
6751 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6753 /* replace to value */
6754 if(barg->tptr && barg->tptr[0]){
6755 size_t l;
6756 char *t;
6758 l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
6759 t = (char *)fs_get((l+1) * sizeof(char));
6760 snprintf(t, l+1, "%s%s%s",
6761 barg->tptr,
6762 (to && *to) ? ", " : "",
6763 (to && *to) ? to : "");
6764 fs_give((void **)&barg->tptr);
6765 if(to)
6766 fs_give((void **)&to);
6768 barg->tptr = t;
6770 else{
6771 if(barg->tptr)
6772 fs_give((void **)&barg->tptr);
6774 barg->tptr = to;
6777 if(barg->aff){
6778 if(!pt){
6779 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6780 pt = (PrivateTop *)(*barg->aff);
6781 memset((void *)pt, 0, sizeof(PrivateTop));
6784 if(no_repo){
6785 if(!pt->affector)
6786 pt->affector =
6787 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6789 af = pt->affector;
6790 af->who = BP_Lcc;
6791 af->cksumlen = strlen(((full_lcc && *full_lcc)
6792 ? *full_lcc : ""));
6793 af->cksumval = line_hash(((full_lcc && *full_lcc)
6794 ? *full_lcc : ""));
6796 else{
6798 * If result is reproducible, we don't keep track here.
6800 if(pt->affector)
6801 fs_give((void **)&pt->affector);
6805 else
6806 fs_give((void **)&to); /* unused in this case */
6809 if(fcc_local){
6810 unsigned long csum;
6813 * If *barg->next->aff is set, that means fcc was set from a list
6814 * during some previous builder call. If the current Lcc line
6815 * contains the old expansion as a prefix, then we should leave
6816 * things as they are. In order to decide that we look at a hash
6817 * value computed from the strings.
6818 * Pt will point to headents[Fcc].bldr_private
6820 pt = NULL;
6821 if(barg && barg->next && barg->next->aff)
6822 pt = (PrivateTop *)(*barg->next->aff);
6824 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6825 int len;
6827 len = strlen(lcc);
6828 if(len >= af->cksumlen){
6829 int save;
6831 save = lcc[af->cksumlen];
6832 lcc[af->cksumlen] = '\0';
6833 csum = line_hash(lcc);
6834 lcc[af->cksumlen] = save;
6836 else
6837 csum = af->cksumval + 1; /* something not equal to cksumval */
6840 if(!pt ||
6841 !pt->affector ||
6842 pt->affector->who != BP_Lcc ||
6843 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6845 /* replace fcc value */
6846 if(barg->next->tptr)
6847 fs_give((void **)&barg->next->tptr);
6849 barg->next->tptr = fcc_local;
6851 if(barg->next->aff){
6852 if(!pt){
6853 *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
6854 pt = (PrivateTop *)(*barg->next->aff);
6855 memset((void *)pt, 0, sizeof(PrivateTop));
6858 if(no_repo){
6859 if(!pt->affector)
6860 pt->affector =
6861 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6863 af = pt->affector;
6864 af->who = BP_Lcc;
6865 af->cksumlen = strlen(((full_lcc && *full_lcc)
6866 ? *full_lcc : ""));
6867 af->cksumval = line_hash(((full_lcc && *full_lcc)
6868 ? *full_lcc : ""));
6870 else{
6872 * If result is reproducible, we don't keep track here.
6874 if(pt->affector)
6875 fs_give((void **)&pt->affector);
6879 else
6880 fs_give((void **)&fcc_local); /* unused in this case */
6884 if(error != NULL && *error == NULL)
6885 *error = cpystr("");
6887 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6888 flush_status_messages(0);
6889 return(ret_val);
6893 /*----------------------------------------------------------------------
6894 Verify and canonicalize news groups names.
6895 Called from the message composer
6897 Args: given_group -- List of groups typed by user
6898 expanded_group -- pointer to point to expanded list, which will be
6899 allocated here and freed in caller. If this is
6900 NULL, don't attempt to validate.
6901 error -- pointer to store error message
6902 fcc -- pointer to point to fcc, which will be
6903 allocated here and freed in caller
6905 Returns: 0 if all is OK
6906 -1 if addresses weren't valid
6908 Test the given list of newstroups against those recognized by our nntp
6909 servers. Testing by actually trying to open the list is much cheaper, both
6910 in bandwidth and memory, than yanking the whole list across the wire.
6911 ----*/
6913 news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
6915 int rv;
6916 char *fccptr = NULL;
6918 if(fcc && fcc->tptr)
6919 fccptr = cpystr(fcc->tptr);
6921 clear_cursor_pos();
6923 rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
6925 /* assign any new fcc to the BUILDER_ARG */
6926 if(fccptr){
6927 if(fcc){
6928 /* it changed */
6929 if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
6930 fs_give((void **) &fcc->tptr);
6931 fcc->tptr = fccptr;
6932 fccptr = NULL;
6936 if(fccptr)
6937 fs_give((void **) &fccptr);
6940 /* deal with any busy indicator */
6941 if(news_busy_cue){
6942 news_busy_cue = 0;
6943 cancel_busy_cue(0);
6944 mark_status_dirty();
6945 display_message('x');
6946 if(mangled)
6947 *mangled |= BUILDER_MESSAGE_DISPLAYED;
6951 return(rv);
6955 void
6956 news_build_busy(void)
6958 news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
6962 #if defined(DOS) || defined(OS2)
6964 /*----------------------------------------------------------------------
6965 Verify that the necessary pieces are around to allow for
6966 message sending under DOS
6968 Args: strict -- tells us if a remote stream is required before
6969 sending is permitted.
6971 The idea is to make sure pine knows enough to put together a valid
6972 from line. The things we MUST know are a user-id, user-domain and
6973 smtp server to dump the message off on. Typically these are
6974 provided in pine's configuration file, but if not, the user is
6975 queried here.
6976 ----*/
6978 dos_valid_from()
6980 char prompt[100], answer[80];
6981 int rc, i, flags;
6982 HelpType help;
6985 * query for user name portion of address, use IMAP login
6986 * name as default
6988 if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
6989 NETMBX mb;
6990 int no_prompt_user_id = 0;
6992 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6993 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6994 && *mb.user){
6995 strncpy(answer, mb.user, sizeof(answer)-1);
6996 answer[sizeof(answer)-1] = '\0';
6998 else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
6999 /* no user-id prompting if set */
7000 no_prompt_user_id = 1;
7001 rc = 0;
7002 if(!ps_global->mail_stream)
7003 do_broach_folder(ps_global->inbox_name,
7004 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
7005 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
7006 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
7007 && *mb.user){
7008 strncpy(answer, mb.user, sizeof(answer)-1);
7009 answer[sizeof(answer)-1] = '\0';
7011 else
7012 answer[0] = '\0';
7014 else
7015 answer[0] = '\0';
7017 if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
7018 /* No prompt, just assume mailbox login is user-id */
7019 no_prompt_user_id = 1;
7020 rc = 0;
7023 snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
7024 prompt[sizeof(prompt)-1] = '\0';
7026 help = NO_HELP;
7027 while(!no_prompt_user_id) {
7028 flags = OE_APPEND_CURRENT;
7029 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7030 sizeof(answer),prompt,NULL,help,&flags);
7031 if(rc == 2)
7032 continue;
7034 if(rc == 3){
7035 help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
7036 continue;
7039 if(rc != 4)
7040 break;
7043 if(rc == 1 || (rc == 0 && !answer[0])) {
7044 q_status_message(SM_ORDER, 3, 4,
7045 _("Send cancelled (User-id must be provided before sending)"));
7046 return(0);
7049 /* save the name */
7050 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
7051 sizeof(prompt)-50, answer);
7052 prompt[sizeof(prompt)-1] = '\0';
7053 if(ps_global->blank_user_id
7054 && !no_prompt_user_id
7055 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7056 set_variable(V_USER_ID, answer, 1, 1, Main);
7058 else{
7059 fs_give((void **)&(ps_global->VAR_USER_ID));
7060 ps_global->VAR_USER_ID = cpystr(answer);
7064 /* query for personal name */
7065 if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
7066 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
7067 answer[0] = '\0';
7068 snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
7069 prompt[sizeof(prompt)-1] = '\0';
7071 help = NO_HELP;
7072 while(1) {
7073 flags = OE_APPEND_CURRENT;
7074 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7075 sizeof(answer),prompt,NULL,help,&flags);
7076 if(rc == 2)
7077 continue;
7079 if(rc == 3){
7080 help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
7081 continue;
7084 if(rc != 4)
7085 break;
7088 if(rc == 0 && answer){ /* save the name */
7089 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
7090 sizeof(prompt)-50, answer);
7091 prompt[sizeof(prompt)-1] = '\0';
7092 if(ps_global->blank_personal_name
7093 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7094 set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
7096 else{
7097 fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
7098 ps_global->VAR_PERSONAL_NAME = cpystr(answer);
7104 * query for host/domain portion of address, using IMAP
7105 * host as default
7107 if(ps_global->blank_user_domain
7108 || ps_global->maildomain == ps_global->localdomain
7109 || ps_global->maildomain == ps_global->hostname){
7110 if(ps_global->inbox_name[0] == '{'){
7111 for(i=0;
7112 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7113 answer[i] = ps_global->inbox_name[i+1];
7115 answer[i] = '\0';
7117 else
7118 answer[0] = '\0';
7120 snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
7121 prompt[sizeof(prompt)-1] = '\0';
7123 help = NO_HELP;
7124 while(1) {
7125 flags = OE_APPEND_CURRENT;
7126 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7127 sizeof(answer),prompt,NULL,help,&flags);
7128 if(rc == 2)
7129 continue;
7131 if(rc == 3){
7132 help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
7133 continue;
7136 if(rc != 4)
7137 break;
7140 if(rc == 1 || (rc == 0 && !answer[0])) {
7141 q_status_message(SM_ORDER, 3, 4,
7142 _("Send cancelled (Host/domain name must be provided before sending)"));
7143 return(0);
7146 /* save the name */
7147 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
7148 sizeof(prompt)-50, answer);
7149 prompt[sizeof(prompt)-1] = '\0';
7150 if(!ps_global->userdomain && !ps_global->blank_user_domain
7151 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7152 set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
7153 fs_give((void **)&(ps_global->maildomain)); /* blast old val */
7154 ps_global->userdomain = cpystr(answer);
7155 ps_global->maildomain = ps_global->userdomain;
7157 else{
7158 fs_give((void **)&(ps_global->maildomain));
7159 ps_global->userdomain = cpystr(answer);
7160 ps_global->maildomain = ps_global->userdomain;
7164 /* check for smtp server */
7165 if(!ps_global->VAR_SMTP_SERVER ||
7166 !ps_global->VAR_SMTP_SERVER[0] ||
7167 !ps_global->VAR_SMTP_SERVER[0][0]){
7168 char **list;
7170 if(ps_global->inbox_name[0] == '{'){
7171 for(i=0;
7172 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7173 answer[i] = ps_global->inbox_name[i+1];
7175 answer[i] = '\0';
7177 else
7178 answer[0] = '\0';
7180 snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
7181 prompt[sizeof(prompt)-1] = '\0';
7183 help = NO_HELP;
7184 while(1) {
7185 flags = OE_APPEND_CURRENT;
7186 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7187 sizeof(answer),prompt,NULL,help,&flags);
7188 if(rc == 2)
7189 continue;
7191 if(rc == 3){
7192 help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
7193 continue;
7196 if(rc != 4)
7197 break;
7200 if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
7201 q_status_message(SM_ORDER, 3, 4,
7202 _("Send cancelled (SMTP server must be provided before sending)"));
7203 return(0);
7206 /* save the name */
7207 list = (char **) fs_get(2 * sizeof(char *));
7208 list[0] = cpystr(answer);
7209 list[1] = NULL;
7210 set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
7211 fs_give((void *)&list[0]);
7212 fs_give((void *)list);
7215 return(1);
7218 #endif /* defined(DOS) || defined(OS2) */