* New version 2.26
[alpine.git] / alpine / send.c
blob2853b86ae5b5934fecc53360a4670ec25cb3bb52
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
16 Functions for composing and sending mail
18 ====*/
21 #include "headers.h"
22 #include "send.h"
23 #include "status.h"
24 #include "mailview.h"
25 #include "mailindx.h"
26 #include "dispfilt.h"
27 #include "keymenu.h"
28 #include "folder.h"
29 #include "radio.h"
30 #include "addrbook.h"
31 #include "reply.h"
32 #include "titlebar.h"
33 #include "signal.h"
34 #include "mailcmd.h"
35 #include "roleconf.h"
36 #include "adrbkcmd.h"
37 #include "busy.h"
38 #include "../pith/debug.h"
39 #include "../pith/state.h"
40 #include "../pith/conf.h"
41 #include "../pith/flag.h"
42 #include "../pith/bldaddr.h"
43 #include "../pith/copyaddr.h"
44 #include "../pith/detach.h"
45 #include "../pith/mimedesc.h"
46 #include "../pith/pipe.h"
47 #include "../pith/addrstring.h"
48 #include "../pith/news.h"
49 #include "../pith/detoken.h"
50 #include "../pith/util.h"
51 #include "../pith/init.h"
52 #include "../pith/mailcmd.h"
53 #include "../pith/ablookup.h"
54 #include "../pith/reply.h"
55 #include "../pith/hist.h"
56 #include "../pith/list.h"
57 #include "../pith/icache.h"
58 #include "../pith/busy.h"
59 #include "../pith/mimetype.h"
60 #include "../pith/send.h"
61 #include "../pith/smime.h"
64 typedef struct body_particulars {
65 unsigned short type, encoding, had_csp;
66 char *subtype, *charset;
67 PARAMETER *parameter;
68 } BODY_PARTICULARS_S;
71 * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook
73 #define HE(PF) ((struct headerentry *)((PF)->extdata))
77 * Internal Prototypes
79 int redraft(MAILSTREAM **, ENVELOPE **, BODY **, char **, char **, REPLY_S **,
80 REDRAFT_POS_S **, PINEFIELD **, ACTION_S **, int);
81 int redraft_prompt(char *, char *, int);
82 int check_for_subject(METAENV *);
83 int check_for_fcc(char *);
84 void free_prompts(PINEFIELD *);
85 int postpone_prompt(void);
86 METAENV *pine_simple_send_header(ENVELOPE *, char **, char ***);
87 void call_mailer_file_result(char *, int);
88 void mark_address_failure_for_pico(METAENV *);
89 BODY_PARTICULARS_S
90 *save_body_particulars(BODY *);
91 void reset_body_particulars(BODY_PARTICULARS_S *, BODY *);
92 void free_body_particulars(BODY_PARTICULARS_S *);
93 long message_format_for_pico(long, int (*)(int));
94 int send_exit_for_pico(struct headerentry *, void (*)(void), int, char **);
95 void new_thread_on_blank_subject(void);
96 char *choose_a_priority(char *);
97 int dont_flow_this_time(void);
98 int mime_type_for_pico(char *);
99 char *cancel_for_pico(void (*)(void));
100 int filter_message_text(char *, ENVELOPE *, BODY *, STORE_S **, METAENV *);
101 void pine_send_newsgroup_name(char *, char*, size_t);
102 void outgoing2strings(METAENV *, BODY *, void **, PATMT **, int);
103 void strings2outgoing(METAENV *, BODY **, PATMT *, int);
104 void create_message_body_text(BODY *, int);
105 void set_body_size(BODY *);
106 int view_as_rich(char *, int);
107 int background_posting(int);
108 int valid_subject(char *, char **, char **,BUILDER_ARG *,int *);
109 int build_addr_lcc(char *, char **, char **, BUILDER_ARG *, int *);
110 int news_build(char *, char **, char **, BUILDER_ARG *, int *);
111 void news_build_busy(void);
112 #if defined(DOS) || defined(OS2)
113 int dos_valid_from(void);
114 #endif /* defined(DOS) || defined(OS2) */
118 * Pointer to buffer to hold pointers into pine data that's needed by pico.
120 static PICO *pbf;
123 static char *g_rolenick = NULL;
126 static char *sending_filter_requested;
127 static char background_requested, flowing_requested;
128 static unsigned call_mailer_flags;
129 static char *priority_requested;
131 /* local global to save busy_cue state */
132 static int news_busy_cue = 0;
136 * Various useful strings
138 #define INTRPT_PMT \
139 _("Continue INTERRUPTED composition (answering \"n\" won't erase it)")
140 #define PSTPND_PMT \
141 _("Continue postponed composition (answering \"No\" won't erase it)")
142 #define FORM_PMT \
143 _("Start composition from Form Letter Folder")
144 #define PSTPN_FORM_PMT \
145 _("Save to Postponed or Form letter folder? ")
146 #define POST_PMT \
147 _("Posted message may go to thousands of readers. Really post")
148 #define INTR_DEL_PMT \
149 _("Deleted messages will be removed from folder after use. Proceed")
153 * Macros to help sort out posting results
155 #define P_MAIL_WIN 0x01
156 #define P_MAIL_LOSE 0x02
157 #define P_MAIL_BITS 0x03
158 #define P_NEWS_WIN 0x04
159 #define P_NEWS_LOSE 0x08
160 #define P_NEWS_BITS 0x0C
161 #define P_FCC_WIN 0x10
162 #define P_FCC_LOSE 0x20
163 #define P_FCC_BITS 0x30
166 #define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE"
170 * For check_for_subject and check_for_fcc
172 #define CF_OK 0x1
173 #define CF_MISSING 0x2
176 /*----------------------------------------------------------------------
177 Compose screen (not forward or reply). Set up envelope, call composer
179 Args: pine_state -- The usual pine structure
181 Little front end for the compose screen
182 ---*/
183 void
184 compose_screen(struct pine *pine_state)
186 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
187 (*redraw)(void) = pine_state->redrawer;
189 pine_state->redrawer = NULL;
190 ps_global->next_screen = SCREEN_FUN_NULL;
191 mailcap_free(); /* free resources we won't be using for a while */
192 compose_mail(NULL, NULL, NULL, NULL, NULL);
193 pine_state->next_screen = prev_screen;
194 pine_state->redrawer = redraw;
198 /*----------------------------------------------------------------------
199 Alternate compose screen. Set up role and call regular compose.
201 Args: pine_state -- The usual pine structure
202 ---*/
203 void
204 alt_compose_screen(struct pine *pine_state)
206 ACTION_S *role = NULL;
207 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
208 (*redraw)(void) = pine_state->redrawer;
210 pine_state->redrawer = NULL;
211 ps_global->next_screen = SCREEN_FUN_NULL;
212 mailcap_free(); /* free resources we won't be using for a while */
214 /* Setup role */
215 if(role_select_screen(pine_state, &role, MC_COMPOSE) < 0){
216 cmd_cancelled("Composition");
217 pine_state->next_screen = prev_screen;
218 pine_state->redrawer = redraw;
219 return;
223 * If default role was selected (NULL) we need to make up a role which
224 * won't do anything, but will cause compose_mail to think there's
225 * already a role so that it won't try to confirm the default.
227 if(role)
228 role = combine_inherited_role(role);
229 else{
230 role = (ACTION_S *)fs_get(sizeof(*role));
231 memset((void *)role, 0, sizeof(*role));
232 role->nick = cpystr("Default Role");
235 pine_state->redrawer = NULL;
236 compose_mail(NULL, NULL, role, NULL, NULL);
237 free_action(&role);
238 pine_state->next_screen = prev_screen;
239 pine_state->redrawer = redraw;
243 /*----------------------------------------------------------------------
244 Format envelope for outgoing message and call editor
246 Args: given_to -- An address to send mail to (usually from command line
247 invocation)
248 fcc_arg -- The fcc that goes with this address.
250 If a "To" line is given format that into the envelope and get ready to call
251 the composer
252 If there's a message postponed, offer to continue it, and set it up,
253 otherwise just fill in the outgoing envelope as blank.
255 NOTE: we ignore postponed and interrupted messages in nr mode
256 ----*/
257 void
258 compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
259 PATMT *attach, gf_io_t inc_text_getc)
261 BODY *body = NULL;
262 ENVELOPE *outgoing = NULL;
263 PINEFIELD *custom = NULL;
264 REPLY_S *reply = NULL;
265 REDRAFT_POS_S *redraft_pos = NULL;
266 ACTION_S *role = NULL;
267 MAILSTREAM *stream;
268 char *fcc_to_free,
269 *fcc = NULL,
270 *lcc = NULL,
271 *sig = NULL;
272 int fcc_is_sticky = 0,
273 to_is_sticky = 0,
274 intrptd = 0,
275 postponed = 0,
276 form = 0;
278 dprint((1,
279 "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n"));
281 /*-- Check for INTERRUPTED mail --*/
282 if(!role_arg && !(given_to || attach)){
283 char file_path[MAXPATH+1];
285 /* build filename and see if it exists. build_path creates
286 * an explicit local path name, so all c-client access is thru
287 * local drivers.
289 file_path[0] = '\0';
290 build_path(file_path,
291 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
292 : ps_global->home_dir,
293 INTERRUPTED_MAIL, sizeof(file_path));
295 /* check to see if the folder exists, the user wants to continue
296 * and that we can actually read something in...
298 if(folder_exists(NULL, file_path) & FEX_ISFILE)
299 intrptd = 1;
302 /*-- Check for postponed mail --*/
303 if(!role_arg
304 && !outgoing /* not replying/forwarding */
305 && !(given_to || attach) /* not command line send */
306 && ps_global->VAR_POSTPONED_FOLDER /* folder to look in */
307 && ps_global->VAR_POSTPONED_FOLDER[0])
308 postponed = 1;
310 /*-- Check for form letter folder --*/
311 if(!role_arg
312 && !outgoing /* not replying/forwarding */
313 && !(given_to || attach) /* not command line send */
314 && ps_global->VAR_FORM_FOLDER /* folder to look in */
315 && ps_global->VAR_FORM_FOLDER[0])
316 form = 1;
318 if(!outgoing && !(given_to || attach)
319 && !role_arg && F_ON(F_ALT_COMPOSE_MENU, ps_global)){
320 char prompt[80];
321 char letters[30];
322 char chosen_task;
323 char *new = "New";
324 char *intrpt = "Interrupted";
325 char *postpnd = "Postponed";
326 char *formltr = "FormLetter";
327 char *roles = "setRole";
328 HelpType help = h_composer_browse;
329 ESCKEY_S compose_style[6];
330 unsigned which_help;
331 int ekey_num;
333 ekey_num = 0;
334 compose_style[ekey_num].ch = 'n';
335 compose_style[ekey_num].rval = 'n';
336 compose_style[ekey_num].name = "N";
337 compose_style[ekey_num++].label = new;
339 if(intrptd){
340 compose_style[ekey_num].ch = 'i';
341 compose_style[ekey_num].rval = 'i';
342 compose_style[ekey_num].name = "I";
343 compose_style[ekey_num++].label = intrpt;
346 if(postponed){
347 compose_style[ekey_num].ch = 'p';
348 compose_style[ekey_num].rval = 'p';
349 compose_style[ekey_num].name = "P";
350 compose_style[ekey_num++].label = postpnd;
353 if(form){
354 compose_style[ekey_num].ch = 'f';
355 compose_style[ekey_num].rval = 'f';
356 compose_style[ekey_num].name = "F";
357 compose_style[ekey_num++].label = formltr;
360 compose_style[ekey_num].ch = 'r';
361 compose_style[ekey_num].rval = 'r';
362 compose_style[ekey_num].name = "R";
363 compose_style[ekey_num++].label = roles;
365 compose_style[ekey_num].ch = -1;
367 if(F_ON(F_BLANK_KEYMENU,ps_global)){
368 char *p;
370 p = letters;
371 *p = '\0';
372 for(ekey_num = 0; compose_style[ekey_num].ch != -1; ekey_num++){
373 if(p - letters < sizeof(letters))
374 *p++ = (char) compose_style[ekey_num].ch;
376 if(compose_style[ekey_num + 1].ch != -1 && p - letters < sizeof(letters))
377 *p++ = ',';
380 if(p - letters < sizeof(letters))
381 *p = '\0';
384 which_help = intrptd + 2 * postponed + 4 * form;
385 switch(which_help){
386 case 1:
387 help = h_compose_intrptd;
388 break;
389 case 2:
390 help = h_compose_postponed;
391 break;
392 case 3:
393 help = h_compose_intrptd_postponed;
394 break;
395 case 4:
396 help = h_compose_form;
397 break;
398 case 5:
399 help = h_compose_intrptd_form;
400 break;
401 case 6:
402 help = h_compose_postponed_form;
403 break;
404 case 7:
405 help = h_compose_intrptd_postponed_form;
406 break;
407 default:
408 help = h_compose_default;
409 break;
412 snprintf(prompt, sizeof(prompt),
413 "Choose a compose method from %s : ",
414 F_ON(F_BLANK_KEYMENU,ps_global) ? letters : "the menu below");
415 prompt[sizeof(prompt)-1] = '\0';
417 chosen_task = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
418 compose_style, 'n', 'x', help, RB_NORM);
419 intrptd = postponed = form = 0;
421 switch(chosen_task){
422 case 'i':
423 intrptd = 1;
424 break;
425 case 'p':
426 postponed = 1;
427 break;
428 case 'r':
430 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
431 (*redraw)(void) = ps_global->redrawer;
433 ps_global->redrawer = NULL;
434 ps_global->next_screen = SCREEN_FUN_NULL;
435 if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
436 cmd_cancelled("Composition");
437 ps_global->next_screen = prev_screen;
438 ps_global->redrawer = redraw;
439 return;
442 ps_global->next_screen = prev_screen;
443 ps_global->redrawer = redraw;
444 if(role)
445 role = combine_inherited_role(role);
447 break;
449 case 'f':
450 form = 1;
451 break;
453 case 'x':
454 q_status_message(SM_ORDER, 0, 3,
455 "Composition cancelled");
456 return;
457 break;
459 default:
460 break;
464 if(intrptd && !outgoing){
465 char file_path[MAXPATH+1];
466 int ret = 'n';
468 file_path[0] = '\0';
469 build_path(file_path,
470 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
471 : ps_global->home_dir,
472 INTERRUPTED_MAIL, sizeof(file_path));
473 if(folder_exists(NULL, file_path) & FEX_ISFILE){
474 if((stream = pine_mail_open(NULL, file_path,
475 SP_USEPOOL|SP_TEMPUSE, NULL))
476 && !stream->halfopen){
478 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
479 (ret = redraft_prompt("Interrupted",INTRPT_PMT,'n')) =='y'){
480 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
481 &redraft_pos, &custom, &role, REDRAFT_DEL)){
482 if(stream)
483 pine_mail_close(stream);
485 return;
488 to_is_sticky++;
490 /* redraft() may or may not have closed stream */
491 if(stream)
492 pine_mail_close(stream);
494 postponed = form = 0;
496 else{
497 pine_mail_close(stream);
498 if(ret == 'x'){
499 q_status_message(SM_ORDER, 0, 3,
500 _("Composition cancelled"));
501 return;
505 else{
506 q_status_message1(SM_ORDER | SM_DING, 3, 3,
507 _("Can't open Interrupted mailbox: %s"),
508 file_path);
509 if(stream)
510 pine_mail_close(stream);
515 if(postponed && !outgoing){
516 int ret = 'n', done = 0;
517 int exists;
519 if((exists=postponed_stream(&stream,
520 ps_global->VAR_POSTPONED_FOLDER,
521 "Postponed", 1)) & FEX_ISFILE){
522 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
523 (ret = redraft_prompt("Postponed",PSTPND_PMT,'n')) == 'y'){
524 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
525 &redraft_pos, &custom, &role,
526 REDRAFT_DEL | REDRAFT_PPND))
527 done++;
529 /* stream may or may not be closed in redraft() */
530 if(stream && (stream != ps_global->mail_stream))
531 pine_mail_close(stream);
533 to_is_sticky++;
534 intrptd = form = 0;
536 else{
537 if(stream != ps_global->mail_stream)
538 pine_mail_close(stream);
540 if(ret == 'x'){
541 q_status_message(SM_ORDER, 0, 3,
542 _("Composition cancelled"));
543 done++;
547 else if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
548 q_status_message(SM_ORDER, 3, 3,
549 _("No postponed messages found!"));
550 done++;
553 if(done)
554 return;
557 if(form && !outgoing){
558 int ret = 'n', done = 0;
559 int exists;
561 if((exists=postponed_stream(&stream,
562 ps_global->VAR_FORM_FOLDER,
563 "Form letter", 1)) & FEX_ISFILE){
564 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
565 (ret = want_to(FORM_PMT,'y','x',NO_HELP,WT_NORM))=='y'){
566 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
567 &redraft_pos, &custom, &role, REDRAFT_NONE))
568 done++;
570 /* stream may or may not be closed in redraft() */
571 if(stream && (stream != ps_global->mail_stream))
572 pine_mail_close(stream);
574 to_is_sticky++;
575 intrptd = postponed = 0;
577 else{
578 if(stream != ps_global->mail_stream)
579 pine_mail_close(stream);
581 if(ret == 'x'){
582 q_status_message(SM_ORDER, 0, 3,
583 _("Composition cancelled"));
584 done++;
588 else{
589 if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
590 q_status_message(SM_ORDER | SM_DING, 3, 3,
591 _("Form letter folder doesn't exist!"));
592 return;
596 if(done)
597 return;
600 /*-- normal composition --*/
601 if(!outgoing){
602 int impl, template_len = 0;
603 long rflags = ROLE_COMPOSE;
604 PAT_STATE dummy;
606 /*================= Compose new message ===============*/
607 body = mail_newbody();
608 outgoing = mail_newenvelope();
610 if(given_to)
611 rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
614 * Setup possible role
616 if(role_arg)
617 role = copy_action(role_arg);
619 if(!role){
620 /* Setup possible compose role */
621 if(nonempty_patterns(rflags, &dummy)){
623 * setup default role
624 * Msgno = -1 means there is no msg.
625 * This will match roles which have the Compose Use turned
626 * on, and have no patterns set, and match the Current
627 * Folder Type.
629 role = set_role_from_msg(ps_global, rflags, -1L, NULL);
631 if(confirm_role(rflags, &role))
632 role = combine_inherited_role(role);
633 else{ /* cancel reply */
634 role = NULL;
635 cmd_cancelled("Composition");
636 return;
641 if(role)
642 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
643 role->nick);
645 outgoing->message_id = generate_message_id(role);
647 * The type of storage object allocated below is vitally
648 * important. See SIMPLIFYING ASSUMPTION #37
650 if((body->contents.text.data = (void *) so_get(PicoText,
651 NULL, EDIT_ACCESS)) != NULL){
652 char ch;
654 if(inc_text_getc){
655 while((*inc_text_getc)(&ch))
656 if(!so_writec(ch, (STORE_S *)body->contents.text.data)){
657 break;
661 else{
662 q_status_message(SM_ORDER | SM_DING, 3, 4,
663 _("Problem creating space for message text."));
664 return;
667 if(role && role->template){
668 char *filtered;
670 impl = 1; /* leave cursor in header if not explicit */
671 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
672 if(filtered){
673 if(*filtered){
674 so_puts((STORE_S *)body->contents.text.data, filtered);
675 if(impl == 1)
676 template_len = strlen(filtered);
679 fs_give((void **)&filtered);
682 else
683 impl = 1;
685 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
686 if(impl == 2)
687 redraft_pos->offset += template_len;
689 if(*sig)
690 so_puts((STORE_S *)body->contents.text.data, sig);
692 fs_give((void **)&sig);
695 body->type = TYPETEXT;
697 if(attach)
698 create_message_body(&body, attach, 0);
701 ps_global->prev_screen = compose_screen;
702 if(!(fcc_to_free = fcc) && !(role && role->fcc))
703 fcc = fcc_arg; /* Didn't pick up fcc, use given */
706 * check whether a build_address-produced fcc is different from
707 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
709 if(fcc){
710 char *tmp_fcc = NULL;
712 if(outgoing->to){
713 tmp_fcc = get_fcc_based_on_to(outgoing->to);
714 if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
715 fcc_is_sticky++; /* cause sticky bit to get set */
718 else if((tmp_fcc = get_fcc(NULL)) != NULL &&
719 !strcmp(fcc, tmp_fcc)){
720 /* not sticky */
722 else
723 fcc_is_sticky++;
725 if(tmp_fcc)
726 fs_give((void **)&tmp_fcc);
729 pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc,
730 reply, redraft_pos, lcc, custom,
731 (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0));
733 if(reply){
734 if(reply->mailbox)
735 fs_give((void **) &reply->mailbox);
736 if(reply->origmbox)
737 fs_give((void **) &reply->origmbox);
738 if(reply->prefix)
739 fs_give((void **) &reply->prefix);
740 if(reply->data.uid.msgs)
741 fs_give((void **) &reply->data.uid.msgs);
742 fs_give((void **) &reply);
745 if(fcc_to_free)
746 fs_give((void **)&fcc_to_free);
748 if(lcc)
749 fs_give((void **)&lcc);
751 mail_free_envelope(&outgoing);
752 pine_free_body(&body);
753 free_redraft_pos(&redraft_pos);
754 free_action(&role);
758 /*----------------------------------------------------------------------
759 Args: stream -- This is where we get the postponed messages from
760 We'll expunge and close it here unless it is mail_stream.
762 These are all return values:
763 ================
764 outgoing --
765 body --
766 fcc --
767 lcc --
768 reply --
769 redraft_pos --
770 custom --
771 role --
772 ================
774 flags --
776 ----*/
778 redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body,
779 char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos,
780 PINEFIELD **custom, ACTION_S **role, int flags)
782 MAILSTREAM *stream;
783 long cont_msg = 1L;
784 STORE_S *so;
786 if(!(streamp && *streamp))
787 return(0);
789 stream = *streamp;
792 * If we're manipulating the current folder, don't bother
793 * with index
795 if(!stream->nmsgs){
796 if(REDRAFT_PPND&flags)
797 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!"));
798 else
799 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!"));
801 return(redraft_cleanup(streamp, FALSE, flags));
803 else if(stream == ps_global->mail_stream
804 && ps_global->prev_screen == mail_index_screen){
806 * Since the user's got this folder already opened and they're
807 * on a selected message, pick that one rather than rebuild
808 * another index screen...
810 cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
812 else if(stream->nmsgs > 1L){ /* offer browser ? */
813 int rv;
815 if(REDRAFT_PPND&flags){ /* set to last message postponed */
816 mn_set_cur(sp_msgmap(stream),
817 mn_get_revsort(sp_msgmap(stream))
818 ? 1L : mn_get_total(sp_msgmap(stream)));
820 else{ /* set to top form letter */
821 mn_set_cur(sp_msgmap(stream), 1L);
824 clear_index_cache(stream, 0);
825 while(1){
826 void *ti;
828 ti = stop_threading_temporarily();
829 rv = index_lister(ps_global, NULL, stream->mailbox,
830 stream, sp_msgmap(stream));
831 restore_threading(&ti);
833 cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream)));
834 if(count_flagged(stream, F_DEL)
835 && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){
836 if(REDRAFT_PPND&flags)
837 q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
838 else
839 q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
841 continue;
844 break;
847 clear_index_cache(stream, 0);
849 if(rv){
850 q_status_message(SM_ORDER, 0, 3, _("Composition cancelled"));
851 (void) redraft_cleanup(streamp, FALSE, flags);
853 if(!*streamp && !ps_global->mail_stream){
854 q_status_message2(SM_ORDER, 3, 7,
855 "No more %.200s, returning to \"%.200s\"",
856 (REDRAFT_PPND&flags) ? "postponed messages"
857 : "form letters",
858 ps_global->inbox_name);
859 if(ps_global && ps_global->ttyo){
860 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
861 ps_global->mangled_footer = 1;
864 do_broach_folder(ps_global->inbox_name,
865 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
867 ps_global->next_screen = mail_index_screen;
870 return(0); /* special case */
874 if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL)
875 return(redraft_work(streamp, cont_msg, outgoing, body,
876 fcc, lcc, reply, redraft_pos, custom,
877 role, flags, so));
878 else
879 return(0);
884 redraft_prompt(char *type, char *prompt, int failure)
886 if(background_posting(FALSE)){
887 q_status_message1(SM_ORDER, 0, 3,
888 _("%s folder unavailable while background posting"),
889 type);
890 return(failure);
893 return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM));
897 /* this is for initializing the fixed header elements in pine_send() */
899 prompt::name::help::prwid::maxlen::realaddr::
900 builder::affected_entry::next_affected::selector::key_label::fileedit::
901 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
902 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
904 static struct headerentry he_template[]={
905 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
906 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
907 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
908 {"From : ", "From", h_composer_from, 10, 0, NULL,
909 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
910 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
911 {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
912 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
913 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
914 {"To : ", "To", h_composer_to, 10, 0, NULL,
915 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
916 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK},
917 {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL,
918 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
919 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
920 {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL,
921 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
922 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
923 {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL,
924 news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL,
925 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
926 {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL,
927 NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, fcc_tab_complete,
928 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE},
929 {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL,
930 build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete,
931 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
932 {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL,
933 NULL, NULL, NULL, NULL, "To Files", NULL, NULL,
934 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE},
935 {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL,
936 valid_subject, NULL, NULL, NULL, NULL, NULL, NULL,
937 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
938 {"", "References", NO_HELP, 10, 0, NULL,
939 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
940 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
941 {"", "Date", NO_HELP, 10, 0, NULL,
942 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
943 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
944 {"", "In-Reply-To", 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 {"", "Message-ID", 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 {"", "X-Priority", 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 {"", "User-Agent", 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 {"", "To", 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 {"", "X-Post-Error",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 {"", "X-Reply-UID", 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-Reply-Mbox", 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-SMTP-Server", 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-Cursor-Pos", 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-Our-ReplyTo", 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 {"", OUR_HDRS_LIST, 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-Auth-Received", 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 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
984 {"", "Sender", NO_HELP, 10, 0, NULL,
985 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
986 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
987 #endif
991 static struct headerentry he_custom_addr_templ={
992 NULL, NULL, h_composer_custom_addr,10, 0, NULL,
993 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
994 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK};
996 static struct headerentry he_custom_free_templ={
997 NULL, NULL, h_composer_custom_free,10, 0, NULL,
998 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
999 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE};
1002 /*----------------------------------------------------------------------
1003 Get addressee for message, then post message
1005 Args: outgoing -- Partially formatted outgoing ENVELOPE
1006 body -- Body of outgoing message
1007 prmpt_who -- Optional prompt for optionally_enter call
1008 prmpt_cnf -- Optional prompt for confirmation call
1009 used_tobufval -- The string that the to was eventually set equal to.
1010 This gets passed back if non-NULL on entry.
1011 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1012 SS_NULLRP - Use null return-path so we'll send an
1013 SMTP MAIL FROM: <>
1015 Result: message "To: " field is provided and message is sent or cancelled.
1017 Fields:
1018 remail -
1019 return_path -
1020 date added here
1021 from added here
1022 sender -
1023 reply_to -
1024 subject passed in, NOT edited but maybe canonized here
1025 to possibly passed in, edited and canonized here
1026 cc -
1027 bcc -
1028 in_reply_to -
1029 message_id -
1031 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1032 with the first part TYPETEXT! All newlines in the text here also end with
1033 CRLF.
1035 Returns 0 on success, -1 on failure.
1036 ----*/
1038 pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */
1039 struct mail_bodystruct **body,
1040 ACTION_S **rolep,
1041 char *prmpt_who,
1042 char *prmpt_cnf,
1043 char **used_tobufval,
1044 int flagsarg)
1046 char **tobufp, *p, tmp[MAILTMPLEN];
1047 void *messagebuf;
1048 int done = 0, retval = 0, x;
1049 int lastrc, rc = 0, ku, i, resize_len, result, fcc_result = 0;
1050 int og2s_done = 0;
1051 HelpType help;
1052 static HISTORY_S *history = NULL;
1053 ESCKEY_S ekey[6];
1054 BUILDER_ARG ba_fcc;
1055 METAENV *header;
1056 ACTION_S *role = rolep ? *rolep : NULL;
1057 PAT_STATE pstate;
1059 dprint((1,"\n === simple send called === \n"));
1061 memset(&ba_fcc, 0, sizeof(BUILDER_ARG));
1063 init_hist(&history, HISTSIZE);
1065 header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp);
1067 /*----- Fill in a few general parts of the envelope ----*/
1068 if(!outgoing->date){
1069 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1070 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
1072 rfc822_date(tmp_20k_buf); /* format and copy new date */
1073 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1074 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
1076 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
1079 if(!outgoing->from){
1080 if(role && role->from){
1081 if(ps_global->never_allow_changing_from)
1082 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1083 else
1084 outgoing->from = copyaddrlist(role->from);
1086 else
1087 outgoing->from = generate_from();
1090 if(!(flagsarg & SS_NULLRP))
1091 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1093 ekey[i = 0].ch = ctrl('T');
1094 ekey[i].rval = 2;
1095 ekey[i].name = "^T";
1096 ekey[i++].label = N_("To AddrBk");
1098 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1099 ekey[i].ch = ctrl('I');
1100 ekey[i].rval = 11;
1101 ekey[i].name = "TAB";
1102 ekey[i++].label = N_("Complete");
1105 if(nonempty_patterns(ROLE_DO_ROLES, &pstate) && first_pattern(&pstate)){
1106 ekey[i].ch = ctrl('R');
1107 ekey[i].rval = 15;
1108 ekey[i].name = "^R";
1109 ekey[i++].label = "Set Role";
1112 ekey[i].ch = KEY_UP;
1113 ekey[i].rval = 30;
1114 ekey[i].name = "";
1115 ku = i;
1116 ekey[i++].label = "";
1118 ekey[i].ch = KEY_DOWN;
1119 ekey[i].rval = 31;
1120 ekey[i].name = "";
1121 ekey[i++].label = "";
1123 ekey[i].ch = -1;
1125 if(outgoing->remail == NULL)
1126 strcpy(tmp, _("FORWARD (as e-mail) to : "));
1128 /*----------------------------------------------------------------------
1129 Loop editing the "To: " field until everything goes well
1130 ----*/
1131 help = NO_HELP;
1133 while(!done){
1134 int flags;
1136 if(outgoing->message_id)
1137 fs_give((void **) &outgoing->message_id);
1139 outgoing->message_id = generate_message_id(role);
1141 if(outgoing->remail){
1142 if(role)
1143 snprintf(tmp, sizeof(tmp), _("BOUNCE (redirect) message using role \"%s\" to : "), role->nick);
1144 else
1145 strncpy(tmp, _("BOUNCE (redirect) message to : "), sizeof(tmp));
1146 tmp[sizeof(tmp)-1] = '\0';
1149 if(!og2s_done){
1150 og2s_done++;
1151 outgoing2strings(header, *body, &messagebuf, NULL, 1);
1154 lastrc = rc;
1155 if(flagsarg & SS_PROMPTFORTO){
1156 if(!*tobufp)
1157 *tobufp = cpystr("");
1159 resize_len = MAX(MAXPATH, strlen(*tobufp));
1160 fs_resize((void **) tobufp, resize_len+1);
1162 if(items_in_hist(history) > 0){
1163 ekey[ku].name = HISTORY_UP_KEYNAME;
1164 ekey[ku].label = HISTORY_KEYLABEL;
1165 ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
1166 ekey[ku+1].label = HISTORY_KEYLABEL;
1168 else{
1169 ekey[ku].name = "";
1170 ekey[ku].label = "";
1171 ekey[ku+1].name = "";
1172 ekey[ku+1].label = "";
1175 flags = OE_APPEND_CURRENT;
1177 rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
1178 0, resize_len,
1179 prmpt_who
1180 ? prmpt_who
1181 : tmp,
1182 ekey, help, &flags);
1184 else
1185 rc = 0;
1187 switch(rc){
1188 case -1:
1189 q_status_message(SM_ORDER | SM_DING, 3, 4,
1190 "Internal problem encountered");
1191 retval = -1;
1192 done++;
1193 break;
1195 case 15 : /* set a role */
1196 {void (*prev_screen)(struct pine *) = NULL, (*redraw)(void) = NULL;
1198 redraw = ps_global->redrawer;
1199 ps_global->redrawer = NULL;
1200 prev_screen = ps_global->prev_screen;
1201 role = NULL;
1202 ps_global->next_screen = SCREEN_FUN_NULL;
1204 if(role_select_screen(ps_global, &role,
1205 outgoing->remail ? MC_BOUNCE : MC_FORWARD) < 0)
1206 cmd_cancelled(_("Set Role"));
1207 else{
1208 if(role)
1209 role = combine_inherited_role(role);
1210 else{
1211 role = (ACTION_S *) fs_get(sizeof(*role));
1212 memset((void *) role, 0, sizeof(*role));
1213 role->nick = cpystr("Default Role");
1217 if(redraw)
1218 (*redraw)();
1220 ps_global->next_screen = prev_screen;
1221 ps_global->redrawer = redraw;
1222 ps_global->mangled_screen = 1;
1224 if(role && role->from && !ps_global->never_allow_changing_from){
1225 mail_free_address (&outgoing->from);
1226 outgoing->from = copyaddrlist(role->from);
1227 if(!(flagsarg & SS_NULLRP)){
1228 fs_give((void **) &outgoing->return_path);
1229 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1232 if(rolep) *rolep = role;
1234 break;
1236 case 30 :
1237 if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){
1238 strncpy(*tobufp, p, resize_len);
1239 (*tobufp)[resize_len-1] = '\0';
1241 else
1242 Writechar(BELL, 0);
1244 break;
1246 case 31 :
1247 if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){
1248 strncpy(*tobufp, p, resize_len);
1249 (*tobufp)[resize_len-1] = '\0';
1251 else
1252 Writechar(BELL, 0);
1254 break;
1256 case 2: /* ^T */
1257 case 0:
1258 {void (*redraw) (void) = ps_global->redrawer;
1259 char *returned_addr = NULL;
1260 int len, l;
1262 if(rc == 2){
1263 int got_something = 0;
1265 push_titlebar_state();
1266 returned_addr = addr_book_bounce();
1269 * Just make it look like user typed this list in.
1271 if(returned_addr){
1272 got_something++;
1273 if((l=resize_len) < (len = strlen(returned_addr)) + 1){
1274 l = len;
1275 fs_resize((void **) tobufp, (size_t) (l+1));
1278 strncpy(*tobufp, returned_addr, l);
1279 (*tobufp)[l] = '\0';
1280 fs_give((void **)&returned_addr);
1283 ClearScreen();
1284 pop_titlebar_state();
1285 redraw_titlebar();
1286 if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
1287 (*ps_global->redrawer)();
1289 if(!got_something)
1290 continue;
1293 if(*tobufp && **tobufp != '\0'){
1294 char *errbuf, *addr;
1295 int tolen;
1297 save_hist(history, *tobufp, 0, NULL);
1299 errbuf = NULL;
1302 * If role has an fcc, use it instead of what build_address
1303 * tells us.
1305 if(role && role->fcc){
1306 if(ba_fcc.tptr)
1307 fs_give((void **) &ba_fcc.tptr);
1309 ba_fcc.tptr = cpystr(role->fcc);
1312 if(build_address(*tobufp, &addr, &errbuf,
1313 (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){
1314 int sendit = 0;
1316 if(errbuf)
1317 fs_give((void **)&errbuf);
1319 if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){
1320 l = tolen;
1321 fs_resize((void **) tobufp, (size_t) (l+1));
1324 strncpy(*tobufp, addr, l);
1325 (*tobufp)[l] = '\0';
1326 if(used_tobufval)
1327 *used_tobufval = cpystr(addr);
1329 /* confirm address */
1330 if(flagsarg & SS_PROMPTFORTO){
1331 char dsn_string[30];
1332 int dsn_label = 0, dsn_show, i;
1333 int verbose_label = 0;
1334 ESCKEY_S opts[13];
1336 strings2outgoing(header, body, NULL, 0);
1338 if((flagsarg & SS_PROMPTFORTO)
1339 && ((x = check_addresses(header)) == CA_BAD
1340 || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE,
1341 ps_global))))
1342 /*--- Addresses didn't check out---*/
1343 continue;
1345 i = 0;
1346 opts[i].ch = 'y';
1347 opts[i].rval = 'y';
1348 opts[i].name = "Y";
1349 opts[i++].label = N_("Yes");
1351 opts[i].ch = 'n';
1352 opts[i].rval = 'n';
1353 opts[i].name = "N";
1354 opts[i++].label = N_("No");
1356 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
1357 if(F_ON(F_VERBOSE_POST, ps_global)){
1358 /* setup keymenu slot to toggle verbose mode */
1359 opts[i].ch = ctrl('W');
1360 opts[i].rval = 12;
1361 opts[i].name = "^W";
1362 verbose_label = i++;
1363 if(F_ON(F_DSN, ps_global)){
1364 opts[i].ch = 0;
1365 opts[i].rval = 0;
1366 opts[i].name = "";
1367 opts[i++].label = "";
1371 /* clear DSN flags */
1372 call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL);
1373 if(F_ON(F_DSN, ps_global)){
1374 /* setup keymenu slots to toggle dsn bits */
1375 opts[i].ch = 'd';
1376 opts[i].rval = 'd';
1377 opts[i].name = "D";
1378 opts[i].label = "DSNOpts";
1379 dsn_label = i++;
1380 opts[i].ch = -2;
1381 opts[i].rval = 's';
1382 opts[i].name = "S";
1383 opts[i++].label = "";
1384 opts[i].ch = -2;
1385 opts[i].rval = 'x';
1386 opts[i].name = "X";
1387 opts[i++].label = "";
1388 opts[i].ch = -2;
1389 opts[i].rval = 'h';
1390 opts[i].name = "H";
1391 opts[i++].label = "";
1394 opts[i].ch = -1;
1396 while(1){
1397 int rv;
1399 dsn_show = (call_mailer_flags & CM_DSN_SHOW);
1400 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
1401 "%s%s%s%s%s%sto \"%s\" ? ",
1402 prmpt_cnf ? prmpt_cnf : "Send message ",
1403 ((call_mailer_flags & CM_VERBOSE)
1404 || (dsn_show))
1405 ? "(" : "",
1406 (call_mailer_flags & CM_VERBOSE)
1407 ? "in verbose mode" : "",
1408 (dsn_show && (call_mailer_flags & CM_VERBOSE))
1409 ? ", " : "",
1410 (dsn_show) ? dsn_string : "",
1411 ((call_mailer_flags & CM_VERBOSE) || dsn_show)
1412 ? ") " : "",
1413 (addr && *addr)
1414 ? addr
1415 : (F_ON(F_FCC_ON_BOUNCE, ps_global)
1416 && ba_fcc.tptr && ba_fcc.tptr[0])
1417 ? ba_fcc.tptr
1418 : "");
1419 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1421 if((strlen(tmp_20k_buf) >
1422 ps_global->ttyo->screen_cols - 2) &&
1423 ps_global->ttyo->screen_cols >= 7)
1424 strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7,
1425 "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7);
1427 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1429 if(verbose_label)
1430 opts[verbose_label].label =
1431 /* TRANSLATORS: several possible key labels follow */
1432 (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
1434 if(F_ON(F_DSN, ps_global)){
1435 if(call_mailer_flags & CM_DSN_SHOW){
1436 opts[dsn_label].label =
1437 (call_mailer_flags & CM_DSN_DELAY)
1438 ? N_("NoDelay") : N_("Delay");
1439 opts[dsn_label+1].ch = 's';
1440 opts[dsn_label+1].label =
1441 (call_mailer_flags & CM_DSN_SUCCESS)
1442 ? N_("NoSuccess") : N_("Success");
1443 opts[dsn_label+2].ch = 'x';
1444 opts[dsn_label+2].label =
1445 (call_mailer_flags & CM_DSN_NEVER)
1446 ? N_("ErrRets") : N_("NoErrRets");
1447 opts[dsn_label+3].ch = 'h';
1448 opts[dsn_label+3].label =
1449 (call_mailer_flags & CM_DSN_FULL)
1450 ? N_("RetHdrs") : N_("RetFull");
1454 rv = radio_buttons(tmp_20k_buf,
1455 -FOOTER_ROWS(ps_global), opts,
1456 'y', 'z', NO_HELP, RB_NORM);
1457 if(rv == 'y'){ /* user ACCEPTS! */
1458 sendit = 1;
1459 break;
1461 else if(rv == 'n'){ /* Declined! */
1462 break;
1464 else if(rv == 'z'){ /* Cancelled! */
1465 break;
1467 else if(rv == 12){ /* flip verbose bit */
1468 if(call_mailer_flags & CM_VERBOSE)
1469 call_mailer_flags &= ~CM_VERBOSE;
1470 else
1471 call_mailer_flags |= CM_VERBOSE;
1473 else if(call_mailer_flags & CM_DSN_SHOW){
1474 if(rv == 's'){ /* flip success bit */
1475 call_mailer_flags ^= CM_DSN_SUCCESS;
1476 /* turn off related bits */
1477 if(call_mailer_flags & CM_DSN_SUCCESS)
1478 call_mailer_flags &= ~(CM_DSN_NEVER);
1480 else if(rv == 'd'){ /* flip delay bit */
1481 call_mailer_flags ^= CM_DSN_DELAY;
1482 /* turn off related bits */
1483 if(call_mailer_flags & CM_DSN_DELAY)
1484 call_mailer_flags &= ~(CM_DSN_NEVER);
1486 else if(rv == 'x'){ /* flip never bit */
1487 call_mailer_flags ^= CM_DSN_NEVER;
1488 /* turn off related bits */
1489 if(call_mailer_flags & CM_DSN_NEVER)
1490 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
1492 else if(rv == 'h'){ /* flip full bit */
1493 call_mailer_flags ^= CM_DSN_FULL;
1496 else if(rv == 'd'){ /* show dsn options */
1497 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
1500 snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"),
1501 (call_mailer_flags & CM_DSN_NEVER)
1502 ? _("Never") : "F",
1503 (call_mailer_flags & CM_DSN_DELAY)
1504 ? "D" : "",
1505 (call_mailer_flags & CM_DSN_SUCCESS)
1506 ? "S" : "",
1507 (call_mailer_flags & CM_DSN_NEVER)
1508 ? ""
1509 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
1510 : "-Hdrs");
1511 dsn_string[sizeof(dsn_string)-1] = '\0';
1515 if(addr)
1516 fs_give((void **)&addr);
1518 if(!(flagsarg & SS_PROMPTFORTO) || sendit){
1519 char *fcc = NULL;
1520 CONTEXT_S *fcc_cntxt = NULL;
1522 if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
1523 if(ba_fcc.tptr)
1524 fcc = cpystr(ba_fcc.tptr);
1526 set_last_fcc(fcc);
1529 * If special name "inbox" then replace it with the
1530 * real inbox path.
1532 if(ps_global->VAR_INBOX_PATH
1533 && strucmp(fcc, ps_global->inbox_name) == 0){
1534 char *replace_fcc;
1536 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
1537 fs_give((void **) &fcc);
1538 fcc = replace_fcc;
1542 /*---- Check out fcc -----*/
1543 if(fcc && *fcc){
1544 (void) commence_fcc(fcc, &fcc_cntxt, FALSE);
1545 if(!lmc.so){
1546 dprint((4,"can't open fcc, cont\n"));
1547 if(!(flagsarg & SS_PROMPTFORTO)){
1548 retval = -1;
1549 fs_give((void **)&fcc);
1550 fcc = NULL;
1551 goto finish;
1553 else
1554 continue;
1556 else
1557 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
1559 else
1560 lmc.so = NULL;
1562 if(!(outgoing->to || outgoing->cc || outgoing->bcc
1563 || lmc.so)){
1564 q_status_message(SM_ORDER, 3, 5, _("No recipients specified!"));
1565 continue;
1568 if(outgoing->to || outgoing->cc || outgoing->bcc){
1569 char **alt_smtp = NULL;
1571 if(role && role->smtp){
1572 if(ps_global->FIX_SMTP_SERVER
1573 && ps_global->FIX_SMTP_SERVER[0])
1574 q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1575 else
1576 alt_smtp = role->smtp;
1579 result = call_mailer(header, *body, alt_smtp,
1580 call_mailer_flags,
1581 call_mailer_file_result,
1582 pipe_callback);
1583 mark_address_failure_for_pico(header);
1585 else
1586 result = 0;
1588 if(result == 1 && !lmc.so)
1589 q_status_message(SM_ORDER, 0, 3, _("Message sent"));
1591 /*----- Was there an fcc involved? -----*/
1592 if(lmc.so){
1593 if(result == 1
1594 || (result == 0
1595 && pine_rfc822_output(header, *body, NULL, NULL))){
1596 char label[50];
1598 strncpy(label, "Fcc", sizeof(label));
1599 label[sizeof(label)-1] = '\0';
1600 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
1601 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
1602 label[sizeof(label)-1] = '\0';
1605 /* Now actually copy to fcc folder and close */
1606 fcc_result =
1607 write_fcc(fcc, fcc_cntxt, lmc.so, NULL,
1608 label,
1609 F_ON(F_MARK_FCC_SEEN, ps_global)
1610 ? "\\SEEN" : NULL);
1612 else if(result == 0){
1613 q_status_message(SM_ORDER,3,5,
1614 _("Fcc Failed!. No message saved."));
1615 retval = -1;
1616 dprint((1, "explicit fcc write failed!\n"));
1619 so_give(&lmc.so);
1622 if(result < 0){
1623 dprint((1, "Bounce failed\n"));
1624 if(!(flagsarg & SS_PROMPTFORTO))
1625 retval = -1;
1626 else
1627 continue;
1629 else if(result == 1){
1630 if(!fcc)
1631 q_status_message(SM_ORDER, 0, 3,
1632 _("Message sent"));
1633 else{
1634 int avail = ps_global->ttyo->screen_cols-2;
1635 int need, fcclen;
1636 char *part1 = "Message sent and ";
1637 char *part2 = fcc_result ? "" : "NOT ";
1638 char *part3 = "copied to ";
1639 fcclen = strlen(fcc);
1641 need = 2 + strlen(part1) + strlen(part2) +
1642 strlen(part3) + fcclen;
1644 if(need > avail && fcclen > 6)
1645 fcclen -= MIN(fcclen-6, need-avail);
1647 q_status_message4(SM_ORDER, 0, 3,
1648 "%s%s%s\"%s\"",
1649 part1, part2, part3,
1650 short_str(fcc,
1651 (char *)tmp_20k_buf,
1652 SIZEOF_20KBUF,
1653 fcclen, FrontDots));
1657 if(fcc)
1658 fs_give((void **)&fcc);
1660 else{
1661 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1662 retval = -1;
1665 else{
1666 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1667 _("Error in address: %s"), errbuf);
1668 if(errbuf)
1669 fs_give((void **)&errbuf);
1671 if(!(flagsarg & SS_PROMPTFORTO))
1672 retval = -1;
1673 else
1674 continue;
1678 else{
1679 q_status_message(SM_ORDER | SM_DING, 3, 5,
1680 _("No addressee! No e-mail sent."));
1681 retval = -1;
1685 done++;
1686 break;
1688 case 1:
1689 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1690 done++;
1691 retval = -1;
1692 break;
1694 case 3:
1695 help = (help == NO_HELP)
1696 ? (outgoing->remail == NULL
1697 ? h_anon_forward
1698 : h_bounce)
1699 : NO_HELP;
1700 break;
1702 case 11:
1703 if(**tobufp){
1704 char *new_nickname = NULL;
1705 int l;
1706 int ambiguity;
1708 ambiguity = abook_nickname_complete(*tobufp, &new_nickname,
1709 (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA);
1710 if(new_nickname){
1711 if(*new_nickname){
1712 if((l=strlen(new_nickname)) > resize_len){
1713 resize_len = l;
1714 fs_resize((void **) tobufp, resize_len+1);
1717 strncpy(*tobufp, new_nickname, l);
1718 (*tobufp)[l] = '\0';
1721 fs_give((void **) &new_nickname);
1724 if(ambiguity != 2)
1725 Writechar(BELL, 0);
1728 break;
1730 case 4: /* can't suspend */
1731 default:
1732 break;
1736 finish:
1737 if(ba_fcc.tptr)
1738 fs_give((void **)&ba_fcc.tptr);
1740 pine_free_env(&header);
1742 return(retval);
1747 * pine_simple_send_header - generate header suitable for simple_sending
1749 METAENV *
1750 pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp)
1752 METAENV *header;
1753 PINEFIELD *pf;
1754 static struct headerentry he_dummy;
1756 header = pine_new_env(outgoing, fccp, tobufpp, NULL);
1758 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1759 for(pf = header->local; pf && pf->name; pf = pf->next)
1760 if(pf->type == Address && !strucmp(pf->name, "to")){
1761 memset((void *) &he_dummy, 0, sizeof(he_dummy));
1762 pf->extdata = (void *) &he_dummy;
1763 HE(pf)->dirty = 1;
1764 break;
1767 return(header);
1772 /*----------------------------------------------------------------------
1773 Prepare data structures for pico, call pico, then post message
1775 Args: outgoing -- Partially formatted outgoing ENVELOPE
1776 body -- Body of outgoing message
1777 editor_title -- Title for anchor line in composer
1778 fcc_arg -- The file carbon copy field
1779 reply -- Struct describing set of msgs being replied-to
1780 lcc_arg --
1781 custom -- custom header list.
1782 sticky_fcc --
1784 Result: message is edited, then postponed, cancelled or sent.
1786 Fields:
1787 remail -
1788 return_path -
1789 date added here
1790 from added here
1791 sender -
1792 reply_to -
1793 subject passed in, edited and cannonized here
1794 to possibly passed in, edited and cannonized here
1795 cc possibly passed in, edited and cannonized here
1796 bcc edited and cannonized here
1797 in_reply_to generated in reply() and passed in
1798 message_id -
1800 Storage for these fields comes from anywhere outside. It is remalloced
1801 here so the composer can realloc them if needed. The copies here are also
1802 freed here.
1804 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1805 with the first part TYPETEXT! All newlines in the text here also end with
1806 CRLF.
1808 There's a further assumption that the text in the TYPETEXT part is
1809 stored in a storage object (see filter.c).
1810 ----*/
1811 void
1812 pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
1813 char *editor_title, ACTION_S *role, char *fcc_arg,
1814 REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg,
1815 PINEFIELD *custom, int flags)
1817 int i, fixed_cnt, total_cnt, index,
1818 editor_result = 0, body_start = 0, use_news_order = 0;
1819 char *p, *addr, *fcc, *fcc_to_free = NULL;
1820 char *start_here_name = NULL;
1821 char *suggested_nntp_server = NULL;
1822 char *title = NULL;
1823 struct headerentry *he, *headents, *he_to = NULL, *he_fcc = NULL, *he_news = NULL, *he_lcc = NULL,
1824 *he_from = NULL;
1825 PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL,
1826 *pf_smtp_server = NULL, *pf_nntp_server = NULL,
1827 *pf_fcc = NULL, *pf_err = NULL, *pf_uid = NULL, *pf_mbox = NULL, *pf_curpos = NULL,
1828 *pf_ourrep = NULL, *pf_ourhdrs = NULL, **sending_order;
1829 METAENV header;
1830 ADDRESS *lcc_addr = NULL;
1831 ADDRESS *nobody_addr = NULL;
1832 BODY_PARTICULARS_S *bp;
1833 STORE_S *orig_so = NULL;
1834 PICO pbuf1, *save_previous_pbuf;
1835 CustomType ct;
1836 REDRAFT_POS_S *local_redraft_pos = NULL;
1838 dprint((1,"\n=== send called ===\n"));
1840 save_previous_pbuf = pbf;
1841 pbf = &pbuf1;
1842 standard_picobuf_setup(pbf);
1845 * Cancel any pending initial commands since pico uses a different
1846 * input routine. If we didn't cancel them, they would happen after
1847 * we returned from the editor, which would be confusing.
1849 if(ps_global->in_init_seq){
1850 ps_global->in_init_seq = 0;
1851 ps_global->save_in_init_seq = 0;
1852 clear_cursor_pos();
1853 if(ps_global->initial_cmds){
1854 if(ps_global->free_initial_cmds)
1855 fs_give((void **)&(ps_global->free_initial_cmds));
1857 ps_global->initial_cmds = 0;
1860 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1863 #if defined(DOS) || defined(OS2)
1864 if(!dos_valid_from()){
1865 pbf = save_previous_pbuf;
1866 return;
1869 pbf->upload = NULL;
1870 #else
1871 pbf->upload = (ps_global->VAR_UPLOAD_CMD
1872 && ps_global->VAR_UPLOAD_CMD[0])
1873 ? upload_msg_to_pico : NULL;
1874 #endif
1876 pbf->msgntext = message_format_for_pico;
1877 pbf->mimetype = mime_type_for_pico;
1878 pbf->exittest = send_exit_for_pico;
1879 pbf->user_says_noflow = dont_flow_this_time;
1880 pbf->newthread = new_thread_on_blank_subject;
1881 ps_global->newthread = 0; /* reset this value */
1882 if(F_OFF(F_CANCEL_CONFIRM, ps_global))
1883 pbf->canceltest = cancel_for_pico;
1884 #ifdef _WINDOWS
1885 pbf->dict = (ps_global->VAR_DICTIONARY
1886 && ps_global->VAR_DICTIONARY[0]
1887 && ps_global->VAR_DICTIONARY[0][0])
1888 ? ps_global->VAR_DICTIONARY : NULL;
1889 pbf->chosen_dict = -1; /* not chosen yet */
1890 #endif /* _WINDOWS */
1891 pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] &&
1892 ps_global->VAR_EDITOR[0][0])
1893 ? ps_global->VAR_EDITOR : NULL;
1894 pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0])
1895 ? ps_global->VAR_SPELLER : NULL;
1896 pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
1897 pbf->quote_str = reply && reply->prefix ? reply->prefix : "> ";
1898 /* We actually want to set this only if message we're sending is flowed */
1899 pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
1900 pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
1901 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
1902 && (strcmp(pbf->quote_str, "> ") == 0
1903 || strcmp(pbf->quote_str, ">") == 0));
1904 pbf->edit_offset = 0;
1905 title = cpystr(set_titlebar(editor_title,
1906 ps_global->mail_stream,
1907 ps_global->context_current,
1908 ps_global->cur_folder,ps_global->msgmap,
1909 0, FolderName, 0, 0, NULL));
1910 pbf->pine_anchor = title;
1912 #if defined(DOS) || defined(OS2)
1913 if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){
1914 pbf->oper_dir = ps_global->VAR_FILE_DIR;
1916 #endif
1918 if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE))
1919 pbf->pine_flags |= P_CHKPTNOW;
1921 /* NOTE: initial cursor position set below */
1923 dprint((9, "flags: %x\n", pbf->pine_flags));
1926 * When user runs compose and the current folder is a newsgroup,
1927 * offer to post to the current newsgroup.
1929 if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups))
1930 && IS_NEWS(ps_global->mail_stream)){
1931 char prompt[200], news_group[MAILTMPLEN];
1933 pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group,
1934 sizeof(news_group));
1937 * Replies don't get this far because To or Newsgroups will already
1938 * be filled in. So must be either ordinary compose or forward.
1939 * Forward sets subject, so use that to tell the difference.
1941 if(news_group[0] && !outgoing->subject){
1942 int ch = 'y';
1943 int ret_val;
1944 char *errmsg = NULL;
1945 BUILDER_ARG *fcc_build = NULL;
1947 if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
1948 snprintf(prompt, sizeof(prompt),
1949 _("Post to current newsgroup (%s)"), news_group);
1950 prompt[sizeof(prompt)-1] = '\0';
1951 ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM);
1954 switch(ch){
1955 case 'y':
1956 if(outgoing->newsgroups)
1957 fs_give((void **)&outgoing->newsgroups);
1959 if(!fcc_arg && !(role && role->fcc)){
1960 fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
1961 memset((void *)fcc_build, 0, sizeof(BUILDER_ARG));
1962 fcc_build->tptr = fcc_to_free;
1965 ret_val = news_build(news_group, &outgoing->newsgroups,
1966 &errmsg, fcc_build, NULL);
1968 if(ret_val == -1){
1969 if(outgoing->newsgroups)
1970 fs_give((void **)&outgoing->newsgroups);
1972 outgoing->newsgroups = cpystr(news_group);
1975 if(!fcc_arg && !(role && role->fcc)){
1976 fcc_arg = fcc_to_free = fcc_build->tptr;
1977 fs_give((void **)&fcc_build);
1980 if(errmsg){
1981 if(*errmsg){
1982 q_status_message(SM_ORDER, 3, 3, errmsg);
1983 display_message(NO_OP_COMMAND);
1986 fs_give((void **)&errmsg);
1989 break;
1991 case 'x': /* ^C */
1992 q_status_message(SM_ORDER, 0, 3, _("Message cancelled"));
1993 dprint((4, "=== send: cancelled\n"));
1994 pbf = save_previous_pbuf;
1995 return;
1997 case 'n':
1998 break;
2000 default:
2001 break;
2005 if(F_ON(F_PREDICT_NNTP_SERVER, ps_global)
2006 && outgoing->newsgroups && *outgoing->newsgroups
2007 && IS_NEWS(ps_global->mail_stream)){
2008 NETMBX news_mb;
2010 if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox,
2011 &news_mb))
2012 if(!strucmp(news_mb.service, "nntp")){
2013 if(*ps_global->mail_stream->original_mailbox == '{'){
2014 char *svcp = NULL, *psvcp;
2016 suggested_nntp_server =
2017 cpystr(ps_global->mail_stream->original_mailbox + 1);
2018 if((p = strindex(suggested_nntp_server, '}')) != NULL)
2019 *p = '\0';
2020 for(p = strindex(suggested_nntp_server, '/'); p && *p;
2021 p = strindex(p, '/')){
2022 /* take out /nntp, which gets added in nntp_open */
2023 if(!struncmp(p, "/nntp", 5))
2024 svcp = p + 5;
2025 else if(!struncmp(p, "/service=nntp", 13))
2026 svcp = p + 13;
2027 else if(!struncmp(p, "/service=\"nntp\"", 15))
2028 svcp = p + 15;
2029 else
2030 p++;
2031 if(svcp){
2032 if(*svcp == '\0')
2033 *p = '\0';
2034 else if(*svcp == '/' || *svcp == ':'){
2035 for(psvcp = p; *svcp; svcp++, psvcp++)
2036 *psvcp = *svcp;
2037 *psvcp = '\0';
2039 svcp = NULL;
2043 else
2044 suggested_nntp_server = cpystr(news_mb.orighost);
2049 * If we don't already have custom headers set and the role has custom
2050 * headers, then incorporate those custom headers into "custom".
2052 if(!custom){
2053 PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL;
2055 dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
2057 * If we allow the Combine argument here, we're saying that we want to
2058 * combine the values from the envelope and the role for the fields To,
2059 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
2060 * we'll have a To in the envelope because we're replying. If our role also
2061 * has a To action, then Combine would combine those two and offer both
2062 * to the user. We've decided against doing this. Instead, we always use
2063 * Replace, and the role's header value replaces the value from the
2064 * envelope. It might also make sense in some cases to do the opposite,
2065 * which would be treating the role headers as defaults, just like
2066 * customized-hdrs.
2068 #ifdef WANT_TO_COMBINE_ADDRESSES
2069 if(role && role->cstm)
2070 rolehdrs = parse_custom_hdrs(role->cstm, Combine);
2071 #else
2072 if(role && role->cstm)
2073 rolehdrs = parse_custom_hdrs(role->cstm, Replace);
2074 #endif
2076 if(rolehdrs){
2077 custom = combine_custom_headers(dflthdrs, rolehdrs);
2078 if(dflthdrs){
2079 free_prompts(dflthdrs);
2080 free_customs(dflthdrs);
2083 if(rolehdrs){
2084 free_prompts(rolehdrs);
2085 free_customs(rolehdrs);
2088 else
2089 custom = dflthdrs;
2092 g_rolenick = role ? role->nick : NULL;
2094 /* how many fixed fields are there? */
2095 for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++)
2098 total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1);
2100 /* the fixed part of the PINEFIELDs */
2101 i = fixed_cnt * sizeof(PINEFIELD);
2102 pfields = (PINEFIELD *)fs_get((size_t) i);
2103 memset(pfields, 0, (size_t) i);
2105 /* temporary headerentry array for pico */
2106 i = (total_cnt + 1) * sizeof(struct headerentry);
2107 headents = (struct headerentry *)fs_get((size_t) i);
2108 memset(headents, 0, (size_t) i);
2110 i = total_cnt * sizeof(PINEFIELD *);
2111 sending_order = (PINEFIELD **)fs_get((size_t) i);
2112 memset(sending_order, 0, (size_t) i);
2114 pbf->headents = headents;
2115 header.env = outgoing;
2116 header.local = pfields;
2117 header.sending_order = sending_order;
2119 /* custom part of PINEFIELDs */
2120 header.custom = custom;
2122 he = headents;
2123 pf = pfields;
2126 * For Address types, pf->addr points to an ADDRESS *.
2127 * If that address is in the "outgoing" envelope, it will
2128 * be freed by the caller, otherwise, it should be freed here.
2129 * Pf->textbuf for an Address is used a little to set up a default,
2130 * but then is freed right away below. Pf->scratch is used for a
2131 * pointer to some alloced space for pico to edit in. Addresses in
2132 * the custom area are freed by free_customs().
2134 * For FreeText types, pf->addr is not used. Pf->text points to a
2135 * pointer that points to the text. Pf->textbuf points to a copy of
2136 * the text that must be freed before we leave, otherwise, it is
2137 * probably a pointer into the envelope and that gets freed by the
2138 * caller.
2140 * He->realaddr is the pointer to the text that pico actually edits.
2143 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2144 # define NN 4
2145 #else
2146 # define NN 3
2147 #endif
2149 if(outgoing->newsgroups && *outgoing->newsgroups)
2150 use_news_order++;
2152 /* initialize the fixed header elements of the two temp arrays */
2153 for(i=0; i < fixed_cnt; i++, pf++){
2154 static int news_order[] = {
2155 N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC,
2156 N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY,
2157 N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX,
2158 N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS
2159 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2160 , N_SENDER
2161 #endif
2164 index = i;
2165 /* slightly different editing order if sending to news */
2166 if(use_news_order &&
2167 index >= 0 && index < sizeof(news_order)/sizeof(news_order[0]))
2168 index = news_order[i];
2170 /* copy the templates */
2171 *he = he_template[index];
2173 pf->name = cpystr(pf_template[index].name);
2174 if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global)){
2175 /* slide string over so it is Sender instead of X-X-Sender */
2176 for(p = pf->name+4; *p != '\0'; p++)
2177 *(p-4) = *p;
2178 *(p-4) = '\0';
2180 pf->type = pf_template[index].type;
2181 pf->canedit = pf_template[index].canedit;
2182 pf->rcptto = pf_template[index].rcptto;
2183 pf->writehdr = pf_template[index].writehdr;
2184 pf->localcopy = pf_template[index].localcopy;
2185 pf->extdata = he;
2186 pf->next = pf + 1;
2188 he->rich_header = view_as_rich(pf->name, he->rich_header);
2189 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2190 he->nickcmpl = NULL;
2192 switch(pf->type){
2193 case FreeText: /* realaddr points to c-client env */
2194 if(index == N_NEWS){
2195 sending_order[1] = pf;
2196 he->realaddr = &outgoing->newsgroups;
2197 he_news = he;
2199 switch(set_default_hdrval(pf, custom)){
2200 case Replace:
2201 if(*he->realaddr)
2202 fs_give((void **)he->realaddr);
2204 *he->realaddr = pf->textbuf;
2205 pf->textbuf = NULL;
2206 he->sticky = 1;
2207 break;
2209 case Combine:
2210 if(*he->realaddr){ /* combine values */
2211 if(pf->textbuf && *pf->textbuf){
2212 char *combined_hdr;
2213 size_t l;
2215 l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
2216 combined_hdr = (char *) fs_get((l+1) * sizeof(char));
2217 strncpy(combined_hdr, *he->realaddr, l);
2218 combined_hdr[l] = '\0';
2219 strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
2220 combined_hdr[l] = '\0';
2221 strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
2222 combined_hdr[l] = '\0';
2224 fs_give((void **)he->realaddr);
2225 *he->realaddr = combined_hdr;
2226 q_status_message(SM_ORDER, 3, 3,
2227 "Adding newsgroup from role");
2228 he->sticky = 1;
2231 else{
2232 *he->realaddr = pf->textbuf;
2233 pf->textbuf = NULL;
2236 break;
2238 case UseAsDef:
2239 /* if no value, use default */
2240 if(!*he->realaddr){
2241 *he->realaddr = pf->textbuf;
2242 pf->textbuf = NULL;
2245 break;
2247 case NoMatch:
2248 break;
2251 /* If there is a newsgroup, we'd better show it */
2252 if(outgoing->newsgroups && *outgoing->newsgroups)
2253 he->rich_header = 0; /* force on by default */
2255 if(pf->textbuf)
2256 fs_give((void **)&pf->textbuf);
2258 pf->text = he->realaddr;
2260 else if(index == N_DATE){
2261 sending_order[2] = pf;
2262 pf->text = (char **) &outgoing->date;
2263 pf->extdata = NULL;
2265 else if(index == N_INREPLY){
2266 sending_order[NN+9] = pf;
2267 pf->text = &outgoing->in_reply_to;
2268 pf->extdata = NULL;
2270 else if(index == N_MSGID){
2271 sending_order[NN+10] = pf;
2272 pf->text = &outgoing->message_id;
2273 pf->extdata = NULL;
2275 else if(index == N_REF){
2276 sending_order[NN+11] = pf;
2277 pf->text = &outgoing->references;
2278 pf->extdata = NULL;
2280 else if(index == N_PRIORITY){
2281 sending_order[NN+12] = pf;
2282 pf->text = &pf->textbuf;
2283 pf->extdata = NULL;
2285 else if(index == N_USERAGENT){
2286 sending_order[NN+13] = pf;
2287 pf->text = &pf->textbuf;
2288 pf->textbuf = generate_user_agent();
2289 pf->extdata = NULL;
2291 else if(index == N_POSTERR){
2292 sending_order[NN+14] = pf;
2293 pf_err = pf;
2294 pf->text = &pf->textbuf;
2295 pf->extdata = NULL;
2297 else if(index == N_RPLUID){
2298 sending_order[NN+15] = pf;
2299 pf_uid = pf;
2300 pf->text = &pf->textbuf;
2301 pf->extdata = NULL;
2303 else if(index == N_RPLMBOX){
2304 sending_order[NN+16] = pf;
2305 pf_mbox = pf;
2306 pf->text = &pf->textbuf;
2307 pf->extdata = NULL;
2309 else if(index == N_SMTP){
2310 sending_order[NN+17] = pf;
2311 pf_smtp_server = pf;
2312 pf->text = &pf->textbuf;
2313 pf->extdata = NULL;
2315 else if(index == N_NNTP){
2316 sending_order[NN+18] = pf;
2317 pf_nntp_server = pf;
2318 pf->text = &pf->textbuf;
2319 pf->extdata = NULL;
2321 else if(index == N_CURPOS){
2322 sending_order[NN+19] = pf;
2323 pf_curpos = pf;
2324 pf->text = &pf->textbuf;
2325 pf->extdata = NULL;
2327 else if(index == N_OURREPLYTO){
2328 sending_order[NN+20] = pf;
2329 pf_ourrep = pf;
2330 pf->text = &pf->textbuf;
2331 pf->extdata = NULL;
2333 else if(index == N_OURHDRS){
2334 sending_order[NN+21] = pf;
2335 pf_ourhdrs = pf;
2336 pf->text = &pf->textbuf;
2337 pf->extdata = NULL;
2339 else if(index == N_AUTHRCVD){
2340 sending_order[0] = pf;
2341 pf_ourhdrs = pf;
2342 pf->text = &pf->textbuf;
2343 pf->extdata = NULL;
2345 else{
2346 q_status_message(SM_ORDER | SM_DING, 3, 7,
2347 "Botched: Unmatched FreeText header in pine_send");
2350 break;
2352 /* can't do a default for this one */
2353 case Attachment:
2354 /* If there is an attachment already, we'd better show them */
2355 if(body && *body && (*body)->type != TYPETEXT)
2356 he->rich_header = 0; /* force on by default */
2358 break;
2360 case Address:
2361 switch(index){
2362 case N_FROM:
2363 sending_order[3] = pf;
2364 pf->addr = &outgoing->from;
2365 if(role && role->from){
2366 if(ps_global->never_allow_changing_from)
2367 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2368 else{
2369 outgoing->from = copyaddrlist(role->from);
2370 he->display_it = 1; /* show it */
2371 he->rich_header = 0;
2375 he_from = he;
2376 break;
2378 case N_TO:
2379 sending_order[NN+2] = pf;
2380 pf->addr = &outgoing->to;
2381 /* If already set, make it act like we typed it in */
2382 if(outgoing->to
2383 && outgoing->to->mailbox
2384 && outgoing->to->mailbox[0]
2385 && flags & PS_STICKY_TO)
2386 he->sticky = 1;
2388 he_to = he;
2389 pf_to = pf;
2390 break;
2392 case N_NOBODY:
2393 sending_order[NN+5] = pf;
2394 pf_nobody = pf;
2395 if(ps_global->VAR_EMPTY_HDR_MSG
2396 && !ps_global->VAR_EMPTY_HDR_MSG[0]){
2397 pf->addr = NULL;
2399 else{
2400 nobody_addr = mail_newaddr();
2401 nobody_addr->next = mail_newaddr();
2402 nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
2403 SIZEOF_20KBUF,
2404 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
2405 ? ps_global->VAR_EMPTY_HDR_MSG
2406 : "undisclosed-recipients"),
2407 ps_global->posting_charmap));
2408 pf->addr = &nobody_addr;
2411 break;
2413 case N_CC:
2414 sending_order[NN+3] = pf;
2415 pf->addr = &outgoing->cc;
2416 break;
2418 case N_BCC:
2419 sending_order[NN+4] = pf;
2420 pf->addr = &outgoing->bcc;
2421 /* if bcc exists, make sure it's exposed so nothing's
2422 * sent by mistake...
2424 if(outgoing->bcc)
2425 he->display_it = 1;
2427 break;
2429 case N_REPLYTO:
2430 sending_order[NN+1] = pf;
2431 pf->addr = &outgoing->reply_to;
2432 if(role && role->replyto){
2433 if(outgoing->reply_to)
2434 mail_free_address(&outgoing->reply_to);
2436 outgoing->reply_to = copyaddrlist(role->replyto);
2437 he->display_it = 1; /* show it */
2438 he->rich_header = 0;
2441 break;
2443 case N_LCC:
2444 sending_order[NN+7] = pf;
2445 pf->addr = &lcc_addr;
2446 he_lcc = he;
2447 if(lcc_arg){
2448 build_address(lcc_arg, &addr, NULL, NULL, NULL);
2449 rfc822_parse_adrlist(&lcc_addr, addr,
2450 ps_global->maildomain);
2451 fs_give((void **)&addr);
2452 he->display_it = 1;
2455 break;
2457 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2458 case N_SENDER:
2459 sending_order[4] = pf;
2460 pf->addr = &outgoing->sender;
2461 break;
2462 #endif
2464 default:
2465 q_status_message1(SM_ORDER,3,7,
2466 "Internal error: Address header %s", comatose(index));
2467 break;
2471 * If this is a reply to news, don't show the regular email
2472 * recipient headers (unless they are non-empty).
2474 if((outgoing->newsgroups && *outgoing->newsgroups)
2475 && (index == N_TO || index == N_CC
2476 || index == N_BCC || index == N_LCC)
2477 && (pf->addr && !*pf->addr)){
2478 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2479 pf->textbuf && *pf->textbuf){
2480 removing_trailing_white_space(pf->textbuf);
2481 (void)removing_double_quotes(pf->textbuf);
2482 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2483 rfc822_parse_adrlist(pf->addr, addr,
2484 ps_global->maildomain);
2485 fs_give((void **)&addr);
2486 if(ct > UseAsDef)
2487 he->sticky = 1;
2489 else
2490 he->rich_header = 1; /* hide */
2494 * If this address doesn't already have a value, then we check
2495 * for a default value assigned by the user.
2497 else if(pf->addr && !*pf->addr){
2498 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2499 (index != N_FROM ||
2500 (!ps_global->never_allow_changing_from &&
2501 F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
2502 pf->textbuf && *pf->textbuf){
2504 removing_trailing_white_space(pf->textbuf);
2505 (void)removing_double_quotes(pf->textbuf);
2508 * Try to set To based on Lcc. Don't attempt Fcc.
2510 if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
2511 BUILDER_ARG *barg = NULL;
2512 char *ppp = NULL;
2514 if(*pf_to->addr)
2515 ppp = addr_list_string(*pf_to->addr, NULL, 1);
2517 if(!ppp)
2518 ppp = cpystr("");
2520 barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
2521 memset(barg, 0, sizeof(*barg));
2522 barg->me = &(he->bldr_private);
2523 barg->aff = &(he_to->bldr_private);
2524 barg->tptr = cpystr(ppp);
2526 build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
2527 he->display_it = 1;
2529 rfc822_parse_adrlist(pf->addr, addr,
2530 ps_global->maildomain);
2531 if(addr)
2532 fs_give((void **) &addr);
2534 if(ct > UseAsDef)
2535 he->sticky = 1;
2537 if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
2538 ADDRESS *a = NULL;
2540 rfc822_parse_adrlist(&a, barg->tptr,
2541 ps_global->maildomain);
2542 if(a){
2543 if(pf_to->addr)
2544 mail_free_address(pf_to->addr);
2546 *pf_to->addr = a;
2550 if(barg){
2551 if(barg->tptr)
2552 fs_give((void **) &barg->tptr);
2554 fs_give((void **) &barg);
2557 if(ppp)
2558 fs_give((void **) &ppp);
2560 else{
2561 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2562 rfc822_parse_adrlist(pf->addr, addr,
2563 ps_global->maildomain);
2564 if(addr)
2565 fs_give((void **) &addr);
2567 if(ct > UseAsDef)
2568 he->sticky = 1;
2572 /* if we still don't have a from */
2573 if(index == N_FROM && !*pf->addr)
2574 *pf->addr = generate_from();
2578 * Addr is already set in the rest of the cases.
2580 else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
2581 ADDRESS *adr = NULL;
2584 * We get to this case of the ifelse if the from or reply-to
2585 * addr was set by a role above.
2588 /* figure out the default value */
2589 (void)set_default_hdrval(pf, custom);
2590 if(pf->textbuf && *pf->textbuf){
2591 removing_trailing_white_space(pf->textbuf);
2592 (void)removing_double_quotes(pf->textbuf);
2593 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2594 rfc822_parse_adrlist(&adr, addr,
2595 ps_global->maildomain);
2596 fs_give((void **)&addr);
2599 /* if value set by role is different from default, show it */
2600 if(adr && !address_is_same(*pf->addr, adr))
2601 he->display_it = 1; /* start this off showing */
2603 /* malformed */
2604 if(!(*pf->addr)->mailbox){
2605 fs_give((void **)pf->addr);
2606 he->display_it = 1;
2609 if(adr)
2610 mail_free_address(&adr);
2612 else if((index == N_TO || index == N_CC || index == N_BCC)
2613 && pf->addr){
2614 ADDRESS *a = NULL, **tail;
2617 * These three are different from the others because we
2618 * might add the addresses to what is already there instead
2619 * of replacing.
2622 switch(set_default_hdrval(pf, custom)){
2623 case Replace:
2624 if(*pf->addr)
2625 mail_free_address(pf->addr);
2627 removing_trailing_white_space(pf->textbuf);
2628 (void)removing_double_quotes(pf->textbuf);
2629 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2630 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2631 fs_give((void **)&addr);
2632 he->sticky = 1;
2633 break;
2635 case Combine:
2636 removing_trailing_white_space(pf->textbuf);
2637 (void)removing_double_quotes(pf->textbuf);
2638 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2639 rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
2640 fs_give((void **)&addr);
2641 he->sticky = 1;
2642 if(a){
2643 for(tail = pf->addr; *tail; tail = &(*tail)->next)
2645 *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
2646 *pf->addr, NULL, a, RCA_ALL);
2647 q_status_message(SM_ORDER, 3, 3,
2648 "Adding addresses from role");
2649 mail_free_address(&a);
2652 break;
2654 case UseAsDef:
2655 case NoMatch:
2656 break;
2659 he->display_it = 1; /* start this off showing */
2661 else if(pf->addr){
2662 switch(set_default_hdrval(pf, custom)){
2663 case Replace:
2664 case Combine:
2665 if(*pf->addr)
2666 mail_free_address(pf->addr);
2668 removing_trailing_white_space(pf->textbuf);
2669 (void)removing_double_quotes(pf->textbuf);
2670 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2671 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2672 fs_give((void **)&addr);
2673 he->sticky = 1;
2674 break;
2676 case UseAsDef:
2677 case NoMatch:
2678 break;
2681 he->display_it = 1;
2684 if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
2685 mail_free_address(pf->addr);
2686 he->display_it = 1; /* start this off showing */
2689 if(pf->textbuf) /* free default value in any case */
2690 fs_give((void **)&pf->textbuf);
2692 /* outgoing2strings will alloc the string pf->scratch below */
2693 he->realaddr = &pf->scratch;
2694 break;
2696 case Fcc:
2697 sending_order[NN+8] = pf;
2698 pf_fcc = pf;
2699 if(role && role->fcc)
2700 fcc = role->fcc;
2701 else
2702 fcc = get_fcc(fcc_arg);
2704 if(fcc_to_free){
2705 fs_give((void **)&fcc_to_free);
2706 fcc_arg = NULL;
2709 if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
2710 he->sticky = 1;
2712 if(role)
2713 role->fcc = NULL;
2715 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
2716 he->display_it = 1; /* start this off showing */
2718 he->realaddr = &fcc;
2719 pf->text = &fcc;
2720 he_fcc = he;
2721 break;
2723 case Subject :
2724 sending_order[NN+6] = pf;
2726 switch(set_default_hdrval(pf, custom)){
2727 case Replace:
2728 case Combine:
2729 pf->scratch = pf->textbuf;
2730 pf->textbuf = NULL;
2731 he->sticky = 1;
2732 if(outgoing->subject)
2733 fs_give((void **)&outgoing->subject);
2735 break;
2737 case UseAsDef:
2738 case NoMatch:
2739 /* if no value, use default */
2740 if(outgoing->subject){
2741 pf->scratch = cpystr(outgoing->subject);
2743 else{
2744 pf->scratch = pf->textbuf;
2745 pf->textbuf = NULL;
2748 break;
2751 he->realaddr = &pf->scratch;
2752 pf->text = &outgoing->subject;
2753 break;
2755 default:
2756 q_status_message1(SM_ORDER,3,7,
2757 "Unknown header type %d in pine_send",
2758 (void *)pf->type);
2759 break;
2763 * We may or may not want to give the user the chance to edit
2764 * the From and Reply-To lines. If they are listed in either
2765 * Default-composer-hdrs or Customized-hdrs, then they can edit
2766 * them, else no.
2767 * If canedit is not set, that means that this header is not in
2768 * the user's customized-hdrs. If rich_header is set, that
2769 * means that this header is not in the user's
2770 * default-composer-hdrs (since From and Reply-To are rich
2771 * by default). So, don't give it an he to edit with in that case.
2773 * For other types, just not setting canedit will cause it to be
2774 * uneditable, regardless of what the user does.
2776 switch(index){
2777 case N_FROM:
2778 /* to allow it, we let this fall through to the reply-to case below */
2779 if(ps_global->never_allow_changing_from ||
2780 (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
2781 !(role && role->from))){
2782 if(pf->canedit || !he->rich_header)
2783 q_status_message(SM_ORDER, 3, 3,
2784 _("Not allowed to change header \"From\""));
2786 memset(he, 0, (size_t)sizeof(*he));
2787 pf->extdata = NULL;
2788 break;
2791 case N_REPLYTO:
2792 if(!pf->canedit && he->rich_header){
2793 memset(he, 0, (size_t)sizeof(*he));
2794 pf->extdata = NULL;
2796 else{
2797 pf->canedit = 1;
2798 he++;
2801 break;
2803 default:
2804 if(!pf->canedit){
2805 memset(he, 0, (size_t)sizeof(*he));
2806 pf->extdata = NULL;
2808 else
2809 he++;
2811 break;
2816 * This is so the builder can tell the composer to fill the affected
2817 * field based on the value in the field on the left.
2819 * Note that this mechanism isn't completely general. Each entry has
2820 * only a single next_affected, so if some other entry points an
2821 * affected entry at an entry with a next_affected, they all inherit
2822 * that next_affected. Since this isn't used much a careful ordering
2823 * of the affected fields should make it a sufficient mechanism.
2825 he_to->affected_entry = he_fcc;
2826 he_news->affected_entry = he_fcc;
2827 he_lcc->affected_entry = he_to;
2828 he_to->next_affected = he_fcc;
2830 (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
2832 i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2834 * Set up headerentries for custom fields.
2835 * NOTE: "i" is assumed to now index first custom field in sending
2836 * order.
2838 for(pf = pf->next; pf && pf->name; pf = pf->next){
2839 char *addr;
2841 if(pf->standard)
2842 continue;
2844 pf->extdata = he;
2845 pf->canedit = 1;
2846 pf->rcptto = 0;
2847 pf->writehdr = 1;
2848 pf->localcopy = 1;
2850 switch(pf->type){
2851 case Address:
2852 if(pf->addr){ /* better be set */
2853 sending_order[i++] = pf;
2854 *he = he_custom_addr_templ;
2855 /* change default text into an ADDRESS */
2856 /* strip quotes around whole default */
2857 removing_trailing_white_space(pf->textbuf);
2858 (void)removing_double_quotes(pf->textbuf);
2859 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2860 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2861 fs_give((void **)&addr);
2862 if(pf->textbuf)
2863 fs_give((void **)&pf->textbuf);
2865 he->realaddr = &pf->scratch;
2866 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2867 he->nickcmpl = NULL;
2870 break;
2872 case FreeText:
2873 sending_order[i++] = pf;
2874 *he = he_custom_free_templ;
2875 he->realaddr = &pf->textbuf;
2876 pf->text = &pf->textbuf;
2877 if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
2878 (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
2879 he->display_it = 1; /* show it */
2881 break;
2883 default:
2884 q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
2885 (void *)pf->type);
2886 break;
2889 he->name = pf->name;
2891 /* use first 8 characters for prompt */
2892 he->prompt = cpystr(" : ");
2893 strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
2895 he->rich_header = view_as_rich(he->name, he->rich_header);
2896 he++;
2900 * Make sure at least *one* field is displayable...
2902 for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
2903 if(HE(pf) && !HE(pf)->rich_header){
2904 index = i;
2905 break;
2909 * None displayable!!! Warn and display defaults.
2911 if(index == -1){
2912 q_status_message(SM_ORDER,0,5,
2913 "No default-composer-hdrs matched, displaying defaults");
2914 for(i = 0, pf = header.local; pf; pf = pf->next, i++)
2915 if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
2916 && HE(pf))
2917 HE(pf)->rich_header = 0;
2921 * Save information about body which set_mime_type_by_grope might change.
2922 * Then, if we get an error sending, we reset these things so that
2923 * grope can do it's thing again after we edit some more.
2925 if ((*body)->type == TYPEMULTIPART)
2926 bp = save_body_particulars(&(*body)->nested.part->body);
2927 else
2928 bp = save_body_particulars(*body);
2931 local_redraft_pos = redraft_pos;
2933 /*----------------------------------------------------------------------
2934 Loop calling the editor until everything goes well
2935 ----*/
2936 while(1){
2937 int saved_user_timeout;
2939 /* Reset body to what it was when we started. */
2940 if ((*body)->type == TYPEMULTIPART)
2941 reset_body_particulars(bp, &(*body)->nested.part->body);
2942 else
2943 reset_body_particulars(bp,*body);
2945 * set initial cursor position based on how many times we've been
2946 * thru the loop...
2948 if(reply && reply->pseudo){
2949 pbf->pine_flags |= reply->data.pico_flags;
2951 else if(body_start){
2952 pbf->pine_flags |= P_BODY;
2953 body_start = 0; /* maybe not next time */
2955 else if(local_redraft_pos){
2956 pbf->edit_offset = local_redraft_pos->offset;
2957 /* set the start_here bit in correct header */
2958 for(pf = header.local; pf && pf->name; pf = pf->next)
2959 if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
2960 && HE(pf)){
2961 HE(pf)->start_here = 1;
2962 break;
2965 /* If didn't find it, we start in body. */
2966 if(!pf || !pf->name)
2967 pbf->pine_flags |= P_BODY;
2969 else if(reply && (!reply->forw && !reply->forwarded)){
2970 pbf->pine_flags |= P_BODY;
2973 /* in case these were turned on in previous pass through loop */
2974 if(pf_nobody){
2975 pf_nobody->writehdr = 0;
2976 pf_nobody->localcopy = 0;
2979 if(pf_fcc)
2980 pf_fcc->localcopy = 0;
2983 * If a sending attempt failed after we passed the message text
2984 * thru a user-defined filter, "orig_so" points to the original
2985 * text. Replace the body's encoded data with the original...
2987 if(orig_so){
2988 STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
2989 ? &(*body)->nested.part->body.contents.text.data
2990 : &(*body)->contents.text.data);
2991 so_give(so);
2992 *so = orig_so;
2993 orig_so = NULL;
2997 * Convert the envelope and body to the string format that
2998 * pico can edit
3000 outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
3002 for(pf = header.local; pf && pf->name; pf = pf->next){
3004 * If this isn't the first time through this loop, we may have
3005 * freed some of the FreeText headers below so that they wouldn't
3006 * show up as empty headers in the finished message. Need to
3007 * alloc them again here so they can be edited.
3009 if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
3010 *HE(pf)->realaddr = cpystr("");
3012 if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
3013 HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
3017 * If From is exposed, probably by a role, then start the cursor
3018 * on the first line which isn't filled in. If it isn't, then we
3019 * don't move the cursor, mostly for back-compat.
3021 if((!reply || reply->forw || reply->forwarded) &&
3022 !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
3023 (he_from->display_it || !he_from->rich_header)){
3024 for(pf = header.local; pf && pf->name; pf = pf->next)
3025 if(HE(pf) &&
3026 (HE(pf)->display_it || !HE(pf)->rich_header) &&
3027 HE(pf)->realaddr &&
3028 (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
3029 HE(pf)->start_here = 1;
3030 break;
3034 #ifdef _WINDOWS
3035 mswin_setwindowmenu (MENU_COMPOSER);
3036 #endif
3038 cancel_busy_cue(-1);
3039 flush_status_messages(1);
3041 /* turn off user input timeout when in composer */
3042 saved_user_timeout = ps_global->hours_to_timeout;
3043 ps_global->hours_to_timeout = 0;
3044 dprint((1, "\n ---- COMPOSER ----\n"));
3045 editor_result = pico(pbf);
3046 dprint((4, "... composer returns (0x%x)\n", editor_result));
3047 ps_global->hours_to_timeout = saved_user_timeout;
3049 #ifdef _WINDOWS
3050 mswin_setwindowmenu (MENU_DEFAULT);
3051 #endif
3052 fix_windsize(ps_global);
3055 * Only reinitialize signals if we didn't receive an interesting
3056 * one while in pico, since pico's return is part of processing that
3057 * signal and it should continue to be ignored.
3059 if(!(editor_result & COMP_GOTHUP))
3060 init_signals(); /* Pico has it's own signal stuff */
3063 * We're going to save in DEADLETTER. Dump attachments first.
3065 if(editor_result & COMP_CANCEL)
3066 free_attachment_list(&pbf->attachments);
3068 /* Turn strings back into structures */
3069 strings2outgoing(&header, body, pbf->attachments, flowing_requested);
3071 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3072 if(outgoing->newsgroups){
3073 sqzspaces(outgoing->newsgroups);
3074 if(!outgoing->newsgroups[0])
3075 fs_give((void **)&(outgoing->newsgroups));
3078 /* Make subject NULL if it is "" (so won't show up in headers) */
3079 if(outgoing->subject && !outgoing->subject[0])
3080 fs_give((void **)&(outgoing->subject));
3082 /* remove custom fields that are empty */
3083 for(pf = header.local; pf && pf->name; pf = pf->next){
3084 if(pf->type == FreeText && pf->textbuf){
3085 if(pf->textbuf[0] == '\0'){
3086 fs_give((void **)&pf->textbuf);
3087 pf->text = NULL;
3092 removing_trailing_white_space(fcc);
3094 /*-------- Stamp it with a current date -------*/
3095 if(outgoing->date) /* update old date */
3096 fs_give((void **)&(outgoing->date));
3098 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3099 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
3101 rfc822_date(tmp_20k_buf); /* format and copy new date */
3102 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3103 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
3105 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
3107 /* Set return_path based on From which is going to be used */
3108 if(outgoing->return_path)
3109 mail_free_address(&outgoing->return_path);
3111 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
3114 * Don't ever believe the sender that is there.
3115 * If From doesn't look quite right, generate our own sender.
3117 if(outgoing->sender)
3118 mail_free_address(&outgoing->sender);
3121 * If the LHS of the address doesn't match, or the RHS
3122 * doesn't match one of localdomain or hostname,
3123 * then add a sender line (really X-X-Sender).
3125 * Don't add a personal_name since the user can change that.
3127 if(F_OFF(F_DISABLE_SENDER, ps_global)
3129 (!outgoing->from
3130 || !outgoing->from->mailbox
3131 || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
3132 || !outgoing->from->host
3133 || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
3134 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
3136 outgoing->sender = mail_newaddr();
3137 outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
3138 outgoing->sender->host = cpystr(ps_global->hostname);
3141 /* To protect the privacy of the user, make sure that the domain
3142 * part in the message id matches the domain in the from, so that
3143 * the user does not disclose more than they are already willing
3144 * to disclose.
3147 if(outgoing->message_id && outgoing->from && !role){
3148 fs_give((void **) &outgoing->message_id);
3149 role = (ACTION_S *) fs_get(sizeof(ACTION_S)); /* create fake role */
3150 memset((void *) role, 0, sizeof(ACTION_S));
3151 role->from = outgoing->from; /* and fill the from field only */
3152 outgoing->message_id = generate_message_id(role); /* new message id */
3153 role->from = NULL; /* disconnect the from part */
3154 fs_give((void **) &role); /* the fake role can be discarded */
3157 if(ps_global->newthread){
3158 if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
3159 if(outgoing->references) fs_give((void **)&outgoing->references);
3162 /*----- Message is edited, now decide what to do with it ----*/
3163 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
3164 /*=========== Postpone or Interrupted message ============*/
3165 CONTEXT_S *fcc_cntxt = NULL;
3166 char folder[MAXPATH+1];
3167 int fcc_result = 0;
3168 char label[50];
3170 dprint((4, "pine_send:%s handling\n",
3171 (editor_result & COMP_SUSPEND)
3172 ? "SUSPEND"
3173 : (editor_result & COMP_GOTHUP)
3174 ? "HUP"
3175 : (editor_result & COMP_CANCEL)
3176 ? "CANCEL" : "HUH?"));
3177 if((editor_result & COMP_CANCEL)
3178 && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
3179 || ps_global->deadlets == 0)){
3180 q_status_message(SM_ORDER, 0, 3, "Message cancelled");
3181 break;
3185 * The idea here is to use the Fcc: writing facility
3186 * to append to the special postponed message folder...
3188 * NOTE: the strategy now is to write the message and
3189 * all attachments as they exist at composition time.
3190 * In other words, attachments are postponed by value
3191 * and not reference. This may change later, but we'll
3192 * need a local "message/external-body" type that
3193 * outgoing2strings knows how to properly set up for
3194 * the composer. Maybe later...
3197 label[0] = '\0';
3199 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
3200 && (editor_result & COMP_SUSPEND)
3201 && (check_addresses(&header) == CA_BAD)){
3202 /*--- Addresses didn't check out---*/
3203 q_status_message(SM_ORDER, 7, 7,
3204 _("Not allowed to postpone message until addresses are qualified"));
3205 continue;
3209 * Build the local message copy so.
3211 * In the HUP case, we'll write the bezerk delimiter by
3212 * hand and output the message directly into the folder.
3213 * It's not only faster, we don't have to worry about
3214 * c-client reentrance and less hands paw over the data so
3215 * there's less chance of a problem.
3217 * In the Postpone case, just create it if the user wants to
3218 * and create a temporary storage object to write into. */
3219 fake_hup:
3220 fcc_result = 0;
3221 lmc.all_written = lmc.text_only = lmc.text_written = 0;
3222 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3223 int newfile = 1;
3224 time_t now = time((time_t *)0);
3226 #if defined(DOS) || defined(OS2)
3228 * we can't assume anything about root or home dirs, so
3229 * just plunk it down in the same place as the pinerc
3231 if(!getenv("HOME")){
3232 char *lc = last_cmpnt(ps_global->pinerc);
3233 folder[0] = '\0';
3234 if(lc != NULL){
3235 strncpy(folder,ps_global->pinerc,
3236 MIN(lc-ps_global->pinerc,sizeof(folder)-1));
3237 folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
3240 strncat(folder, (editor_result & COMP_GOTHUP)
3241 ? INTERRUPTED_MAIL : DEADLETTER,
3242 sizeof(folder)-strlen(folder)-1);
3244 else
3245 #endif
3246 build_path(folder,
3247 ps_global->VAR_OPER_DIR
3248 ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
3249 (editor_result & COMP_GOTHUP)
3250 ? INTERRUPTED_MAIL : DEADLETTER,
3251 sizeof(folder));
3253 if(editor_result & COMP_CANCEL){
3254 char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
3256 if(strlen(folder) + 1 < sizeof(filename))
3257 for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
3258 strncpy(filename, folder, sizeof(filename));
3259 filename[sizeof(filename)-1] = '\0';
3260 strncpy(newfname, filename, sizeof(newfname));
3261 newfname[sizeof(newfname)-1] = '\0';
3263 if(i > 1){
3264 snprintf(nbuf, sizeof(nbuf), "%d", i);
3265 nbuf[sizeof(nbuf)-1] = '\0';
3266 strncat(filename, nbuf,
3267 sizeof(filename)-strlen(filename)-1);
3268 filename[sizeof(filename)-1] = '\0';
3271 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
3272 nbuf[sizeof(nbuf)-1] = '\0';
3273 strncat(newfname, nbuf,
3274 sizeof(newfname)-strlen(newfname)-1);
3275 newfname[sizeof(newfname)-1] = '\0';
3276 (void) rename_file(filename, newfname);
3279 our_unlink(folder);
3281 else
3282 newfile = can_access(folder, ACCESS_EXISTS);
3284 if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
3285 if (outgoing->from){
3286 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
3287 newfile ? "" : "\015\012",
3288 outgoing->from->mailbox,
3289 outgoing->from->host, ctime(&now));
3290 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3291 if(!so_puts(lmc.so, tmp_20k_buf)){
3292 if(editor_result & COMP_CANCEL)
3293 q_status_message2(SM_ORDER | SM_DING, 3, 3,
3294 "Can't write \"%s\": %s",
3295 folder, error_description(errno));
3296 else
3297 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3298 folder ? folder : "?",
3299 error_description(errno)));
3304 else{ /* Must be COMP_SUSPEND */
3305 if(!ps_global->VAR_POSTPONED_FOLDER
3306 || !ps_global->VAR_POSTPONED_FOLDER[0]){
3307 q_status_message(SM_ORDER | SM_DING, 3, 3,
3308 _("No postponed file defined"));
3309 continue;
3313 * Store the cursor position
3315 * First find the header entry with the start_here
3316 * bit set, if any. This means the editor is telling
3317 * us to start on this header field next time.
3319 start_here_name = NULL;
3320 for(pf = header.local; pf && pf->name; pf = pf->next)
3321 if(HE(pf) && HE(pf)->start_here){
3322 start_here_name = pf->name;
3323 break;
3326 /* If there wasn't one, ":" means we start in the body. */
3327 if(!start_here_name || !*start_here_name)
3328 start_here_name = ":";
3330 if(ps_global->VAR_FORM_FOLDER
3331 && ps_global->VAR_FORM_FOLDER[0]
3332 && postpone_prompt() == 'f'){
3333 strncpy(folder, ps_global->VAR_FORM_FOLDER,
3334 sizeof(folder)-1);
3335 folder[sizeof(folder)-1] = '\0';
3336 strncpy(label, "form letter", sizeof(label));
3337 label[sizeof(label)-1] = '\0';
3339 else{
3340 strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
3341 sizeof(folder)-1);
3342 folder[sizeof(folder)-1] = '\0';
3343 strncpy(label, "postponed message", sizeof(label));
3344 label[sizeof(label)-1] = '\0';
3347 lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
3350 if(lmc.so){
3351 size_t sz;
3352 char *lmq = NULL;
3354 /* copy fcc line to postponed or interrupted folder */
3355 if(pf_fcc)
3356 pf_fcc->localcopy = 1;
3358 /* plug error into header for later display to user */
3359 if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
3360 pf_err->writehdr = 1;
3361 pf_err->localcopy = 1;
3362 pf_err->textbuf = lmq;
3366 * if reply, write (UID)folder header field so we can
3367 * later flag the replied-to message \\ANSWERED
3368 * DON'T save MSGNO's.
3370 if(reply && reply->uid){
3371 char uidbuf[MAILTMPLEN], *p;
3372 long i;
3374 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
3375 if(i)
3376 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
3378 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
3381 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3383 pf_uid->writehdr = 1;
3384 pf_uid->localcopy = 1;
3385 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
3386 reply->prefix ? int2string(strlen(reply->prefix))
3387 : (reply->forwarded) ? "": "0 ",
3388 reply->prefix ? " " : "",
3389 reply->prefix ? reply->prefix : "",
3390 i, reply->data.uid.validity,
3391 tmp_20k_buf, reply->mailbox);
3392 uidbuf[sizeof(uidbuf)-1] = '\0';
3393 pf_uid->textbuf = cpystr(uidbuf);
3396 * Logically, this ought to be part of pf_uid, but this
3397 * was added later and so had to be in a separate header
3398 * for backwards compatibility.
3400 pf_mbox->writehdr = 1;
3401 pf_mbox->localcopy = 1;
3402 pf_mbox->textbuf = cpystr(reply->origmbox
3403 ? reply->origmbox
3404 : reply->mailbox);
3407 /* Save cursor position */
3408 if(start_here_name && *start_here_name){
3409 char curposbuf[MAILTMPLEN];
3411 pf_curpos->writehdr = 1;
3412 pf_curpos->localcopy = 1;
3413 snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
3414 pbf->edit_offset);
3415 curposbuf[sizeof(curposbuf)-1] = '\0';
3416 pf_curpos->textbuf = cpystr(curposbuf);
3420 * Work around c-client reply-to bug. C-client will
3421 * return a reply_to in an envelope even if there is
3422 * no reply-to header field. We want to note here whether
3423 * the reply-to is real or not.
3425 if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
3426 pf_ourrep->writehdr = 1;
3427 pf_ourrep->localcopy = 1;
3428 if(outgoing->reply_to)
3429 pf_ourrep->textbuf = cpystr("Full");
3430 else
3431 pf_ourrep->textbuf = cpystr("Empty");
3434 /* Save the role-specific smtp server */
3435 if(role && role->smtp && role->smtp[0]){
3436 char *q, *smtp = NULL;
3437 char **lp;
3438 size_t len = 0;
3441 * Turn the list of smtp servers into a space-
3442 * delimited list in a single string.
3444 for(lp = role->smtp; (q = *lp) != NULL; lp++)
3445 len += (strlen(q) + 1);
3447 if(len){
3448 smtp = (char *) fs_get(len * sizeof(char));
3449 smtp[0] = '\0';
3450 for(lp = role->smtp; (q = *lp) != NULL; lp++){
3451 if(lp != role->smtp)
3452 strncat(smtp, " ", len-strlen(smtp)-1);
3454 strncat(smtp, q, len-strlen(smtp)-1);
3457 smtp[len-1] = '\0';
3460 pf_smtp_server->writehdr = 1;
3461 pf_smtp_server->localcopy = 1;
3462 if(smtp)
3463 pf_smtp_server->textbuf = smtp;
3464 else
3465 pf_smtp_server->textbuf = cpystr("");
3468 /* Save the role-specific nntp server */
3469 if(suggested_nntp_server ||
3470 (role && role->nntp && role->nntp[0])){
3471 char *q, *nntp = NULL;
3472 char **lp;
3473 size_t len = 0;
3475 if(role && role->nntp && role->nntp[0]){
3477 * Turn the list of nntp servers into a space-
3478 * delimited list in a single string.
3480 for(lp = role->nntp; (q = *lp) != NULL; lp++)
3481 len += (strlen(q) + 1);
3483 if(len){
3484 nntp = (char *) fs_get(len * sizeof(char));
3485 nntp[0] = '\0';
3486 for(lp = role->nntp; (q = *lp) != NULL; lp++){
3487 if(lp != role->nntp)
3488 strncat(nntp, " ", len-strlen(nntp)-1);
3490 strncat(nntp, q, len-strlen(nntp)-1);
3493 nntp[len-1] = '\0';
3496 else
3497 nntp = cpystr(suggested_nntp_server);
3499 pf_nntp_server->writehdr = 1;
3500 pf_nntp_server->localcopy = 1;
3501 if(nntp)
3502 pf_nntp_server->textbuf = nntp;
3503 else
3504 pf_nntp_server->textbuf = cpystr("");
3508 * Write the list of custom headers to the
3509 * X-Our-Headers header so that we can recover the
3510 * list in redraft.
3512 sz = 0;
3513 for(pf = header.custom; pf && pf->name; pf = pf->next)
3514 sz += strlen(pf->name) + 1;
3516 if(sz){
3517 char *q;
3519 pf_ourhdrs->writehdr = 1;
3520 pf_ourhdrs->localcopy = 1;
3521 pf_ourhdrs->textbuf = (char *)fs_get(sz);
3522 memset(pf_ourhdrs->textbuf, 0, sz);
3523 q = pf_ourhdrs->textbuf;
3524 for(pf = header.custom; pf && pf->name; pf = pf->next){
3525 if(pf != header.custom)
3526 sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
3528 sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
3531 pf_ourhdrs->textbuf[sz-1] = '\0';;
3535 * We need to make sure any header values that got cleared
3536 * get written to the postponed message (they won't if
3537 * pf->text is NULL). Otherwise, we can't tell previously
3538 * non-existent custom headers or default values from
3539 * custom (or other) headers that got blanked in the
3540 * composer...
3542 for(pf = header.local; pf && pf->name; pf = pf->next)
3543 if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
3544 *(HE(pf)->realaddr) = cpystr("");
3547 * We're saving the message for use later. It may be that the
3548 * characters in the message are not all convertible to the
3549 * user's posting_charmap. We'll save it as UTF-8 instead
3550 * and worry about that the next time they try to send it.
3551 * Use a different save pointer just to be sure we don't
3552 * mess up the other stuff. We should probably make the
3553 * charset an argument.
3555 * We also need to fix the charset of the body part
3556 * the user is editing so that we can read it back
3557 * successfully when we resume the composition.
3559 ps_global->post_utf8 = 1;
3562 PARAMETER *pm;
3563 BODY *bp;
3564 if((*body)->type == TYPEMULTIPART)
3565 bp = &(*body)->nested.part->body;
3566 else
3567 bp = *body;
3569 for(pm = bp->parameter;
3570 pm && strucmp(pm->attribute, "charset") != 0;
3571 pm = pm->next)
3574 if(pm){
3575 if(pm->value)
3576 fs_give((void **) &pm->value);
3578 pm->value = cpystr("UTF-8");
3582 if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
3583 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3584 char *err;
3585 STORE_S *hup_so;
3586 gf_io_t gc, pc;
3587 int we_cancel = 0;
3589 if(editor_result & COMP_CANCEL){
3590 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3591 "Saving to \"%s\"", folder);
3592 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3593 we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
3596 if((hup_so =
3597 so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
3598 gf_set_so_readc(&gc, lmc.so);
3599 gf_set_so_writec(&pc, hup_so);
3600 so_seek(lmc.so, 0L, 0); /* read msg copy and */
3601 so_seek(hup_so, 0L, 2); /* append to folder */
3602 gf_filter_init();
3603 gf_link_filter(gf_nvtnl_local, NULL);
3604 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
3605 dprint((1, "*** PIPE FAILED: %s\n",
3606 err ? err : "?"));
3608 gf_clear_so_readc(lmc.so);
3609 gf_clear_so_writec(hup_so);
3610 so_give(&hup_so);
3612 else
3613 dprint((1, "*** CAN'T CREATE %s: %s\n",
3614 folder ? folder : "?",
3615 error_description(errno)));
3617 if(we_cancel)
3618 cancel_busy_cue(-1);
3620 else
3621 fcc_result = write_fcc(folder, fcc_cntxt,
3622 lmc.so, NULL, label, NULL);
3625 /* discontinue coerced UTF-8 posting */
3626 ps_global->post_utf8 = 0;
3628 so_give(&lmc.so);
3630 else
3631 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3632 error_description(errno)));
3634 if(editor_result & COMP_GOTHUP){
3636 * Special Hack #291: if any hi-byte bits are set in
3637 * editor's result, we put them there.
3639 if(editor_result & 0xff00)
3640 exit(editor_result >> 8);
3642 dprint((1, "Save composition on HUP %sED\n",
3643 fcc_result ? "SUCCEED" : "FAIL"));
3644 hup_signal(); /* Do what we normally do on SIGHUP */
3646 else if((editor_result & COMP_SUSPEND) && fcc_result){
3647 if(ps_global->VAR_FORM_FOLDER
3648 && ps_global->VAR_FORM_FOLDER[0]
3649 && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
3650 q_status_message(SM_ORDER, 0, 3,
3651 _("Composition saved to Form Letter Folder. Select Compose to send."));
3652 else
3653 q_status_message(SM_ORDER, 0, 3,
3654 _("Composition postponed. Select Compose to resume."));
3656 break; /* postpone went OK, get out of here */
3658 else if(editor_result & COMP_CANCEL){
3659 char *lc = NULL;
3661 if(fcc_result && folder)
3662 lc = last_cmpnt(folder);
3664 q_status_message3(SM_ORDER, 0, 3,
3665 _("Message cancelled%s%s%s"),
3666 (lc && *lc) ? " and copied to \"" : "",
3667 (lc && *lc) ? lc : "",
3668 (lc && *lc) ? "\" file" : "");
3669 break;
3671 else{
3672 q_status_message(SM_ORDER, 0, 4,
3673 _("Continuing composition. Message not postponed or sent"));
3674 body_start = 1;
3675 continue; /* postpone failed, jump back in to composer */
3678 else{
3679 /*------ Must be sending mail or posting ! -----*/
3680 int result, valid_addr, continue_with_only_fcc = 0;
3681 CONTEXT_S *fcc_cntxt = NULL;
3683 result = 0;
3684 dprint((4, "=== sending: "));
3686 /* --- If posting, confirm with user ----*/
3687 if(outgoing->newsgroups && *outgoing->newsgroups
3688 && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
3689 && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
3690 q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
3691 dprint((4, "no post, continuing\n"));
3692 continue;
3695 if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
3696 || outgoing->newsgroups)){
3697 if(fcc && fcc[0]){
3698 if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
3699 want_to(_("No recipients, really copy only to Fcc "),
3700 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
3701 continue;
3703 continue_with_only_fcc++;
3705 else{
3706 q_status_message(SM_ORDER, 3, 4,
3707 _("No recipients specified!"));
3708 dprint((4, "no recip, continuing\n"));
3709 continue;
3713 if((valid_addr = check_addresses(&header)) == CA_BAD){
3714 /*--- Addresses didn't check out---*/
3715 dprint((4, "addrs failed, continuing\n"));
3716 continue;
3719 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
3720 && !continue_with_only_fcc
3721 && !(outgoing->to || outgoing->cc || lcc_addr
3722 || outgoing->newsgroups)
3723 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3724 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
3725 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3726 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3727 free_redraft_pos(&local_redraft_pos);
3729 local_redraft_pos
3730 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3731 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3732 local_redraft_pos->hdrname = cpystr(TONAME);
3733 continue;
3736 if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
3737 && check_for_subject(&header) == CF_MISSING){
3738 dprint((4, "No subject, continuing\n"));
3739 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3740 free_redraft_pos(&local_redraft_pos);
3742 local_redraft_pos
3743 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3744 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3745 local_redraft_pos->hdrname = cpystr(SUBJNAME);
3746 continue;
3749 if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
3750 && check_for_fcc(fcc) == CF_MISSING){
3751 dprint((4, "No fcc, continuing\n"));
3752 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3753 free_redraft_pos(&local_redraft_pos);
3755 local_redraft_pos
3756 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3757 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3758 local_redraft_pos->hdrname = cpystr("Fcc");
3759 continue;
3762 set_last_fcc(fcc);
3764 /*---- Check out fcc -----*/
3765 if(fcc && *fcc){
3767 * If special name "inbox" then replace it with the
3768 * real inbox path.
3770 if(ps_global->VAR_INBOX_PATH
3771 && strucmp(fcc, ps_global->inbox_name) == 0){
3772 char *replace_fcc;
3774 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
3775 fs_give((void **)&fcc);
3776 fcc = replace_fcc;
3779 lmc.all_written = lmc.text_written = 0;
3780 /* lmc.text_only set on command line */
3781 if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
3782 /* ---- Open or allocation of fcc failed ----- */
3783 dprint((4,"can't open/allocate fcc, cont'g\n"));
3786 * Find field entry associated with fcc, and start
3787 * composer on it...
3789 for(pf = header.local; pf && pf->name; pf = pf->next)
3790 if(pf->type == Fcc && HE(pf))
3791 HE(pf)->start_here = 1;
3793 continue;
3795 else
3796 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
3798 else
3799 lmc.so = NULL;
3801 /*---- Take care of any requested prefiltering ----*/
3802 if(sending_filter_requested
3803 && !filter_message_text(sending_filter_requested, outgoing,
3804 *body, &orig_so, &header)){
3805 q_status_message1(SM_ORDER, 3, 3,
3806 _("Problem filtering! Nothing sent%s."),
3807 fcc ? " or saved to fcc" : "");
3808 continue;
3811 /*------ Actually post -------*/
3812 if(outgoing->newsgroups){
3813 char **alt_nntp = NULL, *alt_nntp_p[2];
3814 if(((role && role->nntp)
3815 || suggested_nntp_server)){
3816 if(ps_global->FIX_NNTP_SERVER
3817 && ps_global->FIX_NNTP_SERVER[0])
3818 q_status_message(SM_ORDER | SM_DING, 5, 5,
3819 "Using nntp-server that is administratively fixed");
3820 else if(role && role->nntp)
3821 alt_nntp = role->nntp;
3822 else{
3823 alt_nntp_p[0] = suggested_nntp_server;
3824 alt_nntp_p[1] = NULL;
3825 alt_nntp = alt_nntp_p;
3828 if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
3829 dprint((1, "Post failed, continuing\n"));
3830 if(outgoing->message_id)
3831 fs_give((void **) &outgoing->message_id);
3833 outgoing->message_id = generate_message_id(role);
3835 continue;
3837 else
3838 result |= P_NEWS_WIN;
3842 * BUG: IF we've posted the message *and* an fcc was specified
3843 * then we've already got a neatly formatted message in the
3844 * lmc.so. It'd be nice not to have to re-encode everything
3845 * to insert it into the smtp slot...
3849 * Turn on "undisclosed recipients" header if no To or cc.
3851 if(!(outgoing->to || outgoing->cc)
3852 && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
3853 pf_nobody->writehdr = 1;
3854 pf_nobody->localcopy = 1;
3857 if(priority_requested){
3858 (void) set_priority_header(&header, priority_requested);
3859 fs_give((void **) &priority_requested);
3862 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3864 * If requested, launch background posting...
3866 if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
3867 ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
3868 memset(ps_global->post, 0, sizeof(POST_S));
3869 if(fcc)
3870 ps_global->post->fcc = cpystr(fcc);
3872 if((ps_global->post->pid = fork()) == 0){
3874 * Put us in new process group...
3876 setpgrp(0, ps_global->post->pid);
3878 /* BUG: should fix argv[0] to indicate what we're up to */
3881 * If there are any live streams, pretend we never
3882 * knew them. Problem is two processes writing
3883 * same server process.
3884 * This is not clean but we're just going to exit
3885 * right away anyway. We just want to be sure to leave
3886 * the stuff that the parent is going to use alone.
3887 * The next three lines will disable the re-use of the
3888 * existing streams and cause us to open a new one if
3889 * needed.
3891 ps_global->mail_stream = NULL;
3892 ps_global->s_pool.streams = NULL;
3893 ps_global->s_pool.nstream = 0;
3895 /* quell any display output */
3896 ps_global->in_init_seq = 1;
3898 /*------- Actually mail the message ------*/
3899 if(valid_addr == CA_OK
3900 && (outgoing->to || outgoing->cc
3901 || outgoing->bcc || lcc_addr)){
3902 char **alt_smtp = NULL;
3904 if(role && role->smtp){
3905 if(ps_global->FIX_SMTP_SERVER
3906 && ps_global->FIX_SMTP_SERVER[0])
3907 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3908 else
3909 alt_smtp = role->smtp;
3912 result |= (call_mailer(&header, *body, alt_smtp,
3913 call_mailer_flags,
3914 call_mailer_file_result,
3915 pipe_callback) > 0)
3916 ? P_MAIL_WIN : P_MAIL_LOSE;
3918 if(result & P_MAIL_LOSE)
3919 mark_address_failure_for_pico(&header);
3922 /*----- Was there an fcc involved? -----*/
3923 if(lmc.so){
3924 /*------ Write it if at least something worked ------*/
3925 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3926 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3927 && pine_rfc822_output(&header, *body,
3928 NULL, NULL))){
3929 char label[50];
3931 strncpy(label, "Fcc", sizeof(label));
3932 label[sizeof(label)-1] = '\0';
3933 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3934 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3935 label[sizeof(label)-1] = '\0';
3938 /*-- Now actually copy to fcc folder and close --*/
3939 result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
3940 NULL, label,
3941 F_ON(F_MARK_FCC_SEEN, ps_global)
3942 ? "\\SEEN" : NULL))
3943 ? P_FCC_WIN : P_FCC_LOSE;
3945 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3946 q_status_message(SM_ORDER, 3, 5,
3947 _("Fcc Failed!. No message saved."));
3948 dprint((1,
3949 "explicit fcc write failed!\n"));
3950 result |= P_FCC_LOSE;
3953 so_give(&lmc.so);
3956 if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
3958 * Encode child's result in hi-byte of
3959 * editor's result
3961 editor_result = ((result << 8) | COMP_GOTHUP);
3962 goto fake_hup;
3965 exit(result);
3968 if(ps_global->post->pid > 0){
3969 q_status_message(SM_ORDER, 3, 3,
3970 _("Message handed off for posting"));
3971 break; /* up to our child now */
3973 else{
3974 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3975 "Can't fork for send: %s",
3976 error_description(errno));
3977 if(ps_global->post->fcc)
3978 fs_give((void **) &ps_global->post->fcc);
3980 fs_give((void **) &ps_global->post);
3983 if(lmc.so) /* throw away unused store obj */
3984 so_give(&lmc.so);
3986 if(outgoing->message_id)
3987 fs_give((void **) &outgoing->message_id);
3989 outgoing->message_id = generate_message_id(role);
3991 continue; /* if we got here, there was a prob */
3993 #endif /* BACKGROUND_POST */
3995 /*------- Actually mail the message ------*/
3996 if(valid_addr == CA_OK
3997 && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
3998 char **alt_smtp = NULL;
4000 if(role && role->smtp){
4001 if(ps_global->FIX_SMTP_SERVER
4002 && ps_global->FIX_SMTP_SERVER[0])
4003 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
4004 else
4005 alt_smtp = role->smtp;
4008 result |= (call_mailer(&header, *body, alt_smtp,
4009 call_mailer_flags,
4010 call_mailer_file_result,
4011 pipe_callback) > 0)
4012 ? P_MAIL_WIN : P_MAIL_LOSE;
4014 if(result & P_MAIL_LOSE)
4015 mark_address_failure_for_pico(&header);
4018 /*----- Was there an fcc involved? -----*/
4019 if(lmc.so){
4020 /*------ Write it if at least something worked ------*/
4021 if((result & (P_MAIL_WIN | P_NEWS_WIN))
4022 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
4023 && pine_rfc822_output(&header, *body, NULL, NULL))){
4024 char label[50];
4026 strncpy(label, "Fcc", sizeof(label));
4027 label[sizeof(label)-1] = '\0';
4028 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
4029 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
4030 label[sizeof(label)-1] = '\0';
4033 /*-- Now actually copy to fcc folder and close --*/
4034 result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
4035 F_ON(F_MARK_FCC_SEEN, ps_global)
4036 ? "\\SEEN" : NULL))
4037 ? P_FCC_WIN : P_FCC_LOSE;
4039 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
4040 q_status_message(SM_ORDER,3,5,
4041 _("Fcc Failed!. No message saved."));
4042 dprint((1, "explicit fcc write failed!\n"));
4043 result |= P_FCC_LOSE;
4046 so_give(&lmc.so);
4049 /*----- Mail Post FAILED, back to composer -----*/
4050 if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
4051 dprint((1, "Send failed, continuing\n"));
4053 if(result & P_FCC_LOSE){
4055 * Find field entry associated with fcc, and start
4056 * composer on it...
4058 for(pf = header.local; pf && pf->name; pf = pf->next)
4059 if(pf->type == Fcc && HE(pf))
4060 HE(pf)->start_here = 1;
4062 q_status_message(SM_ORDER | SM_DING, 3, 3,
4063 pine_send_status(result, fcc,
4064 tmp_20k_buf, SIZEOF_20KBUF, NULL));
4067 if(outgoing->message_id)
4068 fs_give((void **) &outgoing->message_id);
4070 outgoing->message_id = generate_message_id(role);
4072 continue;
4076 * If message sent *completely* successfully, there's a
4077 * reply struct AND we're allowed to write back state, do it.
4078 * But also protect against shifted message numbers due
4079 * to new mail arrival. Since the number passed is based
4080 * on the real imap msg no, AND we're sure no expunge has
4081 * been done, just fix up the sorted number...
4083 update_answered_flags(reply);
4085 /*----- Signed, sealed, delivered! ------*/
4086 q_status_message(SM_ORDER, 0, 3,
4087 pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
4089 break; /* All's well, pop out of here */
4093 if(orig_so)
4094 so_give(&orig_so);
4096 if(fcc)
4097 fs_give((void **)&fcc);
4099 free_body_particulars(bp);
4101 free_attachment_list(&pbf->attachments);
4103 standard_picobuf_teardown(pbf);
4105 for(i=0; i < fixed_cnt; i++){
4106 if(pfields[i].textbuf)
4107 fs_give((void **)&pfields[i].textbuf);
4109 fs_give((void **)&pfields[i].name);
4112 if(lcc_addr)
4113 mail_free_address(&lcc_addr);
4115 if(nobody_addr)
4116 mail_free_address(&nobody_addr);
4118 free_prompts(header.custom);
4119 free_customs(header.custom);
4120 fs_give((void **)&pfields);
4121 free_headents(&headents);
4122 fs_give((void **)&sending_order);
4123 if(suggested_nntp_server)
4124 fs_give((void **)&suggested_nntp_server);
4125 if(title)
4126 fs_give((void **)&title);
4128 if(local_redraft_pos && local_redraft_pos != redraft_pos)
4129 free_redraft_pos(&local_redraft_pos);
4131 pbf = save_previous_pbuf;
4132 g_rolenick = NULL;
4134 dprint((4, "=== send returning ===\n"));
4139 * Check for subject in outgoing message.
4141 * Asks user whether to proceed with no subject.
4144 check_for_subject(METAENV *header)
4146 PINEFIELD *pf;
4147 int rv = CF_OK;
4149 for(pf = header->local; pf && pf->name; pf = pf->next)
4150 if(pf->type == Subject){
4151 if(pf->text && *pf->text && **pf->text)
4152 rv = CF_OK;
4153 else{
4154 if(want_to("No Subject, send anyway ",
4155 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
4156 rv = CF_OK;
4157 else
4158 rv = CF_MISSING;
4161 break;
4165 return(rv);
4170 * Check for fcc in outgoing message.
4172 * Asks user whether to proceed with no fcc.
4175 check_for_fcc(char *fcc)
4177 int rv = CF_OK;
4179 if(fcc && *fcc)
4180 rv = CF_OK;
4181 else{
4182 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
4183 rv = CF_OK;
4184 else
4185 rv = CF_MISSING;
4188 return(rv);
4193 * Confirm that the user wants to send to MAILER-DAEMON
4196 confirm_daemon_send(void)
4198 return(want_to("Really send this message to the MAILER-DAEMON",
4199 'n', 'n', NO_HELP, WT_NORM) == 'y');
4203 void
4204 free_prompts(PINEFIELD *head)
4206 PINEFIELD *pf;
4208 for(pf = head; pf && pf->name; pf = pf->next){
4209 if(HE(pf) && HE(pf)->prompt)
4210 fs_give((void **)& HE(pf)->prompt);
4216 postpone_prompt(void)
4218 static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
4219 {'f', 'f', "F", N_("Form Letter Folder")},
4220 {-1, 0, NULL, NULL} };
4222 return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
4223 pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
4228 * call__mailer_file_result - some results from call_mailer might be in a file.
4229 * dislplay that file.
4231 void
4232 call_mailer_file_result(char *filename, int style)
4234 if(filename){
4235 if(style & CM_BR_VERBOSE){
4236 display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
4238 else{
4239 display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
4244 void
4245 mark_address_failure_for_pico(METAENV *header)
4247 PINEFIELD *pf;
4248 ADDRESS *a;
4249 int error_count = 0;
4250 struct headerentry *last_he = NULL;
4252 for(pf = header->local; pf && pf->name; pf = pf->next)
4253 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
4254 for(a = *pf->addr; a != NULL; a = a->next)
4255 if(a->error != NULL
4256 && error_count++ < MAX_ADDR_ERROR
4257 && (HE(pf))){
4258 if(last_he) /* start last reported err */
4259 last_he->start_here = 0;
4261 (last_he = HE(pf))->start_here = 1;
4268 * This is specialized routine. It assumes that the only things that we
4269 * care about restoring are the body type, subtype, encoding and the
4270 * state of the charset parameter. It also assumes that if the charset
4271 * parameter exists when we save it, it won't be removed later.
4273 BODY_PARTICULARS_S *
4274 save_body_particulars(struct mail_bodystruct *body)
4276 BODY_PARTICULARS_S *bp;
4277 PARAMETER *pm;
4279 bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
4281 bp->type = body->type;
4282 bp->encoding = body->encoding;
4283 bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
4284 bp->parameter = body->parameter;
4285 for(pm = bp->parameter;
4286 pm && strucmp(pm->attribute, "charset") != 0;
4287 pm = pm->next)
4288 ;/* searching for possible charset parameter */
4290 if(pm){ /* found one */
4291 bp->had_csp = 1; /* saved body had charset parameter */
4292 bp->charset = pm->value ? cpystr(pm->value) : NULL;
4294 else{
4295 bp->had_csp = 0;
4296 bp->charset = NULL;
4299 return(bp);
4303 void
4304 reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
4306 body->type = bp->type;
4307 body->encoding = bp->encoding;
4308 if(body->subtype)
4309 fs_give((void **)&body->subtype);
4311 body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
4313 if(bp->parameter){
4314 PARAMETER *pm, *pm_prev = NULL;
4316 for(pm = body->parameter;
4317 pm && strucmp(pm->attribute, "charset") != 0;
4318 pm = pm->next)
4319 pm_prev = pm;
4321 if(pm){ /* body has charset parameter */
4322 if(bp->had_csp){ /* reset to what it used to be */
4323 if(pm->value)
4324 fs_give((void **)&pm->value);
4326 pm->value = bp->charset ? cpystr(bp->charset) : NULL;
4328 else{ /* remove charset parameter */
4329 if(pm_prev)
4330 pm_prev->next = pm->next;
4331 else
4332 body->parameter = pm->next;
4334 mail_free_body_parameter(&pm);
4337 else{
4338 if(bp->had_csp){
4340 * This can't happen because grope never removes
4341 * the charset parameter.
4343 q_status_message(SM_ORDER | SM_DING, 5, 5,
4344 "Programmer error: saved charset but no current charset param in pine_send");
4347 else{
4348 ok, still no parameter
4353 else{
4354 if(body->parameter)
4355 mail_free_body_parameter(&body->parameter);
4357 body->parameter = NULL;
4362 void
4363 free_body_particulars(BODY_PARTICULARS_S *bp)
4365 if(bp){
4366 if(bp->subtype)
4367 fs_give((void **)&bp->subtype);
4369 if(bp->charset)
4370 fs_give((void **)&bp->charset);
4372 fs_give((void **)&bp);
4377 /*----------------------------------------------------------------------
4378 Build a status message suitable for framing
4380 Returns: pointer to resulting buffer
4381 ---*/
4382 char *
4383 pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
4385 int avail = ps_global->ttyo->screen_cols - 2;
4386 int fixedneed, need, lenfcc;
4387 char *part1, *part2, *part3, *part4, *part5;
4388 char fbuf[MAILTMPLEN+1];
4390 part1 = (result & P_NEWS_WIN)
4391 ? "posted"
4392 : (result & P_NEWS_LOSE)
4393 ? "NOT POSTED"
4394 : "";
4395 part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
4396 && (result & P_FCC_BITS))
4397 ? ", "
4398 : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
4399 ? " and "
4400 : "";
4401 part3 = (result & P_MAIL_WIN)
4402 ? "sent"
4403 : (result & P_MAIL_LOSE)
4404 ? "NOT SENT"
4405 : "";
4406 part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
4407 ? " and "
4408 : "";
4409 part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
4410 ? "ONLY copied to "
4411 : (result & P_FCC_WIN)
4412 ? "copied to "
4413 : (result & P_FCC_LOSE)
4414 ? "NOT copied to "
4415 : "";
4416 lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
4418 fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
4419 strlen(part4) + strlen(part5);
4420 need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
4422 if(need > avail && fixedneed + 3 >= avail){
4423 /* dots on end of fixed, no fcc */
4424 snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
4425 part1, part2, part3, part4, part5);
4426 short_str(fbuf, buf, buflen, avail, EndDots);
4428 else if(need > avail){
4429 /* include whole fixed part, quotes and dots at end of fcc name */
4430 if(lenfcc > 0)
4431 lenfcc = MAX(1, lenfcc-(need-avail));
4433 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4434 part1, part2, part3, part4, part5,
4435 (result & P_FCC_BITS) ? "\"" : "",
4436 short_str((result & P_FCC_BITS) ? fcc_name : "",
4437 fbuf, sizeof(fbuf), lenfcc, FrontDots),
4438 (result & P_FCC_BITS) ? "\"" : "");
4440 else{
4441 /* whole thing */
4442 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4443 part1, part2, part3, part4, part5,
4444 (result & P_FCC_BITS) ? "\"" : "",
4445 (result & P_FCC_BITS) ? fcc_name : "",
4446 (result & P_FCC_BITS) ? "\"" : "");
4449 if(goodorbad)
4450 *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
4452 return(buf);
4455 /* Callback from Pico to set the conditions for Alpine to start a new thread
4458 void
4459 new_thread_on_blank_subject(void)
4461 ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
4466 /*----------------------------------------------------------------------
4467 Call back for pico to insert the specified message's text
4469 Args: n -- message number to format
4470 f -- function to use to output the formatted message
4473 Returns: returns msg number formatted on success, zero on error.
4474 ----*/
4475 long
4476 message_format_for_pico(long int n, int (*f) (int))
4478 ENVELOPE *e;
4479 BODY *b;
4480 char *old_quote = NULL;
4481 long rv = n;
4483 if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
4484 && (e = pine_mail_fetchstructure(ps_global->mail_stream,
4485 mn_m2raw(ps_global->msgmap, n), &b)))){
4486 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4487 flush_status_messages(0);
4488 return(0L);
4491 /* temporarily assign a new quote string */
4492 old_quote = pbf->quote_str;
4493 pbf->quote_str = reply_quote_str(e);
4495 /* build separator line */
4496 reply_delimiter(e, NULL, f);
4498 /* actually write message text */
4499 if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
4500 FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
4501 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4502 flush_status_messages(0);
4503 rv = 0L;
4506 fs_give((void **)&pbf->quote_str);
4507 pbf->quote_str = old_quote;
4508 return(rv);
4512 /*----------------------------------------------------------------------
4513 Call back for pico to prompt the user for exit confirmation
4515 Args: dflt -- default answer for confirmation prompt
4517 Returns: either NULL if the user accepts exit, or string containing
4518 reason why the user declined.
4519 ----*/
4521 send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
4522 char **result)
4524 int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
4525 int dsn_label = 0, fcc_label = 0, lparen;
4526 int flowing_label = 0, double_rad;
4527 char *rstr = NULL, *p, *lc, *optp;
4528 char dsn_string[30];
4529 void (*redraw)(void) = ps_global->redrawer;
4530 ESCKEY_S opts[18];
4531 struct filters {
4532 char *filter;
4533 int index;
4534 struct filters *prev, *next;
4535 } *filters = NULL, *fp;
4537 sending_filter_requested = NULL;
4538 call_mailer_flags = 0;
4539 background_requested = 0;
4540 flowing_requested = allow_flowed ? 1 : 0;
4541 lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
4542 if(priority_requested)
4543 fs_give((void **) &priority_requested);
4545 if(background_posting(FALSE)){
4546 if(result)
4547 *result = "Can't send while background posting. Use postpone.";
4549 return(1);
4552 if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
4553 if(result)
4554 *result = NULL;
4556 return(0);
4559 ps_global->redrawer = redraw_pico;
4561 if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
4562 (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
4565 * Build list of available filters...
4567 for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
4568 for(p = ps_global->VAR_SEND_FILTER[i];
4569 *p && !isspace((unsigned char)*p); p++)
4572 c = *p;
4573 *p = '\0';
4574 if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
4575 && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
4576 *p = c;
4577 continue;
4580 fp = (struct filters *)fs_get(sizeof(struct filters));
4581 fp->index = i;
4582 if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
4583 fp->filter = cpystr(lc);
4585 else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
4586 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
4587 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4588 fp->filter = cpystr(tmp_20k_buf);
4590 else
4591 fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
4593 *p = c;
4595 if(filters){
4596 fp->next = filters;
4597 fp->prev = filters->prev;
4598 fp->prev->next = filters->prev = fp;
4600 else{
4601 filters = (struct filters *)fs_get(sizeof(struct filters));
4602 filters->index = -1;
4603 filters->filter = NULL;
4604 filters->next = filters->prev = fp;
4605 fp->next = fp->prev = filters;
4609 i = 0;
4610 opts[i].ch = 'y';
4611 opts[i].rval = 'y';
4612 opts[i].name = "Y";
4613 opts[i++].label = N_("Yes");
4615 opts[i].ch = 'n';
4616 opts[i].rval = 'n';
4617 opts[i].name = "N";
4618 opts[i++].label = N_("No");
4620 if(filters){
4621 /* set global_filter_pointer to desired filter or NULL if none */
4622 /* prepare two keymenu slots for selecting filter */
4623 opts[i].ch = ctrl('P');
4624 opts[i].rval = 10;
4625 opts[i].name = "^P";
4626 opts[i++].label = N_("Prev Filter");
4628 opts[i].ch = ctrl('N');
4629 opts[i].rval = 11;
4630 opts[i].name = "^N";
4631 opts[i++].label = N_("Next Filter");
4633 if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
4634 filters = filters->next;
4637 if(F_ON(F_VERBOSE_POST, ps_global)){
4638 /* setup keymenu slot to toggle verbose mode */
4639 opts[i].ch = ctrl('W');
4640 opts[i].rval = 12;
4641 opts[i].name = "^W";
4642 verbose_label = i++;
4645 if(allow_flowed){
4646 /* setup keymenu slot to toggle flowed mode */
4647 opts[i].ch = ctrl('V');
4648 opts[i].rval = 22;
4649 opts[i].name = "^V";
4650 flowing_label = i++;
4651 flowing_requested = 1;
4654 if(F_ON(F_NO_FCC_ATTACH, ps_global)){
4655 /* setup keymenu slot to toggle attacment on fcc */
4656 opts[i].ch = ctrl('F');
4657 opts[i].rval = 21;
4658 opts[i].name = "^F";
4659 fcc_label = i++;
4662 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4663 if(F_ON(F_BACKGROUND_POST, ps_global)){
4664 opts[i].ch = ctrl('R');
4665 opts[i].rval = 15;
4666 opts[i].name = "^R";
4667 bg_label = i++;
4669 #endif
4671 opts[i].ch = 'p';
4672 opts[i].rval = 'p';
4673 opts[i].name = "P";
4674 opts[i++].label = N_("Priority");
4676 #ifdef SMIME
4677 if(F_OFF(F_DONT_DO_SMIME, ps_global)){
4678 opts[i].ch = 'e';
4679 opts[i].rval = 'e';
4680 opts[i].name = "E";
4681 opts[i++].label = "Encrypt";
4683 opts[i].ch = 'g';
4684 opts[i].rval = 'g';
4685 opts[i].name = "G";
4686 opts[i++].label = "Sign";
4688 if(ps_global->smime){
4689 ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
4690 ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
4693 #endif
4695 double_rad = i;
4697 if(F_ON(F_DSN, ps_global)){
4698 /* setup keymenu slots to toggle dsn bits */
4699 opts[i].ch = 'd';
4700 opts[i].rval = 'd';
4701 opts[i].name = "D";
4702 opts[i].label = N_("DSNOpts");
4703 dsn_label = i++;
4704 opts[i].ch = -2;
4705 opts[i].rval = 's';
4706 opts[i].name = "S";
4707 opts[i++].label = "";
4708 opts[i].ch = -2;
4709 opts[i].rval = 'x';
4710 opts[i].name = "X";
4711 opts[i++].label = "";
4712 opts[i].ch = -2;
4713 opts[i].rval = 'h';
4714 opts[i].name = "H";
4715 opts[i++].label = "";
4718 if(filters){
4719 opts[i].ch = KEY_UP;
4720 opts[i].rval = 10;
4721 opts[i].name = "";
4722 opts[i++].label = "";
4724 opts[i].ch = KEY_DOWN;
4725 opts[i].rval = 11;
4726 opts[i].name = "";
4727 opts[i++].label = "";
4730 opts[i].ch = -1;
4732 fix_windsize(ps_global);
4734 while(1){
4735 if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
4736 *p = '\0';
4737 else
4738 p = NULL;
4740 lparen = 0;
4741 strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
4742 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4743 optp = tmp_20k_buf + strlen(tmp_20k_buf);
4745 if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
4746 sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4748 if(allow_flowed && !flowing_requested){
4749 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4750 if(!lparen){
4751 *optp++ = ' ';
4752 *optp++ = '(';
4753 lparen++;
4755 else
4756 *optp++ = ' ';
4759 sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4762 if(filters){
4763 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4764 if(!lparen){
4765 *optp++ = ' ';
4766 *optp++ = '(';
4767 lparen++;
4769 else{
4770 *optp++ = ',';
4771 *optp++ = ' ';
4775 if(filters->filter){
4776 sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4777 sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4778 if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
4779 *optp++ = '\"';
4781 else
4782 sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4785 if((call_mailer_flags & CM_VERBOSE) || background_requested){
4786 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4787 if(!lparen){
4788 *optp++ = ' ';
4789 *optp++ = '(';
4790 lparen++;
4792 else
4793 *optp++ = ' ';
4796 sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4797 if(call_mailer_flags & CM_VERBOSE)
4798 sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4800 if(background_requested)
4801 sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4803 sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4806 if(g_rolenick && !(he && he[N_FROM].dirty)){
4807 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4808 if(!lparen){
4809 *optp++ = ' ';
4810 *optp++ = '(';
4811 lparen++;
4813 else
4814 *optp++ = ' ';
4817 sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4818 sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4819 sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4822 if(call_mailer_flags & CM_DSN_SHOW){
4823 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4824 if(!lparen){
4825 *optp++ = ' ';
4826 *optp++ = '(';
4827 lparen++;
4829 else{
4830 *optp++ = ',';
4831 *optp++ = ' ';
4835 sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4838 #ifdef SMIME
4839 if(ps_global->smime && ps_global->smime->do_encrypt){
4840 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4841 if(!lparen){
4842 *optp++ = ' ';
4843 *optp++ = '(';
4844 lparen++;
4846 else{
4847 *optp++ = ',';
4848 *optp++ = ' ';
4852 sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4855 if(ps_global->smime && ps_global->smime->do_sign){
4856 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4857 if(!lparen){
4858 *optp++ = ' ';
4859 *optp++ = '(';
4860 lparen++;
4862 else{
4863 *optp++ = ',';
4864 *optp++ = ' ';
4868 sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4870 #endif
4872 if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
4873 *optp++ = ')';
4875 sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4876 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4878 if(p)
4879 *p = ' ';
4881 if(flowing_label)
4882 opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
4884 if(verbose_label)
4885 opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
4887 if(bg_label)
4888 opts[bg_label].label = background_requested
4889 ? N_("Foreground") : N_("Background");
4891 if(fcc_label)
4892 opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
4893 : N_("No Fcc Atmts ");
4895 if(F_ON(F_DSN, ps_global)){
4896 if(call_mailer_flags & CM_DSN_SHOW){
4897 opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
4898 ? N_("NoDelay") : N_("Delay");
4899 opts[dsn_label+1].ch = 's';
4900 opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
4901 ? N_("NoSuccess") : N_("Success");
4902 opts[dsn_label+2].ch = 'x';
4903 opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
4904 ? N_("ErrRets") : N_("NoErrRets");
4905 opts[dsn_label+3].ch = 'h';
4906 opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
4907 ? N_("RetHdrs") : N_("RetFull");
4911 if(double_rad +
4912 ((call_mailer_flags & CM_DSN_SHOW)
4913 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
4914 rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4915 'y', 'z',
4916 (F_ON(F_DSN, ps_global) && allow_flowed)
4917 ? h_send_prompt_dsn_flowed :
4918 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4919 allow_flowed ? h_send_prompt_flowed :
4920 h_send_prompt,
4921 RB_NORM);
4922 else
4923 rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4924 'y', 'z',
4925 (double_rad +
4926 ((call_mailer_flags & CM_DSN_SHOW)
4927 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
4928 ? NO_HELP :
4929 (F_ON(F_DSN, ps_global) && allow_flowed)
4930 ? h_send_prompt_dsn_flowed :
4931 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4932 allow_flowed ? h_send_prompt_flowed :
4933 h_send_prompt,
4934 RB_NORM);
4936 if(rv == 'y'){ /* user ACCEPTS! */
4937 break;
4939 else if(rv == 'n'){ /* Declined! */
4940 rstr = _("No Message Sent");
4941 break;
4943 else if(rv == 'z'){ /* Cancelled! */
4944 rstr = _("Send Cancelled");
4945 break;
4947 else if(rv == 10){ /* PREVIOUS filter */
4948 filters = filters->prev;
4950 else if(rv == 11){ /* NEXT filter */
4951 filters = filters->next;
4953 else if(rv == 21){
4954 lmc.text_only = !lmc.text_only;
4956 else if(rv == 12){ /* flip verbose bit */
4957 if(call_mailer_flags & CM_VERBOSE)
4958 call_mailer_flags &= ~CM_VERBOSE;
4959 else
4960 call_mailer_flags |= CM_VERBOSE;
4962 if((call_mailer_flags & CM_VERBOSE) && background_requested)
4963 background_requested = 0;
4965 else if(rv == 22){ /* flip flowing bit */
4966 flowing_requested = !flowing_requested;
4968 else if(rv == 15){
4969 if((background_requested = !background_requested)
4970 && (call_mailer_flags & CM_VERBOSE))
4971 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
4973 else if(call_mailer_flags & CM_DSN_SHOW){
4974 if(rv == 's'){ /* flip success bit */
4975 call_mailer_flags ^= CM_DSN_SUCCESS;
4976 /* turn off related bits */
4977 if(call_mailer_flags & CM_DSN_SUCCESS)
4978 call_mailer_flags &= ~(CM_DSN_NEVER);
4980 else if(rv == 'd'){ /* flip delay bit */
4981 call_mailer_flags ^= CM_DSN_DELAY;
4982 /* turn off related bits */
4983 if(call_mailer_flags & CM_DSN_DELAY)
4984 call_mailer_flags &= ~(CM_DSN_NEVER);
4986 else if(rv == 'x'){ /* flip never bit */
4987 call_mailer_flags ^= CM_DSN_NEVER;
4988 /* turn off related bits */
4989 if(call_mailer_flags & CM_DSN_NEVER)
4990 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
4992 else if(rv == 'h'){ /* flip full bit */
4993 call_mailer_flags ^= CM_DSN_FULL;
4996 else if(rv == 'd'){ /* show dsn options */
4998 * When you turn on DSN, the default is to notify on
4999 * failure, success, or delay; and to return the whole
5000 * body on failure.
5002 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
5004 else if(rv == 'p'){ /* choose X-Priority */
5005 char *prio;
5007 prio = choose_a_priority(priority_requested);
5008 if((ps_global->redrawer = redraw_pico) != NULL){
5009 (*ps_global->redrawer)();
5010 fix_windsize(ps_global);
5013 if(prio){
5014 if(priority_requested)
5015 fs_give((void **) &priority_requested);
5017 if(prio[0])
5018 priority_requested = prio;
5019 else
5020 fs_give((void **) &prio);
5023 #ifdef SMIME
5024 else if(rv=='e'){
5025 if(ps_global->smime)
5026 ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
5028 else if(rv=='g'){
5029 if(ps_global->smime)
5030 ps_global->smime->do_sign = !ps_global->smime->do_sign;
5032 #endif
5034 snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
5035 (call_mailer_flags & CM_DSN_NEVER)
5036 ? "Never" : "F",
5037 (call_mailer_flags & CM_DSN_DELAY)
5038 ? "D" : "",
5039 (call_mailer_flags & CM_DSN_SUCCESS)
5040 ? "S" : "",
5041 (call_mailer_flags & CM_DSN_NEVER)
5042 ? ""
5043 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
5044 : "-Hdrs");
5045 dsn_string[sizeof(dsn_string)-1] = '\0';
5048 /* remember selection */
5049 if(filters && filters->index > -1)
5050 sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
5052 if(filters){
5053 filters->prev->next = NULL; /* tie off list */
5054 while(filters){ /* then free it */
5055 fp = filters->next;
5056 if(filters->filter)
5057 fs_give((void **)&filters->filter);
5059 fs_give((void **)&filters);
5060 filters = fp;
5064 if(old_suspend)
5065 (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
5067 if(result)
5068 *result = rstr;
5070 ps_global->redrawer = redraw;
5072 return((rstr == NULL) ? 0 : 1);
5077 * Allow user to choose a priority for sending.
5079 * Returns an allocated priority on success, NULL otherwise.
5081 char *
5082 choose_a_priority(char *default_val)
5084 char *choice = NULL;
5085 char **priority_list, **lp;
5086 char *starting_val = NULL;
5087 char *none;
5088 int i, cnt;
5089 PRIORITY_S *p;
5091 for(cnt = 0, p = priorities; p && p->desc; p++)
5092 cnt++;
5094 cnt++; /* for NONE entry */
5095 lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
5096 memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
5098 for(i = 0, p = priorities; p && p->desc; p++){
5099 *lp = cpystr(p->desc);
5100 if(default_val && !strcmp(default_val, p->desc))
5101 starting_val = (*lp);
5103 lp++;
5106 none = _("NONE - No X-Priority header included");
5107 *lp = cpystr(none);
5108 if(!starting_val)
5109 starting_val = (*lp);
5111 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5112 TRANSLATORS: Print something1 using something2.
5113 "priorities" is something1 */
5114 choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
5115 _("priorities"), h_select_priority_screen,
5116 _("HELP FOR SELECTING A PRIORITY"),
5117 starting_val);
5119 if(!choice)
5120 q_status_message(SM_ORDER, 1, 4, _("No change"));
5121 else if(!strcmp(choice, none))
5122 choice[0] = '\0';
5124 free_list_array(&priority_list);
5126 return(choice);
5131 dont_flow_this_time(void)
5133 return(flowing_requested ? 0 : 1);
5137 /*----------------------------------------------------------------------
5138 Call back for pico to display mime type of attachment
5140 Args: file -- filename being attached
5142 Returns: returns 1 on success (message queued), zero otherwise (don't know
5143 type so nothing queued).
5144 ----*/
5146 mime_type_for_pico(char *file)
5148 BODY *body;
5149 int rv;
5150 void *file_contents;
5152 body = mail_newbody();
5153 body->type = TYPEOTHER;
5154 body->encoding = ENCOTHER;
5156 /* don't know where the cursor's been, reset it */
5157 clear_cursor_pos();
5158 if(!set_mime_type_by_extension(body, file)){
5159 if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5160 body->contents.text.data = file_contents;
5161 set_mime_type_by_grope(body);
5165 if(body->type != TYPEOTHER){
5166 rv = 1;
5167 q_status_message3(SM_ORDER, 0, 3,
5168 _("File %s attached as type %s/%s"), file,
5169 body_types[body->type],
5170 body->subtype ? body->subtype : rfc822_default_subtype(body->type));
5172 else
5173 rv = 0;
5175 pine_free_body(&body);
5176 return(rv);
5180 /*----------------------------------------------------------------------
5181 Call back for pico to receive an uploaded message
5183 Args: fname -- name for uploaded file (empty if they want us to assign it)
5184 size -- pointer to long to hold the attachment's size
5186 Notes: the attachment is uploaded to a temp file, and
5188 Returns: TRUE on success, FALSE otherwise
5189 ----*/
5191 upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
5193 char cmd[MAXPATH+1], *fnp = NULL;
5194 char *locale_name = NULL;
5195 long l;
5196 PIPE_S *syspipe;
5198 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5199 fname ? fname : "<NO FILE>"));
5201 if(!fname) /* no place for file name */
5202 return(0);
5204 if(!*fname){ /* caller wants temp file */
5205 if((fnp = temp_nam(NULL, "pu")) != NULL){
5206 strncpy(fname, fnp, fnlen);
5207 fname[fnlen-1] = '\0';
5208 our_unlink(fnp);
5209 fs_give((void **)&fnp);
5212 else
5213 locale_name = convert_to_locale(fname);
5215 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
5216 ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
5217 if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
5218 0, pipe_callback, pipe_report_error)) != NULL){
5219 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
5220 if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
5221 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5222 "Error determining size of %s: %s", fname,
5223 fnp = error_description(errno));
5224 dprint((1,
5225 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5226 cmd ? cmd : "?",
5227 fname ? fname : "?",
5228 fnp ? fnp : "?"));
5230 else if(size)
5231 *size = l;
5233 if(locale_name)
5234 fs_give((void **) &locale_name);
5236 return(l >= 0);
5238 else
5239 q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
5241 if(locale_name)
5242 fs_give((void **) &locale_name);
5244 return(0);
5248 char *
5249 cancel_for_pico(void (*redraw_pico)(void))
5251 int rv;
5252 char *rstr = NULL;
5253 char *prompt =
5254 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5255 void (*redraw)(void) = ps_global->redrawer;
5256 static ESCKEY_S opts[] = {
5257 {'c', 'c', "C", N_("Confirm")},
5258 {'n', 'n', "N", N_("No")},
5259 {'y', 'y', "", ""},
5260 {-1, 0, NULL, NULL}
5263 ps_global->redrawer = redraw_pico;
5264 fix_windsize(ps_global);
5266 while(1){
5267 rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
5268 'n', 'x', h_confirm_cancel, RB_NORM);
5269 if(rv == 'c'){ /* user ACCEPTS! */
5270 rstr = "";
5271 break;
5273 else if(rv == 'y'){
5274 q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
5275 display_message('x');
5277 else
5278 break;
5281 ps_global->redrawer = redraw;
5282 return(rstr);
5286 /*----------------------------------------------------------------------
5287 Pass the first text segment of the message thru the "send filter"
5289 Args: body pointer and address for storage object of old data
5291 Returns: returns 1 on success, zero on error.
5292 ----*/
5294 filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
5295 STORE_S **old, METAENV *header)
5297 char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
5298 int key = 0, include_hdrs = 0;
5299 gf_io_t gc, pc;
5300 STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
5301 ? &body->nested.part->body.contents.text.data
5302 : &body->contents.text.data),
5303 *tmp_so = NULL, *tmpf_so,
5304 *save_local_so, *readthis_so = NULL, *our_tmpf_so = NULL;
5305 #define DO_HEADERS 1
5307 if(fcmd
5308 && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
5309 &key, &include_hdrs, NULL))){
5310 if(tmpf){
5312 * We need WRITE_TO_LOCALE here because the user is going to
5313 * be operating on tmpf. We need to change it back after they
5314 * are done.
5316 if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
5317 if(key){
5318 so_puts(tmpf_so, filter_session_key());
5319 so_puts(tmpf_so, NEWLINE);
5323 * If the headers are wanted for filtering, we can just
5324 * stick them in the tmpf file that is already there before
5325 * putting the body in.
5327 if(include_hdrs){
5328 save_local_so = lmc.so;
5329 lmc.so = tmpf_so; /* write it to tmpf_so */
5330 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5331 pine_rfc822_header(header, body, NULL, NULL);
5332 lmc.so = save_local_so;
5335 so_seek(*so, 0L, 0);
5336 gf_set_so_readc(&gc, *so);
5337 gf_set_so_writec(&pc, tmpf_so);
5338 gf_filter_init();
5339 errstr = gf_pipe(gc, pc);
5340 gf_clear_so_readc(*so);
5341 gf_clear_so_writec(tmpf_so);
5342 so_give(&tmpf_so);
5344 else
5345 errstr = "Can't create space for filter temporary file.";
5347 else if(include_hdrs){
5349 * Gf_filter wants a single storage object to read from.
5350 * If headers are wanted for filtering we'll have to put them
5351 * and the body into a temp file first and then use that
5352 * as the storage object for gf_filter.
5353 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5354 * takes care of that.
5356 if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
5357 /* put headers in our_tmpf_so */
5358 save_local_so = lmc.so;
5359 lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
5360 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5361 pine_rfc822_header(header, body, NULL, NULL);
5362 lmc.so = save_local_so;
5364 /* put body in our_tmpf_so */
5365 so_seek(*so, 0L, 0);
5366 gf_set_so_readc(&gc, *so);
5367 gf_set_so_writec(&pc, our_tmpf_so);
5368 gf_filter_init();
5369 errstr = gf_pipe(gc, pc);
5370 gf_clear_so_readc(*so);
5371 gf_clear_so_writec(our_tmpf_so);
5373 /* tell gf_filter to read from our_tmpf_so instead of *so */
5374 readthis_so = our_tmpf_so;
5376 else
5377 errstr = "Can't create space for temporary file.";
5379 else
5380 readthis_so = *so;
5382 if(!errstr){
5383 if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
5384 gf_set_so_writec(&pc, tmp_so);
5385 ps_global->mangled_screen = 1;
5386 suspend_busy_cue();
5387 ClearScreen();
5388 fflush(stdout);
5389 if(tmpf){
5390 PIPE_S *fpipe;
5392 if((fpipe = open_system_pipe(cmd, NULL, NULL,
5393 PIPE_NOSHELL | PIPE_RESET,
5394 0, pipe_callback, pipe_report_error)) != NULL){
5395 if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
5397 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5398 if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5399 gf_set_so_readc(&gc, tmpf_so);
5400 gf_filter_init();
5401 errstr = gf_pipe(gc, pc);
5402 gf_clear_so_readc(tmpf_so);
5403 so_give(&tmpf_so);
5405 else
5406 errstr = "Can't open temp file filter wrote.";
5408 else
5409 errstr = "Filter command returned error.";
5411 else
5412 errstr = "Can't exec filter text.";
5414 else
5415 errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
5416 readthis_so, pc, NULL, 0, 0,
5417 pipe_callback);
5419 if(our_tmpf_so)
5420 so_give(&our_tmpf_so);
5422 gf_clear_so_writec(tmp_so);
5424 #ifdef _WINDOWS
5425 if(!errstr){
5427 * Can't really be using stdout, so don't print message that
5428 * gets printed in the else. Ideally the program being called
5429 * will wait after showing the message, we might want to look
5430 * into doing the waiting in console based apps... or not.
5432 #else
5433 if(errstr){
5434 unsigned long ch;
5436 fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
5437 fflush(stdout);
5438 while((ch = read_char(300)) != ctrl('M')
5439 && ch != NO_OP_IDLE)
5440 putchar(BELL);
5442 else{
5443 #endif /* _WINDOWS */
5444 BODY *b = (body->type == TYPEMULTIPART)
5445 ? &body->nested.part->body : body;
5447 *old = *so; /* save old so */
5448 *so = tmp_so; /* return new one */
5449 (*so)->attr = copy_parameters((*old)->attr);
5452 * If the command said it would return new MIME
5453 * mime type data, check it out...
5455 if(mtf){
5456 char buf[MAILTMPLEN], *s;
5457 FILE *fp;
5459 if((fp = our_fopen(mtf, "rb")) != NULL){
5460 if(fgets(buf, sizeof(buf), fp)
5461 && !struncmp(buf, "content-", 8)
5462 && (s = strchr(buf+8, ':'))){
5463 BODY *nb = mail_newbody();
5465 for(*s++ = '\0'; *s == ' '; s++)
5468 rfc822_parse_content_header(nb,
5469 (char *) ucase((unsigned char *) buf+8),s);
5470 if(nb->type == TYPETEXT
5471 && nb->subtype
5472 && (!b->subtype
5473 || strucmp(b->subtype, nb->subtype))){
5474 if(b->subtype)
5475 fs_give((void **) &b->subtype);
5477 b->subtype = nb->subtype;
5478 nb->subtype = NULL;
5480 mail_free_body_parameter(&b->parameter);
5481 b->parameter = nb->parameter;
5482 nb->parameter = NULL;
5483 mail_free_body_parameter(&nb->parameter);
5486 mail_free_body(&nb);
5489 fclose(fp);
5494 * Reevaluate the encoding in case form's changed...
5496 b->encoding = ENCOTHER;
5497 set_mime_type_by_grope(b);
5500 ClearScreen();
5501 resume_busy_cue(0);
5503 else
5504 errstr = "Can't create space for filtered text.";
5507 fs_give((void **)&cmd);
5509 else
5510 return(0);
5512 if(tmpf){
5513 our_unlink(tmpf);
5514 fs_give((void **)&tmpf);
5517 if(mtf){
5518 our_unlink(mtf);
5519 fs_give((void **) &mtf);
5522 if(resultf){
5523 if(name_file_size(resultf) > 0L)
5524 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
5525 our_unlink(resultf);
5526 fs_give((void **)&resultf);
5528 else if(errstr){
5529 if(tmp_so)
5530 so_give(&tmp_so);
5532 q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
5533 errstr);
5534 dprint((1, "Filter FAILED: %s\n",
5535 errstr ? errstr : "?"));
5538 return(errstr == NULL);
5542 /*----------------------------------------------------------------------
5543 Copy the newsgroup name of the given mailbox into the given buffer
5545 Args:
5547 Returns:
5548 ----*/
5549 void
5550 pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
5552 NETMBX mb;
5554 if(*mailbox == '#'){ /* Strip the leading "#news." */
5555 strncpy(group_name, mailbox + 6, len-1);
5556 group_name[len-1] = '\0';
5558 else if(mail_valid_net_parse(mailbox, &mb)){
5559 pine_send_newsgroup_name(mb.mailbox, group_name, len);
5561 else
5562 *group_name = '\0';
5566 /*----------------------------------------------------------------------
5567 Set up fields for passing to pico. Assumes first text part is
5568 intended to be passed along for editing, and is in the form of
5569 of a storage object brought into existence sometime before pico_send().
5570 -----*/
5571 void
5572 outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
5573 PATMT **pico_a, int from_bounce)
5575 PINEFIELD *pf;
5578 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5579 * is guaranteed to be of type PicoText!
5581 if(bod->type == TYPETEXT){
5582 *text = so_text((STORE_S *) bod->contents.text.data);
5584 /* mark storage object as user edited */
5585 if(!from_bounce)
5586 (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
5588 else if(bod->type == TYPEMULTIPART){
5589 PART *part;
5590 PATMT **ppa;
5591 char *type, *name, *p;
5592 int name_l;
5595 * We used to jump out the window if the first part wasn't text,
5596 * but that may not be the case when bouncing a message with
5597 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5598 * contents of the first part to send is still ALWAYS in a
5599 * PicoText storage object, *AND* if that object doesn't contain
5600 * data of type text, then it must contain THE ENCODED NON-TEXT
5601 * DATA of the piece being sent.
5603 * It's up to the programmer to make sure that such a message is
5604 * sent via pine_simple_send and never get to the composer via
5605 * pine_send.
5607 * Make sense?
5609 *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
5611 /* mark storage object as user edited */
5612 if(!from_bounce)
5613 (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
5616 * If we already had a list, blast it now, so we can build a new
5617 * attachment list that reflects what's really there...
5619 if(pico_a){
5620 free_attachment_list(pico_a);
5623 /* Simplifyihg assumption #28e. (see cross reference)
5624 All parts in the body passed in here that are not already
5625 in the attachments list are added to the end of the attachments
5626 list. Attachment items not in the body list will be taken care
5627 of in strings2outgoing, but they are unlikely to occur
5630 for(part = bod->nested.part->next; part != NULL; part = part->next) {
5631 /* Already in list? */
5632 for(ppa = pico_a;
5633 *ppa && strcmp((*ppa)->id, part->body.id);
5634 ppa = &(*ppa)->next)
5637 if(!*ppa){ /* Not in the list! append it... */
5638 *ppa = (PATMT *)fs_get(sizeof(PATMT));
5640 if(part->body.description){
5641 char *p;
5642 size_t len;
5644 len = 4*strlen(part->body.description)+1;
5645 p = (char *)fs_get(len*sizeof(char));
5646 if(rfc1522_decode_to_utf8((unsigned char *)p,
5647 len, part->body.description) == (unsigned char *) p){
5648 (*ppa)->description = p;
5650 else{
5651 fs_give((void **)&p);
5652 (*ppa)->description = cpystr(part->body.description);
5655 else
5656 (*ppa)->description = cpystr("");
5658 type = type_desc(part->body.type, part->body.subtype,
5659 part->body.parameter, NULL, 0);
5662 * If we can find a "name" parm, display that too...
5664 if((name = parameter_val(part->body.parameter, "name")) != NULL){
5665 /* Convert any [ or ]'s the name contained */
5666 for(p = name; *p ; p++)
5667 if(*p == '[')
5668 *p = '(';
5669 else if(*p == ']')
5670 *p = ')';
5672 name_l = p - name;
5674 else
5675 name_l = 0;
5677 (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
5679 snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
5680 name ? ": " : "", name ? name : "");
5681 (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
5683 if(name)
5684 fs_give((void **) &name);
5686 (*ppa)->flags = A_FLIT;
5687 (*ppa)->size = cpystr(byte_string(
5688 send_body_size(&part->body)));
5689 if(!part->body.id)
5690 part->body.id = generate_message_id(NULL);
5692 (*ppa)->id = cpystr(part->body.id);
5693 (*ppa)->next = NULL;
5700 /*------------------------------------------------------------------
5701 Malloc strings to pass to composer editor because it expects
5702 such strings so it can realloc them
5703 -----------------------------------------------------------------*/
5705 * turn any address fields into text strings
5708 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5709 * NOT to be RFC1522 decoded. Said differently, they're understood
5710 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5711 * original charset tagging as far into the compose/send pipe as
5712 * we can.
5714 for(pf = header->local; pf && pf->name; pf = pf->next)
5715 if(pf->canedit)
5716 switch(pf->type){
5717 case Address :
5718 if(pf->addr){
5719 char *p, *t, *u;
5720 long l;
5722 pf->scratch = addr_list_string(*pf->addr, NULL, 1);
5725 * Scan for and fix-up patently bogus fields.
5727 * NOTE: collaboration with this code and what's done in
5728 * reply.c:reply_cp_addr to package up the bogus stuff
5729 * is required.
5731 for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
5732 for(t = p; ; t--)
5733 if(*t == '&'){ /* find "leading" token */
5734 int replacelen;
5737 * Rfc822_cat has been changed so that it now quotes
5738 * this sometimes. So we have to look out for quotes
5739 * which confuse the decoder. It was only quoting
5740 * because we were putting \r \n in the input, I think.
5742 if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
5743 t[-1] = p[-1] = ' ';
5745 *t++ = ' '; /* replace token */
5746 *p = '\0'; /* tie off string */
5747 u = rfc822_base64((unsigned char *) t,
5748 (unsigned long) strlen(t),
5749 (unsigned long *) &l);
5750 if(!u)
5751 u = "";
5753 replacelen = strlen(t);
5754 *p = '@'; /* restore 'p' */
5755 rplstr(p, strlen(p), 12, ""); /* clear special token */
5756 rplstr(t, strlen(u)-replacelen+1, replacelen, u);
5757 if(u)
5758 fs_give((void **) &u);
5760 if(HE(pf))
5761 HE(pf)->start_here = 1;
5763 break;
5765 else if(t == pf->scratch)
5766 break;
5768 removing_leading_white_space(pf->scratch);
5769 if(pf->scratch){
5770 size_t l;
5773 * Replace control characters with ^C notation, unless
5774 * some conditions are met (see istrncpy).
5775 * If user doesn't edit, then we switch back to the
5776 * original version. If user does edit, then all bets
5777 * are off.
5779 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5780 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5781 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5782 fs_give((void **)&pf->scratch);
5783 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5786 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5790 break;
5792 case Subject :
5793 if(pf->text){
5794 char *p, *src;
5795 size_t len;
5797 src = pf->scratch ? pf->scratch
5798 : (*pf->text) ? *pf->text : "";
5800 len = 4*strlen(src)+1;
5801 p = (char *)fs_get(len * sizeof(char));
5802 if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
5803 if(pf->scratch)
5804 fs_give((void **)&pf->scratch);
5806 pf->scratch = p;
5808 else{
5809 fs_give((void **)&p);
5810 if(!pf->scratch)
5811 pf->scratch = cpystr(src);
5814 if(pf->scratch){
5815 size_t l;
5818 * Replace control characters with ^C notation, unless
5819 * some conditions are met (see istrncpy).
5820 * If user doesn't edit, then we switch back to the
5821 * original version. If user does edit, then all bets
5822 * are off.
5824 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5825 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5826 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5827 fs_give((void **)&pf->scratch);
5828 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5831 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5835 break;
5837 default :
5838 break;
5843 /*----------------------------------------------------------------------
5844 Restore fields returned from pico to form useful to sending
5845 routines.
5846 -----*/
5847 void
5848 strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
5850 PINEFIELD *pf;
5851 int we_cancel = 0;
5853 we_cancel = busy_cue(NULL, NULL, 1);
5856 * turn any local address strings into address lists
5858 for(pf = header->local; pf && pf->name; pf = pf->next)
5859 if(pf->scratch){
5860 char *the_address = NULL;
5862 switch(pf->type){
5863 case Address :
5864 removing_trailing_white_space(pf->scratch);
5866 if((the_address || *pf->scratch) && pf->addr){
5867 ADDRESS *new_addr = NULL;
5868 static char *fakedomain = "@";
5870 if(!the_address)
5871 the_address = pf->scratch;
5873 rfc822_parse_adrlist(&new_addr, the_address,
5874 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
5875 ? fakedomain : ps_global->maildomain);
5876 mail_free_address(pf->addr); /* free old addrs */
5877 *pf->addr = new_addr; /* assign new addr */
5879 else if(pf->addr)
5880 mail_free_address(pf->addr); /* free old addrs */
5882 break;
5884 case Subject :
5885 if(*pf->text)
5886 fs_give((void **)pf->text);
5888 if(*pf->scratch){
5889 *pf->text = cpystr(pf->scratch);
5892 break;
5894 default :
5895 break;
5898 fs_give((void **)&pf->scratch); /* free now useless text */
5901 create_message_body(bod, attach, flow_it);
5903 if(we_cancel)
5904 cancel_busy_cue(-1);
5908 /*----------------------------------------------------------------------
5910 The head of the body list here is always either TEXT or MULTIPART. It may be
5911 changed from TEXT to MULTIPART if there are attachments to be added
5912 and it is not already multipart.
5913 ----*/
5914 void
5915 create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
5917 PART *p, **pp;
5918 PATMT *pa;
5919 BODY *tmp_body, *text_body = NULL;
5920 void *file_contents;
5921 PARAMETER **parmp;
5922 char *lc;
5924 TIME_STAMP("create_body start.", 1);
5926 * if conditions are met short circuit MIME wrapping
5928 if((*b)->type != TYPEMULTIPART && !attach){
5930 /* only override assigned encoding if it might need upgrading */
5931 if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
5932 (*b)->encoding = ENCOTHER;
5934 create_message_body_text(*b, flow_it);
5936 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
5937 || !((*b)->encoding == ENC8BIT
5938 || (*b)->encoding == ENCBINARY)){
5939 TIME_STAMP("create_body end.", 1);
5940 return;
5942 else /* protect 8bit in multipart */
5943 text_body = *b;
5946 if((*b)->type == TYPETEXT) {
5947 /*-- Current type is text, but there are attachments to add --*/
5948 /*-- Upgrade to a TYPEMULTIPART --*/
5949 tmp_body = (BODY *)mail_newbody();
5950 tmp_body->type = TYPEMULTIPART;
5951 tmp_body->nested.part = mail_newbody_part();
5952 if(text_body){
5954 * Why do we do this?
5955 * The problem is that base64 or quoted-printable encoding is
5956 * sensitive to having random data appended to it's end. If
5957 * we use a single part TEXT message and something in between
5958 * us and the end appends advertising without adjusting for
5959 * the encoding, the message is screwed up. So we wrap the
5960 * text part inside a multipart and then the appended data
5961 * will come after the boundary.
5963 * We wish we could do this on the way out the door in a
5964 * child of post_rfc822_output because at that point we know
5965 * the character set and the encoding being used. For example,
5966 * iso-2022-jp is an encoding that is not sensitive to data
5967 * appended to the end, so it wouldn't need to be wrapped.
5968 * We could conceivably have post_rfc822_body inspect the
5969 * body and change it before doing the output. It would work
5970 * but would be very fragile. We'd be passed a body from
5971 * c-client to output and instead of just doing the output
5972 * we'd change the body and then output it. Not worth it
5973 * since the multipart wrapping is completely correct for
5974 * MIME-aware mailers.
5976 (void) copy_body(&(tmp_body->nested.part->body), *b);
5977 /* move contents which were NOT copied */
5978 tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
5979 (*b)->contents.text.data = NULL;
5981 else{
5982 tmp_body->nested.part->body = **b;
5984 (*b)->subtype = (*b)->id = (*b)->description = NULL;
5985 (*b)->parameter = NULL;
5986 (*b)->contents.text.data = NULL;
5989 pine_free_body(b);
5990 *b = tmp_body;
5993 if(!text_body){
5994 /*-- Now type must be MULTIPART with first part text --*/
5995 (*b)->nested.part->body.encoding = ENCOTHER;
5996 create_message_body_text(&((*b)->nested.part->body), flow_it);
5999 /*------ Go through the parts list remove those to be deleted -----*/
6000 for(pp = &(*b)->nested.part->next; *pp;){
6001 for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
6002 /* already existed? */
6003 if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
6004 char *orig_descp = NULL, *cs = NULL;
6007 * decode original to see if it matches what was decoded
6008 * when we sent it in.
6011 if((*pp)->body.description)
6012 orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
6013 SIZEOF_20KBUF, (*pp)->body.description);
6015 if(!(*pp)->body.description /* update description? */
6016 || (pa->description && strcmp(pa->description, orig_descp))){
6017 if((*pp)->body.description)
6018 fs_give((void **) &(*pp)->body.description);
6020 /* encoding happens as msg text is written */
6021 (*pp)->body.description = cpystr(pa->description);
6024 if(cs)
6025 fs_give((void **) &cs);
6027 break;
6030 if(pa == NULL){
6031 p = *pp; /* prepare to zap *pp */
6032 *pp = p->next; /* pull next one in list up */
6033 p->next = NULL; /* tie off removed node */
6035 pine_free_body_data(&p->body); /* clean up contained data */
6036 mail_free_body_part(&p); /* free up the part */
6038 else
6039 pp = &(*pp)->next;
6042 /*---------- Now add any new attachments ---------*/
6043 for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
6044 for(pa = attach; pa != NULL; pa = pa->next) {
6045 if(pa->id != NULL)
6046 continue; /* Has an ID, it's old */
6049 * the idea is handle ALL attachments as open FILE *'s. Actual
6050 * encoding and such is handled at the time the message
6051 * is shoved into the mail slot or written to disk...
6053 * Also, we never unlink a file, so it's up to whoever opens
6054 * it to deal with tmpfile issues.
6056 if((file_contents = (void *)so_get(FileStar, pa->filename,
6057 READ_ACCESS)) == NULL){
6058 q_status_message2(SM_ORDER | SM_DING, 3, 4,
6059 _("Error \"%s\", couldn't attach file \"%s\""),
6060 error_description(errno), pa->filename);
6061 display_message('x');
6062 continue;
6065 p->next = mail_newbody_part();
6066 p = p->next;
6067 p->body.id = generate_message_id(NULL);
6068 p->body.contents.text.data = file_contents;
6071 * Set type to unknown and let set_mime_type_by_* figure it out.
6072 * Always encode attachments we add as BINARY.
6074 p->body.type = TYPEOTHER;
6075 p->body.encoding = ENCBINARY;
6076 p->body.size.bytes = name_file_size(pa->filename);
6077 if(!set_mime_type_by_extension(&p->body, pa->filename)){
6078 set_mime_type_by_grope(&p->body);
6079 set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
6082 so_release((STORE_S *)p->body.contents.text.data);
6084 if(pa->description) /* encoding happens when msg written */
6085 p->body.description = cpystr(pa->description);
6087 /* Add name attribute for backward compatibility */
6088 for(parmp = &p->body.parameter; *parmp; )
6089 if(!struncmp((*parmp)->attribute, "name", 4)
6090 && (!*((*parmp)->attribute + 4)
6091 || *((*parmp)->attribute + 4) == '*')){
6092 PARAMETER *free_me = *parmp;
6093 *parmp = (*parmp)->next;
6094 free_me->next = NULL;
6095 mail_free_body_parameter(&free_me);
6097 else
6098 parmp = &(*parmp)->next;
6100 set_parameter(parmp, "name",
6101 pa->filename
6102 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6103 : NULL);
6105 /* Then set the Content-Disposition ala RFC1806 */
6106 if(!p->body.disposition.type){
6107 p->body.disposition.type = cpystr("attachment");
6108 for(parmp = &p->body.disposition.parameter; *parmp; )
6109 if(!struncmp((*parmp)->attribute, "filename", 4)
6110 && (!*((*parmp)->attribute + 4)
6111 || *((*parmp)->attribute + 4) == '*')){
6112 PARAMETER *free_me = *parmp;
6113 *parmp = (*parmp)->next;
6114 free_me->next = NULL;
6115 mail_free_body_parameter(&free_me);
6117 else
6118 parmp = &(*parmp)->next;
6120 set_parameter(parmp, "filename",
6121 pa->filename
6122 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6123 : NULL);
6126 p->next = NULL;
6127 pa->id = cpystr(p->body.id);
6131 * Now, if this multipart has but one text piece (that is, no
6132 * attachments), then downgrade from a composite type to a discrete
6133 * text/plain message if CTE is not 8bit.
6135 if(!(*b)->nested.part->next
6136 && (*b)->nested.part->body.type == TYPETEXT
6137 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
6138 || !((*b)->nested.part->body.encoding == ENC8BIT
6139 || (*b)->nested.part->body.encoding == ENCBINARY))){
6140 /* Clone the interesting body part */
6141 tmp_body = mail_newbody();
6142 *tmp_body = (*b)->nested.part->body;
6143 /* and rub out what we don't want cleaned up when it's free'd */
6144 mail_initbody(&(*b)->nested.part->body);
6145 mail_free_body(b);
6146 *b = tmp_body;
6150 TIME_STAMP("create_body end.", 1);
6155 * Fill in text BODY part's structure
6158 void
6159 create_message_body_text(struct mail_bodystruct *b, int flow_it)
6161 set_mime_type_by_grope(b);
6162 if(b != NULL){
6163 remove_parameter(&b->parameter, "format"); /* we will set it up below */
6164 remove_parameter(&b->parameter, "delsp"); /* we never set this up */
6166 if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
6167 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
6168 && flow_it)
6169 set_parameter(b ? &b->parameter : NULL, "format", "flowed");
6171 set_body_size(b);
6176 * free_attachment_list - free attachments in given list
6178 void
6179 free_attachment_list(PATMT **alist)
6181 PATMT *leading;
6183 while(alist && *alist){ /* pointer pointing to something */
6184 leading = (*alist)->next;
6185 if((*alist)->description)
6186 fs_give((void **)&(*alist)->description);
6188 if((*alist)->filename){
6189 if((*alist)->flags & A_TMP)
6190 if(our_unlink((*alist)->filename) < 0)
6191 dprint((1, "-- Can't unlink(%s): %s\n",
6192 (*alist)->filename ? (*alist)->filename : "?",
6193 error_description(errno)));
6195 fs_give((void **)&(*alist)->filename);
6198 if((*alist)->size)
6199 fs_give((void **)&(*alist)->size);
6201 if((*alist)->id)
6202 fs_give((void **)&(*alist)->id);
6204 fs_give((void **)alist);
6206 *alist = leading;
6211 void
6212 set_body_size(struct mail_bodystruct *b)
6214 unsigned char c;
6215 int we_cancel = 0;
6217 we_cancel = busy_cue(NULL, NULL, 1);
6218 so_seek((STORE_S *)b->contents.text.data, 0L, 0);
6219 b->size.bytes = 0L;
6220 while(so_readc(&c, (STORE_S *)b->contents.text.data))
6221 b->size.bytes++;
6223 if(we_cancel)
6224 cancel_busy_cue(-1);
6229 * view_as_rich - set the rich_header flag
6231 * name - name of the header field
6232 * deflt - default value to return if user didn't set it
6234 * Note: if the user tries to turn them all off with "", then
6235 * we take that to mean default, since otherwise there is no
6236 * way to get to the headers.
6239 view_as_rich(char *name, int deflt)
6241 char **p;
6242 char *q;
6244 p = ps_global->VAR_COMP_HDRS;
6246 if(p && *p && **p){
6247 for(; (q = *p) != NULL; p++){
6248 if(!struncmp(q, name, strlen(name)))
6249 return 0; /* 0 means we *do* view it by default */
6252 return 1; /* 1 means it starts out hidden */
6254 return(deflt);
6259 * background_posting - return whether or not we're already in the process
6260 * of posting
6263 background_posting(int gripe)
6265 if(ps_global->post){
6266 if(gripe)
6267 q_status_message(SM_ORDER|SM_DING, 3, 3,
6268 _("Can't post while posting!"));
6269 return(1);
6272 return(0);
6276 /*----------------------------------------------------------------------
6277 Validate the given subject relative to any news groups.
6279 Args: none
6281 Returns: always returns 1, but also returns error if
6282 ----*/
6284 valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
6286 struct headerentry *hp;
6288 if(expanded)
6289 *expanded = cpystr(given);
6291 if(error){
6293 * Now look for any header entry we passed to pico that has to do
6294 * with news. If there's no subject, gripe.
6296 for(hp = pbf->headents; hp->prompt; hp++)
6297 if(hp->help == h_composer_news){
6298 if(hp->hd_text->text[0] && !*given)
6299 *error = cpystr(
6300 _("News postings MUST have a subject! Please add one!"));
6302 break;
6306 return(0);
6311 * This is the build_address used by the composer to check for an address
6312 * in the addrbook.
6314 * Args: to -- the passed in line to parse
6315 * full_to -- Address of a pointer to return the full address in.
6316 * This will be allocated here and freed by the caller.
6317 * error -- Address of a pointer to return an error message in.
6318 * This will be allocated here and freed by the caller.
6319 * barg -- Address of a pointer to return the fcc in is in
6320 * fcc->tptr. It will have already been allocated by the
6321 * caller but we may free it and reallocate if we wish.
6322 * Caller will free it.
6324 * Result: 0 is returned if address was OK,
6325 * -1 if address wasn't OK.
6327 * Side effect: Can flush addrbook entry cache entries so they need to be
6328 * re-fetched afterwords.
6331 build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
6333 char *p;
6334 int ret_val, no_repo = 0, *save_nesting_level;
6335 BuildTo bldto;
6336 PrivateTop *pt = NULL;
6337 PrivateAffector *af = NULL;
6338 char *fcc_local = NULL;
6339 jmp_buf save_jmp_buf;
6341 dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
6343 /* check to see if to string is empty to avoid work */
6344 for(p = to; p && *p && isspace((unsigned char)*p); p++)
6345 ;/* do nothing */
6347 if(!p || !*p){
6348 if(full_to)
6349 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
6351 return 0;
6354 if(full_to != NULL)
6355 *full_to = (char *)NULL;
6357 if(error != NULL)
6358 *error = (char *)NULL;
6360 /* No guarantee cursor or status line is how we saved it */
6361 clear_cursor_pos();
6362 mark_status_unknown();
6364 if(ps_global->remote_abook_validity > 0 &&
6365 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6366 *mangled |= BUILDER_SCREEN_MANGLED;
6369 * If we end up jumping back here because somebody else changed one of
6370 * our addrbooks out from underneath us, we may well leak some memory.
6371 * That's probably ok since this will be very rare.
6373 * The reason for the memcpy of the jmp_buf is that we may actually
6374 * be indirectly calling this function from within the address book.
6375 * For example, we may be in the address book screen and then run
6376 * the ComposeTo command which puts us in the composer, then we call
6377 * build_address from there which resets addrbook_changed_unexpectedly.
6378 * Once we leave build_address we need to reset addrbook_changed_un...
6379 * because this position on the stack will no longer be valid.
6380 * Same is true of the other setjmp's in this file which are wrapped
6381 * in memcpy calls.
6383 save_nesting_level = cpyint(ab_nesting_level);
6384 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6385 if(setjmp(addrbook_changed_unexpectedly)){
6386 no_repo = 0;
6387 pt = NULL;
6388 af = NULL;
6389 fcc_local = NULL;
6390 if(error != NULL)
6391 *error = (char *)NULL;
6393 if(full_to && *full_to)
6394 fs_give((void **)full_to);
6396 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6397 dprint((1,
6398 "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
6399 addrbook_reset();
6400 ab_nesting_level = *save_nesting_level;
6403 ab_nesting_level++;
6404 bldto.type = Str;
6405 bldto.arg.str = to;
6406 ret_val = build_address_internal(bldto, full_to, error,
6407 barg ? &fcc_local : NULL,
6408 &no_repo, NULL, save_and_restore,
6409 0, mangled);
6410 ab_nesting_level--;
6411 if(save_nesting_level)
6412 fs_give((void **)&save_nesting_level);
6415 * Have to rfc1522_decode the full_to string before sending it back.
6417 if(full_to && *full_to ){
6418 char *q;
6419 size_t len;
6421 len = 4*strlen(*full_to)+1;
6422 q = (char *)fs_get(len * sizeof(char));
6423 p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
6425 /* p == q means that decoding happened, p is decoded *full_to */
6426 if(p == q){
6427 fs_give((void **)full_to);
6428 *full_to = p;
6430 else
6431 fs_give((void **)&q);
6434 if(fcc_local){
6435 unsigned long csum;
6437 /* Pt will point to headents[Fcc].bldr_private */
6438 pt = NULL;
6439 if(barg && barg->aff)
6440 pt = (PrivateTop *)(*barg->aff);
6443 * If *barg->aff is set, that means fcc was set from a list
6444 * during some previous builder call.
6445 * If the current To line contains the old expansion as a prefix, then
6446 * we should leave things as they are. In order to decide that,
6447 * we look at a hash value computed from the strings.
6449 if(pt && (af=pt->affector) && af->who == BP_To){
6450 int len;
6452 len = strlen(to);
6453 if(len >= af->cksumlen){
6454 int save;
6456 save = to[af->cksumlen];
6457 to[af->cksumlen] = '\0';
6458 csum = line_hash(to);
6459 to[af->cksumlen] = save;
6461 else
6462 csum = af->cksumval + 1; /* something not equal to cksumval */
6465 if(!pt ||
6466 !pt->affector ||
6467 (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
6469 /* replace fcc value */
6470 if(barg->tptr)
6471 fs_give((void **)&barg->tptr);
6473 barg->tptr = fcc_local;
6475 if(barg->aff){
6476 if(!pt){
6477 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6478 pt = (PrivateTop *)(*barg->aff);
6479 memset((void *)pt, 0, sizeof(PrivateTop));
6482 if(no_repo){
6483 if(!pt->affector)
6484 pt->affector =
6485 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6487 af = pt->affector;
6488 af->who = BP_To;
6489 af->cksumlen = strlen(((full_to && *full_to)
6490 ? *full_to : ""));
6491 af->cksumval = line_hash(((full_to && *full_to)
6492 ? *full_to : ""));
6494 else{
6496 * If result is reproducible, we don't keep track here.
6498 if(pt->affector)
6499 fs_give((void **)&pt->affector);
6503 else
6504 fs_give((void **)&fcc_local); /* unused in this case */
6507 /* This is so pico will erase the old message */
6508 if(error != NULL && *error == NULL)
6509 *error = cpystr("");
6511 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6512 flush_status_messages(1);
6513 return(ret_val);
6518 * This is the builder used by the composer for the Lcc line.
6520 * Args: lcc -- the passed in Lcc line to parse
6521 * full_lcc -- Address of a pointer to return the full address in.
6522 * This will be allocated here and freed by the caller.
6523 * error -- Address of a pointer to return an error message in.
6524 * This is not allocated so should not be freed by the caller.
6525 * barg -- This is a pointer to text for affected entries which
6526 * we may be changing. The first one in the list is the
6527 * To entry. We may put the name of the list in empty
6528 * group syntax form there (like List Name: ;).
6529 * The second one in the list is the fcc field.
6530 * The tptr members already point to text allocated in the
6531 * caller. We may free and reallocate here, caller will
6532 * free the result in any case.
6534 * Result: 0 is returned if address was OK,
6535 * -1 if address wasn't OK.
6537 * Side effect: Can flush addrbook entry cache entries so they need to be
6538 * re-fetched afterwords.
6541 build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
6543 int ret_val,
6544 no_repo = 0; /* fcc or lcc not reproducible */
6545 int *save_nesting_level;
6546 BuildTo bldlcc;
6547 PrivateTop *pt = NULL;
6548 PrivateAffector *af = NULL;
6549 char *p,
6550 *fcc_local = NULL,
6551 *to = NULL,
6552 *dummy;
6553 jmp_buf save_jmp_buf;
6555 dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
6557 /* check to see if to string is empty to avoid work */
6558 for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
6559 ;/* do nothing */
6561 if(!p || !*p){
6562 if(full_lcc)
6563 *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
6565 return 0;
6568 if(error != NULL)
6569 *error = (char *)NULL;
6571 if(ps_global->remote_abook_validity > 0 &&
6572 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6573 *mangled |= BUILDER_SCREEN_MANGLED;
6576 * If we end up jumping back here because somebody else changed one of
6577 * our addrbooks out from underneath us, we may well leak some memory.
6578 * That's probably ok since this will be very rare.
6580 save_nesting_level = cpyint(ab_nesting_level);
6581 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6582 if(setjmp(addrbook_changed_unexpectedly)){
6583 no_repo = 0;
6584 pt = NULL;
6585 af = NULL;
6586 fcc_local = NULL;
6587 to = NULL;
6588 if(error != NULL)
6589 *error = (char *)NULL;
6591 if(full_lcc && *full_lcc)
6592 fs_give((void **)full_lcc);
6594 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6595 dprint((1,
6596 "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
6597 addrbook_reset();
6598 ab_nesting_level = *save_nesting_level;
6601 ab_nesting_level++;
6602 bldlcc.type = Str;
6603 bldlcc.arg.str = lcc;
6606 * To is first affected_entry and Fcc is second.
6607 * The conditional stuff for the fcc argument says to only change the
6608 * fcc if the fcc pointer is passed in non-null, and the To pointer
6609 * is also non-null. If they are null, that means they've already been
6610 * entered (are sticky). We don't affect fcc if either fcc or To has
6611 * been typed in.
6613 ret_val = build_address_internal(bldlcc,
6614 full_lcc,
6615 error,
6616 (barg && barg->next && barg->next->tptr && barg->tptr)
6617 ? &fcc_local : NULL,
6618 &no_repo,
6619 (barg && barg->tptr) ? &to : NULL,
6620 save_and_restore, 0, mangled);
6622 ab_nesting_level--;
6623 if(save_nesting_level)
6624 fs_give((void **)&save_nesting_level);
6626 /* full_lcc is what ends up in the Lcc: line */
6627 if(full_lcc && *full_lcc){
6628 size_t len;
6631 * Have to rfc1522_decode the full_lcc string before sending it back.
6633 len = 4*strlen(*full_lcc)+1;
6634 p = (char *)fs_get(len * sizeof(char));
6635 if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
6636 fs_give((void **)full_lcc);
6637 *full_lcc = p;
6639 else
6640 fs_give((void **)&p);
6643 /* to is what ends up in the To: line */
6644 if(to && *to){
6645 unsigned long csum;
6646 size_t len;
6649 * Have to rfc1522_decode the full_to string before sending it back.
6651 len = 4*strlen(to)+1;
6652 p = (char *)fs_get(len * sizeof(char));
6653 dummy = NULL;
6654 if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
6656 * If the caller wants us to try to preserve the charset
6657 * information (they set aff) we copy it into encoded->etext.
6658 * We don't have to worry about pasting together pieces of
6659 * etext like we do in build_address because whenever the
6660 * Lcc line is setting the To line it will be setting the
6661 * whole line, not modifying it.
6662 * Pt will point to headents[To].bldr_private.
6664 if(barg && barg->aff){
6665 pt = (PrivateTop *)(*barg->aff);
6667 if(!pt){
6668 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6669 pt = (PrivateTop *)(*barg->aff);
6670 memset((void *)pt, 0, sizeof(PrivateTop));
6674 fs_give((void **)&to);
6675 to = p;
6677 else
6678 fs_give((void **)&p);
6680 if(dummy)
6681 fs_give((void **)&dummy);
6685 * This part is recording the fact that the To line was set to
6686 * what it is by entering something on the Lcc line. In particular,
6687 * if a list alias was entered here then the fullname of the list
6688 * goes in the To line. We save this affector information so that
6689 * we can tell it shouldn't be modified if we call build_addr_lcc
6690 * again unless we actually modified what's in the Lcc line so that
6691 * it doesn't start with the same thing. The problem we're solving
6692 * is that the contents of the Lcc line no longer look like the
6693 * list they were derived from.
6694 * Pt will point to headents[To].bldr_private.
6696 if(barg && barg->aff)
6697 pt = (PrivateTop *)(*barg->aff);
6699 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6700 int len;
6702 len = strlen(lcc);
6703 if(len >= af->cksumlen){
6704 int save;
6706 save = lcc[af->cksumlen];
6707 lcc[af->cksumlen] = '\0';
6708 csum = line_hash(lcc);
6709 lcc[af->cksumlen] = save;
6711 else
6712 csum = af->cksumval + 1; /* so they aren't equal */
6715 if(!pt ||
6716 !pt->affector ||
6717 pt->affector->who != BP_Lcc ||
6718 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6720 /* replace to value */
6721 if(barg->tptr && barg->tptr[0]){
6722 size_t l;
6723 char *t;
6725 l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
6726 t = (char *)fs_get((l+1) * sizeof(char));
6727 snprintf(t, l+1, "%s%s%s",
6728 barg->tptr,
6729 (to && *to) ? ", " : "",
6730 (to && *to) ? to : "");
6731 fs_give((void **)&barg->tptr);
6732 if(to)
6733 fs_give((void **)&to);
6735 barg->tptr = t;
6737 else{
6738 if(barg->tptr)
6739 fs_give((void **)&barg->tptr);
6741 barg->tptr = to;
6744 if(barg->aff){
6745 if(!pt){
6746 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6747 pt = (PrivateTop *)(*barg->aff);
6748 memset((void *)pt, 0, sizeof(PrivateTop));
6751 if(no_repo){
6752 if(!pt->affector)
6753 pt->affector =
6754 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6756 af = pt->affector;
6757 af->who = BP_Lcc;
6758 af->cksumlen = strlen(((full_lcc && *full_lcc)
6759 ? *full_lcc : ""));
6760 af->cksumval = line_hash(((full_lcc && *full_lcc)
6761 ? *full_lcc : ""));
6763 else{
6765 * If result is reproducible, we don't keep track here.
6767 if(pt->affector)
6768 fs_give((void **)&pt->affector);
6772 else
6773 fs_give((void **)&to); /* unused in this case */
6776 if(fcc_local){
6777 unsigned long csum;
6780 * If *barg->next->aff is set, that means fcc was set from a list
6781 * during some previous builder call. If the current Lcc line
6782 * contains the old expansion as a prefix, then we should leave
6783 * things as they are. In order to decide that we look at a hash
6784 * value computed from the strings.
6785 * Pt will point to headents[Fcc].bldr_private
6787 pt = NULL;
6788 if(barg && barg->next && barg->next->aff)
6789 pt = (PrivateTop *)(*barg->next->aff);
6791 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6792 int len;
6794 len = strlen(lcc);
6795 if(len >= af->cksumlen){
6796 int save;
6798 save = lcc[af->cksumlen];
6799 lcc[af->cksumlen] = '\0';
6800 csum = line_hash(lcc);
6801 lcc[af->cksumlen] = save;
6803 else
6804 csum = af->cksumval + 1; /* something not equal to cksumval */
6807 if(!pt ||
6808 !pt->affector ||
6809 pt->affector->who != BP_Lcc ||
6810 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6812 /* replace fcc value */
6813 if(barg->next->tptr)
6814 fs_give((void **)&barg->next->tptr);
6816 barg->next->tptr = fcc_local;
6818 if(barg->next->aff){
6819 if(!pt){
6820 *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
6821 pt = (PrivateTop *)(*barg->next->aff);
6822 memset((void *)pt, 0, sizeof(PrivateTop));
6825 if(no_repo){
6826 if(!pt->affector)
6827 pt->affector =
6828 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6830 af = pt->affector;
6831 af->who = BP_Lcc;
6832 af->cksumlen = strlen(((full_lcc && *full_lcc)
6833 ? *full_lcc : ""));
6834 af->cksumval = line_hash(((full_lcc && *full_lcc)
6835 ? *full_lcc : ""));
6837 else{
6839 * If result is reproducible, we don't keep track here.
6841 if(pt->affector)
6842 fs_give((void **)&pt->affector);
6846 else
6847 fs_give((void **)&fcc_local); /* unused in this case */
6851 if(error != NULL && *error == NULL)
6852 *error = cpystr("");
6854 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6855 flush_status_messages(0);
6856 return(ret_val);
6860 /*----------------------------------------------------------------------
6861 Verify and canonicalize news groups names.
6862 Called from the message composer
6864 Args: given_group -- List of groups typed by user
6865 expanded_group -- pointer to point to expanded list, which will be
6866 allocated here and freed in caller. If this is
6867 NULL, don't attempt to validate.
6868 error -- pointer to store error message
6869 fcc -- pointer to point to fcc, which will be
6870 allocated here and freed in caller
6872 Returns: 0 if all is OK
6873 -1 if addresses weren't valid
6875 Test the given list of newstroups against those recognized by our nntp
6876 servers. Testing by actually trying to open the list is much cheaper, both
6877 in bandwidth and memory, than yanking the whole list across the wire.
6878 ----*/
6880 news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
6882 int rv;
6883 char *fccptr = NULL;
6885 if(fcc && fcc->tptr)
6886 fccptr = cpystr(fcc->tptr);
6888 clear_cursor_pos();
6890 rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
6892 /* assign any new fcc to the BUILDER_ARG */
6893 if(fccptr){
6894 if(fcc){
6895 /* it changed */
6896 if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
6897 fs_give((void **) &fcc->tptr);
6898 fcc->tptr = fccptr;
6899 fccptr = NULL;
6903 if(fccptr)
6904 fs_give((void **) &fccptr);
6907 /* deal with any busy indicator */
6908 if(news_busy_cue){
6909 news_busy_cue = 0;
6910 cancel_busy_cue(0);
6911 mark_status_dirty();
6912 display_message('x');
6913 if(mangled)
6914 *mangled |= BUILDER_MESSAGE_DISPLAYED;
6918 return(rv);
6922 void
6923 news_build_busy(void)
6925 news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
6929 #if defined(DOS) || defined(OS2)
6931 /*----------------------------------------------------------------------
6932 Verify that the necessary pieces are around to allow for
6933 message sending under DOS
6935 Args: strict -- tells us if a remote stream is required before
6936 sending is permitted.
6938 The idea is to make sure pine knows enough to put together a valid
6939 from line. The things we MUST know are a user-id, user-domain and
6940 smtp server to dump the message off on. Typically these are
6941 provided in pine's configuration file, but if not, the user is
6942 queried here.
6943 ----*/
6945 dos_valid_from()
6947 char prompt[100], answer[80];
6948 int rc, i, flags;
6949 HelpType help;
6952 * query for user name portion of address, use IMAP login
6953 * name as default
6955 if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
6956 NETMBX mb;
6957 int no_prompt_user_id = 0;
6959 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6960 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6961 && *mb.user){
6962 strncpy(answer, mb.user, sizeof(answer)-1);
6963 answer[sizeof(answer)-1] = '\0';
6965 else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
6966 /* no user-id prompting if set */
6967 no_prompt_user_id = 1;
6968 rc = 0;
6969 if(!ps_global->mail_stream)
6970 do_broach_folder(ps_global->inbox_name,
6971 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
6972 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6973 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6974 && *mb.user){
6975 strncpy(answer, mb.user, sizeof(answer)-1);
6976 answer[sizeof(answer)-1] = '\0';
6978 else
6979 answer[0] = '\0';
6981 else
6982 answer[0] = '\0';
6984 if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
6985 /* No prompt, just assume mailbox login is user-id */
6986 no_prompt_user_id = 1;
6987 rc = 0;
6990 snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
6991 prompt[sizeof(prompt)-1] = '\0';
6993 help = NO_HELP;
6994 while(!no_prompt_user_id) {
6995 flags = OE_APPEND_CURRENT;
6996 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
6997 sizeof(answer),prompt,NULL,help,&flags);
6998 if(rc == 2)
6999 continue;
7001 if(rc == 3){
7002 help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
7003 continue;
7006 if(rc != 4)
7007 break;
7010 if(rc == 1 || (rc == 0 && !answer[0])) {
7011 q_status_message(SM_ORDER, 3, 4,
7012 _("Send cancelled (User-id must be provided before sending)"));
7013 return(0);
7016 /* save the name */
7017 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
7018 sizeof(prompt)-50, answer);
7019 prompt[sizeof(prompt)-1] = '\0';
7020 if(ps_global->blank_user_id
7021 && !no_prompt_user_id
7022 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7023 set_variable(V_USER_ID, answer, 1, 1, Main);
7025 else{
7026 fs_give((void **)&(ps_global->VAR_USER_ID));
7027 ps_global->VAR_USER_ID = cpystr(answer);
7031 /* query for personal name */
7032 if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
7033 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
7034 answer[0] = '\0';
7035 snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
7036 prompt[sizeof(prompt)-1] = '\0';
7038 help = NO_HELP;
7039 while(1) {
7040 flags = OE_APPEND_CURRENT;
7041 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7042 sizeof(answer),prompt,NULL,help,&flags);
7043 if(rc == 2)
7044 continue;
7046 if(rc == 3){
7047 help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
7048 continue;
7051 if(rc != 4)
7052 break;
7055 if(rc == 0 && answer){ /* save the name */
7056 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
7057 sizeof(prompt)-50, answer);
7058 prompt[sizeof(prompt)-1] = '\0';
7059 if(ps_global->blank_personal_name
7060 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7061 set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
7063 else{
7064 fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
7065 ps_global->VAR_PERSONAL_NAME = cpystr(answer);
7071 * query for host/domain portion of address, using IMAP
7072 * host as default
7074 if(ps_global->blank_user_domain
7075 || ps_global->maildomain == ps_global->localdomain
7076 || ps_global->maildomain == ps_global->hostname){
7077 if(ps_global->inbox_name[0] == '{'){
7078 for(i=0;
7079 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7080 answer[i] = ps_global->inbox_name[i+1];
7082 answer[i] = '\0';
7084 else
7085 answer[0] = '\0';
7087 snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
7088 prompt[sizeof(prompt)-1] = '\0';
7090 help = NO_HELP;
7091 while(1) {
7092 flags = OE_APPEND_CURRENT;
7093 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7094 sizeof(answer),prompt,NULL,help,&flags);
7095 if(rc == 2)
7096 continue;
7098 if(rc == 3){
7099 help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
7100 continue;
7103 if(rc != 4)
7104 break;
7107 if(rc == 1 || (rc == 0 && !answer[0])) {
7108 q_status_message(SM_ORDER, 3, 4,
7109 _("Send cancelled (Host/domain name must be provided before sending)"));
7110 return(0);
7113 /* save the name */
7114 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
7115 sizeof(prompt)-50, answer);
7116 prompt[sizeof(prompt)-1] = '\0';
7117 if(!ps_global->userdomain && !ps_global->blank_user_domain
7118 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7119 set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
7120 fs_give((void **)&(ps_global->maildomain)); /* blast old val */
7121 ps_global->userdomain = cpystr(answer);
7122 ps_global->maildomain = ps_global->userdomain;
7124 else{
7125 fs_give((void **)&(ps_global->maildomain));
7126 ps_global->userdomain = cpystr(answer);
7127 ps_global->maildomain = ps_global->userdomain;
7131 /* check for smtp server */
7132 if(!ps_global->VAR_SMTP_SERVER ||
7133 !ps_global->VAR_SMTP_SERVER[0] ||
7134 !ps_global->VAR_SMTP_SERVER[0][0]){
7135 char **list;
7137 if(ps_global->inbox_name[0] == '{'){
7138 for(i=0;
7139 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7140 answer[i] = ps_global->inbox_name[i+1];
7142 answer[i] = '\0';
7144 else
7145 answer[0] = '\0';
7147 snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
7148 prompt[sizeof(prompt)-1] = '\0';
7150 help = NO_HELP;
7151 while(1) {
7152 flags = OE_APPEND_CURRENT;
7153 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7154 sizeof(answer),prompt,NULL,help,&flags);
7155 if(rc == 2)
7156 continue;
7158 if(rc == 3){
7159 help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
7160 continue;
7163 if(rc != 4)
7164 break;
7167 if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
7168 q_status_message(SM_ORDER, 3, 4,
7169 _("Send cancelled (SMTP server must be provided before sending)"));
7170 return(0);
7173 /* save the name */
7174 list = (char **) fs_get(2 * sizeof(char *));
7175 list[0] = cpystr(answer);
7176 list[1] = NULL;
7177 set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
7178 fs_give((void *)&list[0]);
7179 fs_give((void *)list);
7182 return(1);
7185 #endif /* defined(DOS) || defined(OS2) */