* Extension of the privacy changes to the generation of message-id when replying,
[alpine.git] / alpine / send.c
blob4d988a0c0c922dd60d6217bfca8f8845aca9136e
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: send.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2020 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 Functions for composing and sending mail
22 ====*/
25 #include "headers.h"
26 #include "send.h"
27 #include "status.h"
28 #include "mailview.h"
29 #include "mailindx.h"
30 #include "dispfilt.h"
31 #include "keymenu.h"
32 #include "folder.h"
33 #include "radio.h"
34 #include "addrbook.h"
35 #include "reply.h"
36 #include "titlebar.h"
37 #include "signal.h"
38 #include "mailcmd.h"
39 #include "roleconf.h"
40 #include "adrbkcmd.h"
41 #include "busy.h"
42 #include "../pith/debug.h"
43 #include "../pith/state.h"
44 #include "../pith/conf.h"
45 #include "../pith/flag.h"
46 #include "../pith/bldaddr.h"
47 #include "../pith/copyaddr.h"
48 #include "../pith/detach.h"
49 #include "../pith/mimedesc.h"
50 #include "../pith/pipe.h"
51 #include "../pith/addrstring.h"
52 #include "../pith/news.h"
53 #include "../pith/detoken.h"
54 #include "../pith/util.h"
55 #include "../pith/init.h"
56 #include "../pith/mailcmd.h"
57 #include "../pith/ablookup.h"
58 #include "../pith/reply.h"
59 #include "../pith/hist.h"
60 #include "../pith/list.h"
61 #include "../pith/icache.h"
62 #include "../pith/busy.h"
63 #include "../pith/mimetype.h"
64 #include "../pith/send.h"
65 #include "../pith/smime.h"
68 typedef struct body_particulars {
69 unsigned short type, encoding, had_csp;
70 char *subtype, *charset;
71 PARAMETER *parameter;
72 } BODY_PARTICULARS_S;
75 * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook
77 #define HE(PF) ((struct headerentry *)((PF)->extdata))
81 * Internal Prototypes
83 int redraft(MAILSTREAM **, ENVELOPE **, BODY **, char **, char **, REPLY_S **,
84 REDRAFT_POS_S **, PINEFIELD **, ACTION_S **, int);
85 int redraft_prompt(char *, char *, int);
86 int check_for_subject(METAENV *);
87 int check_for_fcc(char *);
88 void free_prompts(PINEFIELD *);
89 int postpone_prompt(void);
90 METAENV *pine_simple_send_header(ENVELOPE *, char **, char ***);
91 void call_mailer_file_result(char *, int);
92 void mark_address_failure_for_pico(METAENV *);
93 BODY_PARTICULARS_S
94 *save_body_particulars(BODY *);
95 void reset_body_particulars(BODY_PARTICULARS_S *, BODY *);
96 void free_body_particulars(BODY_PARTICULARS_S *);
97 long message_format_for_pico(long, int (*)(int));
98 int send_exit_for_pico(struct headerentry *, void (*)(void), int, char **);
99 void new_thread_on_blank_subject(void);
100 char *choose_a_priority(char *);
101 int dont_flow_this_time(void);
102 int mime_type_for_pico(char *);
103 char *cancel_for_pico(void (*)(void));
104 int filter_message_text(char *, ENVELOPE *, BODY *, STORE_S **, METAENV *);
105 void pine_send_newsgroup_name(char *, char*, size_t);
106 void outgoing2strings(METAENV *, BODY *, void **, PATMT **, int);
107 void strings2outgoing(METAENV *, BODY **, PATMT *, int);
108 void create_message_body_text(BODY *, int);
109 void set_body_size(BODY *);
110 int view_as_rich(char *, int);
111 int background_posting(int);
112 int valid_subject(char *, char **, char **,BUILDER_ARG *,int *);
113 int build_addr_lcc(char *, char **, char **, BUILDER_ARG *, int *);
114 int news_build(char *, char **, char **, BUILDER_ARG *, int *);
115 void news_build_busy(void);
116 #if defined(DOS) || defined(OS2)
117 int dos_valid_from(void);
118 #endif /* defined(DOS) || defined(OS2) */
122 * Pointer to buffer to hold pointers into pine data that's needed by pico.
124 static PICO *pbf;
127 static char *g_rolenick = NULL;
130 static char *sending_filter_requested;
131 static char background_requested, flowing_requested;
132 static unsigned call_mailer_flags;
133 static char *priority_requested;
135 /* local global to save busy_cue state */
136 static int news_busy_cue = 0;
140 * Various useful strings
142 #define INTRPT_PMT \
143 _("Continue INTERRUPTED composition (answering \"n\" won't erase it)")
144 #define PSTPND_PMT \
145 _("Continue postponed composition (answering \"No\" won't erase it)")
146 #define FORM_PMT \
147 _("Start composition from Form Letter Folder")
148 #define PSTPN_FORM_PMT \
149 _("Save to Postponed or Form letter folder? ")
150 #define POST_PMT \
151 _("Posted message may go to thousands of readers. Really post")
152 #define INTR_DEL_PMT \
153 _("Deleted messages will be removed from folder after use. Proceed")
157 * Macros to help sort out posting results
159 #define P_MAIL_WIN 0x01
160 #define P_MAIL_LOSE 0x02
161 #define P_MAIL_BITS 0x03
162 #define P_NEWS_WIN 0x04
163 #define P_NEWS_LOSE 0x08
164 #define P_NEWS_BITS 0x0C
165 #define P_FCC_WIN 0x10
166 #define P_FCC_LOSE 0x20
167 #define P_FCC_BITS 0x30
170 #define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE"
174 * For check_for_subject and check_for_fcc
176 #define CF_OK 0x1
177 #define CF_MISSING 0x2
180 /*----------------------------------------------------------------------
181 Compose screen (not forward or reply). Set up envelope, call composer
183 Args: pine_state -- The usual pine structure
185 Little front end for the compose screen
186 ---*/
187 void
188 compose_screen(struct pine *pine_state)
190 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
191 (*redraw)(void) = pine_state->redrawer;
193 pine_state->redrawer = NULL;
194 ps_global->next_screen = SCREEN_FUN_NULL;
195 mailcap_free(); /* free resources we won't be using for a while */
196 compose_mail(NULL, NULL, NULL, NULL, NULL);
197 pine_state->next_screen = prev_screen;
198 pine_state->redrawer = redraw;
202 /*----------------------------------------------------------------------
203 Alternate compose screen. Set up role and call regular compose.
205 Args: pine_state -- The usual pine structure
206 ---*/
207 void
208 alt_compose_screen(struct pine *pine_state)
210 ACTION_S *role = NULL;
211 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
212 (*redraw)(void) = pine_state->redrawer;
214 pine_state->redrawer = NULL;
215 ps_global->next_screen = SCREEN_FUN_NULL;
216 mailcap_free(); /* free resources we won't be using for a while */
218 /* Setup role */
219 if(role_select_screen(pine_state, &role, MC_COMPOSE) < 0){
220 cmd_cancelled("Composition");
221 pine_state->next_screen = prev_screen;
222 pine_state->redrawer = redraw;
223 return;
227 * If default role was selected (NULL) we need to make up a role which
228 * won't do anything, but will cause compose_mail to think there's
229 * already a role so that it won't try to confirm the default.
231 if(role)
232 role = combine_inherited_role(role);
233 else{
234 role = (ACTION_S *)fs_get(sizeof(*role));
235 memset((void *)role, 0, sizeof(*role));
236 role->nick = cpystr("Default Role");
239 pine_state->redrawer = NULL;
240 compose_mail(NULL, NULL, role, NULL, NULL);
241 free_action(&role);
242 pine_state->next_screen = prev_screen;
243 pine_state->redrawer = redraw;
247 /*----------------------------------------------------------------------
248 Format envelope for outgoing message and call editor
250 Args: given_to -- An address to send mail to (usually from command line
251 invocation)
252 fcc_arg -- The fcc that goes with this address.
254 If a "To" line is given format that into the envelope and get ready to call
255 the composer
256 If there's a message postponed, offer to continue it, and set it up,
257 otherwise just fill in the outgoing envelope as blank.
259 NOTE: we ignore postponed and interrupted messages in nr mode
260 ----*/
261 void
262 compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
263 PATMT *attach, gf_io_t inc_text_getc)
265 BODY *body = NULL;
266 ENVELOPE *outgoing = NULL;
267 PINEFIELD *custom = NULL;
268 REPLY_S *reply = NULL;
269 REDRAFT_POS_S *redraft_pos = NULL;
270 ACTION_S *role = NULL;
271 MAILSTREAM *stream;
272 char *fcc_to_free,
273 *fcc = NULL,
274 *lcc = NULL,
275 *sig = NULL;
276 int fcc_is_sticky = 0,
277 to_is_sticky = 0,
278 intrptd = 0,
279 postponed = 0,
280 form = 0;
282 dprint((1,
283 "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n"));
285 /*-- Check for INTERRUPTED mail --*/
286 if(!role_arg && !(given_to || attach)){
287 char file_path[MAXPATH+1];
289 /* build filename and see if it exists. build_path creates
290 * an explicit local path name, so all c-client access is thru
291 * local drivers.
293 file_path[0] = '\0';
294 build_path(file_path,
295 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
296 : ps_global->home_dir,
297 INTERRUPTED_MAIL, sizeof(file_path));
299 /* check to see if the folder exists, the user wants to continue
300 * and that we can actually read something in...
302 if(folder_exists(NULL, file_path) & FEX_ISFILE)
303 intrptd = 1;
306 /*-- Check for postponed mail --*/
307 if(!role_arg
308 && !outgoing /* not replying/forwarding */
309 && !(given_to || attach) /* not command line send */
310 && ps_global->VAR_POSTPONED_FOLDER /* folder to look in */
311 && ps_global->VAR_POSTPONED_FOLDER[0])
312 postponed = 1;
314 /*-- Check for form letter folder --*/
315 if(!role_arg
316 && !outgoing /* not replying/forwarding */
317 && !(given_to || attach) /* not command line send */
318 && ps_global->VAR_FORM_FOLDER /* folder to look in */
319 && ps_global->VAR_FORM_FOLDER[0])
320 form = 1;
322 if(!outgoing && !(given_to || attach)
323 && !role_arg && F_ON(F_ALT_COMPOSE_MENU, ps_global)){
324 char prompt[80];
325 char letters[30];
326 char chosen_task;
327 char *new = "New";
328 char *intrpt = "Interrupted";
329 char *postpnd = "Postponed";
330 char *formltr = "FormLetter";
331 char *roles = "setRole";
332 HelpType help = h_composer_browse;
333 ESCKEY_S compose_style[6];
334 unsigned which_help;
335 int ekey_num;
337 ekey_num = 0;
338 compose_style[ekey_num].ch = 'n';
339 compose_style[ekey_num].rval = 'n';
340 compose_style[ekey_num].name = "N";
341 compose_style[ekey_num++].label = new;
343 if(intrptd){
344 compose_style[ekey_num].ch = 'i';
345 compose_style[ekey_num].rval = 'i';
346 compose_style[ekey_num].name = "I";
347 compose_style[ekey_num++].label = intrpt;
350 if(postponed){
351 compose_style[ekey_num].ch = 'p';
352 compose_style[ekey_num].rval = 'p';
353 compose_style[ekey_num].name = "P";
354 compose_style[ekey_num++].label = postpnd;
357 if(form){
358 compose_style[ekey_num].ch = 'f';
359 compose_style[ekey_num].rval = 'f';
360 compose_style[ekey_num].name = "F";
361 compose_style[ekey_num++].label = formltr;
364 compose_style[ekey_num].ch = 'r';
365 compose_style[ekey_num].rval = 'r';
366 compose_style[ekey_num].name = "R";
367 compose_style[ekey_num++].label = roles;
369 compose_style[ekey_num].ch = -1;
371 if(F_ON(F_BLANK_KEYMENU,ps_global)){
372 char *p;
374 p = letters;
375 *p = '\0';
376 for(ekey_num = 0; compose_style[ekey_num].ch != -1; ekey_num++){
377 if(p - letters < sizeof(letters))
378 *p++ = (char) compose_style[ekey_num].ch;
380 if(compose_style[ekey_num + 1].ch != -1 && p - letters < sizeof(letters))
381 *p++ = ',';
384 if(p - letters < sizeof(letters))
385 *p = '\0';
388 which_help = intrptd + 2 * postponed + 4 * form;
389 switch(which_help){
390 case 1:
391 help = h_compose_intrptd;
392 break;
393 case 2:
394 help = h_compose_postponed;
395 break;
396 case 3:
397 help = h_compose_intrptd_postponed;
398 break;
399 case 4:
400 help = h_compose_form;
401 break;
402 case 5:
403 help = h_compose_intrptd_form;
404 break;
405 case 6:
406 help = h_compose_postponed_form;
407 break;
408 case 7:
409 help = h_compose_intrptd_postponed_form;
410 break;
411 default:
412 help = h_compose_default;
413 break;
416 snprintf(prompt, sizeof(prompt),
417 "Choose a compose method from %s : ",
418 F_ON(F_BLANK_KEYMENU,ps_global) ? letters : "the menu below");
419 prompt[sizeof(prompt)-1] = '\0';
421 chosen_task = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
422 compose_style, 'n', 'x', help, RB_NORM);
423 intrptd = postponed = form = 0;
425 switch(chosen_task){
426 case 'i':
427 intrptd = 1;
428 break;
429 case 'p':
430 postponed = 1;
431 break;
432 case 'r':
434 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
435 (*redraw)(void) = ps_global->redrawer;
437 ps_global->redrawer = NULL;
438 ps_global->next_screen = SCREEN_FUN_NULL;
439 if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
440 cmd_cancelled("Composition");
441 ps_global->next_screen = prev_screen;
442 ps_global->redrawer = redraw;
443 return;
446 ps_global->next_screen = prev_screen;
447 ps_global->redrawer = redraw;
448 if(role)
449 role = combine_inherited_role(role);
451 break;
453 case 'f':
454 form = 1;
455 break;
457 case 'x':
458 q_status_message(SM_ORDER, 0, 3,
459 "Composition cancelled");
460 return;
461 break;
463 default:
464 break;
468 if(intrptd && !outgoing){
469 char file_path[MAXPATH+1];
470 int ret = 'n';
472 file_path[0] = '\0';
473 build_path(file_path,
474 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
475 : ps_global->home_dir,
476 INTERRUPTED_MAIL, sizeof(file_path));
477 if(folder_exists(NULL, file_path) & FEX_ISFILE){
478 if((stream = pine_mail_open(NULL, file_path,
479 SP_USEPOOL|SP_TEMPUSE, NULL))
480 && !stream->halfopen){
482 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
483 (ret = redraft_prompt("Interrupted",INTRPT_PMT,'n')) =='y'){
484 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
485 &redraft_pos, &custom, &role, REDRAFT_DEL)){
486 if(stream)
487 pine_mail_close(stream);
489 return;
492 to_is_sticky++;
494 /* redraft() may or may not have closed stream */
495 if(stream)
496 pine_mail_close(stream);
498 postponed = form = 0;
500 else{
501 pine_mail_close(stream);
502 if(ret == 'x'){
503 q_status_message(SM_ORDER, 0, 3,
504 _("Composition cancelled"));
505 return;
509 else{
510 q_status_message1(SM_ORDER | SM_DING, 3, 3,
511 _("Can't open Interrupted mailbox: %s"),
512 file_path);
513 if(stream)
514 pine_mail_close(stream);
519 if(postponed && !outgoing){
520 int ret = 'n', done = 0;
521 int exists;
523 if((exists=postponed_stream(&stream,
524 ps_global->VAR_POSTPONED_FOLDER,
525 "Postponed", 0)) & FEX_ISFILE){
526 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
527 (ret = redraft_prompt("Postponed",PSTPND_PMT,'n')) == 'y'){
528 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
529 &redraft_pos, &custom, &role,
530 REDRAFT_DEL | REDRAFT_PPND))
531 done++;
533 /* stream may or may not be closed in redraft() */
534 if(stream && (stream != ps_global->mail_stream))
535 pine_mail_close(stream);
537 to_is_sticky++;
538 intrptd = form = 0;
540 else{
541 if(stream != ps_global->mail_stream)
542 pine_mail_close(stream);
544 if(ret == 'x'){
545 q_status_message(SM_ORDER, 0, 3,
546 _("Composition cancelled"));
547 done++;
551 else if(F_ON(F_ALT_COMPOSE_MENU, ps_global))
552 done++;
554 if(done)
555 return;
558 if(form && !outgoing){
559 int ret = 'n', done = 0;
560 int exists;
562 if((exists=postponed_stream(&stream,
563 ps_global->VAR_FORM_FOLDER,
564 "Form letter", 1)) & FEX_ISFILE){
565 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
566 (ret = want_to(FORM_PMT,'y','x',NO_HELP,WT_NORM))=='y'){
567 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
568 &redraft_pos, &custom, &role, REDRAFT_NONE))
569 done++;
571 /* stream may or may not be closed in redraft() */
572 if(stream && (stream != ps_global->mail_stream))
573 pine_mail_close(stream);
575 to_is_sticky++;
576 intrptd = postponed = 0;
578 else{
579 if(stream != ps_global->mail_stream)
580 pine_mail_close(stream);
582 if(ret == 'x'){
583 q_status_message(SM_ORDER, 0, 3,
584 _("Composition cancelled"));
585 done++;
589 else{
590 if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
591 q_status_message(SM_ORDER | SM_DING, 3, 3,
592 _("Form letter folder doesn't exist!"));
593 return;
597 if(done)
598 return;
601 /*-- normal composition --*/
602 if(!outgoing){
603 int impl, template_len = 0;
604 long rflags = ROLE_COMPOSE;
605 PAT_STATE dummy;
606 char *hostpart;
608 /*================= Compose new message ===============*/
609 body = mail_newbody();
610 outgoing = mail_newenvelope();
612 if(given_to)
613 rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
616 * Setup possible role
618 if(role_arg)
619 role = copy_action(role_arg);
621 if(!role){
622 /* Setup possible compose role */
623 if(nonempty_patterns(rflags, &dummy)){
625 * setup default role
626 * Msgno = -1 means there is no msg.
627 * This will match roles which have the Compose Use turned
628 * on, and have no patterns set, and match the Current
629 * Folder Type.
631 role = set_role_from_msg(ps_global, rflags, -1L, NULL);
633 if(confirm_role(rflags, &role))
634 role = combine_inherited_role(role);
635 else{ /* cancel reply */
636 role = NULL;
637 cmd_cancelled("Composition");
638 return;
643 if(role)
644 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
645 role->nick);
648 * set ps_global->hostname to something sensible, if possible,
649 * for purposes of generating a message id
651 hostpart = cpystr(ps_global->hostname);
652 fs_give((void **) &ps_global->hostname);
653 if(role && role->from)
654 ps_global->hostname = cpystr(role->from->host ? role->from->host : "huh");
655 else if(ps_global->maildomain) /* as in generate_from() */
656 ps_global->hostname = cpystr(ps_global->maildomain);
657 else
658 ps_global->hostname = cpystr(hostpart); /* all for nothing */
659 outgoing->message_id = generate_message_id(role);
660 /* undo the changes above */
661 fs_give((void **) &ps_global->hostname);
662 ps_global->hostname = cpystr(hostpart);
663 fs_give((void **) &hostpart);
666 * The type of storage object allocated below is vitally
667 * important. See SIMPLIFYING ASSUMPTION #37
669 if((body->contents.text.data = (void *) so_get(PicoText,
670 NULL, EDIT_ACCESS)) != NULL){
671 char ch;
673 if(inc_text_getc){
674 while((*inc_text_getc)(&ch))
675 if(!so_writec(ch, (STORE_S *)body->contents.text.data)){
676 break;
680 else{
681 q_status_message(SM_ORDER | SM_DING, 3, 4,
682 _("Problem creating space for message text."));
683 return;
686 if(role && role->template){
687 char *filtered;
689 impl = 1; /* leave cursor in header if not explicit */
690 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
691 if(filtered){
692 if(*filtered){
693 so_puts((STORE_S *)body->contents.text.data, filtered);
694 if(impl == 1)
695 template_len = strlen(filtered);
698 fs_give((void **)&filtered);
701 else
702 impl = 1;
704 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
705 if(impl == 2)
706 redraft_pos->offset += template_len;
708 if(*sig)
709 so_puts((STORE_S *)body->contents.text.data, sig);
711 fs_give((void **)&sig);
714 body->type = TYPETEXT;
716 if(attach)
717 create_message_body(&body, attach, 0);
720 ps_global->prev_screen = compose_screen;
721 if(!(fcc_to_free = fcc) && !(role && role->fcc))
722 fcc = fcc_arg; /* Didn't pick up fcc, use given */
725 * check whether a build_address-produced fcc is different from
726 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
728 if(fcc){
729 char *tmp_fcc = NULL;
731 if(outgoing->to){
732 tmp_fcc = get_fcc_based_on_to(outgoing->to);
733 if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
734 fcc_is_sticky++; /* cause sticky bit to get set */
737 else if((tmp_fcc = get_fcc(NULL)) != NULL &&
738 !strcmp(fcc, tmp_fcc)){
739 /* not sticky */
741 else
742 fcc_is_sticky++;
744 if(tmp_fcc)
745 fs_give((void **)&tmp_fcc);
748 pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc,
749 reply, redraft_pos, lcc, custom,
750 (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0));
752 if(reply){
753 if(reply->mailbox)
754 fs_give((void **) &reply->mailbox);
755 if(reply->origmbox)
756 fs_give((void **) &reply->origmbox);
757 if(reply->prefix)
758 fs_give((void **) &reply->prefix);
759 if(reply->data.uid.msgs)
760 fs_give((void **) &reply->data.uid.msgs);
761 fs_give((void **) &reply);
764 if(fcc_to_free)
765 fs_give((void **)&fcc_to_free);
767 if(lcc)
768 fs_give((void **)&lcc);
770 mail_free_envelope(&outgoing);
771 pine_free_body(&body);
772 free_redraft_pos(&redraft_pos);
773 free_action(&role);
777 /*----------------------------------------------------------------------
778 Args: stream -- This is where we get the postponed messages from
779 We'll expunge and close it here unless it is mail_stream.
781 These are all return values:
782 ================
783 outgoing --
784 body --
785 fcc --
786 lcc --
787 reply --
788 redraft_pos --
789 custom --
790 role --
791 ================
793 flags --
795 ----*/
797 redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body,
798 char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos,
799 PINEFIELD **custom, ACTION_S **role, int flags)
801 MAILSTREAM *stream;
802 long cont_msg = 1L;
803 STORE_S *so;
805 if(!(streamp && *streamp))
806 return(0);
808 stream = *streamp;
811 * If we're manipulating the current folder, don't bother
812 * with index
814 if(!stream->nmsgs){
815 if(REDRAFT_PPND&flags)
816 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!"));
817 else
818 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!"));
820 return(redraft_cleanup(streamp, FALSE, flags));
822 else if(stream == ps_global->mail_stream
823 && ps_global->prev_screen == mail_index_screen){
825 * Since the user's got this folder already opened and they're
826 * on a selected message, pick that one rather than rebuild
827 * another index screen...
829 cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
831 else if(stream->nmsgs > 1L){ /* offer browser ? */
832 int rv;
834 if(REDRAFT_PPND&flags){ /* set to last message postponed */
835 mn_set_cur(sp_msgmap(stream),
836 mn_get_revsort(sp_msgmap(stream))
837 ? 1L : mn_get_total(sp_msgmap(stream)));
839 else{ /* set to top form letter */
840 mn_set_cur(sp_msgmap(stream), 1L);
843 clear_index_cache(stream, 0);
844 while(1){
845 void *ti;
847 ti = stop_threading_temporarily();
848 rv = index_lister(ps_global, NULL, stream->mailbox,
849 stream, sp_msgmap(stream));
850 restore_threading(&ti);
852 cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream)));
853 if(count_flagged(stream, F_DEL)
854 && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){
855 if(REDRAFT_PPND&flags)
856 q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
857 else
858 q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
860 continue;
863 break;
866 clear_index_cache(stream, 0);
868 if(rv){
869 q_status_message(SM_ORDER, 0, 3, _("Composition cancelled"));
870 (void) redraft_cleanup(streamp, FALSE, flags);
872 if(!*streamp && !ps_global->mail_stream){
873 q_status_message2(SM_ORDER, 3, 7,
874 "No more %.200s, returning to \"%.200s\"",
875 (REDRAFT_PPND&flags) ? "postponed messages"
876 : "form letters",
877 ps_global->inbox_name);
878 if(ps_global && ps_global->ttyo){
879 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
880 ps_global->mangled_footer = 1;
883 do_broach_folder(ps_global->inbox_name,
884 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
886 ps_global->next_screen = mail_index_screen;
889 return(0); /* special case */
893 if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL)
894 return(redraft_work(streamp, cont_msg, outgoing, body,
895 fcc, lcc, reply, redraft_pos, custom,
896 role, flags, so));
897 else
898 return(0);
903 redraft_prompt(char *type, char *prompt, int failure)
905 if(background_posting(FALSE)){
906 q_status_message1(SM_ORDER, 0, 3,
907 _("%s folder unavailable while background posting"),
908 type);
909 return(failure);
912 return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM));
916 /* this is for initializing the fixed header elements in pine_send() */
918 prompt::name::help::prwid::maxlen::realaddr::
919 builder::affected_entry::next_affected::selector::key_label::fileedit::
920 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
921 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
923 static struct headerentry he_template[]={
924 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
925 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
926 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
927 {"From : ", "From", h_composer_from, 10, 0, NULL,
928 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
929 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
930 {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
931 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
932 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
933 {"To : ", "To", h_composer_to, 10, 0, NULL,
934 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
935 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK},
936 {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL,
937 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
938 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
939 {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL,
940 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
941 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
942 {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL,
943 news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL,
944 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
945 {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL,
946 NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, fcc_tab_complete,
947 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE},
948 {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL,
949 build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete,
950 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
951 {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL,
952 NULL, NULL, NULL, NULL, "To Files", NULL, NULL,
953 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE},
954 {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL,
955 valid_subject, NULL, NULL, NULL, NULL, NULL, NULL,
956 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
957 {"", "References", NO_HELP, 10, 0, NULL,
958 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
959 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
960 {"", "Date", NO_HELP, 10, 0, NULL,
961 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
962 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
963 {"", "In-Reply-To", NO_HELP, 10, 0, NULL,
964 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
965 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
966 {"", "Message-ID", NO_HELP, 10, 0, NULL,
967 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
968 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
969 {"", "X-Priority", NO_HELP, 10, 0, NULL,
970 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
971 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
972 {"", "User-Agent", NO_HELP, 10, 0, NULL,
973 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
974 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
975 {"", "To", NO_HELP, 10, 0, NULL,
976 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
977 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
978 {"", "X-Post-Error",NO_HELP, 10, 0, NULL,
979 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
980 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
981 {"", "X-Reply-UID", NO_HELP, 10, 0, NULL,
982 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
983 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
984 {"", "X-Reply-Mbox", 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 {"", "X-SMTP-Server", NO_HELP, 10, 0, NULL,
988 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
989 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
990 {"", "X-Cursor-Pos", NO_HELP, 10, 0, NULL,
991 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
992 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
993 {"", "X-Our-ReplyTo", NO_HELP, 10, 0, NULL,
994 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
995 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
996 {"", OUR_HDRS_LIST, NO_HELP, 10, 0, NULL,
997 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
998 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
999 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
1000 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1001 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
1002 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
1003 {"", "Sender", NO_HELP, 10, 0, NULL,
1004 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1005 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
1006 #endif
1010 static struct headerentry he_custom_addr_templ={
1011 NULL, NULL, h_composer_custom_addr,10, 0, NULL,
1012 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
1013 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK};
1015 static struct headerentry he_custom_free_templ={
1016 NULL, NULL, h_composer_custom_free,10, 0, NULL,
1017 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1018 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE};
1021 /*----------------------------------------------------------------------
1022 Get addressee for message, then post message
1024 Args: outgoing -- Partially formatted outgoing ENVELOPE
1025 body -- Body of outgoing message
1026 prmpt_who -- Optional prompt for optionally_enter call
1027 prmpt_cnf -- Optional prompt for confirmation call
1028 used_tobufval -- The string that the to was eventually set equal to.
1029 This gets passed back if non-NULL on entry.
1030 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1031 SS_NULLRP - Use null return-path so we'll send an
1032 SMTP MAIL FROM: <>
1034 Result: message "To: " field is provided and message is sent or cancelled.
1036 Fields:
1037 remail -
1038 return_path -
1039 date added here
1040 from added here
1041 sender -
1042 reply_to -
1043 subject passed in, NOT edited but maybe canonized here
1044 to possibly passed in, edited and canonized here
1045 cc -
1046 bcc -
1047 in_reply_to -
1048 message_id -
1050 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1051 with the first part TYPETEXT! All newlines in the text here also end with
1052 CRLF.
1054 Returns 0 on success, -1 on failure.
1055 ----*/
1057 pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */
1058 struct mail_bodystruct **body,
1059 ACTION_S **rolep,
1060 char *prmpt_who,
1061 char *prmpt_cnf,
1062 char **used_tobufval,
1063 int flagsarg)
1065 char **tobufp, *p, tmp[MAILTMPLEN];
1066 void *messagebuf;
1067 int done = 0, retval = 0, x;
1068 int lastrc, rc = 0, ku, i, resize_len, result, fcc_result;
1069 int og2s_done = 0;
1070 HelpType help;
1071 static HISTORY_S *history = NULL;
1072 ESCKEY_S ekey[6];
1073 BUILDER_ARG ba_fcc;
1074 METAENV *header;
1075 ACTION_S *role = rolep ? *rolep : NULL;
1076 PAT_STATE pstate;
1078 dprint((1,"\n === simple send called === \n"));
1080 memset(&ba_fcc, 0, sizeof(BUILDER_ARG));
1082 init_hist(&history, HISTSIZE);
1084 header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp);
1086 /*----- Fill in a few general parts of the envelope ----*/
1087 if(!outgoing->date){
1088 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1089 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
1091 rfc822_date(tmp_20k_buf); /* format and copy new date */
1092 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1093 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
1095 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
1098 if(!outgoing->from){
1099 if(role && role->from){
1100 if(ps_global->never_allow_changing_from)
1101 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1102 else
1103 outgoing->from = copyaddrlist(role->from);
1105 else
1106 outgoing->from = generate_from();
1109 if(!(flagsarg & SS_NULLRP))
1110 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1112 ekey[i = 0].ch = ctrl('T');
1113 ekey[i].rval = 2;
1114 ekey[i].name = "^T";
1115 ekey[i++].label = N_("To AddrBk");
1117 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1118 ekey[i].ch = ctrl('I');
1119 ekey[i].rval = 11;
1120 ekey[i].name = "TAB";
1121 ekey[i++].label = N_("Complete");
1124 if(nonempty_patterns(ROLE_DO_ROLES, &pstate) && first_pattern(&pstate)){
1125 ekey[i].ch = ctrl('R');
1126 ekey[i].rval = 15;
1127 ekey[i].name = "^R";
1128 ekey[i++].label = "Set Role";
1131 ekey[i].ch = KEY_UP;
1132 ekey[i].rval = 30;
1133 ekey[i].name = "";
1134 ku = i;
1135 ekey[i++].label = "";
1137 ekey[i].ch = KEY_DOWN;
1138 ekey[i].rval = 31;
1139 ekey[i].name = "";
1140 ekey[i++].label = "";
1142 ekey[i].ch = -1;
1144 if(outgoing->remail == NULL)
1145 strcpy(tmp, _("FORWARD (as e-mail) to : "));
1147 /*----------------------------------------------------------------------
1148 Loop editing the "To: " field until everything goes well
1149 ----*/
1150 help = NO_HELP;
1152 while(!done){
1153 int flags;
1155 if(outgoing->message_id)
1156 fs_give((void **) &outgoing->message_id);
1158 outgoing->message_id = generate_message_id(role);
1160 if(outgoing->remail){
1161 if(role)
1162 snprintf(tmp, sizeof(tmp), _("BOUNCE (redirect) message using role \"%s\" to : "), role->nick);
1163 else
1164 strncpy(tmp, _("BOUNCE (redirect) message to : "), sizeof(tmp));
1165 tmp[sizeof(tmp)-1] = '\0';
1168 if(!og2s_done){
1169 og2s_done++;
1170 outgoing2strings(header, *body, &messagebuf, NULL, 1);
1173 lastrc = rc;
1174 if(flagsarg & SS_PROMPTFORTO){
1175 if(!*tobufp)
1176 *tobufp = cpystr("");
1178 resize_len = MAX(MAXPATH, strlen(*tobufp));
1179 fs_resize((void **) tobufp, resize_len+1);
1181 if(items_in_hist(history) > 0){
1182 ekey[ku].name = HISTORY_UP_KEYNAME;
1183 ekey[ku].label = HISTORY_KEYLABEL;
1184 ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
1185 ekey[ku+1].label = HISTORY_KEYLABEL;
1187 else{
1188 ekey[ku].name = "";
1189 ekey[ku].label = "";
1190 ekey[ku+1].name = "";
1191 ekey[ku+1].label = "";
1194 flags = OE_APPEND_CURRENT;
1196 rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
1197 0, resize_len,
1198 prmpt_who
1199 ? prmpt_who
1200 : tmp,
1201 ekey, help, &flags);
1203 else
1204 rc = 0;
1206 switch(rc){
1207 case -1:
1208 q_status_message(SM_ORDER | SM_DING, 3, 4,
1209 "Internal problem encountered");
1210 retval = -1;
1211 done++;
1212 break;
1214 case 15 : /* set a role */
1215 {void (*prev_screen)(struct pine *) = NULL, (*redraw)(void) = NULL;
1217 redraw = ps_global->redrawer;
1218 ps_global->redrawer = NULL;
1219 prev_screen = ps_global->prev_screen;
1220 role = NULL;
1221 ps_global->next_screen = SCREEN_FUN_NULL;
1223 if(role_select_screen(ps_global, &role,
1224 outgoing->remail ? MC_BOUNCE : MC_FORWARD) < 0)
1225 cmd_cancelled(_("Set Role"));
1226 else{
1227 if(role)
1228 role = combine_inherited_role(role);
1229 else{
1230 role = (ACTION_S *) fs_get(sizeof(*role));
1231 memset((void *) role, 0, sizeof(*role));
1232 role->nick = cpystr("Default Role");
1236 if(redraw)
1237 (*redraw)();
1239 ps_global->next_screen = prev_screen;
1240 ps_global->redrawer = redraw;
1241 ps_global->mangled_screen = 1;
1243 if(role && role->from && !ps_global->never_allow_changing_from){
1244 mail_free_address (&outgoing->from);
1245 outgoing->from = copyaddrlist(role->from);
1246 if(!(flagsarg & SS_NULLRP)){
1247 fs_give((void **) &outgoing->return_path);
1248 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1251 if(rolep) *rolep = role;
1253 break;
1255 case 30 :
1256 if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){
1257 strncpy(*tobufp, p, resize_len);
1258 (*tobufp)[resize_len-1] = '\0';
1260 else
1261 Writechar(BELL, 0);
1263 break;
1265 case 31 :
1266 if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){
1267 strncpy(*tobufp, p, resize_len);
1268 (*tobufp)[resize_len-1] = '\0';
1270 else
1271 Writechar(BELL, 0);
1273 break;
1275 case 2: /* ^T */
1276 case 0:
1277 {void (*redraw) (void) = ps_global->redrawer;
1278 char *returned_addr = NULL;
1279 int len, l;
1281 if(rc == 2){
1282 int got_something = 0;
1284 push_titlebar_state();
1285 returned_addr = addr_book_bounce();
1288 * Just make it look like user typed this list in.
1290 if(returned_addr){
1291 got_something++;
1292 if((l=resize_len) < (len = strlen(returned_addr)) + 1){
1293 l = len;
1294 fs_resize((void **) tobufp, (size_t) (l+1));
1297 strncpy(*tobufp, returned_addr, l);
1298 (*tobufp)[l] = '\0';
1299 fs_give((void **)&returned_addr);
1302 ClearScreen();
1303 pop_titlebar_state();
1304 redraw_titlebar();
1305 if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
1306 (*ps_global->redrawer)();
1308 if(!got_something)
1309 continue;
1312 if(*tobufp && **tobufp != '\0'){
1313 char *errbuf, *addr;
1314 int tolen;
1316 save_hist(history, *tobufp, 0, NULL);
1318 errbuf = NULL;
1321 * If role has an fcc, use it instead of what build_address
1322 * tells us.
1324 if(role && role->fcc){
1325 if(ba_fcc.tptr)
1326 fs_give((void **) &ba_fcc.tptr);
1328 ba_fcc.tptr = cpystr(role->fcc);
1331 if(build_address(*tobufp, &addr, &errbuf,
1332 (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){
1333 int sendit = 0;
1335 if(errbuf)
1336 fs_give((void **)&errbuf);
1338 if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){
1339 l = tolen;
1340 fs_resize((void **) tobufp, (size_t) (l+1));
1343 strncpy(*tobufp, addr, l);
1344 (*tobufp)[l] = '\0';
1345 if(used_tobufval)
1346 *used_tobufval = cpystr(addr);
1348 /* confirm address */
1349 if(flagsarg & SS_PROMPTFORTO){
1350 char dsn_string[30];
1351 int dsn_label = 0, dsn_show, i;
1352 int verbose_label = 0;
1353 ESCKEY_S opts[13];
1355 strings2outgoing(header, body, NULL, 0);
1357 if((flagsarg & SS_PROMPTFORTO)
1358 && ((x = check_addresses(header)) == CA_BAD
1359 || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE,
1360 ps_global))))
1361 /*--- Addresses didn't check out---*/
1362 continue;
1364 i = 0;
1365 opts[i].ch = 'y';
1366 opts[i].rval = 'y';
1367 opts[i].name = "Y";
1368 opts[i++].label = N_("Yes");
1370 opts[i].ch = 'n';
1371 opts[i].rval = 'n';
1372 opts[i].name = "N";
1373 opts[i++].label = N_("No");
1375 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
1376 if(F_ON(F_VERBOSE_POST, ps_global)){
1377 /* setup keymenu slot to toggle verbose mode */
1378 opts[i].ch = ctrl('W');
1379 opts[i].rval = 12;
1380 opts[i].name = "^W";
1381 verbose_label = i++;
1382 if(F_ON(F_DSN, ps_global)){
1383 opts[i].ch = 0;
1384 opts[i].rval = 0;
1385 opts[i].name = "";
1386 opts[i++].label = "";
1390 /* clear DSN flags */
1391 call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL);
1392 if(F_ON(F_DSN, ps_global)){
1393 /* setup keymenu slots to toggle dsn bits */
1394 opts[i].ch = 'd';
1395 opts[i].rval = 'd';
1396 opts[i].name = "D";
1397 opts[i].label = "DSNOpts";
1398 dsn_label = i++;
1399 opts[i].ch = -2;
1400 opts[i].rval = 's';
1401 opts[i].name = "S";
1402 opts[i++].label = "";
1403 opts[i].ch = -2;
1404 opts[i].rval = 'x';
1405 opts[i].name = "X";
1406 opts[i++].label = "";
1407 opts[i].ch = -2;
1408 opts[i].rval = 'h';
1409 opts[i].name = "H";
1410 opts[i++].label = "";
1413 opts[i].ch = -1;
1415 while(1){
1416 int rv;
1418 dsn_show = (call_mailer_flags & CM_DSN_SHOW);
1419 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
1420 "%s%s%s%s%s%sto \"%s\" ? ",
1421 prmpt_cnf ? prmpt_cnf : "Send message ",
1422 ((call_mailer_flags & CM_VERBOSE)
1423 || (dsn_show))
1424 ? "(" : "",
1425 (call_mailer_flags & CM_VERBOSE)
1426 ? "in verbose mode" : "",
1427 (dsn_show && (call_mailer_flags & CM_VERBOSE))
1428 ? ", " : "",
1429 (dsn_show) ? dsn_string : "",
1430 ((call_mailer_flags & CM_VERBOSE) || dsn_show)
1431 ? ") " : "",
1432 (addr && *addr)
1433 ? addr
1434 : (F_ON(F_FCC_ON_BOUNCE, ps_global)
1435 && ba_fcc.tptr && ba_fcc.tptr[0])
1436 ? ba_fcc.tptr
1437 : "");
1438 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1440 if((strlen(tmp_20k_buf) >
1441 ps_global->ttyo->screen_cols - 2) &&
1442 ps_global->ttyo->screen_cols >= 7)
1443 strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7,
1444 "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7);
1446 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1448 if(verbose_label)
1449 opts[verbose_label].label =
1450 /* TRANSLATORS: several possible key labels follow */
1451 (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
1453 if(F_ON(F_DSN, ps_global)){
1454 if(call_mailer_flags & CM_DSN_SHOW){
1455 opts[dsn_label].label =
1456 (call_mailer_flags & CM_DSN_DELAY)
1457 ? N_("NoDelay") : N_("Delay");
1458 opts[dsn_label+1].ch = 's';
1459 opts[dsn_label+1].label =
1460 (call_mailer_flags & CM_DSN_SUCCESS)
1461 ? N_("NoSuccess") : N_("Success");
1462 opts[dsn_label+2].ch = 'x';
1463 opts[dsn_label+2].label =
1464 (call_mailer_flags & CM_DSN_NEVER)
1465 ? N_("ErrRets") : N_("NoErrRets");
1466 opts[dsn_label+3].ch = 'h';
1467 opts[dsn_label+3].label =
1468 (call_mailer_flags & CM_DSN_FULL)
1469 ? N_("RetHdrs") : N_("RetFull");
1473 rv = radio_buttons(tmp_20k_buf,
1474 -FOOTER_ROWS(ps_global), opts,
1475 'y', 'z', NO_HELP, RB_NORM);
1476 if(rv == 'y'){ /* user ACCEPTS! */
1477 sendit = 1;
1478 break;
1480 else if(rv == 'n'){ /* Declined! */
1481 break;
1483 else if(rv == 'z'){ /* Cancelled! */
1484 break;
1486 else if(rv == 12){ /* flip verbose bit */
1487 if(call_mailer_flags & CM_VERBOSE)
1488 call_mailer_flags &= ~CM_VERBOSE;
1489 else
1490 call_mailer_flags |= CM_VERBOSE;
1492 else if(call_mailer_flags & CM_DSN_SHOW){
1493 if(rv == 's'){ /* flip success bit */
1494 call_mailer_flags ^= CM_DSN_SUCCESS;
1495 /* turn off related bits */
1496 if(call_mailer_flags & CM_DSN_SUCCESS)
1497 call_mailer_flags &= ~(CM_DSN_NEVER);
1499 else if(rv == 'd'){ /* flip delay bit */
1500 call_mailer_flags ^= CM_DSN_DELAY;
1501 /* turn off related bits */
1502 if(call_mailer_flags & CM_DSN_DELAY)
1503 call_mailer_flags &= ~(CM_DSN_NEVER);
1505 else if(rv == 'x'){ /* flip never bit */
1506 call_mailer_flags ^= CM_DSN_NEVER;
1507 /* turn off related bits */
1508 if(call_mailer_flags & CM_DSN_NEVER)
1509 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
1511 else if(rv == 'h'){ /* flip full bit */
1512 call_mailer_flags ^= CM_DSN_FULL;
1515 else if(rv == 'd'){ /* show dsn options */
1516 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
1519 snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"),
1520 (call_mailer_flags & CM_DSN_NEVER)
1521 ? _("Never") : "F",
1522 (call_mailer_flags & CM_DSN_DELAY)
1523 ? "D" : "",
1524 (call_mailer_flags & CM_DSN_SUCCESS)
1525 ? "S" : "",
1526 (call_mailer_flags & CM_DSN_NEVER)
1527 ? ""
1528 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
1529 : "-Hdrs");
1530 dsn_string[sizeof(dsn_string)-1] = '\0';
1534 if(addr)
1535 fs_give((void **)&addr);
1537 if(!(flagsarg & SS_PROMPTFORTO) || sendit){
1538 char *fcc = NULL;
1539 CONTEXT_S *fcc_cntxt = NULL;
1541 if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
1542 if(ba_fcc.tptr)
1543 fcc = cpystr(ba_fcc.tptr);
1545 set_last_fcc(fcc);
1548 * If special name "inbox" then replace it with the
1549 * real inbox path.
1551 if(ps_global->VAR_INBOX_PATH
1552 && strucmp(fcc, ps_global->inbox_name) == 0){
1553 char *replace_fcc;
1555 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
1556 fs_give((void **) &fcc);
1557 fcc = replace_fcc;
1561 /*---- Check out fcc -----*/
1562 if(fcc && *fcc){
1563 (void) commence_fcc(fcc, &fcc_cntxt, FALSE);
1564 if(!lmc.so){
1565 dprint((4,"can't open fcc, cont\n"));
1566 if(!(flagsarg & SS_PROMPTFORTO)){
1567 retval = -1;
1568 fs_give((void **)&fcc);
1569 fcc = NULL;
1570 goto finish;
1572 else
1573 continue;
1575 else
1576 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
1578 else
1579 lmc.so = NULL;
1581 if(!(outgoing->to || outgoing->cc || outgoing->bcc
1582 || lmc.so)){
1583 q_status_message(SM_ORDER, 3, 5, _("No recipients specified!"));
1584 continue;
1587 if(outgoing->to || outgoing->cc || outgoing->bcc){
1588 char **alt_smtp = NULL;
1590 if(role && role->smtp){
1591 if(ps_global->FIX_SMTP_SERVER
1592 && ps_global->FIX_SMTP_SERVER[0])
1593 q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1594 else
1595 alt_smtp = role->smtp;
1598 result = call_mailer(header, *body, alt_smtp,
1599 call_mailer_flags,
1600 call_mailer_file_result,
1601 pipe_callback);
1602 mark_address_failure_for_pico(header);
1604 else
1605 result = 0;
1607 if(result == 1 && !lmc.so)
1608 q_status_message(SM_ORDER, 0, 3, _("Message sent"));
1610 /*----- Was there an fcc involved? -----*/
1611 if(lmc.so){
1612 if(result == 1
1613 || (result == 0
1614 && pine_rfc822_output(header, *body, NULL, NULL))){
1615 char label[50];
1617 strncpy(label, "Fcc", sizeof(label));
1618 label[sizeof(label)-1] = '\0';
1619 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
1620 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
1621 label[sizeof(label)-1] = '\0';
1624 /* Now actually copy to fcc folder and close */
1625 fcc_result =
1626 write_fcc(fcc, fcc_cntxt, lmc.so, NULL,
1627 label,
1628 F_ON(F_MARK_FCC_SEEN, ps_global)
1629 ? "\\SEEN" : NULL);
1631 else if(result == 0){
1632 q_status_message(SM_ORDER,3,5,
1633 _("Fcc Failed!. No message saved."));
1634 retval = -1;
1635 dprint((1, "explicit fcc write failed!\n"));
1638 so_give(&lmc.so);
1641 if(result < 0){
1642 dprint((1, "Bounce failed\n"));
1643 if(!(flagsarg & SS_PROMPTFORTO))
1644 retval = -1;
1645 else
1646 continue;
1648 else if(result == 1){
1649 if(!fcc)
1650 q_status_message(SM_ORDER, 0, 3,
1651 _("Message sent"));
1652 else{
1653 int avail = ps_global->ttyo->screen_cols-2;
1654 int need, fcclen;
1655 char *part1 = "Message sent and ";
1656 char *part2 = fcc_result ? "" : "NOT ";
1657 char *part3 = "copied to ";
1658 fcclen = strlen(fcc);
1660 need = 2 + strlen(part1) + strlen(part2) +
1661 strlen(part3) + fcclen;
1663 if(need > avail && fcclen > 6)
1664 fcclen -= MIN(fcclen-6, need-avail);
1666 q_status_message4(SM_ORDER, 0, 3,
1667 "%s%s%s\"%s\"",
1668 part1, part2, part3,
1669 short_str(fcc,
1670 (char *)tmp_20k_buf,
1671 SIZEOF_20KBUF,
1672 fcclen, FrontDots));
1676 if(fcc)
1677 fs_give((void **)&fcc);
1679 else{
1680 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1681 retval = -1;
1684 else{
1685 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1686 _("Error in address: %s"), errbuf);
1687 if(errbuf)
1688 fs_give((void **)&errbuf);
1690 if(!(flagsarg & SS_PROMPTFORTO))
1691 retval = -1;
1692 else
1693 continue;
1697 else{
1698 q_status_message(SM_ORDER | SM_DING, 3, 5,
1699 _("No addressee! No e-mail sent."));
1700 retval = -1;
1704 done++;
1705 break;
1707 case 1:
1708 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1709 done++;
1710 retval = -1;
1711 break;
1713 case 3:
1714 help = (help == NO_HELP)
1715 ? (outgoing->remail == NULL
1716 ? h_anon_forward
1717 : h_bounce)
1718 : NO_HELP;
1719 break;
1721 case 11:
1722 if(**tobufp){
1723 char *new_nickname = NULL;
1724 int l;
1725 int ambiguity;
1727 ambiguity = abook_nickname_complete(*tobufp, &new_nickname,
1728 (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA);
1729 if(new_nickname){
1730 if(*new_nickname){
1731 if((l=strlen(new_nickname)) > resize_len){
1732 resize_len = l;
1733 fs_resize((void **) tobufp, resize_len+1);
1736 strncpy(*tobufp, new_nickname, l);
1737 (*tobufp)[l] = '\0';
1740 fs_give((void **) &new_nickname);
1743 if(ambiguity != 2)
1744 Writechar(BELL, 0);
1747 break;
1749 case 4: /* can't suspend */
1750 default:
1751 break;
1755 finish:
1756 if(ba_fcc.tptr)
1757 fs_give((void **)&ba_fcc.tptr);
1759 pine_free_env(&header);
1761 return(retval);
1766 * pine_simple_send_header - generate header suitable for simple_sending
1768 METAENV *
1769 pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp)
1771 METAENV *header;
1772 PINEFIELD *pf;
1773 static struct headerentry he_dummy;
1775 header = pine_new_env(outgoing, fccp, tobufpp, NULL);
1777 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1778 for(pf = header->local; pf && pf->name; pf = pf->next)
1779 if(pf->type == Address && !strucmp(pf->name, "to")){
1780 memset((void *) &he_dummy, 0, sizeof(he_dummy));
1781 pf->extdata = (void *) &he_dummy;
1782 HE(pf)->dirty = 1;
1783 break;
1786 return(header);
1791 /*----------------------------------------------------------------------
1792 Prepare data structures for pico, call pico, then post message
1794 Args: outgoing -- Partially formatted outgoing ENVELOPE
1795 body -- Body of outgoing message
1796 editor_title -- Title for anchor line in composer
1797 fcc_arg -- The file carbon copy field
1798 reply -- Struct describing set of msgs being replied-to
1799 lcc_arg --
1800 custom -- custom header list.
1801 sticky_fcc --
1803 Result: message is edited, then postponed, cancelled or sent.
1805 Fields:
1806 remail -
1807 return_path -
1808 date added here
1809 from added here
1810 sender -
1811 reply_to -
1812 subject passed in, edited and cannonized here
1813 to possibly passed in, edited and cannonized here
1814 cc possibly passed in, edited and cannonized here
1815 bcc edited and cannonized here
1816 in_reply_to generated in reply() and passed in
1817 message_id -
1819 Storage for these fields comes from anywhere outside. It is remalloced
1820 here so the composer can realloc them if needed. The copies here are also
1821 freed here.
1823 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1824 with the first part TYPETEXT! All newlines in the text here also end with
1825 CRLF.
1827 There's a further assumption that the text in the TYPETEXT part is
1828 stored in a storage object (see filter.c).
1829 ----*/
1830 void
1831 pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
1832 char *editor_title, ACTION_S *role, char *fcc_arg,
1833 REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg,
1834 PINEFIELD *custom, int flags)
1836 int i, fixed_cnt, total_cnt, index,
1837 editor_result = 0, body_start = 0, use_news_order = 0;
1838 char *p, *addr, *fcc, *fcc_to_free = NULL;
1839 char *start_here_name = NULL;
1840 char *suggested_nntp_server = NULL;
1841 char *title = NULL;
1842 struct headerentry *he, *headents, *he_to, *he_fcc, *he_news = NULL, *he_lcc = NULL,
1843 *he_from = NULL;
1844 PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL,
1845 *pf_smtp_server, *pf_nntp_server,
1846 *pf_fcc = NULL, *pf_err, *pf_uid, *pf_mbox, *pf_curpos,
1847 *pf_ourrep, *pf_ourhdrs, **sending_order;
1848 METAENV header;
1849 ADDRESS *lcc_addr = NULL;
1850 ADDRESS *nobody_addr = NULL;
1851 BODY_PARTICULARS_S *bp;
1852 STORE_S *orig_so = NULL;
1853 PICO pbuf1, *save_previous_pbuf;
1854 CustomType ct;
1855 REDRAFT_POS_S *local_redraft_pos = NULL;
1857 dprint((1,"\n=== send called ===\n"));
1859 save_previous_pbuf = pbf;
1860 pbf = &pbuf1;
1861 standard_picobuf_setup(pbf);
1864 * Cancel any pending initial commands since pico uses a different
1865 * input routine. If we didn't cancel them, they would happen after
1866 * we returned from the editor, which would be confusing.
1868 if(ps_global->in_init_seq){
1869 ps_global->in_init_seq = 0;
1870 ps_global->save_in_init_seq = 0;
1871 clear_cursor_pos();
1872 if(ps_global->initial_cmds){
1873 if(ps_global->free_initial_cmds)
1874 fs_give((void **)&(ps_global->free_initial_cmds));
1876 ps_global->initial_cmds = 0;
1879 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1882 #if defined(DOS) || defined(OS2)
1883 if(!dos_valid_from()){
1884 pbf = save_previous_pbuf;
1885 return;
1888 pbf->upload = NULL;
1889 #else
1890 pbf->upload = (ps_global->VAR_UPLOAD_CMD
1891 && ps_global->VAR_UPLOAD_CMD[0])
1892 ? upload_msg_to_pico : NULL;
1893 #endif
1895 pbf->msgntext = message_format_for_pico;
1896 pbf->mimetype = mime_type_for_pico;
1897 pbf->exittest = send_exit_for_pico;
1898 pbf->user_says_noflow = dont_flow_this_time;
1899 pbf->newthread = new_thread_on_blank_subject;
1900 ps_global->newthread = 0; /* reset this value */
1901 if(F_OFF(F_CANCEL_CONFIRM, ps_global))
1902 pbf->canceltest = cancel_for_pico;
1903 #ifdef _WINDOWS
1904 pbf->dict = (ps_global->VAR_DICTIONARY
1905 && ps_global->VAR_DICTIONARY[0]
1906 && ps_global->VAR_DICTIONARY[0][0])
1907 ? ps_global->VAR_DICTIONARY : NULL;
1908 pbf->chosen_dict = -1; /* not chosen yet */
1909 #endif /* _WINDOWS */
1910 pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] &&
1911 ps_global->VAR_EDITOR[0][0])
1912 ? ps_global->VAR_EDITOR : NULL;
1913 pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0])
1914 ? ps_global->VAR_SPELLER : NULL;
1915 pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
1916 pbf->quote_str = reply && reply->prefix ? reply->prefix : "> ";
1917 /* We actually want to set this only if message we're sending is flowed */
1918 pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
1919 pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
1920 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
1921 && (strcmp(pbf->quote_str, "> ") == 0
1922 || strcmp(pbf->quote_str, ">") == 0));
1923 pbf->edit_offset = 0;
1924 title = cpystr(set_titlebar(editor_title,
1925 ps_global->mail_stream,
1926 ps_global->context_current,
1927 ps_global->cur_folder,ps_global->msgmap,
1928 0, FolderName, 0, 0, NULL));
1929 pbf->pine_anchor = title;
1931 #if defined(DOS) || defined(OS2)
1932 if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){
1933 pbf->oper_dir = ps_global->VAR_FILE_DIR;
1935 #endif
1937 if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE))
1938 pbf->pine_flags |= P_CHKPTNOW;
1940 /* NOTE: initial cursor position set below */
1942 dprint((9, "flags: %x\n", pbf->pine_flags));
1945 * When user runs compose and the current folder is a newsgroup,
1946 * offer to post to the current newsgroup.
1948 if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups))
1949 && IS_NEWS(ps_global->mail_stream)){
1950 char prompt[200], news_group[MAILTMPLEN];
1952 pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group,
1953 sizeof(news_group));
1956 * Replies don't get this far because To or Newsgroups will already
1957 * be filled in. So must be either ordinary compose or forward.
1958 * Forward sets subject, so use that to tell the difference.
1960 if(news_group[0] && !outgoing->subject){
1961 int ch = 'y';
1962 int ret_val;
1963 char *errmsg = NULL;
1964 BUILDER_ARG *fcc_build = NULL;
1966 if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
1967 snprintf(prompt, sizeof(prompt),
1968 _("Post to current newsgroup (%s)"), news_group);
1969 prompt[sizeof(prompt)-1] = '\0';
1970 ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM);
1973 switch(ch){
1974 case 'y':
1975 if(outgoing->newsgroups)
1976 fs_give((void **)&outgoing->newsgroups);
1978 if(!fcc_arg && !(role && role->fcc)){
1979 fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
1980 memset((void *)fcc_build, 0, sizeof(BUILDER_ARG));
1981 fcc_build->tptr = fcc_to_free;
1984 ret_val = news_build(news_group, &outgoing->newsgroups,
1985 &errmsg, fcc_build, NULL);
1987 if(ret_val == -1){
1988 if(outgoing->newsgroups)
1989 fs_give((void **)&outgoing->newsgroups);
1991 outgoing->newsgroups = cpystr(news_group);
1994 if(!fcc_arg && !(role && role->fcc)){
1995 fcc_arg = fcc_to_free = fcc_build->tptr;
1996 fs_give((void **)&fcc_build);
1999 if(errmsg){
2000 if(*errmsg){
2001 q_status_message(SM_ORDER, 3, 3, errmsg);
2002 display_message(NO_OP_COMMAND);
2005 fs_give((void **)&errmsg);
2008 break;
2010 case 'x': /* ^C */
2011 q_status_message(SM_ORDER, 0, 3, _("Message cancelled"));
2012 dprint((4, "=== send: cancelled\n"));
2013 pbf = save_previous_pbuf;
2014 return;
2016 case 'n':
2017 break;
2019 default:
2020 break;
2024 if(F_ON(F_PREDICT_NNTP_SERVER, ps_global)
2025 && outgoing->newsgroups && *outgoing->newsgroups
2026 && IS_NEWS(ps_global->mail_stream)){
2027 NETMBX news_mb;
2029 if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox,
2030 &news_mb))
2031 if(!strucmp(news_mb.service, "nntp")){
2032 if(*ps_global->mail_stream->original_mailbox == '{'){
2033 char *svcp = NULL, *psvcp;
2035 suggested_nntp_server =
2036 cpystr(ps_global->mail_stream->original_mailbox + 1);
2037 if((p = strindex(suggested_nntp_server, '}')) != NULL)
2038 *p = '\0';
2039 for(p = strindex(suggested_nntp_server, '/'); p && *p;
2040 p = strindex(p, '/')){
2041 /* take out /nntp, which gets added in nntp_open */
2042 if(!struncmp(p, "/nntp", 5))
2043 svcp = p + 5;
2044 else if(!struncmp(p, "/service=nntp", 13))
2045 svcp = p + 13;
2046 else if(!struncmp(p, "/service=\"nntp\"", 15))
2047 svcp = p + 15;
2048 else
2049 p++;
2050 if(svcp){
2051 if(*svcp == '\0')
2052 *p = '\0';
2053 else if(*svcp == '/' || *svcp == ':'){
2054 for(psvcp = p; *svcp; svcp++, psvcp++)
2055 *psvcp = *svcp;
2056 *psvcp = '\0';
2058 svcp = NULL;
2062 else
2063 suggested_nntp_server = cpystr(news_mb.orighost);
2068 * If we don't already have custom headers set and the role has custom
2069 * headers, then incorporate those custom headers into "custom".
2071 if(!custom){
2072 PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL;
2074 dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
2076 * If we allow the Combine argument here, we're saying that we want to
2077 * combine the values from the envelope and the role for the fields To,
2078 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
2079 * we'll have a To in the envelope because we're replying. If our role also
2080 * has a To action, then Combine would combine those two and offer both
2081 * to the user. We've decided against doing this. Instead, we always use
2082 * Replace, and the role's header value replaces the value from the
2083 * envelope. It might also make sense in some cases to do the opposite,
2084 * which would be treating the role headers as defaults, just like
2085 * customized-hdrs.
2087 #ifdef WANT_TO_COMBINE_ADDRESSES
2088 if(role && role->cstm)
2089 rolehdrs = parse_custom_hdrs(role->cstm, Combine);
2090 #else
2091 if(role && role->cstm)
2092 rolehdrs = parse_custom_hdrs(role->cstm, Replace);
2093 #endif
2095 if(rolehdrs){
2096 custom = combine_custom_headers(dflthdrs, rolehdrs);
2097 if(dflthdrs){
2098 free_prompts(dflthdrs);
2099 free_customs(dflthdrs);
2102 if(rolehdrs){
2103 free_prompts(rolehdrs);
2104 free_customs(rolehdrs);
2107 else
2108 custom = dflthdrs;
2111 g_rolenick = role ? role->nick : NULL;
2113 /* how many fixed fields are there? */
2114 for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++)
2117 total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1);
2119 /* the fixed part of the PINEFIELDs */
2120 i = fixed_cnt * sizeof(PINEFIELD);
2121 pfields = (PINEFIELD *)fs_get((size_t) i);
2122 memset(pfields, 0, (size_t) i);
2124 /* temporary headerentry array for pico */
2125 i = (total_cnt + 1) * sizeof(struct headerentry);
2126 headents = (struct headerentry *)fs_get((size_t) i);
2127 memset(headents, 0, (size_t) i);
2129 i = total_cnt * sizeof(PINEFIELD *);
2130 sending_order = (PINEFIELD **)fs_get((size_t) i);
2131 memset(sending_order, 0, (size_t) i);
2133 pbf->headents = headents;
2134 header.env = outgoing;
2135 header.local = pfields;
2136 header.sending_order = sending_order;
2138 /* custom part of PINEFIELDs */
2139 header.custom = custom;
2141 he = headents;
2142 pf = pfields;
2145 * For Address types, pf->addr points to an ADDRESS *.
2146 * If that address is in the "outgoing" envelope, it will
2147 * be freed by the caller, otherwise, it should be freed here.
2148 * Pf->textbuf for an Address is used a little to set up a default,
2149 * but then is freed right away below. Pf->scratch is used for a
2150 * pointer to some alloced space for pico to edit in. Addresses in
2151 * the custom area are freed by free_customs().
2153 * For FreeText types, pf->addr is not used. Pf->text points to a
2154 * pointer that points to the text. Pf->textbuf points to a copy of
2155 * the text that must be freed before we leave, otherwise, it is
2156 * probably a pointer into the envelope and that gets freed by the
2157 * caller.
2159 * He->realaddr is the pointer to the text that pico actually edits.
2162 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2163 # define NN 4
2164 #else
2165 # define NN 3
2166 #endif
2168 if(outgoing->newsgroups && *outgoing->newsgroups)
2169 use_news_order++;
2171 /* initialize the fixed header elements of the two temp arrays */
2172 for(i=0; i < fixed_cnt; i++, pf++){
2173 static int news_order[] = {
2174 N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC,
2175 N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY,
2176 N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX,
2177 N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS
2178 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2179 , N_SENDER
2180 #endif
2183 index = i;
2184 /* slightly different editing order if sending to news */
2185 if(use_news_order &&
2186 index >= 0 && index < sizeof(news_order)/sizeof(news_order[0]))
2187 index = news_order[i];
2189 /* copy the templates */
2190 *he = he_template[index];
2192 pf->name = cpystr(pf_template[index].name);
2193 if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global)){
2194 /* slide string over so it is Sender instead of X-X-Sender */
2195 for(p = pf->name+4; *p != '\0'; p++)
2196 *(p-4) = *p;
2197 *(p-4) = '\0';
2199 pf->type = pf_template[index].type;
2200 pf->canedit = pf_template[index].canedit;
2201 pf->rcptto = pf_template[index].rcptto;
2202 pf->writehdr = pf_template[index].writehdr;
2203 pf->localcopy = pf_template[index].localcopy;
2204 pf->extdata = he;
2205 pf->next = pf + 1;
2207 he->rich_header = view_as_rich(pf->name, he->rich_header);
2208 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2209 he->nickcmpl = NULL;
2211 switch(pf->type){
2212 case FreeText: /* realaddr points to c-client env */
2213 if(index == N_NEWS){
2214 sending_order[1] = pf;
2215 he->realaddr = &outgoing->newsgroups;
2216 he_news = he;
2218 switch(set_default_hdrval(pf, custom)){
2219 case Replace:
2220 if(*he->realaddr)
2221 fs_give((void **)he->realaddr);
2223 *he->realaddr = pf->textbuf;
2224 pf->textbuf = NULL;
2225 he->sticky = 1;
2226 break;
2228 case Combine:
2229 if(*he->realaddr){ /* combine values */
2230 if(pf->textbuf && *pf->textbuf){
2231 char *combined_hdr;
2232 size_t l;
2234 l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
2235 combined_hdr = (char *) fs_get((l+1) * sizeof(char));
2236 strncpy(combined_hdr, *he->realaddr, l);
2237 combined_hdr[l] = '\0';
2238 strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
2239 combined_hdr[l] = '\0';
2240 strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
2241 combined_hdr[l] = '\0';
2243 fs_give((void **)he->realaddr);
2244 *he->realaddr = combined_hdr;
2245 q_status_message(SM_ORDER, 3, 3,
2246 "Adding newsgroup from role");
2247 he->sticky = 1;
2250 else{
2251 *he->realaddr = pf->textbuf;
2252 pf->textbuf = NULL;
2255 break;
2257 case UseAsDef:
2258 /* if no value, use default */
2259 if(!*he->realaddr){
2260 *he->realaddr = pf->textbuf;
2261 pf->textbuf = NULL;
2264 break;
2266 case NoMatch:
2267 break;
2270 /* If there is a newsgroup, we'd better show it */
2271 if(outgoing->newsgroups && *outgoing->newsgroups)
2272 he->rich_header = 0; /* force on by default */
2274 if(pf->textbuf)
2275 fs_give((void **)&pf->textbuf);
2277 pf->text = he->realaddr;
2279 else if(index == N_DATE){
2280 sending_order[2] = pf;
2281 pf->text = (char **) &outgoing->date;
2282 pf->extdata = NULL;
2284 else if(index == N_INREPLY){
2285 sending_order[NN+9] = pf;
2286 pf->text = &outgoing->in_reply_to;
2287 pf->extdata = NULL;
2289 else if(index == N_MSGID){
2290 sending_order[NN+10] = pf;
2291 pf->text = &outgoing->message_id;
2292 pf->extdata = NULL;
2294 else if(index == N_REF){
2295 sending_order[NN+11] = pf;
2296 pf->text = &outgoing->references;
2297 pf->extdata = NULL;
2299 else if(index == N_PRIORITY){
2300 sending_order[NN+12] = pf;
2301 pf->text = &pf->textbuf;
2302 pf->extdata = NULL;
2304 else if(index == N_USERAGENT){
2305 sending_order[NN+13] = pf;
2306 pf->text = &pf->textbuf;
2307 pf->textbuf = generate_user_agent();
2308 pf->extdata = NULL;
2310 else if(index == N_POSTERR){
2311 sending_order[NN+14] = pf;
2312 pf_err = pf;
2313 pf->text = &pf->textbuf;
2314 pf->extdata = NULL;
2316 else if(index == N_RPLUID){
2317 sending_order[NN+15] = pf;
2318 pf_uid = pf;
2319 pf->text = &pf->textbuf;
2320 pf->extdata = NULL;
2322 else if(index == N_RPLMBOX){
2323 sending_order[NN+16] = pf;
2324 pf_mbox = pf;
2325 pf->text = &pf->textbuf;
2326 pf->extdata = NULL;
2328 else if(index == N_SMTP){
2329 sending_order[NN+17] = pf;
2330 pf_smtp_server = pf;
2331 pf->text = &pf->textbuf;
2332 pf->extdata = NULL;
2334 else if(index == N_NNTP){
2335 sending_order[NN+18] = pf;
2336 pf_nntp_server = pf;
2337 pf->text = &pf->textbuf;
2338 pf->extdata = NULL;
2340 else if(index == N_CURPOS){
2341 sending_order[NN+19] = pf;
2342 pf_curpos = pf;
2343 pf->text = &pf->textbuf;
2344 pf->extdata = NULL;
2346 else if(index == N_OURREPLYTO){
2347 sending_order[NN+20] = pf;
2348 pf_ourrep = pf;
2349 pf->text = &pf->textbuf;
2350 pf->extdata = NULL;
2352 else if(index == N_OURHDRS){
2353 sending_order[NN+21] = pf;
2354 pf_ourhdrs = pf;
2355 pf->text = &pf->textbuf;
2356 pf->extdata = NULL;
2358 else if(index == N_AUTHRCVD){
2359 sending_order[0] = pf;
2360 pf_ourhdrs = pf;
2361 pf->text = &pf->textbuf;
2362 pf->extdata = NULL;
2364 else{
2365 q_status_message(SM_ORDER | SM_DING, 3, 7,
2366 "Botched: Unmatched FreeText header in pine_send");
2369 break;
2371 /* can't do a default for this one */
2372 case Attachment:
2373 /* If there is an attachment already, we'd better show them */
2374 if(body && *body && (*body)->type != TYPETEXT)
2375 he->rich_header = 0; /* force on by default */
2377 break;
2379 case Address:
2380 switch(index){
2381 case N_FROM:
2382 sending_order[3] = pf;
2383 pf->addr = &outgoing->from;
2384 if(role && role->from){
2385 if(ps_global->never_allow_changing_from)
2386 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2387 else{
2388 outgoing->from = copyaddrlist(role->from);
2389 he->display_it = 1; /* show it */
2390 he->rich_header = 0;
2394 he_from = he;
2395 break;
2397 case N_TO:
2398 sending_order[NN+2] = pf;
2399 pf->addr = &outgoing->to;
2400 /* If already set, make it act like we typed it in */
2401 if(outgoing->to
2402 && outgoing->to->mailbox
2403 && outgoing->to->mailbox[0]
2404 && flags & PS_STICKY_TO)
2405 he->sticky = 1;
2407 he_to = he;
2408 pf_to = pf;
2409 break;
2411 case N_NOBODY:
2412 sending_order[NN+5] = pf;
2413 pf_nobody = pf;
2414 if(ps_global->VAR_EMPTY_HDR_MSG
2415 && !ps_global->VAR_EMPTY_HDR_MSG[0]){
2416 pf->addr = NULL;
2418 else{
2419 nobody_addr = mail_newaddr();
2420 nobody_addr->next = mail_newaddr();
2421 nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
2422 SIZEOF_20KBUF,
2423 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
2424 ? ps_global->VAR_EMPTY_HDR_MSG
2425 : "undisclosed-recipients"),
2426 ps_global->posting_charmap));
2427 pf->addr = &nobody_addr;
2430 break;
2432 case N_CC:
2433 sending_order[NN+3] = pf;
2434 pf->addr = &outgoing->cc;
2435 break;
2437 case N_BCC:
2438 sending_order[NN+4] = pf;
2439 pf->addr = &outgoing->bcc;
2440 /* if bcc exists, make sure it's exposed so nothing's
2441 * sent by mistake...
2443 if(outgoing->bcc)
2444 he->display_it = 1;
2446 break;
2448 case N_REPLYTO:
2449 sending_order[NN+1] = pf;
2450 pf->addr = &outgoing->reply_to;
2451 if(role && role->replyto){
2452 if(outgoing->reply_to)
2453 mail_free_address(&outgoing->reply_to);
2455 outgoing->reply_to = copyaddrlist(role->replyto);
2456 he->display_it = 1; /* show it */
2457 he->rich_header = 0;
2460 break;
2462 case N_LCC:
2463 sending_order[NN+7] = pf;
2464 pf->addr = &lcc_addr;
2465 he_lcc = he;
2466 if(lcc_arg){
2467 build_address(lcc_arg, &addr, NULL, NULL, NULL);
2468 rfc822_parse_adrlist(&lcc_addr, addr,
2469 ps_global->maildomain);
2470 fs_give((void **)&addr);
2471 he->display_it = 1;
2474 break;
2476 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2477 case N_SENDER:
2478 sending_order[4] = pf;
2479 pf->addr = &outgoing->sender;
2480 break;
2481 #endif
2483 default:
2484 q_status_message1(SM_ORDER,3,7,
2485 "Internal error: Address header %s", comatose(index));
2486 break;
2490 * If this is a reply to news, don't show the regular email
2491 * recipient headers (unless they are non-empty).
2493 if((outgoing->newsgroups && *outgoing->newsgroups)
2494 && (index == N_TO || index == N_CC
2495 || index == N_BCC || index == N_LCC)
2496 && (pf->addr && !*pf->addr)){
2497 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2498 pf->textbuf && *pf->textbuf){
2499 removing_trailing_white_space(pf->textbuf);
2500 (void)removing_double_quotes(pf->textbuf);
2501 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2502 rfc822_parse_adrlist(pf->addr, addr,
2503 ps_global->maildomain);
2504 fs_give((void **)&addr);
2505 if(ct > UseAsDef)
2506 he->sticky = 1;
2508 else
2509 he->rich_header = 1; /* hide */
2513 * If this address doesn't already have a value, then we check
2514 * for a default value assigned by the user.
2516 else if(pf->addr && !*pf->addr){
2517 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2518 (index != N_FROM ||
2519 (!ps_global->never_allow_changing_from &&
2520 F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
2521 pf->textbuf && *pf->textbuf){
2523 removing_trailing_white_space(pf->textbuf);
2524 (void)removing_double_quotes(pf->textbuf);
2527 * Try to set To based on Lcc. Don't attempt Fcc.
2529 if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
2530 BUILDER_ARG *barg = NULL;
2531 char *ppp = NULL;
2533 if(*pf_to->addr)
2534 ppp = addr_list_string(*pf_to->addr, NULL, 1);
2536 if(!ppp)
2537 ppp = cpystr("");
2539 barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
2540 memset(barg, 0, sizeof(*barg));
2541 barg->me = &(he->bldr_private);
2542 barg->aff = &(he_to->bldr_private);
2543 barg->tptr = cpystr(ppp);
2545 build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
2546 he->display_it = 1;
2548 rfc822_parse_adrlist(pf->addr, addr,
2549 ps_global->maildomain);
2550 if(addr)
2551 fs_give((void **) &addr);
2553 if(ct > UseAsDef)
2554 he->sticky = 1;
2556 if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
2557 ADDRESS *a = NULL;
2559 rfc822_parse_adrlist(&a, barg->tptr,
2560 ps_global->maildomain);
2561 if(a){
2562 if(pf_to->addr)
2563 mail_free_address(pf_to->addr);
2565 *pf_to->addr = a;
2569 if(barg){
2570 if(barg->tptr)
2571 fs_give((void **) &barg->tptr);
2573 fs_give((void **) &barg);
2576 if(ppp)
2577 fs_give((void **) &ppp);
2579 else{
2580 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2581 rfc822_parse_adrlist(pf->addr, addr,
2582 ps_global->maildomain);
2583 if(addr)
2584 fs_give((void **) &addr);
2586 if(ct > UseAsDef)
2587 he->sticky = 1;
2591 /* if we still don't have a from */
2592 if(index == N_FROM && !*pf->addr)
2593 *pf->addr = generate_from();
2597 * Addr is already set in the rest of the cases.
2599 else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
2600 ADDRESS *adr = NULL;
2603 * We get to this case of the ifelse if the from or reply-to
2604 * addr was set by a role above.
2607 /* figure out the default value */
2608 (void)set_default_hdrval(pf, custom);
2609 if(pf->textbuf && *pf->textbuf){
2610 removing_trailing_white_space(pf->textbuf);
2611 (void)removing_double_quotes(pf->textbuf);
2612 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2613 rfc822_parse_adrlist(&adr, addr,
2614 ps_global->maildomain);
2615 fs_give((void **)&addr);
2618 /* if value set by role is different from default, show it */
2619 if(adr && !address_is_same(*pf->addr, adr))
2620 he->display_it = 1; /* start this off showing */
2622 /* malformed */
2623 if(!(*pf->addr)->mailbox){
2624 fs_give((void **)pf->addr);
2625 he->display_it = 1;
2628 if(adr)
2629 mail_free_address(&adr);
2631 else if((index == N_TO || index == N_CC || index == N_BCC)
2632 && pf->addr){
2633 ADDRESS *a = NULL, **tail;
2636 * These three are different from the others because we
2637 * might add the addresses to what is already there instead
2638 * of replacing.
2641 switch(set_default_hdrval(pf, custom)){
2642 case Replace:
2643 if(*pf->addr)
2644 mail_free_address(pf->addr);
2646 removing_trailing_white_space(pf->textbuf);
2647 (void)removing_double_quotes(pf->textbuf);
2648 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2649 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2650 fs_give((void **)&addr);
2651 he->sticky = 1;
2652 break;
2654 case Combine:
2655 removing_trailing_white_space(pf->textbuf);
2656 (void)removing_double_quotes(pf->textbuf);
2657 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2658 rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
2659 fs_give((void **)&addr);
2660 he->sticky = 1;
2661 if(a){
2662 for(tail = pf->addr; *tail; tail = &(*tail)->next)
2664 *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
2665 *pf->addr, NULL, a, RCA_ALL);
2666 q_status_message(SM_ORDER, 3, 3,
2667 "Adding addresses from role");
2668 mail_free_address(&a);
2671 break;
2673 case UseAsDef:
2674 case NoMatch:
2675 break;
2678 he->display_it = 1; /* start this off showing */
2680 else if(pf->addr){
2681 switch(set_default_hdrval(pf, custom)){
2682 case Replace:
2683 case Combine:
2684 if(*pf->addr)
2685 mail_free_address(pf->addr);
2687 removing_trailing_white_space(pf->textbuf);
2688 (void)removing_double_quotes(pf->textbuf);
2689 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2690 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2691 fs_give((void **)&addr);
2692 he->sticky = 1;
2693 break;
2695 case UseAsDef:
2696 case NoMatch:
2697 break;
2700 he->display_it = 1;
2703 if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
2704 mail_free_address(pf->addr);
2705 he->display_it = 1; /* start this off showing */
2708 if(pf->textbuf) /* free default value in any case */
2709 fs_give((void **)&pf->textbuf);
2711 /* outgoing2strings will alloc the string pf->scratch below */
2712 he->realaddr = &pf->scratch;
2713 break;
2715 case Fcc:
2716 sending_order[NN+8] = pf;
2717 pf_fcc = pf;
2718 if(role && role->fcc)
2719 fcc = role->fcc;
2720 else
2721 fcc = get_fcc(fcc_arg);
2723 if(fcc_to_free){
2724 fs_give((void **)&fcc_to_free);
2725 fcc_arg = NULL;
2728 if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
2729 he->sticky = 1;
2731 if(role)
2732 role->fcc = NULL;
2734 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
2735 he->display_it = 1; /* start this off showing */
2737 he->realaddr = &fcc;
2738 pf->text = &fcc;
2739 he_fcc = he;
2740 break;
2742 case Subject :
2743 sending_order[NN+6] = pf;
2745 switch(set_default_hdrval(pf, custom)){
2746 case Replace:
2747 case Combine:
2748 pf->scratch = pf->textbuf;
2749 pf->textbuf = NULL;
2750 he->sticky = 1;
2751 if(outgoing->subject)
2752 fs_give((void **)&outgoing->subject);
2754 break;
2756 case UseAsDef:
2757 case NoMatch:
2758 /* if no value, use default */
2759 if(outgoing->subject){
2760 pf->scratch = cpystr(outgoing->subject);
2762 else{
2763 pf->scratch = pf->textbuf;
2764 pf->textbuf = NULL;
2767 break;
2770 he->realaddr = &pf->scratch;
2771 pf->text = &outgoing->subject;
2772 break;
2774 default:
2775 q_status_message1(SM_ORDER,3,7,
2776 "Unknown header type %d in pine_send",
2777 (void *)pf->type);
2778 break;
2782 * We may or may not want to give the user the chance to edit
2783 * the From and Reply-To lines. If they are listed in either
2784 * Default-composer-hdrs or Customized-hdrs, then they can edit
2785 * them, else no.
2786 * If canedit is not set, that means that this header is not in
2787 * the user's customized-hdrs. If rich_header is set, that
2788 * means that this header is not in the user's
2789 * default-composer-hdrs (since From and Reply-To are rich
2790 * by default). So, don't give it an he to edit with in that case.
2792 * For other types, just not setting canedit will cause it to be
2793 * uneditable, regardless of what the user does.
2795 switch(index){
2796 case N_FROM:
2797 /* to allow it, we let this fall through to the reply-to case below */
2798 if(ps_global->never_allow_changing_from ||
2799 (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
2800 !(role && role->from))){
2801 if(pf->canedit || !he->rich_header)
2802 q_status_message(SM_ORDER, 3, 3,
2803 _("Not allowed to change header \"From\""));
2805 memset(he, 0, (size_t)sizeof(*he));
2806 pf->extdata = NULL;
2807 break;
2810 case N_REPLYTO:
2811 if(!pf->canedit && he->rich_header){
2812 memset(he, 0, (size_t)sizeof(*he));
2813 pf->extdata = NULL;
2815 else{
2816 pf->canedit = 1;
2817 he++;
2820 break;
2822 default:
2823 if(!pf->canedit){
2824 memset(he, 0, (size_t)sizeof(*he));
2825 pf->extdata = NULL;
2827 else
2828 he++;
2830 break;
2835 * This is so the builder can tell the composer to fill the affected
2836 * field based on the value in the field on the left.
2838 * Note that this mechanism isn't completely general. Each entry has
2839 * only a single next_affected, so if some other entry points an
2840 * affected entry at an entry with a next_affected, they all inherit
2841 * that next_affected. Since this isn't used much a careful ordering
2842 * of the affected fields should make it a sufficient mechanism.
2844 he_to->affected_entry = he_fcc;
2845 he_news->affected_entry = he_fcc;
2846 he_lcc->affected_entry = he_to;
2847 he_to->next_affected = he_fcc;
2849 (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
2851 i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2853 * Set up headerentries for custom fields.
2854 * NOTE: "i" is assumed to now index first custom field in sending
2855 * order.
2857 for(pf = pf->next; pf && pf->name; pf = pf->next){
2858 char *addr;
2860 if(pf->standard)
2861 continue;
2863 pf->extdata = he;
2864 pf->canedit = 1;
2865 pf->rcptto = 0;
2866 pf->writehdr = 1;
2867 pf->localcopy = 1;
2869 switch(pf->type){
2870 case Address:
2871 if(pf->addr){ /* better be set */
2872 sending_order[i++] = pf;
2873 *he = he_custom_addr_templ;
2874 /* change default text into an ADDRESS */
2875 /* strip quotes around whole default */
2876 removing_trailing_white_space(pf->textbuf);
2877 (void)removing_double_quotes(pf->textbuf);
2878 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2879 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2880 fs_give((void **)&addr);
2881 if(pf->textbuf)
2882 fs_give((void **)&pf->textbuf);
2884 he->realaddr = &pf->scratch;
2885 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2886 he->nickcmpl = NULL;
2889 break;
2891 case FreeText:
2892 sending_order[i++] = pf;
2893 *he = he_custom_free_templ;
2894 he->realaddr = &pf->textbuf;
2895 pf->text = &pf->textbuf;
2896 if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
2897 (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
2898 he->display_it = 1; /* show it */
2900 break;
2902 default:
2903 q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
2904 (void *)pf->type);
2905 break;
2908 he->name = pf->name;
2910 /* use first 8 characters for prompt */
2911 he->prompt = cpystr(" : ");
2912 strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
2914 he->rich_header = view_as_rich(he->name, he->rich_header);
2915 he++;
2919 * Make sure at least *one* field is displayable...
2921 for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
2922 if(HE(pf) && !HE(pf)->rich_header){
2923 index = i;
2924 break;
2928 * None displayable!!! Warn and display defaults.
2930 if(index == -1){
2931 q_status_message(SM_ORDER,0,5,
2932 "No default-composer-hdrs matched, displaying defaults");
2933 for(i = 0, pf = header.local; pf; pf = pf->next, i++)
2934 if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
2935 && HE(pf))
2936 HE(pf)->rich_header = 0;
2940 * Save information about body which set_mime_type_by_grope might change.
2941 * Then, if we get an error sending, we reset these things so that
2942 * grope can do it's thing again after we edit some more.
2944 if ((*body)->type == TYPEMULTIPART)
2945 bp = save_body_particulars(&(*body)->nested.part->body);
2946 else
2947 bp = save_body_particulars(*body);
2950 local_redraft_pos = redraft_pos;
2952 /*----------------------------------------------------------------------
2953 Loop calling the editor until everything goes well
2954 ----*/
2955 while(1){
2956 int saved_user_timeout;
2958 /* Reset body to what it was when we started. */
2959 if ((*body)->type == TYPEMULTIPART)
2960 reset_body_particulars(bp, &(*body)->nested.part->body);
2961 else
2962 reset_body_particulars(bp,*body);
2964 * set initial cursor position based on how many times we've been
2965 * thru the loop...
2967 if(reply && reply->pseudo){
2968 pbf->pine_flags |= reply->data.pico_flags;
2970 else if(body_start){
2971 pbf->pine_flags |= P_BODY;
2972 body_start = 0; /* maybe not next time */
2974 else if(local_redraft_pos){
2975 pbf->edit_offset = local_redraft_pos->offset;
2976 /* set the start_here bit in correct header */
2977 for(pf = header.local; pf && pf->name; pf = pf->next)
2978 if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
2979 && HE(pf)){
2980 HE(pf)->start_here = 1;
2981 break;
2984 /* If didn't find it, we start in body. */
2985 if(!pf || !pf->name)
2986 pbf->pine_flags |= P_BODY;
2988 else if(reply && (!reply->forw && !reply->forwarded)){
2989 pbf->pine_flags |= P_BODY;
2992 /* in case these were turned on in previous pass through loop */
2993 if(pf_nobody){
2994 pf_nobody->writehdr = 0;
2995 pf_nobody->localcopy = 0;
2998 if(pf_fcc)
2999 pf_fcc->localcopy = 0;
3002 * If a sending attempt failed after we passed the message text
3003 * thru a user-defined filter, "orig_so" points to the original
3004 * text. Replace the body's encoded data with the original...
3006 if(orig_so){
3007 STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
3008 ? &(*body)->nested.part->body.contents.text.data
3009 : &(*body)->contents.text.data);
3010 so_give(so);
3011 *so = orig_so;
3012 orig_so = NULL;
3016 * Convert the envelope and body to the string format that
3017 * pico can edit
3019 outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
3021 for(pf = header.local; pf && pf->name; pf = pf->next){
3023 * If this isn't the first time through this loop, we may have
3024 * freed some of the FreeText headers below so that they wouldn't
3025 * show up as empty headers in the finished message. Need to
3026 * alloc them again here so they can be edited.
3028 if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
3029 *HE(pf)->realaddr = cpystr("");
3031 if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
3032 HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
3036 * If From is exposed, probably by a role, then start the cursor
3037 * on the first line which isn't filled in. If it isn't, then we
3038 * don't move the cursor, mostly for back-compat.
3040 if((!reply || reply->forw || reply->forwarded) &&
3041 !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
3042 (he_from->display_it || !he_from->rich_header)){
3043 for(pf = header.local; pf && pf->name; pf = pf->next)
3044 if(HE(pf) &&
3045 (HE(pf)->display_it || !HE(pf)->rich_header) &&
3046 HE(pf)->realaddr &&
3047 (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
3048 HE(pf)->start_here = 1;
3049 break;
3053 #ifdef _WINDOWS
3054 mswin_setwindowmenu (MENU_COMPOSER);
3055 #endif
3057 cancel_busy_cue(-1);
3058 flush_status_messages(1);
3060 /* turn off user input timeout when in composer */
3061 saved_user_timeout = ps_global->hours_to_timeout;
3062 ps_global->hours_to_timeout = 0;
3063 dprint((1, "\n ---- COMPOSER ----\n"));
3064 editor_result = pico(pbf);
3065 dprint((4, "... composer returns (0x%x)\n", editor_result));
3066 ps_global->hours_to_timeout = saved_user_timeout;
3068 #ifdef _WINDOWS
3069 mswin_setwindowmenu (MENU_DEFAULT);
3070 #endif
3071 fix_windsize(ps_global);
3074 * Only reinitialize signals if we didn't receive an interesting
3075 * one while in pico, since pico's return is part of processing that
3076 * signal and it should continue to be ignored.
3078 if(!(editor_result & COMP_GOTHUP))
3079 init_signals(); /* Pico has it's own signal stuff */
3082 * We're going to save in DEADLETTER. Dump attachments first.
3084 if(editor_result & COMP_CANCEL)
3085 free_attachment_list(&pbf->attachments);
3087 /* Turn strings back into structures */
3088 strings2outgoing(&header, body, pbf->attachments, flowing_requested);
3090 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3091 if(outgoing->newsgroups){
3092 sqzspaces(outgoing->newsgroups);
3093 if(!outgoing->newsgroups[0])
3094 fs_give((void **)&(outgoing->newsgroups));
3097 /* Make subject NULL if it is "" (so won't show up in headers) */
3098 if(outgoing->subject && !outgoing->subject[0])
3099 fs_give((void **)&(outgoing->subject));
3101 /* remove custom fields that are empty */
3102 for(pf = header.local; pf && pf->name; pf = pf->next){
3103 if(pf->type == FreeText && pf->textbuf){
3104 if(pf->textbuf[0] == '\0'){
3105 fs_give((void **)&pf->textbuf);
3106 pf->text = NULL;
3111 removing_trailing_white_space(fcc);
3113 /*-------- Stamp it with a current date -------*/
3114 if(outgoing->date) /* update old date */
3115 fs_give((void **)&(outgoing->date));
3117 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3118 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
3120 rfc822_date(tmp_20k_buf); /* format and copy new date */
3121 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3122 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
3124 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
3126 /* Set return_path based on From which is going to be used */
3127 if(outgoing->return_path)
3128 mail_free_address(&outgoing->return_path);
3130 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
3133 * Don't ever believe the sender that is there.
3134 * If From doesn't look quite right, generate our own sender.
3136 if(outgoing->sender)
3137 mail_free_address(&outgoing->sender);
3140 * If the LHS of the address doesn't match, or the RHS
3141 * doesn't match one of localdomain or hostname,
3142 * then add a sender line (really X-X-Sender).
3144 * Don't add a personal_name since the user can change that.
3146 if(F_OFF(F_DISABLE_SENDER, ps_global)
3148 (!outgoing->from
3149 || !outgoing->from->mailbox
3150 || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
3151 || !outgoing->from->host
3152 || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
3153 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
3155 outgoing->sender = mail_newaddr();
3156 outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
3157 outgoing->sender->host = cpystr(ps_global->hostname);
3160 if(ps_global->newthread){
3161 if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
3162 if(outgoing->references) fs_give((void **)&outgoing->references);
3165 /*----- Message is edited, now decide what to do with it ----*/
3166 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
3167 /*=========== Postpone or Interrupted message ============*/
3168 CONTEXT_S *fcc_cntxt = NULL;
3169 char folder[MAXPATH+1];
3170 int fcc_result = 0;
3171 char label[50];
3173 dprint((4, "pine_send:%s handling\n",
3174 (editor_result & COMP_SUSPEND)
3175 ? "SUSPEND"
3176 : (editor_result & COMP_GOTHUP)
3177 ? "HUP"
3178 : (editor_result & COMP_CANCEL)
3179 ? "CANCEL" : "HUH?"));
3180 if((editor_result & COMP_CANCEL)
3181 && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
3182 || ps_global->deadlets == 0)){
3183 q_status_message(SM_ORDER, 0, 3, "Message cancelled");
3184 break;
3188 * The idea here is to use the Fcc: writing facility
3189 * to append to the special postponed message folder...
3191 * NOTE: the strategy now is to write the message and
3192 * all attachments as they exist at composition time.
3193 * In other words, attachments are postponed by value
3194 * and not reference. This may change later, but we'll
3195 * need a local "message/external-body" type that
3196 * outgoing2strings knows how to properly set up for
3197 * the composer. Maybe later...
3200 label[0] = '\0';
3202 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
3203 && (editor_result & COMP_SUSPEND)
3204 && (check_addresses(&header) == CA_BAD)){
3205 /*--- Addresses didn't check out---*/
3206 q_status_message(SM_ORDER, 7, 7,
3207 _("Not allowed to postpone message until addresses are qualified"));
3208 continue;
3212 * Build the local message copy so.
3214 * In the HUP case, we'll write the bezerk delimiter by
3215 * hand and output the message directly into the folder.
3216 * It's not only faster, we don't have to worry about
3217 * c-client reentrance and less hands paw over the data so
3218 * there's less chance of a problem.
3220 * In the Postpone case, just create it if the user wants to
3221 * and create a temporary storage object to write into. */
3222 fake_hup:
3223 lmc.all_written = lmc.text_only = lmc.text_written = 0;
3224 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3225 int newfile = 1;
3226 time_t now = time((time_t *)0);
3228 #if defined(DOS) || defined(OS2)
3230 * we can't assume anything about root or home dirs, so
3231 * just plunk it down in the same place as the pinerc
3233 if(!getenv("HOME")){
3234 char *lc = last_cmpnt(ps_global->pinerc);
3235 folder[0] = '\0';
3236 if(lc != NULL){
3237 strncpy(folder,ps_global->pinerc,
3238 MIN(lc-ps_global->pinerc,sizeof(folder)-1));
3239 folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
3242 strncat(folder, (editor_result & COMP_GOTHUP)
3243 ? INTERRUPTED_MAIL : DEADLETTER,
3244 sizeof(folder)-strlen(folder)-1);
3246 else
3247 #endif
3248 build_path(folder,
3249 ps_global->VAR_OPER_DIR
3250 ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
3251 (editor_result & COMP_GOTHUP)
3252 ? INTERRUPTED_MAIL : DEADLETTER,
3253 sizeof(folder));
3255 if(editor_result & COMP_CANCEL){
3256 char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
3258 if(strlen(folder) + 1 < sizeof(filename))
3259 for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
3260 strncpy(filename, folder, sizeof(filename));
3261 filename[sizeof(filename)-1] = '\0';
3262 strncpy(newfname, filename, sizeof(newfname));
3263 newfname[sizeof(newfname)-1] = '\0';
3265 if(i > 1){
3266 snprintf(nbuf, sizeof(nbuf), "%d", i);
3267 nbuf[sizeof(nbuf)-1] = '\0';
3268 strncat(filename, nbuf,
3269 sizeof(filename)-strlen(filename)-1);
3270 filename[sizeof(filename)-1] = '\0';
3273 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
3274 nbuf[sizeof(nbuf)-1] = '\0';
3275 strncat(newfname, nbuf,
3276 sizeof(newfname)-strlen(newfname)-1);
3277 newfname[sizeof(newfname)-1] = '\0';
3278 (void) rename_file(filename, newfname);
3281 our_unlink(folder);
3283 else
3284 newfile = can_access(folder, ACCESS_EXISTS);
3286 if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
3287 if (outgoing->from){
3288 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
3289 newfile ? "" : "\015\012",
3290 outgoing->from->mailbox,
3291 outgoing->from->host, ctime(&now));
3292 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3293 if(!so_puts(lmc.so, tmp_20k_buf)){
3294 if(editor_result & COMP_CANCEL)
3295 q_status_message2(SM_ORDER | SM_DING, 3, 3,
3296 "Can't write \"%s\": %s",
3297 folder, error_description(errno));
3298 else
3299 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3300 folder ? folder : "?",
3301 error_description(errno)));
3306 else{ /* Must be COMP_SUSPEND */
3307 if(!ps_global->VAR_POSTPONED_FOLDER
3308 || !ps_global->VAR_POSTPONED_FOLDER[0]){
3309 q_status_message(SM_ORDER | SM_DING, 3, 3,
3310 _("No postponed file defined"));
3311 continue;
3315 * Store the cursor position
3317 * First find the header entry with the start_here
3318 * bit set, if any. This means the editor is telling
3319 * us to start on this header field next time.
3321 start_here_name = NULL;
3322 for(pf = header.local; pf && pf->name; pf = pf->next)
3323 if(HE(pf) && HE(pf)->start_here){
3324 start_here_name = pf->name;
3325 break;
3328 /* If there wasn't one, ":" means we start in the body. */
3329 if(!start_here_name || !*start_here_name)
3330 start_here_name = ":";
3332 if(ps_global->VAR_FORM_FOLDER
3333 && ps_global->VAR_FORM_FOLDER[0]
3334 && postpone_prompt() == 'f'){
3335 strncpy(folder, ps_global->VAR_FORM_FOLDER,
3336 sizeof(folder)-1);
3337 folder[sizeof(folder)-1] = '\0';
3338 strncpy(label, "form letter", sizeof(label));
3339 label[sizeof(label)-1] = '\0';
3341 else{
3342 strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
3343 sizeof(folder)-1);
3344 folder[sizeof(folder)-1] = '\0';
3345 strncpy(label, "postponed message", sizeof(label));
3346 label[sizeof(label)-1] = '\0';
3349 lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
3352 if(lmc.so){
3353 size_t sz;
3354 char *lmq = NULL;
3356 /* copy fcc line to postponed or interrupted folder */
3357 if(pf_fcc)
3358 pf_fcc->localcopy = 1;
3360 /* plug error into header for later display to user */
3361 if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
3362 pf_err->writehdr = 1;
3363 pf_err->localcopy = 1;
3364 pf_err->textbuf = lmq;
3368 * if reply, write (UID)folder header field so we can
3369 * later flag the replied-to message \\ANSWERED
3370 * DON'T save MSGNO's.
3372 if(reply && reply->uid){
3373 char uidbuf[MAILTMPLEN], *p;
3374 long i;
3376 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
3377 if(i)
3378 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
3380 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
3383 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3385 pf_uid->writehdr = 1;
3386 pf_uid->localcopy = 1;
3387 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
3388 reply->prefix ? int2string(strlen(reply->prefix))
3389 : (reply->forwarded) ? "": "0 ",
3390 reply->prefix ? " " : "",
3391 reply->prefix ? reply->prefix : "",
3392 i, reply->data.uid.validity,
3393 tmp_20k_buf, reply->mailbox);
3394 uidbuf[sizeof(uidbuf)-1] = '\0';
3395 pf_uid->textbuf = cpystr(uidbuf);
3398 * Logically, this ought to be part of pf_uid, but this
3399 * was added later and so had to be in a separate header
3400 * for backwards compatibility.
3402 pf_mbox->writehdr = 1;
3403 pf_mbox->localcopy = 1;
3404 pf_mbox->textbuf = cpystr(reply->origmbox
3405 ? reply->origmbox
3406 : reply->mailbox);
3409 /* Save cursor position */
3410 if(start_here_name && *start_here_name){
3411 char curposbuf[MAILTMPLEN];
3413 pf_curpos->writehdr = 1;
3414 pf_curpos->localcopy = 1;
3415 snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
3416 pbf->edit_offset);
3417 curposbuf[sizeof(curposbuf)-1] = '\0';
3418 pf_curpos->textbuf = cpystr(curposbuf);
3422 * Work around c-client reply-to bug. C-client will
3423 * return a reply_to in an envelope even if there is
3424 * no reply-to header field. We want to note here whether
3425 * the reply-to is real or not.
3427 if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
3428 pf_ourrep->writehdr = 1;
3429 pf_ourrep->localcopy = 1;
3430 if(outgoing->reply_to)
3431 pf_ourrep->textbuf = cpystr("Full");
3432 else
3433 pf_ourrep->textbuf = cpystr("Empty");
3436 /* Save the role-specific smtp server */
3437 if(role && role->smtp && role->smtp[0]){
3438 char *q, *smtp = NULL;
3439 char **lp;
3440 size_t len = 0;
3443 * Turn the list of smtp servers into a space-
3444 * delimited list in a single string.
3446 for(lp = role->smtp; (q = *lp) != NULL; lp++)
3447 len += (strlen(q) + 1);
3449 if(len){
3450 smtp = (char *) fs_get(len * sizeof(char));
3451 smtp[0] = '\0';
3452 for(lp = role->smtp; (q = *lp) != NULL; lp++){
3453 if(lp != role->smtp)
3454 strncat(smtp, " ", len-strlen(smtp)-1);
3456 strncat(smtp, q, len-strlen(smtp)-1);
3459 smtp[len-1] = '\0';
3462 pf_smtp_server->writehdr = 1;
3463 pf_smtp_server->localcopy = 1;
3464 if(smtp)
3465 pf_smtp_server->textbuf = smtp;
3466 else
3467 pf_smtp_server->textbuf = cpystr("");
3470 /* Save the role-specific nntp server */
3471 if(suggested_nntp_server ||
3472 (role && role->nntp && role->nntp[0])){
3473 char *q, *nntp = NULL;
3474 char **lp;
3475 size_t len = 0;
3477 if(role && role->nntp && role->nntp[0]){
3479 * Turn the list of nntp servers into a space-
3480 * delimited list in a single string.
3482 for(lp = role->nntp; (q = *lp) != NULL; lp++)
3483 len += (strlen(q) + 1);
3485 if(len){
3486 nntp = (char *) fs_get(len * sizeof(char));
3487 nntp[0] = '\0';
3488 for(lp = role->nntp; (q = *lp) != NULL; lp++){
3489 if(lp != role->nntp)
3490 strncat(nntp, " ", len-strlen(nntp)-1);
3492 strncat(nntp, q, len-strlen(nntp)-1);
3495 nntp[len-1] = '\0';
3498 else
3499 nntp = cpystr(suggested_nntp_server);
3501 pf_nntp_server->writehdr = 1;
3502 pf_nntp_server->localcopy = 1;
3503 if(nntp)
3504 pf_nntp_server->textbuf = nntp;
3505 else
3506 pf_nntp_server->textbuf = cpystr("");
3510 * Write the list of custom headers to the
3511 * X-Our-Headers header so that we can recover the
3512 * list in redraft.
3514 sz = 0;
3515 for(pf = header.custom; pf && pf->name; pf = pf->next)
3516 sz += strlen(pf->name) + 1;
3518 if(sz){
3519 char *q;
3521 pf_ourhdrs->writehdr = 1;
3522 pf_ourhdrs->localcopy = 1;
3523 pf_ourhdrs->textbuf = (char *)fs_get(sz);
3524 memset(pf_ourhdrs->textbuf, 0, sz);
3525 q = pf_ourhdrs->textbuf;
3526 for(pf = header.custom; pf && pf->name; pf = pf->next){
3527 if(pf != header.custom)
3528 sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
3530 sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
3533 pf_ourhdrs->textbuf[sz-1] = '\0';;
3537 * We need to make sure any header values that got cleared
3538 * get written to the postponed message (they won't if
3539 * pf->text is NULL). Otherwise, we can't tell previously
3540 * non-existent custom headers or default values from
3541 * custom (or other) headers that got blanked in the
3542 * composer...
3544 for(pf = header.local; pf && pf->name; pf = pf->next)
3545 if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
3546 *(HE(pf)->realaddr) = cpystr("");
3549 * We're saving the message for use later. It may be that the
3550 * characters in the message are not all convertible to the
3551 * user's posting_charmap. We'll save it as UTF-8 instead
3552 * and worry about that the next time they try to send it.
3553 * Use a different save pointer just to be sure we don't
3554 * mess up the other stuff. We should probably make the
3555 * charset an argument.
3557 * We also need to fix the charset of the body part
3558 * the user is editing so that we can read it back
3559 * successfully when we resume the composition.
3561 ps_global->post_utf8 = 1;
3564 PARAMETER *pm;
3565 BODY *bp;
3566 if((*body)->type == TYPEMULTIPART)
3567 bp = &(*body)->nested.part->body;
3568 else
3569 bp = *body;
3571 for(pm = bp->parameter;
3572 pm && strucmp(pm->attribute, "charset") != 0;
3573 pm = pm->next)
3576 if(pm){
3577 if(pm->value)
3578 fs_give((void **) &pm->value);
3580 pm->value = cpystr("UTF-8");
3584 if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
3585 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3586 char *err;
3587 STORE_S *hup_so;
3588 gf_io_t gc, pc;
3589 int we_cancel = 0;
3591 if(editor_result & COMP_CANCEL){
3592 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3593 "Saving to \"%s\"", folder);
3594 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3595 we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
3598 if((hup_so =
3599 so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
3600 gf_set_so_readc(&gc, lmc.so);
3601 gf_set_so_writec(&pc, hup_so);
3602 so_seek(lmc.so, 0L, 0); /* read msg copy and */
3603 so_seek(hup_so, 0L, 2); /* append to folder */
3604 gf_filter_init();
3605 gf_link_filter(gf_nvtnl_local, NULL);
3606 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
3607 dprint((1, "*** PIPE FAILED: %s\n",
3608 err ? err : "?"));
3610 gf_clear_so_readc(lmc.so);
3611 gf_clear_so_writec(hup_so);
3612 so_give(&hup_so);
3614 else
3615 dprint((1, "*** CAN'T CREATE %s: %s\n",
3616 folder ? folder : "?",
3617 error_description(errno)));
3619 if(we_cancel)
3620 cancel_busy_cue(-1);
3622 else
3623 fcc_result = write_fcc(folder, fcc_cntxt,
3624 lmc.so, NULL, label, NULL);
3627 /* discontinue coerced UTF-8 posting */
3628 ps_global->post_utf8 = 0;
3630 so_give(&lmc.so);
3632 else
3633 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3634 error_description(errno)));
3636 if(editor_result & COMP_GOTHUP){
3638 * Special Hack #291: if any hi-byte bits are set in
3639 * editor's result, we put them there.
3641 if(editor_result & 0xff00)
3642 exit(editor_result >> 8);
3644 dprint((1, "Save composition on HUP %sED\n",
3645 fcc_result ? "SUCCEED" : "FAIL"));
3646 hup_signal(); /* Do what we normally do on SIGHUP */
3648 else if((editor_result & COMP_SUSPEND) && fcc_result){
3649 if(ps_global->VAR_FORM_FOLDER
3650 && ps_global->VAR_FORM_FOLDER[0]
3651 && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
3652 q_status_message(SM_ORDER, 0, 3,
3653 _("Composition saved to Form Letter Folder. Select Compose to send."));
3654 else
3655 q_status_message(SM_ORDER, 0, 3,
3656 _("Composition postponed. Select Compose to resume."));
3658 break; /* postpone went OK, get out of here */
3660 else if(editor_result & COMP_CANCEL){
3661 char *lc = NULL;
3663 if(fcc_result && folder)
3664 lc = last_cmpnt(folder);
3666 q_status_message3(SM_ORDER, 0, 3,
3667 _("Message cancelled%s%s%s"),
3668 (lc && *lc) ? " and copied to \"" : "",
3669 (lc && *lc) ? lc : "",
3670 (lc && *lc) ? "\" file" : "");
3671 break;
3673 else{
3674 q_status_message(SM_ORDER, 0, 4,
3675 _("Continuing composition. Message not postponed or sent"));
3676 body_start = 1;
3677 continue; /* postpone failed, jump back in to composer */
3680 else{
3681 /*------ Must be sending mail or posting ! -----*/
3682 int result, valid_addr, continue_with_only_fcc = 0;
3683 CONTEXT_S *fcc_cntxt = NULL;
3685 result = 0;
3686 dprint((4, "=== sending: "));
3688 /* --- If posting, confirm with user ----*/
3689 if(outgoing->newsgroups && *outgoing->newsgroups
3690 && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
3691 && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
3692 q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
3693 dprint((4, "no post, continuing\n"));
3694 continue;
3697 if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
3698 || outgoing->newsgroups)){
3699 if(fcc && fcc[0]){
3700 if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
3701 want_to(_("No recipients, really copy only to Fcc "),
3702 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
3703 continue;
3705 continue_with_only_fcc++;
3707 else{
3708 q_status_message(SM_ORDER, 3, 4,
3709 _("No recipients specified!"));
3710 dprint((4, "no recip, continuing\n"));
3711 continue;
3715 if((valid_addr = check_addresses(&header)) == CA_BAD){
3716 /*--- Addresses didn't check out---*/
3717 dprint((4, "addrs failed, continuing\n"));
3718 continue;
3721 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
3722 && !continue_with_only_fcc
3723 && !(outgoing->to || outgoing->cc || lcc_addr
3724 || outgoing->newsgroups)
3725 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3726 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
3727 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3728 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3729 free_redraft_pos(&local_redraft_pos);
3731 local_redraft_pos
3732 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3733 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3734 local_redraft_pos->hdrname = cpystr(TONAME);
3735 continue;
3738 if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
3739 && check_for_subject(&header) == CF_MISSING){
3740 dprint((4, "No subject, continuing\n"));
3741 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3742 free_redraft_pos(&local_redraft_pos);
3744 local_redraft_pos
3745 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3746 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3747 local_redraft_pos->hdrname = cpystr(SUBJNAME);
3748 continue;
3751 if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
3752 && check_for_fcc(fcc) == CF_MISSING){
3753 dprint((4, "No fcc, continuing\n"));
3754 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3755 free_redraft_pos(&local_redraft_pos);
3757 local_redraft_pos
3758 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3759 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3760 local_redraft_pos->hdrname = cpystr("Fcc");
3761 continue;
3764 set_last_fcc(fcc);
3766 /*---- Check out fcc -----*/
3767 if(fcc && *fcc){
3769 * If special name "inbox" then replace it with the
3770 * real inbox path.
3772 if(ps_global->VAR_INBOX_PATH
3773 && strucmp(fcc, ps_global->inbox_name) == 0){
3774 char *replace_fcc;
3776 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
3777 fs_give((void **)&fcc);
3778 fcc = replace_fcc;
3781 lmc.all_written = lmc.text_written = 0;
3782 /* lmc.text_only set on command line */
3783 if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
3784 /* ---- Open or allocation of fcc failed ----- */
3785 dprint((4,"can't open/allocate fcc, cont'g\n"));
3788 * Find field entry associated with fcc, and start
3789 * composer on it...
3791 for(pf = header.local; pf && pf->name; pf = pf->next)
3792 if(pf->type == Fcc && HE(pf))
3793 HE(pf)->start_here = 1;
3795 continue;
3797 else
3798 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
3800 else
3801 lmc.so = NULL;
3803 /*---- Take care of any requested prefiltering ----*/
3804 if(sending_filter_requested
3805 && !filter_message_text(sending_filter_requested, outgoing,
3806 *body, &orig_so, &header)){
3807 q_status_message1(SM_ORDER, 3, 3,
3808 _("Problem filtering! Nothing sent%s."),
3809 fcc ? " or saved to fcc" : "");
3810 continue;
3813 /*------ Actually post -------*/
3814 if(outgoing->newsgroups){
3815 char **alt_nntp = NULL, *alt_nntp_p[2];
3816 if(((role && role->nntp)
3817 || suggested_nntp_server)){
3818 if(ps_global->FIX_NNTP_SERVER
3819 && ps_global->FIX_NNTP_SERVER[0])
3820 q_status_message(SM_ORDER | SM_DING, 5, 5,
3821 "Using nntp-server that is administratively fixed");
3822 else if(role && role->nntp)
3823 alt_nntp = role->nntp;
3824 else{
3825 alt_nntp_p[0] = suggested_nntp_server;
3826 alt_nntp_p[1] = NULL;
3827 alt_nntp = alt_nntp_p;
3830 if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
3831 dprint((1, "Post failed, continuing\n"));
3832 if(outgoing->message_id)
3833 fs_give((void **) &outgoing->message_id);
3835 outgoing->message_id = generate_message_id(role);
3837 continue;
3839 else
3840 result |= P_NEWS_WIN;
3844 * BUG: IF we've posted the message *and* an fcc was specified
3845 * then we've already got a neatly formatted message in the
3846 * lmc.so. It'd be nice not to have to re-encode everything
3847 * to insert it into the smtp slot...
3851 * Turn on "undisclosed recipients" header if no To or cc.
3853 if(!(outgoing->to || outgoing->cc)
3854 && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
3855 pf_nobody->writehdr = 1;
3856 pf_nobody->localcopy = 1;
3859 if(priority_requested){
3860 (void) set_priority_header(&header, priority_requested);
3861 fs_give((void **) &priority_requested);
3864 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3866 * If requested, launch background posting...
3868 if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
3869 ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
3870 memset(ps_global->post, 0, sizeof(POST_S));
3871 if(fcc)
3872 ps_global->post->fcc = cpystr(fcc);
3874 if((ps_global->post->pid = fork()) == 0){
3876 * Put us in new process group...
3878 setpgrp(0, ps_global->post->pid);
3880 /* BUG: should fix argv[0] to indicate what we're up to */
3883 * If there are any live streams, pretend we never
3884 * knew them. Problem is two processes writing
3885 * same server process.
3886 * This is not clean but we're just going to exit
3887 * right away anyway. We just want to be sure to leave
3888 * the stuff that the parent is going to use alone.
3889 * The next three lines will disable the re-use of the
3890 * existing streams and cause us to open a new one if
3891 * needed.
3893 ps_global->mail_stream = NULL;
3894 ps_global->s_pool.streams = NULL;
3895 ps_global->s_pool.nstream = 0;
3897 /* quell any display output */
3898 ps_global->in_init_seq = 1;
3900 /*------- Actually mail the message ------*/
3901 if(valid_addr == CA_OK
3902 && (outgoing->to || outgoing->cc
3903 || outgoing->bcc || lcc_addr)){
3904 char **alt_smtp = NULL;
3906 if(role && role->smtp){
3907 if(ps_global->FIX_SMTP_SERVER
3908 && ps_global->FIX_SMTP_SERVER[0])
3909 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3910 else
3911 alt_smtp = role->smtp;
3914 result |= (call_mailer(&header, *body, alt_smtp,
3915 call_mailer_flags,
3916 call_mailer_file_result,
3917 pipe_callback) > 0)
3918 ? P_MAIL_WIN : P_MAIL_LOSE;
3920 if(result & P_MAIL_LOSE)
3921 mark_address_failure_for_pico(&header);
3924 /*----- Was there an fcc involved? -----*/
3925 if(lmc.so){
3926 /*------ Write it if at least something worked ------*/
3927 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3928 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3929 && pine_rfc822_output(&header, *body,
3930 NULL, NULL))){
3931 char label[50];
3933 strncpy(label, "Fcc", sizeof(label));
3934 label[sizeof(label)-1] = '\0';
3935 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3936 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3937 label[sizeof(label)-1] = '\0';
3940 /*-- Now actually copy to fcc folder and close --*/
3941 result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
3942 NULL, label,
3943 F_ON(F_MARK_FCC_SEEN, ps_global)
3944 ? "\\SEEN" : NULL))
3945 ? P_FCC_WIN : P_FCC_LOSE;
3947 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3948 q_status_message(SM_ORDER, 3, 5,
3949 _("Fcc Failed!. No message saved."));
3950 dprint((1,
3951 "explicit fcc write failed!\n"));
3952 result |= P_FCC_LOSE;
3955 so_give(&lmc.so);
3958 if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
3960 * Encode child's result in hi-byte of
3961 * editor's result
3963 editor_result = ((result << 8) | COMP_GOTHUP);
3964 goto fake_hup;
3967 exit(result);
3970 if(ps_global->post->pid > 0){
3971 q_status_message(SM_ORDER, 3, 3,
3972 _("Message handed off for posting"));
3973 break; /* up to our child now */
3975 else{
3976 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3977 "Can't fork for send: %s",
3978 error_description(errno));
3979 if(ps_global->post->fcc)
3980 fs_give((void **) &ps_global->post->fcc);
3982 fs_give((void **) &ps_global->post);
3985 if(lmc.so) /* throw away unused store obj */
3986 so_give(&lmc.so);
3988 if(outgoing->message_id)
3989 fs_give((void **) &outgoing->message_id);
3991 outgoing->message_id = generate_message_id(role);
3993 continue; /* if we got here, there was a prob */
3995 #endif /* BACKGROUND_POST */
3997 /*------- Actually mail the message ------*/
3998 if(valid_addr == CA_OK
3999 && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
4000 char **alt_smtp = NULL;
4002 if(role && role->smtp){
4003 if(ps_global->FIX_SMTP_SERVER
4004 && ps_global->FIX_SMTP_SERVER[0])
4005 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
4006 else
4007 alt_smtp = role->smtp;
4010 result |= (call_mailer(&header, *body, alt_smtp,
4011 call_mailer_flags,
4012 call_mailer_file_result,
4013 pipe_callback) > 0)
4014 ? P_MAIL_WIN : P_MAIL_LOSE;
4016 if(result & P_MAIL_LOSE)
4017 mark_address_failure_for_pico(&header);
4020 /*----- Was there an fcc involved? -----*/
4021 if(lmc.so){
4022 /*------ Write it if at least something worked ------*/
4023 if((result & (P_MAIL_WIN | P_NEWS_WIN))
4024 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
4025 && pine_rfc822_output(&header, *body, NULL, NULL))){
4026 char label[50];
4028 strncpy(label, "Fcc", sizeof(label));
4029 label[sizeof(label)-1] = '\0';
4030 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
4031 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
4032 label[sizeof(label)-1] = '\0';
4035 /*-- Now actually copy to fcc folder and close --*/
4036 result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
4037 F_ON(F_MARK_FCC_SEEN, ps_global)
4038 ? "\\SEEN" : NULL))
4039 ? P_FCC_WIN : P_FCC_LOSE;
4041 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
4042 q_status_message(SM_ORDER,3,5,
4043 _("Fcc Failed!. No message saved."));
4044 dprint((1, "explicit fcc write failed!\n"));
4045 result |= P_FCC_LOSE;
4048 so_give(&lmc.so);
4051 /*----- Mail Post FAILED, back to composer -----*/
4052 if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
4053 dprint((1, "Send failed, continuing\n"));
4055 if(result & P_FCC_LOSE){
4057 * Find field entry associated with fcc, and start
4058 * composer on it...
4060 for(pf = header.local; pf && pf->name; pf = pf->next)
4061 if(pf->type == Fcc && HE(pf))
4062 HE(pf)->start_here = 1;
4064 q_status_message(SM_ORDER | SM_DING, 3, 3,
4065 pine_send_status(result, fcc,
4066 tmp_20k_buf, SIZEOF_20KBUF, NULL));
4069 if(outgoing->message_id)
4070 fs_give((void **) &outgoing->message_id);
4072 outgoing->message_id = generate_message_id(role);
4074 continue;
4078 * If message sent *completely* successfully, there's a
4079 * reply struct AND we're allowed to write back state, do it.
4080 * But also protect against shifted message numbers due
4081 * to new mail arrival. Since the number passed is based
4082 * on the real imap msg no, AND we're sure no expunge has
4083 * been done, just fix up the sorted number...
4085 update_answered_flags(reply);
4087 /*----- Signed, sealed, delivered! ------*/
4088 q_status_message(SM_ORDER, 0, 3,
4089 pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
4091 break; /* All's well, pop out of here */
4095 if(orig_so)
4096 so_give(&orig_so);
4098 if(fcc)
4099 fs_give((void **)&fcc);
4101 free_body_particulars(bp);
4103 free_attachment_list(&pbf->attachments);
4105 standard_picobuf_teardown(pbf);
4107 for(i=0; i < fixed_cnt; i++){
4108 if(pfields[i].textbuf)
4109 fs_give((void **)&pfields[i].textbuf);
4111 fs_give((void **)&pfields[i].name);
4114 if(lcc_addr)
4115 mail_free_address(&lcc_addr);
4117 if(nobody_addr)
4118 mail_free_address(&nobody_addr);
4120 free_prompts(header.custom);
4121 free_customs(header.custom);
4122 fs_give((void **)&pfields);
4123 free_headents(&headents);
4124 fs_give((void **)&sending_order);
4125 if(suggested_nntp_server)
4126 fs_give((void **)&suggested_nntp_server);
4127 if(title)
4128 fs_give((void **)&title);
4130 if(local_redraft_pos && local_redraft_pos != redraft_pos)
4131 free_redraft_pos(&local_redraft_pos);
4133 pbf = save_previous_pbuf;
4134 g_rolenick = NULL;
4136 dprint((4, "=== send returning ===\n"));
4141 * Check for subject in outgoing message.
4143 * Asks user whether to proceed with no subject.
4146 check_for_subject(METAENV *header)
4148 PINEFIELD *pf;
4149 int rv = CF_OK;
4151 for(pf = header->local; pf && pf->name; pf = pf->next)
4152 if(pf->type == Subject){
4153 if(pf->text && *pf->text && **pf->text)
4154 rv = CF_OK;
4155 else{
4156 if(want_to("No Subject, send anyway ",
4157 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
4158 rv = CF_OK;
4159 else
4160 rv = CF_MISSING;
4163 break;
4167 return(rv);
4172 * Check for fcc in outgoing message.
4174 * Asks user whether to proceed with no fcc.
4177 check_for_fcc(char *fcc)
4179 int rv = CF_OK;
4181 if(fcc && *fcc)
4182 rv = CF_OK;
4183 else{
4184 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
4185 rv = CF_OK;
4186 else
4187 rv = CF_MISSING;
4190 return(rv);
4195 * Confirm that the user wants to send to MAILER-DAEMON
4198 confirm_daemon_send(void)
4200 return(want_to("Really send this message to the MAILER-DAEMON",
4201 'n', 'n', NO_HELP, WT_NORM) == 'y');
4205 void
4206 free_prompts(PINEFIELD *head)
4208 PINEFIELD *pf;
4210 for(pf = head; pf && pf->name; pf = pf->next){
4211 if(HE(pf) && HE(pf)->prompt)
4212 fs_give((void **)& HE(pf)->prompt);
4218 postpone_prompt(void)
4220 static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
4221 {'f', 'f', "F", N_("Form Letter Folder")},
4222 {-1, 0, NULL, NULL} };
4224 return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
4225 pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
4230 * call__mailer_file_result - some results from call_mailer might be in a file.
4231 * dislplay that file.
4233 void
4234 call_mailer_file_result(char *filename, int style)
4236 if(filename){
4237 if(style & CM_BR_VERBOSE){
4238 display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
4240 else{
4241 display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
4246 void
4247 mark_address_failure_for_pico(METAENV *header)
4249 PINEFIELD *pf;
4250 ADDRESS *a;
4251 int error_count = 0;
4252 struct headerentry *last_he = NULL;
4254 for(pf = header->local; pf && pf->name; pf = pf->next)
4255 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
4256 for(a = *pf->addr; a != NULL; a = a->next)
4257 if(a->error != NULL
4258 && error_count++ < MAX_ADDR_ERROR
4259 && (HE(pf))){
4260 if(last_he) /* start last reported err */
4261 last_he->start_here = 0;
4263 (last_he = HE(pf))->start_here = 1;
4270 * This is specialized routine. It assumes that the only things that we
4271 * care about restoring are the body type, subtype, encoding and the
4272 * state of the charset parameter. It also assumes that if the charset
4273 * parameter exists when we save it, it won't be removed later.
4275 BODY_PARTICULARS_S *
4276 save_body_particulars(struct mail_bodystruct *body)
4278 BODY_PARTICULARS_S *bp;
4279 PARAMETER *pm;
4281 bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
4283 bp->type = body->type;
4284 bp->encoding = body->encoding;
4285 bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
4286 bp->parameter = body->parameter;
4287 for(pm = bp->parameter;
4288 pm && strucmp(pm->attribute, "charset") != 0;
4289 pm = pm->next)
4290 ;/* searching for possible charset parameter */
4292 if(pm){ /* found one */
4293 bp->had_csp = 1; /* saved body had charset parameter */
4294 bp->charset = pm->value ? cpystr(pm->value) : NULL;
4296 else{
4297 bp->had_csp = 0;
4298 bp->charset = NULL;
4301 return(bp);
4305 void
4306 reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
4308 body->type = bp->type;
4309 body->encoding = bp->encoding;
4310 if(body->subtype)
4311 fs_give((void **)&body->subtype);
4313 body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
4315 if(bp->parameter){
4316 PARAMETER *pm, *pm_prev = NULL;
4318 for(pm = body->parameter;
4319 pm && strucmp(pm->attribute, "charset") != 0;
4320 pm = pm->next)
4321 pm_prev = pm;
4323 if(pm){ /* body has charset parameter */
4324 if(bp->had_csp){ /* reset to what it used to be */
4325 if(pm->value)
4326 fs_give((void **)&pm->value);
4328 pm->value = bp->charset ? cpystr(bp->charset) : NULL;
4330 else{ /* remove charset parameter */
4331 if(pm_prev)
4332 pm_prev->next = pm->next;
4333 else
4334 body->parameter = pm->next;
4336 mail_free_body_parameter(&pm);
4339 else{
4340 if(bp->had_csp){
4342 * This can't happen because grope never removes
4343 * the charset parameter.
4345 q_status_message(SM_ORDER | SM_DING, 5, 5,
4346 "Programmer error: saved charset but no current charset param in pine_send");
4349 else{
4350 ok, still no parameter
4355 else{
4356 if(body->parameter)
4357 mail_free_body_parameter(&body->parameter);
4359 body->parameter = NULL;
4364 void
4365 free_body_particulars(BODY_PARTICULARS_S *bp)
4367 if(bp){
4368 if(bp->subtype)
4369 fs_give((void **)&bp->subtype);
4371 if(bp->charset)
4372 fs_give((void **)&bp->charset);
4374 fs_give((void **)&bp);
4379 /*----------------------------------------------------------------------
4380 Build a status message suitable for framing
4382 Returns: pointer to resulting buffer
4383 ---*/
4384 char *
4385 pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
4387 int avail = ps_global->ttyo->screen_cols - 2;
4388 int fixedneed, need, lenfcc;
4389 char *part1, *part2, *part3, *part4, *part5;
4390 char fbuf[MAILTMPLEN+1];
4392 part1 = (result & P_NEWS_WIN)
4393 ? "posted"
4394 : (result & P_NEWS_LOSE)
4395 ? "NOT POSTED"
4396 : "";
4397 part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
4398 && (result & P_FCC_BITS))
4399 ? ", "
4400 : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
4401 ? " and "
4402 : "";
4403 part3 = (result & P_MAIL_WIN)
4404 ? "sent"
4405 : (result & P_MAIL_LOSE)
4406 ? "NOT SENT"
4407 : "";
4408 part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
4409 ? " and "
4410 : "";
4411 part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
4412 ? "ONLY copied to "
4413 : (result & P_FCC_WIN)
4414 ? "copied to "
4415 : (result & P_FCC_LOSE)
4416 ? "NOT copied to "
4417 : "";
4418 lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
4420 fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
4421 strlen(part4) + strlen(part5);
4422 need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
4424 if(need > avail && fixedneed + 3 >= avail){
4425 /* dots on end of fixed, no fcc */
4426 snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
4427 part1, part2, part3, part4, part5);
4428 short_str(fbuf, buf, buflen, avail, EndDots);
4430 else if(need > avail){
4431 /* include whole fixed part, quotes and dots at end of fcc name */
4432 if(lenfcc > 0)
4433 lenfcc = MAX(1, lenfcc-(need-avail));
4435 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4436 part1, part2, part3, part4, part5,
4437 (result & P_FCC_BITS) ? "\"" : "",
4438 short_str((result & P_FCC_BITS) ? fcc_name : "",
4439 fbuf, sizeof(fbuf), lenfcc, FrontDots),
4440 (result & P_FCC_BITS) ? "\"" : "");
4442 else{
4443 /* whole thing */
4444 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4445 part1, part2, part3, part4, part5,
4446 (result & P_FCC_BITS) ? "\"" : "",
4447 (result & P_FCC_BITS) ? fcc_name : "",
4448 (result & P_FCC_BITS) ? "\"" : "");
4451 if(goodorbad)
4452 *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
4454 return(buf);
4457 /* Callback from Pico to set the conditions for Alpine to start a new thread
4460 void
4461 new_thread_on_blank_subject(void)
4463 ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
4468 /*----------------------------------------------------------------------
4469 Call back for pico to insert the specified message's text
4471 Args: n -- message number to format
4472 f -- function to use to output the formatted message
4475 Returns: returns msg number formatted on success, zero on error.
4476 ----*/
4477 long
4478 message_format_for_pico(long int n, int (*f) (int))
4480 ENVELOPE *e;
4481 BODY *b;
4482 char *old_quote = NULL;
4483 long rv = n;
4485 if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
4486 && (e = pine_mail_fetchstructure(ps_global->mail_stream,
4487 mn_m2raw(ps_global->msgmap, n), &b)))){
4488 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4489 flush_status_messages(0);
4490 return(0L);
4493 /* temporarily assign a new quote string */
4494 old_quote = pbf->quote_str;
4495 pbf->quote_str = reply_quote_str(e);
4497 /* build separator line */
4498 reply_delimiter(e, NULL, f);
4500 /* actually write message text */
4501 if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
4502 FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
4503 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4504 flush_status_messages(0);
4505 rv = 0L;
4508 fs_give((void **)&pbf->quote_str);
4509 pbf->quote_str = old_quote;
4510 return(rv);
4514 /*----------------------------------------------------------------------
4515 Call back for pico to prompt the user for exit confirmation
4517 Args: dflt -- default answer for confirmation prompt
4519 Returns: either NULL if the user accepts exit, or string containing
4520 reason why the user declined.
4521 ----*/
4523 send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
4524 char **result)
4526 int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
4527 int dsn_label = 0, fcc_label = 0, lparen;
4528 int flowing_label = 0, double_rad;
4529 char *rstr = NULL, *p, *lc, *optp;
4530 char dsn_string[30];
4531 void (*redraw)(void) = ps_global->redrawer;
4532 ESCKEY_S opts[18];
4533 struct filters {
4534 char *filter;
4535 int index;
4536 struct filters *prev, *next;
4537 } *filters = NULL, *fp;
4539 sending_filter_requested = NULL;
4540 call_mailer_flags = 0;
4541 background_requested = 0;
4542 flowing_requested = allow_flowed ? 1 : 0;
4543 lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
4544 if(priority_requested)
4545 fs_give((void **) &priority_requested);
4547 if(background_posting(FALSE)){
4548 if(result)
4549 *result = "Can't send while background posting. Use postpone.";
4551 return(1);
4554 if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
4555 if(result)
4556 *result = NULL;
4558 return(0);
4561 ps_global->redrawer = redraw_pico;
4563 if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
4564 (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
4567 * Build list of available filters...
4569 for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
4570 for(p = ps_global->VAR_SEND_FILTER[i];
4571 *p && !isspace((unsigned char)*p); p++)
4574 c = *p;
4575 *p = '\0';
4576 if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
4577 && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
4578 *p = c;
4579 continue;
4582 fp = (struct filters *)fs_get(sizeof(struct filters));
4583 fp->index = i;
4584 if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
4585 fp->filter = cpystr(lc);
4587 else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
4588 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
4589 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4590 fp->filter = cpystr(tmp_20k_buf);
4592 else
4593 fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
4595 *p = c;
4597 if(filters){
4598 fp->next = filters;
4599 fp->prev = filters->prev;
4600 fp->prev->next = filters->prev = fp;
4602 else{
4603 filters = (struct filters *)fs_get(sizeof(struct filters));
4604 filters->index = -1;
4605 filters->filter = NULL;
4606 filters->next = filters->prev = fp;
4607 fp->next = fp->prev = filters;
4611 i = 0;
4612 opts[i].ch = 'y';
4613 opts[i].rval = 'y';
4614 opts[i].name = "Y";
4615 opts[i++].label = N_("Yes");
4617 opts[i].ch = 'n';
4618 opts[i].rval = 'n';
4619 opts[i].name = "N";
4620 opts[i++].label = N_("No");
4622 if(filters){
4623 /* set global_filter_pointer to desired filter or NULL if none */
4624 /* prepare two keymenu slots for selecting filter */
4625 opts[i].ch = ctrl('P');
4626 opts[i].rval = 10;
4627 opts[i].name = "^P";
4628 opts[i++].label = N_("Prev Filter");
4630 opts[i].ch = ctrl('N');
4631 opts[i].rval = 11;
4632 opts[i].name = "^N";
4633 opts[i++].label = N_("Next Filter");
4635 if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
4636 filters = filters->next;
4639 if(F_ON(F_VERBOSE_POST, ps_global)){
4640 /* setup keymenu slot to toggle verbose mode */
4641 opts[i].ch = ctrl('W');
4642 opts[i].rval = 12;
4643 opts[i].name = "^W";
4644 verbose_label = i++;
4647 if(allow_flowed){
4648 /* setup keymenu slot to toggle flowed mode */
4649 opts[i].ch = ctrl('V');
4650 opts[i].rval = 22;
4651 opts[i].name = "^V";
4652 flowing_label = i++;
4653 flowing_requested = 1;
4656 if(F_ON(F_NO_FCC_ATTACH, ps_global)){
4657 /* setup keymenu slot to toggle attacment on fcc */
4658 opts[i].ch = ctrl('F');
4659 opts[i].rval = 21;
4660 opts[i].name = "^F";
4661 fcc_label = i++;
4664 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4665 if(F_ON(F_BACKGROUND_POST, ps_global)){
4666 opts[i].ch = ctrl('R');
4667 opts[i].rval = 15;
4668 opts[i].name = "^R";
4669 bg_label = i++;
4671 #endif
4673 opts[i].ch = 'p';
4674 opts[i].rval = 'p';
4675 opts[i].name = "P";
4676 opts[i++].label = N_("Priority");
4678 #ifdef SMIME
4679 if(F_OFF(F_DONT_DO_SMIME, ps_global)){
4680 opts[i].ch = 'e';
4681 opts[i].rval = 'e';
4682 opts[i].name = "E";
4683 opts[i++].label = "Encrypt";
4685 opts[i].ch = 'g';
4686 opts[i].rval = 'g';
4687 opts[i].name = "G";
4688 opts[i++].label = "Sign";
4690 if(ps_global->smime){
4691 ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
4692 ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
4695 #endif
4697 double_rad = i;
4699 if(F_ON(F_DSN, ps_global)){
4700 /* setup keymenu slots to toggle dsn bits */
4701 opts[i].ch = 'd';
4702 opts[i].rval = 'd';
4703 opts[i].name = "D";
4704 opts[i].label = N_("DSNOpts");
4705 dsn_label = i++;
4706 opts[i].ch = -2;
4707 opts[i].rval = 's';
4708 opts[i].name = "S";
4709 opts[i++].label = "";
4710 opts[i].ch = -2;
4711 opts[i].rval = 'x';
4712 opts[i].name = "X";
4713 opts[i++].label = "";
4714 opts[i].ch = -2;
4715 opts[i].rval = 'h';
4716 opts[i].name = "H";
4717 opts[i++].label = "";
4720 if(filters){
4721 opts[i].ch = KEY_UP;
4722 opts[i].rval = 10;
4723 opts[i].name = "";
4724 opts[i++].label = "";
4726 opts[i].ch = KEY_DOWN;
4727 opts[i].rval = 11;
4728 opts[i].name = "";
4729 opts[i++].label = "";
4732 opts[i].ch = -1;
4734 fix_windsize(ps_global);
4736 while(1){
4737 if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
4738 *p = '\0';
4739 else
4740 p = NULL;
4742 lparen = 0;
4743 strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
4744 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4745 optp = tmp_20k_buf + strlen(tmp_20k_buf);
4747 if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
4748 sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4750 if(allow_flowed && !flowing_requested){
4751 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4752 if(!lparen){
4753 *optp++ = ' ';
4754 *optp++ = '(';
4755 lparen++;
4757 else
4758 *optp++ = ' ';
4761 sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4764 if(filters){
4765 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4766 if(!lparen){
4767 *optp++ = ' ';
4768 *optp++ = '(';
4769 lparen++;
4771 else{
4772 *optp++ = ',';
4773 *optp++ = ' ';
4777 if(filters->filter){
4778 sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4779 sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4780 if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
4781 *optp++ = '\"';
4783 else
4784 sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4787 if((call_mailer_flags & CM_VERBOSE) || background_requested){
4788 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4789 if(!lparen){
4790 *optp++ = ' ';
4791 *optp++ = '(';
4792 lparen++;
4794 else
4795 *optp++ = ' ';
4798 sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4799 if(call_mailer_flags & CM_VERBOSE)
4800 sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4802 if(background_requested)
4803 sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4805 sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4808 if(g_rolenick && !(he && he[N_FROM].dirty)){
4809 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4810 if(!lparen){
4811 *optp++ = ' ';
4812 *optp++ = '(';
4813 lparen++;
4815 else
4816 *optp++ = ' ';
4819 sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4820 sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4821 sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4824 if(call_mailer_flags & CM_DSN_SHOW){
4825 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4826 if(!lparen){
4827 *optp++ = ' ';
4828 *optp++ = '(';
4829 lparen++;
4831 else{
4832 *optp++ = ',';
4833 *optp++ = ' ';
4837 sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4840 #ifdef SMIME
4841 if(ps_global->smime && ps_global->smime->do_encrypt){
4842 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4843 if(!lparen){
4844 *optp++ = ' ';
4845 *optp++ = '(';
4846 lparen++;
4848 else{
4849 *optp++ = ',';
4850 *optp++ = ' ';
4854 sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4857 if(ps_global->smime && ps_global->smime->do_sign){
4858 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4859 if(!lparen){
4860 *optp++ = ' ';
4861 *optp++ = '(';
4862 lparen++;
4864 else{
4865 *optp++ = ',';
4866 *optp++ = ' ';
4870 sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4872 #endif
4874 if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
4875 *optp++ = ')';
4877 sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4878 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4880 if(p)
4881 *p = ' ';
4883 if(flowing_label)
4884 opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
4886 if(verbose_label)
4887 opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
4889 if(bg_label)
4890 opts[bg_label].label = background_requested
4891 ? N_("Foreground") : N_("Background");
4893 if(fcc_label)
4894 opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
4895 : N_("No Fcc Atmts ");
4897 if(F_ON(F_DSN, ps_global)){
4898 if(call_mailer_flags & CM_DSN_SHOW){
4899 opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
4900 ? N_("NoDelay") : N_("Delay");
4901 opts[dsn_label+1].ch = 's';
4902 opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
4903 ? N_("NoSuccess") : N_("Success");
4904 opts[dsn_label+2].ch = 'x';
4905 opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
4906 ? N_("ErrRets") : N_("NoErrRets");
4907 opts[dsn_label+3].ch = 'h';
4908 opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
4909 ? N_("RetHdrs") : N_("RetFull");
4913 if(double_rad +
4914 ((call_mailer_flags & CM_DSN_SHOW)
4915 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
4916 rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4917 'y', 'z',
4918 (F_ON(F_DSN, ps_global) && allow_flowed)
4919 ? h_send_prompt_dsn_flowed :
4920 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4921 allow_flowed ? h_send_prompt_flowed :
4922 h_send_prompt,
4923 RB_NORM);
4924 else
4925 rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4926 'y', 'z',
4927 (double_rad +
4928 ((call_mailer_flags & CM_DSN_SHOW)
4929 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
4930 ? NO_HELP :
4931 (F_ON(F_DSN, ps_global) && allow_flowed)
4932 ? h_send_prompt_dsn_flowed :
4933 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4934 allow_flowed ? h_send_prompt_flowed :
4935 h_send_prompt,
4936 RB_NORM);
4938 if(rv == 'y'){ /* user ACCEPTS! */
4939 break;
4941 else if(rv == 'n'){ /* Declined! */
4942 rstr = _("No Message Sent");
4943 break;
4945 else if(rv == 'z'){ /* Cancelled! */
4946 rstr = _("Send Cancelled");
4947 break;
4949 else if(rv == 10){ /* PREVIOUS filter */
4950 filters = filters->prev;
4952 else if(rv == 11){ /* NEXT filter */
4953 filters = filters->next;
4955 else if(rv == 21){
4956 lmc.text_only = !lmc.text_only;
4958 else if(rv == 12){ /* flip verbose bit */
4959 if(call_mailer_flags & CM_VERBOSE)
4960 call_mailer_flags &= ~CM_VERBOSE;
4961 else
4962 call_mailer_flags |= CM_VERBOSE;
4964 if((call_mailer_flags & CM_VERBOSE) && background_requested)
4965 background_requested = 0;
4967 else if(rv == 22){ /* flip flowing bit */
4968 flowing_requested = !flowing_requested;
4970 else if(rv == 15){
4971 if((background_requested = !background_requested)
4972 && (call_mailer_flags & CM_VERBOSE))
4973 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
4975 else if(call_mailer_flags & CM_DSN_SHOW){
4976 if(rv == 's'){ /* flip success bit */
4977 call_mailer_flags ^= CM_DSN_SUCCESS;
4978 /* turn off related bits */
4979 if(call_mailer_flags & CM_DSN_SUCCESS)
4980 call_mailer_flags &= ~(CM_DSN_NEVER);
4982 else if(rv == 'd'){ /* flip delay bit */
4983 call_mailer_flags ^= CM_DSN_DELAY;
4984 /* turn off related bits */
4985 if(call_mailer_flags & CM_DSN_DELAY)
4986 call_mailer_flags &= ~(CM_DSN_NEVER);
4988 else if(rv == 'x'){ /* flip never bit */
4989 call_mailer_flags ^= CM_DSN_NEVER;
4990 /* turn off related bits */
4991 if(call_mailer_flags & CM_DSN_NEVER)
4992 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
4994 else if(rv == 'h'){ /* flip full bit */
4995 call_mailer_flags ^= CM_DSN_FULL;
4998 else if(rv == 'd'){ /* show dsn options */
5000 * When you turn on DSN, the default is to notify on
5001 * failure, success, or delay; and to return the whole
5002 * body on failure.
5004 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
5006 else if(rv == 'p'){ /* choose X-Priority */
5007 char *prio;
5009 prio = choose_a_priority(priority_requested);
5010 if((ps_global->redrawer = redraw_pico) != NULL){
5011 (*ps_global->redrawer)();
5012 fix_windsize(ps_global);
5015 if(prio){
5016 if(priority_requested)
5017 fs_give((void **) &priority_requested);
5019 if(prio[0])
5020 priority_requested = prio;
5021 else
5022 fs_give((void **) &prio);
5025 #ifdef SMIME
5026 else if(rv=='e'){
5027 if(ps_global->smime)
5028 ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
5030 else if(rv=='g'){
5031 if(ps_global->smime)
5032 ps_global->smime->do_sign = !ps_global->smime->do_sign;
5034 #endif
5036 snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
5037 (call_mailer_flags & CM_DSN_NEVER)
5038 ? "Never" : "F",
5039 (call_mailer_flags & CM_DSN_DELAY)
5040 ? "D" : "",
5041 (call_mailer_flags & CM_DSN_SUCCESS)
5042 ? "S" : "",
5043 (call_mailer_flags & CM_DSN_NEVER)
5044 ? ""
5045 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
5046 : "-Hdrs");
5047 dsn_string[sizeof(dsn_string)-1] = '\0';
5050 /* remember selection */
5051 if(filters && filters->index > -1)
5052 sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
5054 if(filters){
5055 filters->prev->next = NULL; /* tie off list */
5056 while(filters){ /* then free it */
5057 fp = filters->next;
5058 if(filters->filter)
5059 fs_give((void **)&filters->filter);
5061 fs_give((void **)&filters);
5062 filters = fp;
5066 if(old_suspend)
5067 (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
5069 if(result)
5070 *result = rstr;
5072 ps_global->redrawer = redraw;
5074 return((rstr == NULL) ? 0 : 1);
5079 * Allow user to choose a priority for sending.
5081 * Returns an allocated priority on success, NULL otherwise.
5083 char *
5084 choose_a_priority(char *default_val)
5086 char *choice = NULL;
5087 char **priority_list, **lp;
5088 char *starting_val = NULL;
5089 char *none;
5090 int i, cnt;
5091 PRIORITY_S *p;
5093 for(cnt = 0, p = priorities; p && p->desc; p++)
5094 cnt++;
5096 cnt++; /* for NONE entry */
5097 lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
5098 memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
5100 for(i = 0, p = priorities; p && p->desc; p++){
5101 *lp = cpystr(p->desc);
5102 if(default_val && !strcmp(default_val, p->desc))
5103 starting_val = (*lp);
5105 lp++;
5108 none = _("NONE - No X-Priority header included");
5109 *lp = cpystr(none);
5110 if(!starting_val)
5111 starting_val = (*lp);
5113 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5114 TRANSLATORS: Print something1 using something2.
5115 "priorities" is something1 */
5116 choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
5117 _("priorities"), h_select_priority_screen,
5118 _("HELP FOR SELECTING A PRIORITY"),
5119 starting_val);
5121 if(!choice)
5122 q_status_message(SM_ORDER, 1, 4, _("No change"));
5123 else if(!strcmp(choice, none))
5124 choice[0] = '\0';
5126 free_list_array(&priority_list);
5128 return(choice);
5133 dont_flow_this_time(void)
5135 return(flowing_requested ? 0 : 1);
5139 /*----------------------------------------------------------------------
5140 Call back for pico to display mime type of attachment
5142 Args: file -- filename being attached
5144 Returns: returns 1 on success (message queued), zero otherwise (don't know
5145 type so nothing queued).
5146 ----*/
5148 mime_type_for_pico(char *file)
5150 BODY *body;
5151 int rv;
5152 void *file_contents;
5154 body = mail_newbody();
5155 body->type = TYPEOTHER;
5156 body->encoding = ENCOTHER;
5158 /* don't know where the cursor's been, reset it */
5159 clear_cursor_pos();
5160 if(!set_mime_type_by_extension(body, file)){
5161 if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5162 body->contents.text.data = file_contents;
5163 set_mime_type_by_grope(body);
5167 if(body->type != TYPEOTHER){
5168 rv = 1;
5169 q_status_message3(SM_ORDER, 0, 3,
5170 _("File %s attached as type %s/%s"), file,
5171 body_types[body->type],
5172 body->subtype ? body->subtype : rfc822_default_subtype(body->type));
5174 else
5175 rv = 0;
5177 pine_free_body(&body);
5178 return(rv);
5182 /*----------------------------------------------------------------------
5183 Call back for pico to receive an uploaded message
5185 Args: fname -- name for uploaded file (empty if they want us to assign it)
5186 size -- pointer to long to hold the attachment's size
5188 Notes: the attachment is uploaded to a temp file, and
5190 Returns: TRUE on success, FALSE otherwise
5191 ----*/
5193 upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
5195 char cmd[MAXPATH+1], *fnp = NULL;
5196 char *locale_name = NULL;
5197 long l;
5198 PIPE_S *syspipe;
5200 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5201 fname ? fname : "<NO FILE>"));
5203 if(!fname) /* no place for file name */
5204 return(0);
5206 if(!*fname){ /* caller wants temp file */
5207 if((fnp = temp_nam(NULL, "pu")) != NULL){
5208 strncpy(fname, fnp, fnlen);
5209 fname[fnlen-1] = '\0';
5210 our_unlink(fnp);
5211 fs_give((void **)&fnp);
5214 else
5215 locale_name = convert_to_locale(fname);
5217 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
5218 ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
5219 if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
5220 0, pipe_callback, pipe_report_error)) != NULL){
5221 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
5222 if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
5223 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5224 "Error determining size of %s: %s", fname,
5225 fnp = error_description(errno));
5226 dprint((1,
5227 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5228 cmd ? cmd : "?",
5229 fname ? fname : "?",
5230 fnp ? fnp : "?"));
5232 else if(size)
5233 *size = l;
5235 if(locale_name)
5236 fs_give((void **) &locale_name);
5238 return(l >= 0);
5240 else
5241 q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
5243 if(locale_name)
5244 fs_give((void **) &locale_name);
5246 return(0);
5250 char *
5251 cancel_for_pico(void (*redraw_pico)(void))
5253 int rv;
5254 char *rstr = NULL;
5255 char *prompt =
5256 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5257 void (*redraw)(void) = ps_global->redrawer;
5258 static ESCKEY_S opts[] = {
5259 {'c', 'c', "C", N_("Confirm")},
5260 {'n', 'n', "N", N_("No")},
5261 {'y', 'y', "", ""},
5262 {-1, 0, NULL, NULL}
5265 ps_global->redrawer = redraw_pico;
5266 fix_windsize(ps_global);
5268 while(1){
5269 rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
5270 'n', 'x', h_confirm_cancel, RB_NORM);
5271 if(rv == 'c'){ /* user ACCEPTS! */
5272 rstr = "";
5273 break;
5275 else if(rv == 'y'){
5276 q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
5277 display_message('x');
5279 else
5280 break;
5283 ps_global->redrawer = redraw;
5284 return(rstr);
5288 /*----------------------------------------------------------------------
5289 Pass the first text segment of the message thru the "send filter"
5291 Args: body pointer and address for storage object of old data
5293 Returns: returns 1 on success, zero on error.
5294 ----*/
5296 filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
5297 STORE_S **old, METAENV *header)
5299 char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
5300 int key = 0, include_hdrs = 0;
5301 gf_io_t gc, pc;
5302 STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
5303 ? &body->nested.part->body.contents.text.data
5304 : &body->contents.text.data),
5305 *tmp_so = NULL, *tmpf_so,
5306 *save_local_so, *readthis_so, *our_tmpf_so = NULL;
5307 #define DO_HEADERS 1
5309 if(fcmd
5310 && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
5311 &key, &include_hdrs, NULL))){
5312 if(tmpf){
5314 * We need WRITE_TO_LOCALE here because the user is going to
5315 * be operating on tmpf. We need to change it back after they
5316 * are done.
5318 if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
5319 if(key){
5320 so_puts(tmpf_so, filter_session_key());
5321 so_puts(tmpf_so, NEWLINE);
5325 * If the headers are wanted for filtering, we can just
5326 * stick them in the tmpf file that is already there before
5327 * putting the body in.
5329 if(include_hdrs){
5330 save_local_so = lmc.so;
5331 lmc.so = tmpf_so; /* write it to tmpf_so */
5332 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5333 pine_rfc822_header(header, body, NULL, NULL);
5334 lmc.so = save_local_so;
5337 so_seek(*so, 0L, 0);
5338 gf_set_so_readc(&gc, *so);
5339 gf_set_so_writec(&pc, tmpf_so);
5340 gf_filter_init();
5341 errstr = gf_pipe(gc, pc);
5342 gf_clear_so_readc(*so);
5343 gf_clear_so_writec(tmpf_so);
5344 so_give(&tmpf_so);
5346 else
5347 errstr = "Can't create space for filter temporary file.";
5349 else if(include_hdrs){
5351 * Gf_filter wants a single storage object to read from.
5352 * If headers are wanted for filtering we'll have to put them
5353 * and the body into a temp file first and then use that
5354 * as the storage object for gf_filter.
5355 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5356 * takes care of that.
5358 if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
5359 /* put headers in our_tmpf_so */
5360 save_local_so = lmc.so;
5361 lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
5362 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5363 pine_rfc822_header(header, body, NULL, NULL);
5364 lmc.so = save_local_so;
5366 /* put body in our_tmpf_so */
5367 so_seek(*so, 0L, 0);
5368 gf_set_so_readc(&gc, *so);
5369 gf_set_so_writec(&pc, our_tmpf_so);
5370 gf_filter_init();
5371 errstr = gf_pipe(gc, pc);
5372 gf_clear_so_readc(*so);
5373 gf_clear_so_writec(our_tmpf_so);
5375 /* tell gf_filter to read from our_tmpf_so instead of *so */
5376 readthis_so = our_tmpf_so;
5378 else
5379 errstr = "Can't create space for temporary file.";
5381 else
5382 readthis_so = *so;
5384 if(!errstr){
5385 if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
5386 gf_set_so_writec(&pc, tmp_so);
5387 ps_global->mangled_screen = 1;
5388 suspend_busy_cue();
5389 ClearScreen();
5390 fflush(stdout);
5391 if(tmpf){
5392 PIPE_S *fpipe;
5394 if((fpipe = open_system_pipe(cmd, NULL, NULL,
5395 PIPE_NOSHELL | PIPE_RESET,
5396 0, pipe_callback, pipe_report_error)) != NULL){
5397 if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
5399 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5400 if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5401 gf_set_so_readc(&gc, tmpf_so);
5402 gf_filter_init();
5403 errstr = gf_pipe(gc, pc);
5404 gf_clear_so_readc(tmpf_so);
5405 so_give(&tmpf_so);
5407 else
5408 errstr = "Can't open temp file filter wrote.";
5410 else
5411 errstr = "Filter command returned error.";
5413 else
5414 errstr = "Can't exec filter text.";
5416 else
5417 errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
5418 readthis_so, pc, NULL, 0, 0,
5419 pipe_callback);
5421 if(our_tmpf_so)
5422 so_give(&our_tmpf_so);
5424 gf_clear_so_writec(tmp_so);
5426 #ifdef _WINDOWS
5427 if(!errstr){
5429 * Can't really be using stdout, so don't print message that
5430 * gets printed in the else. Ideally the program being called
5431 * will wait after showing the message, we might want to look
5432 * into doing the waiting in console based apps... or not.
5434 #else
5435 if(errstr){
5436 unsigned long ch;
5438 fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
5439 fflush(stdout);
5440 while((ch = read_char(300)) != ctrl('M')
5441 && ch != NO_OP_IDLE)
5442 putchar(BELL);
5444 else{
5445 #endif /* _WINDOWS */
5446 BODY *b = (body->type == TYPEMULTIPART)
5447 ? &body->nested.part->body : body;
5449 *old = *so; /* save old so */
5450 *so = tmp_so; /* return new one */
5451 (*so)->attr = copy_parameters((*old)->attr);
5454 * If the command said it would return new MIME
5455 * mime type data, check it out...
5457 if(mtf){
5458 char buf[MAILTMPLEN], *s;
5459 FILE *fp;
5461 if((fp = our_fopen(mtf, "rb")) != NULL){
5462 if(fgets(buf, sizeof(buf), fp)
5463 && !struncmp(buf, "content-", 8)
5464 && (s = strchr(buf+8, ':'))){
5465 BODY *nb = mail_newbody();
5467 for(*s++ = '\0'; *s == ' '; s++)
5470 rfc822_parse_content_header(nb,
5471 (char *) ucase((unsigned char *) buf+8),s);
5472 if(nb->type == TYPETEXT
5473 && nb->subtype
5474 && (!b->subtype
5475 || strucmp(b->subtype, nb->subtype))){
5476 if(b->subtype)
5477 fs_give((void **) &b->subtype);
5479 b->subtype = nb->subtype;
5480 nb->subtype = NULL;
5482 mail_free_body_parameter(&b->parameter);
5483 b->parameter = nb->parameter;
5484 nb->parameter = NULL;
5485 mail_free_body_parameter(&nb->parameter);
5488 mail_free_body(&nb);
5491 fclose(fp);
5496 * Reevaluate the encoding in case form's changed...
5498 b->encoding = ENCOTHER;
5499 set_mime_type_by_grope(b);
5502 ClearScreen();
5503 resume_busy_cue(0);
5505 else
5506 errstr = "Can't create space for filtered text.";
5509 fs_give((void **)&cmd);
5511 else
5512 return(0);
5514 if(tmpf){
5515 our_unlink(tmpf);
5516 fs_give((void **)&tmpf);
5519 if(mtf){
5520 our_unlink(mtf);
5521 fs_give((void **) &mtf);
5524 if(resultf){
5525 if(name_file_size(resultf) > 0L)
5526 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
5527 our_unlink(resultf);
5528 fs_give((void **)&resultf);
5530 else if(errstr){
5531 if(tmp_so)
5532 so_give(&tmp_so);
5534 q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
5535 errstr);
5536 dprint((1, "Filter FAILED: %s\n",
5537 errstr ? errstr : "?"));
5540 return(errstr == NULL);
5544 /*----------------------------------------------------------------------
5545 Copy the newsgroup name of the given mailbox into the given buffer
5547 Args:
5549 Returns:
5550 ----*/
5551 void
5552 pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
5554 NETMBX mb;
5556 if(*mailbox == '#'){ /* Strip the leading "#news." */
5557 strncpy(group_name, mailbox + 6, len-1);
5558 group_name[len-1] = '\0';
5560 else if(mail_valid_net_parse(mailbox, &mb)){
5561 pine_send_newsgroup_name(mb.mailbox, group_name, len);
5563 else
5564 *group_name = '\0';
5568 /*----------------------------------------------------------------------
5569 Set up fields for passing to pico. Assumes first text part is
5570 intended to be passed along for editing, and is in the form of
5571 of a storage object brought into existence sometime before pico_send().
5572 -----*/
5573 void
5574 outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
5575 PATMT **pico_a, int from_bounce)
5577 PINEFIELD *pf;
5580 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5581 * is guaranteed to be of type PicoText!
5583 if(bod->type == TYPETEXT){
5584 *text = so_text((STORE_S *) bod->contents.text.data);
5586 /* mark storage object as user edited */
5587 if(!from_bounce)
5588 (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
5590 else if(bod->type == TYPEMULTIPART){
5591 PART *part;
5592 PATMT **ppa;
5593 char *type, *name, *p;
5594 int name_l;
5597 * We used to jump out the window if the first part wasn't text,
5598 * but that may not be the case when bouncing a message with
5599 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5600 * contents of the first part to send is still ALWAYS in a
5601 * PicoText storage object, *AND* if that object doesn't contain
5602 * data of type text, then it must contain THE ENCODED NON-TEXT
5603 * DATA of the piece being sent.
5605 * It's up to the programmer to make sure that such a message is
5606 * sent via pine_simple_send and never get to the composer via
5607 * pine_send.
5609 * Make sense?
5611 *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
5613 /* mark storage object as user edited */
5614 if(!from_bounce)
5615 (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
5618 * If we already had a list, blast it now, so we can build a new
5619 * attachment list that reflects what's really there...
5621 if(pico_a){
5622 free_attachment_list(pico_a);
5625 /* Simplifyihg assumption #28e. (see cross reference)
5626 All parts in the body passed in here that are not already
5627 in the attachments list are added to the end of the attachments
5628 list. Attachment items not in the body list will be taken care
5629 of in strings2outgoing, but they are unlikely to occur
5632 for(part = bod->nested.part->next; part != NULL; part = part->next) {
5633 /* Already in list? */
5634 for(ppa = pico_a;
5635 *ppa && strcmp((*ppa)->id, part->body.id);
5636 ppa = &(*ppa)->next)
5639 if(!*ppa){ /* Not in the list! append it... */
5640 *ppa = (PATMT *)fs_get(sizeof(PATMT));
5642 if(part->body.description){
5643 char *p;
5644 size_t len;
5646 len = 4*strlen(part->body.description)+1;
5647 p = (char *)fs_get(len*sizeof(char));
5648 if(rfc1522_decode_to_utf8((unsigned char *)p,
5649 len, part->body.description) == (unsigned char *) p){
5650 (*ppa)->description = p;
5652 else{
5653 fs_give((void **)&p);
5654 (*ppa)->description = cpystr(part->body.description);
5657 else
5658 (*ppa)->description = cpystr("");
5660 type = type_desc(part->body.type, part->body.subtype,
5661 part->body.parameter, NULL, 0);
5664 * If we can find a "name" parm, display that too...
5666 if((name = parameter_val(part->body.parameter, "name")) != NULL){
5667 /* Convert any [ or ]'s the name contained */
5668 for(p = name; *p ; p++)
5669 if(*p == '[')
5670 *p = '(';
5671 else if(*p == ']')
5672 *p = ')';
5674 name_l = p - name;
5676 else
5677 name_l = 0;
5679 (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
5681 snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
5682 name ? ": " : "", name ? name : "");
5683 (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
5685 if(name)
5686 fs_give((void **) &name);
5688 (*ppa)->flags = A_FLIT;
5689 (*ppa)->size = cpystr(byte_string(
5690 send_body_size(&part->body)));
5691 if(!part->body.id)
5692 part->body.id = generate_message_id(NULL);
5694 (*ppa)->id = cpystr(part->body.id);
5695 (*ppa)->next = NULL;
5702 /*------------------------------------------------------------------
5703 Malloc strings to pass to composer editor because it expects
5704 such strings so it can realloc them
5705 -----------------------------------------------------------------*/
5707 * turn any address fields into text strings
5710 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5711 * NOT to be RFC1522 decoded. Said differently, they're understood
5712 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5713 * original charset tagging as far into the compose/send pipe as
5714 * we can.
5716 for(pf = header->local; pf && pf->name; pf = pf->next)
5717 if(pf->canedit)
5718 switch(pf->type){
5719 case Address :
5720 if(pf->addr){
5721 char *p, *t, *u;
5722 long l;
5724 pf->scratch = addr_list_string(*pf->addr, NULL, 1);
5727 * Scan for and fix-up patently bogus fields.
5729 * NOTE: collaboration with this code and what's done in
5730 * reply.c:reply_cp_addr to package up the bogus stuff
5731 * is required.
5733 for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
5734 for(t = p; ; t--)
5735 if(*t == '&'){ /* find "leading" token */
5736 int replacelen;
5739 * Rfc822_cat has been changed so that it now quotes
5740 * this sometimes. So we have to look out for quotes
5741 * which confuse the decoder. It was only quoting
5742 * because we were putting \r \n in the input, I think.
5744 if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
5745 t[-1] = p[-1] = ' ';
5747 *t++ = ' '; /* replace token */
5748 *p = '\0'; /* tie off string */
5749 u = rfc822_base64((unsigned char *) t,
5750 (unsigned long) strlen(t),
5751 (unsigned long *) &l);
5752 if(!u)
5753 u = "";
5755 replacelen = strlen(t);
5756 *p = '@'; /* restore 'p' */
5757 rplstr(p, strlen(p), 12, ""); /* clear special token */
5758 rplstr(t, strlen(u)-replacelen+1, replacelen, u);
5759 if(u)
5760 fs_give((void **) &u);
5762 if(HE(pf))
5763 HE(pf)->start_here = 1;
5765 break;
5767 else if(t == pf->scratch)
5768 break;
5770 removing_leading_white_space(pf->scratch);
5771 if(pf->scratch){
5772 size_t l;
5775 * Replace control characters with ^C notation, unless
5776 * some conditions are met (see istrncpy).
5777 * If user doesn't edit, then we switch back to the
5778 * original version. If user does edit, then all bets
5779 * are off.
5781 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5782 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5783 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5784 fs_give((void **)&pf->scratch);
5785 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5788 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5792 break;
5794 case Subject :
5795 if(pf->text){
5796 char *p, *src;
5797 size_t len;
5799 src = pf->scratch ? pf->scratch
5800 : (*pf->text) ? *pf->text : "";
5802 len = 4*strlen(src)+1;
5803 p = (char *)fs_get(len * sizeof(char));
5804 if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
5805 if(pf->scratch)
5806 fs_give((void **)&pf->scratch);
5808 pf->scratch = p;
5810 else{
5811 fs_give((void **)&p);
5812 if(!pf->scratch)
5813 pf->scratch = cpystr(src);
5816 if(pf->scratch){
5817 size_t l;
5820 * Replace control characters with ^C notation, unless
5821 * some conditions are met (see istrncpy).
5822 * If user doesn't edit, then we switch back to the
5823 * original version. If user does edit, then all bets
5824 * are off.
5826 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5827 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5828 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5829 fs_give((void **)&pf->scratch);
5830 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5833 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5837 break;
5839 default :
5840 break;
5845 /*----------------------------------------------------------------------
5846 Restore fields returned from pico to form useful to sending
5847 routines.
5848 -----*/
5849 void
5850 strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
5852 PINEFIELD *pf;
5853 int we_cancel = 0;
5855 we_cancel = busy_cue(NULL, NULL, 1);
5858 * turn any local address strings into address lists
5860 for(pf = header->local; pf && pf->name; pf = pf->next)
5861 if(pf->scratch){
5862 char *the_address = NULL;
5864 switch(pf->type){
5865 case Address :
5866 removing_trailing_white_space(pf->scratch);
5868 if((the_address || *pf->scratch) && pf->addr){
5869 ADDRESS *new_addr = NULL;
5870 static char *fakedomain = "@";
5872 if(!the_address)
5873 the_address = pf->scratch;
5875 rfc822_parse_adrlist(&new_addr, the_address,
5876 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
5877 ? fakedomain : ps_global->maildomain);
5878 mail_free_address(pf->addr); /* free old addrs */
5879 *pf->addr = new_addr; /* assign new addr */
5881 else if(pf->addr)
5882 mail_free_address(pf->addr); /* free old addrs */
5884 break;
5886 case Subject :
5887 if(*pf->text)
5888 fs_give((void **)pf->text);
5890 if(*pf->scratch){
5891 *pf->text = cpystr(pf->scratch);
5894 break;
5896 default :
5897 break;
5900 fs_give((void **)&pf->scratch); /* free now useless text */
5903 create_message_body(bod, attach, flow_it);
5905 if(we_cancel)
5906 cancel_busy_cue(-1);
5910 /*----------------------------------------------------------------------
5912 The head of the body list here is always either TEXT or MULTIPART. It may be
5913 changed from TEXT to MULTIPART if there are attachments to be added
5914 and it is not already multipart.
5915 ----*/
5916 void
5917 create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
5919 PART *p, **pp;
5920 PATMT *pa;
5921 BODY *tmp_body, *text_body = NULL;
5922 void *file_contents;
5923 PARAMETER **parmp;
5924 char *lc;
5926 TIME_STAMP("create_body start.", 1);
5928 * if conditions are met short circuit MIME wrapping
5930 if((*b)->type != TYPEMULTIPART && !attach){
5932 /* only override assigned encoding if it might need upgrading */
5933 if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
5934 (*b)->encoding = ENCOTHER;
5936 create_message_body_text(*b, flow_it);
5938 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
5939 || !((*b)->encoding == ENC8BIT
5940 || (*b)->encoding == ENCBINARY)){
5941 TIME_STAMP("create_body end.", 1);
5942 return;
5944 else /* protect 8bit in multipart */
5945 text_body = *b;
5948 if((*b)->type == TYPETEXT) {
5949 /*-- Current type is text, but there are attachments to add --*/
5950 /*-- Upgrade to a TYPEMULTIPART --*/
5951 tmp_body = (BODY *)mail_newbody();
5952 tmp_body->type = TYPEMULTIPART;
5953 tmp_body->nested.part = mail_newbody_part();
5954 if(text_body){
5956 * Why do we do this?
5957 * The problem is that base64 or quoted-printable encoding is
5958 * sensitive to having random data appended to it's end. If
5959 * we use a single part TEXT message and something in between
5960 * us and the end appends advertising without adjusting for
5961 * the encoding, the message is screwed up. So we wrap the
5962 * text part inside a multipart and then the appended data
5963 * will come after the boundary.
5965 * We wish we could do this on the way out the door in a
5966 * child of post_rfc822_output because at that point we know
5967 * the character set and the encoding being used. For example,
5968 * iso-2022-jp is an encoding that is not sensitive to data
5969 * appended to the end, so it wouldn't need to be wrapped.
5970 * We could conceivably have post_rfc822_body inspect the
5971 * body and change it before doing the output. It would work
5972 * but would be very fragile. We'd be passed a body from
5973 * c-client to output and instead of just doing the output
5974 * we'd change the body and then output it. Not worth it
5975 * since the multipart wrapping is completely correct for
5976 * MIME-aware mailers.
5978 (void) copy_body(&(tmp_body->nested.part->body), *b);
5979 /* move contents which were NOT copied */
5980 tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
5981 (*b)->contents.text.data = NULL;
5983 else{
5984 tmp_body->nested.part->body = **b;
5986 (*b)->subtype = (*b)->id = (*b)->description = NULL;
5987 (*b)->parameter = NULL;
5988 (*b)->contents.text.data = NULL;
5991 pine_free_body(b);
5992 *b = tmp_body;
5995 if(!text_body){
5996 /*-- Now type must be MULTIPART with first part text --*/
5997 (*b)->nested.part->body.encoding = ENCOTHER;
5998 create_message_body_text(&((*b)->nested.part->body), flow_it);
6001 /*------ Go through the parts list remove those to be deleted -----*/
6002 for(pp = &(*b)->nested.part->next; *pp;){
6003 for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
6004 /* already existed? */
6005 if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
6006 char *orig_descp = NULL, *cs = NULL;
6009 * decode original to see if it matches what was decoded
6010 * when we sent it in.
6013 if((*pp)->body.description)
6014 orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
6015 SIZEOF_20KBUF, (*pp)->body.description);
6017 if(!(*pp)->body.description /* update description? */
6018 || (pa->description && strcmp(pa->description, orig_descp))){
6019 if((*pp)->body.description)
6020 fs_give((void **) &(*pp)->body.description);
6022 /* encoding happens as msg text is written */
6023 (*pp)->body.description = cpystr(pa->description);
6026 if(cs)
6027 fs_give((void **) &cs);
6029 break;
6032 if(pa == NULL){
6033 p = *pp; /* prepare to zap *pp */
6034 *pp = p->next; /* pull next one in list up */
6035 p->next = NULL; /* tie off removed node */
6037 pine_free_body_data(&p->body); /* clean up contained data */
6038 mail_free_body_part(&p); /* free up the part */
6040 else
6041 pp = &(*pp)->next;
6044 /*---------- Now add any new attachments ---------*/
6045 for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
6046 for(pa = attach; pa != NULL; pa = pa->next) {
6047 if(pa->id != NULL)
6048 continue; /* Has an ID, it's old */
6051 * the idea is handle ALL attachments as open FILE *'s. Actual
6052 * encoding and such is handled at the time the message
6053 * is shoved into the mail slot or written to disk...
6055 * Also, we never unlink a file, so it's up to whoever opens
6056 * it to deal with tmpfile issues.
6058 if((file_contents = (void *)so_get(FileStar, pa->filename,
6059 READ_ACCESS)) == NULL){
6060 q_status_message2(SM_ORDER | SM_DING, 3, 4,
6061 _("Error \"%s\", couldn't attach file \"%s\""),
6062 error_description(errno), pa->filename);
6063 display_message('x');
6064 continue;
6067 p->next = mail_newbody_part();
6068 p = p->next;
6069 p->body.id = generate_message_id(NULL);
6070 p->body.contents.text.data = file_contents;
6073 * Set type to unknown and let set_mime_type_by_* figure it out.
6074 * Always encode attachments we add as BINARY.
6076 p->body.type = TYPEOTHER;
6077 p->body.encoding = ENCBINARY;
6078 p->body.size.bytes = name_file_size(pa->filename);
6079 if(!set_mime_type_by_extension(&p->body, pa->filename)){
6080 set_mime_type_by_grope(&p->body);
6081 set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
6084 so_release((STORE_S *)p->body.contents.text.data);
6086 if(pa->description) /* encoding happens when msg written */
6087 p->body.description = cpystr(pa->description);
6089 /* Add name attribute for backward compatibility */
6090 for(parmp = &p->body.parameter; *parmp; )
6091 if(!struncmp((*parmp)->attribute, "name", 4)
6092 && (!*((*parmp)->attribute + 4)
6093 || *((*parmp)->attribute + 4) == '*')){
6094 PARAMETER *free_me = *parmp;
6095 *parmp = (*parmp)->next;
6096 free_me->next = NULL;
6097 mail_free_body_parameter(&free_me);
6099 else
6100 parmp = &(*parmp)->next;
6102 set_parameter(parmp, "name",
6103 pa->filename
6104 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6105 : NULL);
6107 /* Then set the Content-Disposition ala RFC1806 */
6108 if(!p->body.disposition.type){
6109 p->body.disposition.type = cpystr("attachment");
6110 for(parmp = &p->body.disposition.parameter; *parmp; )
6111 if(!struncmp((*parmp)->attribute, "filename", 4)
6112 && (!*((*parmp)->attribute + 4)
6113 || *((*parmp)->attribute + 4) == '*')){
6114 PARAMETER *free_me = *parmp;
6115 *parmp = (*parmp)->next;
6116 free_me->next = NULL;
6117 mail_free_body_parameter(&free_me);
6119 else
6120 parmp = &(*parmp)->next;
6122 set_parameter(parmp, "filename",
6123 pa->filename
6124 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6125 : NULL);
6128 p->next = NULL;
6129 pa->id = cpystr(p->body.id);
6133 * Now, if this multipart has but one text piece (that is, no
6134 * attachments), then downgrade from a composite type to a discrete
6135 * text/plain message if CTE is not 8bit.
6137 if(!(*b)->nested.part->next
6138 && (*b)->nested.part->body.type == TYPETEXT
6139 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
6140 || !((*b)->nested.part->body.encoding == ENC8BIT
6141 || (*b)->nested.part->body.encoding == ENCBINARY))){
6142 /* Clone the interesting body part */
6143 tmp_body = mail_newbody();
6144 *tmp_body = (*b)->nested.part->body;
6145 /* and rub out what we don't want cleaned up when it's free'd */
6146 mail_initbody(&(*b)->nested.part->body);
6147 mail_free_body(b);
6148 *b = tmp_body;
6152 TIME_STAMP("create_body end.", 1);
6157 * Fill in text BODY part's structure
6160 void
6161 create_message_body_text(struct mail_bodystruct *b, int flow_it)
6163 set_mime_type_by_grope(b);
6164 if(b != NULL){
6165 remove_parameter(&b->parameter, "format"); /* we will set it up below */
6166 remove_parameter(&b->parameter, "delsp"); /* we never set this up */
6168 if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
6169 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
6170 && flow_it)
6171 set_parameter(b ? &b->parameter : NULL, "format", "flowed");
6173 set_body_size(b);
6178 * free_attachment_list - free attachments in given list
6180 void
6181 free_attachment_list(PATMT **alist)
6183 PATMT *leading;
6185 while(alist && *alist){ /* pointer pointing to something */
6186 leading = (*alist)->next;
6187 if((*alist)->description)
6188 fs_give((void **)&(*alist)->description);
6190 if((*alist)->filename){
6191 if((*alist)->flags & A_TMP)
6192 if(our_unlink((*alist)->filename) < 0)
6193 dprint((1, "-- Can't unlink(%s): %s\n",
6194 (*alist)->filename ? (*alist)->filename : "?",
6195 error_description(errno)));
6197 fs_give((void **)&(*alist)->filename);
6200 if((*alist)->size)
6201 fs_give((void **)&(*alist)->size);
6203 if((*alist)->id)
6204 fs_give((void **)&(*alist)->id);
6206 fs_give((void **)alist);
6208 *alist = leading;
6213 void
6214 set_body_size(struct mail_bodystruct *b)
6216 unsigned char c;
6217 int we_cancel = 0;
6219 we_cancel = busy_cue(NULL, NULL, 1);
6220 so_seek((STORE_S *)b->contents.text.data, 0L, 0);
6221 b->size.bytes = 0L;
6222 while(so_readc(&c, (STORE_S *)b->contents.text.data))
6223 b->size.bytes++;
6225 if(we_cancel)
6226 cancel_busy_cue(-1);
6231 * view_as_rich - set the rich_header flag
6233 * name - name of the header field
6234 * deflt - default value to return if user didn't set it
6236 * Note: if the user tries to turn them all off with "", then
6237 * we take that to mean default, since otherwise there is no
6238 * way to get to the headers.
6241 view_as_rich(char *name, int deflt)
6243 char **p;
6244 char *q;
6246 p = ps_global->VAR_COMP_HDRS;
6248 if(p && *p && **p){
6249 for(; (q = *p) != NULL; p++){
6250 if(!struncmp(q, name, strlen(name)))
6251 return 0; /* 0 means we *do* view it by default */
6254 return 1; /* 1 means it starts out hidden */
6256 return(deflt);
6261 * background_posting - return whether or not we're already in the process
6262 * of posting
6265 background_posting(int gripe)
6267 if(ps_global->post){
6268 if(gripe)
6269 q_status_message(SM_ORDER|SM_DING, 3, 3,
6270 _("Can't post while posting!"));
6271 return(1);
6274 return(0);
6278 /*----------------------------------------------------------------------
6279 Validate the given subject relative to any news groups.
6281 Args: none
6283 Returns: always returns 1, but also returns error if
6284 ----*/
6286 valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
6288 struct headerentry *hp;
6290 if(expanded)
6291 *expanded = cpystr(given);
6293 if(error){
6295 * Now look for any header entry we passed to pico that has to do
6296 * with news. If there's no subject, gripe.
6298 for(hp = pbf->headents; hp->prompt; hp++)
6299 if(hp->help == h_composer_news){
6300 if(hp->hd_text->text[0] && !*given)
6301 *error = cpystr(
6302 _("News postings MUST have a subject! Please add one!"));
6304 break;
6308 return(0);
6313 * This is the build_address used by the composer to check for an address
6314 * in the addrbook.
6316 * Args: to -- the passed in line to parse
6317 * full_to -- Address of a pointer to return the full address in.
6318 * This will be allocated here and freed by the caller.
6319 * error -- Address of a pointer to return an error message in.
6320 * This will be allocated here and freed by the caller.
6321 * barg -- Address of a pointer to return the fcc in is in
6322 * fcc->tptr. It will have already been allocated by the
6323 * caller but we may free it and reallocate if we wish.
6324 * Caller will free it.
6326 * Result: 0 is returned if address was OK,
6327 * -1 if address wasn't OK.
6329 * Side effect: Can flush addrbook entry cache entries so they need to be
6330 * re-fetched afterwords.
6333 build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
6335 char *p;
6336 int ret_val, no_repo = 0, *save_nesting_level;
6337 BuildTo bldto;
6338 PrivateTop *pt = NULL;
6339 PrivateAffector *af = NULL;
6340 char *fcc_local = NULL;
6341 jmp_buf save_jmp_buf;
6343 dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
6345 /* check to see if to string is empty to avoid work */
6346 for(p = to; p && *p && isspace((unsigned char)*p); p++)
6347 ;/* do nothing */
6349 if(!p || !*p){
6350 if(full_to)
6351 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
6353 return 0;
6356 if(full_to != NULL)
6357 *full_to = (char *)NULL;
6359 if(error != NULL)
6360 *error = (char *)NULL;
6362 /* No guarantee cursor or status line is how we saved it */
6363 clear_cursor_pos();
6364 mark_status_unknown();
6366 if(ps_global->remote_abook_validity > 0 &&
6367 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6368 *mangled |= BUILDER_SCREEN_MANGLED;
6371 * If we end up jumping back here because somebody else changed one of
6372 * our addrbooks out from underneath us, we may well leak some memory.
6373 * That's probably ok since this will be very rare.
6375 * The reason for the memcpy of the jmp_buf is that we may actually
6376 * be indirectly calling this function from within the address book.
6377 * For example, we may be in the address book screen and then run
6378 * the ComposeTo command which puts us in the composer, then we call
6379 * build_address from there which resets addrbook_changed_unexpectedly.
6380 * Once we leave build_address we need to reset addrbook_changed_un...
6381 * because this position on the stack will no longer be valid.
6382 * Same is true of the other setjmp's in this file which are wrapped
6383 * in memcpy calls.
6385 save_nesting_level = cpyint(ab_nesting_level);
6386 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6387 if(setjmp(addrbook_changed_unexpectedly)){
6388 no_repo = 0;
6389 pt = NULL;
6390 af = NULL;
6391 fcc_local = NULL;
6392 if(error != NULL)
6393 *error = (char *)NULL;
6395 if(full_to && *full_to)
6396 fs_give((void **)full_to);
6398 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6399 dprint((1,
6400 "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
6401 addrbook_reset();
6402 ab_nesting_level = *save_nesting_level;
6405 ab_nesting_level++;
6406 bldto.type = Str;
6407 bldto.arg.str = to;
6408 ret_val = build_address_internal(bldto, full_to, error,
6409 barg ? &fcc_local : NULL,
6410 &no_repo, NULL, save_and_restore,
6411 0, mangled);
6412 ab_nesting_level--;
6413 if(save_nesting_level)
6414 fs_give((void **)&save_nesting_level);
6417 * Have to rfc1522_decode the full_to string before sending it back.
6419 if(full_to && *full_to ){
6420 char *q;
6421 size_t len;
6423 len = 4*strlen(*full_to)+1;
6424 q = (char *)fs_get(len * sizeof(char));
6425 p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
6427 /* p == q means that decoding happened, p is decoded *full_to */
6428 if(p == q){
6429 fs_give((void **)full_to);
6430 *full_to = p;
6432 else
6433 fs_give((void **)&q);
6436 if(fcc_local){
6437 unsigned long csum;
6439 /* Pt will point to headents[Fcc].bldr_private */
6440 pt = NULL;
6441 if(barg && barg->aff)
6442 pt = (PrivateTop *)(*barg->aff);
6445 * If *barg->aff is set, that means fcc was set from a list
6446 * during some previous builder call.
6447 * If the current To line contains the old expansion as a prefix, then
6448 * we should leave things as they are. In order to decide that,
6449 * we look at a hash value computed from the strings.
6451 if(pt && (af=pt->affector) && af->who == BP_To){
6452 int len;
6454 len = strlen(to);
6455 if(len >= af->cksumlen){
6456 int save;
6458 save = to[af->cksumlen];
6459 to[af->cksumlen] = '\0';
6460 csum = line_hash(to);
6461 to[af->cksumlen] = save;
6463 else
6464 csum = af->cksumval + 1; /* something not equal to cksumval */
6467 if(!pt ||
6468 !pt->affector ||
6469 (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
6471 /* replace fcc value */
6472 if(barg->tptr)
6473 fs_give((void **)&barg->tptr);
6475 barg->tptr = fcc_local;
6477 if(barg->aff){
6478 if(!pt){
6479 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6480 pt = (PrivateTop *)(*barg->aff);
6481 memset((void *)pt, 0, sizeof(PrivateTop));
6484 if(no_repo){
6485 if(!pt->affector)
6486 pt->affector =
6487 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6489 af = pt->affector;
6490 af->who = BP_To;
6491 af->cksumlen = strlen(((full_to && *full_to)
6492 ? *full_to : ""));
6493 af->cksumval = line_hash(((full_to && *full_to)
6494 ? *full_to : ""));
6496 else{
6498 * If result is reproducible, we don't keep track here.
6500 if(pt->affector)
6501 fs_give((void **)&pt->affector);
6505 else
6506 fs_give((void **)&fcc_local); /* unused in this case */
6509 /* This is so pico will erase the old message */
6510 if(error != NULL && *error == NULL)
6511 *error = cpystr("");
6513 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6514 flush_status_messages(1);
6515 return(ret_val);
6520 * This is the builder used by the composer for the Lcc line.
6522 * Args: lcc -- the passed in Lcc line to parse
6523 * full_lcc -- Address of a pointer to return the full address in.
6524 * This will be allocated here and freed by the caller.
6525 * error -- Address of a pointer to return an error message in.
6526 * This is not allocated so should not be freed by the caller.
6527 * barg -- This is a pointer to text for affected entries which
6528 * we may be changing. The first one in the list is the
6529 * To entry. We may put the name of the list in empty
6530 * group syntax form there (like List Name: ;).
6531 * The second one in the list is the fcc field.
6532 * The tptr members already point to text allocated in the
6533 * caller. We may free and reallocate here, caller will
6534 * free the result in any case.
6536 * Result: 0 is returned if address was OK,
6537 * -1 if address wasn't OK.
6539 * Side effect: Can flush addrbook entry cache entries so they need to be
6540 * re-fetched afterwords.
6543 build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
6545 int ret_val,
6546 no_repo = 0; /* fcc or lcc not reproducible */
6547 int *save_nesting_level;
6548 BuildTo bldlcc;
6549 PrivateTop *pt = NULL;
6550 PrivateAffector *af = NULL;
6551 char *p,
6552 *fcc_local = NULL,
6553 *to = NULL,
6554 *dummy;
6555 jmp_buf save_jmp_buf;
6557 dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
6559 /* check to see if to string is empty to avoid work */
6560 for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
6561 ;/* do nothing */
6563 if(!p || !*p){
6564 if(full_lcc)
6565 *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
6567 return 0;
6570 if(error != NULL)
6571 *error = (char *)NULL;
6573 if(ps_global->remote_abook_validity > 0 &&
6574 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6575 *mangled |= BUILDER_SCREEN_MANGLED;
6578 * If we end up jumping back here because somebody else changed one of
6579 * our addrbooks out from underneath us, we may well leak some memory.
6580 * That's probably ok since this will be very rare.
6582 save_nesting_level = cpyint(ab_nesting_level);
6583 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6584 if(setjmp(addrbook_changed_unexpectedly)){
6585 no_repo = 0;
6586 pt = NULL;
6587 af = NULL;
6588 fcc_local = NULL;
6589 to = NULL;
6590 if(error != NULL)
6591 *error = (char *)NULL;
6593 if(full_lcc && *full_lcc)
6594 fs_give((void **)full_lcc);
6596 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6597 dprint((1,
6598 "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
6599 addrbook_reset();
6600 ab_nesting_level = *save_nesting_level;
6603 ab_nesting_level++;
6604 bldlcc.type = Str;
6605 bldlcc.arg.str = lcc;
6608 * To is first affected_entry and Fcc is second.
6609 * The conditional stuff for the fcc argument says to only change the
6610 * fcc if the fcc pointer is passed in non-null, and the To pointer
6611 * is also non-null. If they are null, that means they've already been
6612 * entered (are sticky). We don't affect fcc if either fcc or To has
6613 * been typed in.
6615 ret_val = build_address_internal(bldlcc,
6616 full_lcc,
6617 error,
6618 (barg && barg->next && barg->next->tptr && barg->tptr)
6619 ? &fcc_local : NULL,
6620 &no_repo,
6621 (barg && barg->tptr) ? &to : NULL,
6622 save_and_restore, 0, mangled);
6624 ab_nesting_level--;
6625 if(save_nesting_level)
6626 fs_give((void **)&save_nesting_level);
6628 /* full_lcc is what ends up in the Lcc: line */
6629 if(full_lcc && *full_lcc){
6630 size_t len;
6633 * Have to rfc1522_decode the full_lcc string before sending it back.
6635 len = 4*strlen(*full_lcc)+1;
6636 p = (char *)fs_get(len * sizeof(char));
6637 if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
6638 fs_give((void **)full_lcc);
6639 *full_lcc = p;
6641 else
6642 fs_give((void **)&p);
6645 /* to is what ends up in the To: line */
6646 if(to && *to){
6647 unsigned long csum;
6648 size_t len;
6651 * Have to rfc1522_decode the full_to string before sending it back.
6653 len = 4*strlen(to)+1;
6654 p = (char *)fs_get(len * sizeof(char));
6655 dummy = NULL;
6656 if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
6658 * If the caller wants us to try to preserve the charset
6659 * information (they set aff) we copy it into encoded->etext.
6660 * We don't have to worry about pasting together pieces of
6661 * etext like we do in build_address because whenever the
6662 * Lcc line is setting the To line it will be setting the
6663 * whole line, not modifying it.
6664 * Pt will point to headents[To].bldr_private.
6666 if(barg && barg->aff){
6667 pt = (PrivateTop *)(*barg->aff);
6669 if(!pt){
6670 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6671 pt = (PrivateTop *)(*barg->aff);
6672 memset((void *)pt, 0, sizeof(PrivateTop));
6676 fs_give((void **)&to);
6677 to = p;
6679 else
6680 fs_give((void **)&p);
6682 if(dummy)
6683 fs_give((void **)&dummy);
6687 * This part is recording the fact that the To line was set to
6688 * what it is by entering something on the Lcc line. In particular,
6689 * if a list alias was entered here then the fullname of the list
6690 * goes in the To line. We save this affector information so that
6691 * we can tell it shouldn't be modified if we call build_addr_lcc
6692 * again unless we actually modified what's in the Lcc line so that
6693 * it doesn't start with the same thing. The problem we're solving
6694 * is that the contents of the Lcc line no longer look like the
6695 * list they were derived from.
6696 * Pt will point to headents[To].bldr_private.
6698 if(barg && barg->aff)
6699 pt = (PrivateTop *)(*barg->aff);
6701 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6702 int len;
6704 len = strlen(lcc);
6705 if(len >= af->cksumlen){
6706 int save;
6708 save = lcc[af->cksumlen];
6709 lcc[af->cksumlen] = '\0';
6710 csum = line_hash(lcc);
6711 lcc[af->cksumlen] = save;
6713 else
6714 csum = af->cksumval + 1; /* so they aren't equal */
6717 if(!pt ||
6718 !pt->affector ||
6719 pt->affector->who != BP_Lcc ||
6720 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6722 /* replace to value */
6723 if(barg->tptr && barg->tptr[0]){
6724 size_t l;
6725 char *t;
6727 l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
6728 t = (char *)fs_get((l+1) * sizeof(char));
6729 snprintf(t, l+1, "%s%s%s",
6730 barg->tptr,
6731 (to && *to) ? ", " : "",
6732 (to && *to) ? to : "");
6733 fs_give((void **)&barg->tptr);
6734 if(to)
6735 fs_give((void **)&to);
6737 barg->tptr = t;
6739 else{
6740 if(barg->tptr)
6741 fs_give((void **)&barg->tptr);
6743 barg->tptr = to;
6746 if(barg->aff){
6747 if(!pt){
6748 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6749 pt = (PrivateTop *)(*barg->aff);
6750 memset((void *)pt, 0, sizeof(PrivateTop));
6753 if(no_repo){
6754 if(!pt->affector)
6755 pt->affector =
6756 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6758 af = pt->affector;
6759 af->who = BP_Lcc;
6760 af->cksumlen = strlen(((full_lcc && *full_lcc)
6761 ? *full_lcc : ""));
6762 af->cksumval = line_hash(((full_lcc && *full_lcc)
6763 ? *full_lcc : ""));
6765 else{
6767 * If result is reproducible, we don't keep track here.
6769 if(pt->affector)
6770 fs_give((void **)&pt->affector);
6774 else
6775 fs_give((void **)&to); /* unused in this case */
6778 if(fcc_local){
6779 unsigned long csum;
6782 * If *barg->next->aff is set, that means fcc was set from a list
6783 * during some previous builder call. If the current Lcc line
6784 * contains the old expansion as a prefix, then we should leave
6785 * things as they are. In order to decide that we look at a hash
6786 * value computed from the strings.
6787 * Pt will point to headents[Fcc].bldr_private
6789 pt = NULL;
6790 if(barg && barg->next && barg->next->aff)
6791 pt = (PrivateTop *)(*barg->next->aff);
6793 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6794 int len;
6796 len = strlen(lcc);
6797 if(len >= af->cksumlen){
6798 int save;
6800 save = lcc[af->cksumlen];
6801 lcc[af->cksumlen] = '\0';
6802 csum = line_hash(lcc);
6803 lcc[af->cksumlen] = save;
6805 else
6806 csum = af->cksumval + 1; /* something not equal to cksumval */
6809 if(!pt ||
6810 !pt->affector ||
6811 pt->affector->who != BP_Lcc ||
6812 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6814 /* replace fcc value */
6815 if(barg->next->tptr)
6816 fs_give((void **)&barg->next->tptr);
6818 barg->next->tptr = fcc_local;
6820 if(barg->next->aff){
6821 if(!pt){
6822 *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
6823 pt = (PrivateTop *)(*barg->next->aff);
6824 memset((void *)pt, 0, sizeof(PrivateTop));
6827 if(no_repo){
6828 if(!pt->affector)
6829 pt->affector =
6830 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6832 af = pt->affector;
6833 af->who = BP_Lcc;
6834 af->cksumlen = strlen(((full_lcc && *full_lcc)
6835 ? *full_lcc : ""));
6836 af->cksumval = line_hash(((full_lcc && *full_lcc)
6837 ? *full_lcc : ""));
6839 else{
6841 * If result is reproducible, we don't keep track here.
6843 if(pt->affector)
6844 fs_give((void **)&pt->affector);
6848 else
6849 fs_give((void **)&fcc_local); /* unused in this case */
6853 if(error != NULL && *error == NULL)
6854 *error = cpystr("");
6856 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6857 flush_status_messages(0);
6858 return(ret_val);
6862 /*----------------------------------------------------------------------
6863 Verify and canonicalize news groups names.
6864 Called from the message composer
6866 Args: given_group -- List of groups typed by user
6867 expanded_group -- pointer to point to expanded list, which will be
6868 allocated here and freed in caller. If this is
6869 NULL, don't attempt to validate.
6870 error -- pointer to store error message
6871 fcc -- pointer to point to fcc, which will be
6872 allocated here and freed in caller
6874 Returns: 0 if all is OK
6875 -1 if addresses weren't valid
6877 Test the given list of newstroups against those recognized by our nntp
6878 servers. Testing by actually trying to open the list is much cheaper, both
6879 in bandwidth and memory, than yanking the whole list across the wire.
6880 ----*/
6882 news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
6884 int rv;
6885 char *fccptr = NULL;
6887 if(fcc && fcc->tptr)
6888 fccptr = cpystr(fcc->tptr);
6890 clear_cursor_pos();
6892 rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
6894 /* assign any new fcc to the BUILDER_ARG */
6895 if(fccptr){
6896 if(fcc){
6897 /* it changed */
6898 if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
6899 fs_give((void **) &fcc->tptr);
6900 fcc->tptr = fccptr;
6901 fccptr = NULL;
6905 if(fccptr)
6906 fs_give((void **) &fccptr);
6909 /* deal with any busy indicator */
6910 if(news_busy_cue){
6911 news_busy_cue = 0;
6912 cancel_busy_cue(0);
6913 mark_status_dirty();
6914 display_message('x');
6915 if(mangled)
6916 *mangled |= BUILDER_MESSAGE_DISPLAYED;
6920 return(rv);
6924 void
6925 news_build_busy(void)
6927 news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
6931 #if defined(DOS) || defined(OS2)
6933 /*----------------------------------------------------------------------
6934 Verify that the necessary pieces are around to allow for
6935 message sending under DOS
6937 Args: strict -- tells us if a remote stream is required before
6938 sending is permitted.
6940 The idea is to make sure pine knows enough to put together a valid
6941 from line. The things we MUST know are a user-id, user-domain and
6942 smtp server to dump the message off on. Typically these are
6943 provided in pine's configuration file, but if not, the user is
6944 queried here.
6945 ----*/
6947 dos_valid_from()
6949 char prompt[100], answer[80];
6950 int rc, i, flags;
6951 HelpType help;
6954 * query for user name portion of address, use IMAP login
6955 * name as default
6957 if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
6958 NETMBX mb;
6959 int no_prompt_user_id = 0;
6961 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6962 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6963 && *mb.user){
6964 strncpy(answer, mb.user, sizeof(answer)-1);
6965 answer[sizeof(answer)-1] = '\0';
6967 else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
6968 /* no user-id prompting if set */
6969 no_prompt_user_id = 1;
6970 rc = 0;
6971 if(!ps_global->mail_stream)
6972 do_broach_folder(ps_global->inbox_name,
6973 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
6974 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6975 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6976 && *mb.user){
6977 strncpy(answer, mb.user, sizeof(answer)-1);
6978 answer[sizeof(answer)-1] = '\0';
6980 else
6981 answer[0] = '\0';
6983 else
6984 answer[0] = '\0';
6986 if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
6987 /* No prompt, just assume mailbox login is user-id */
6988 no_prompt_user_id = 1;
6989 rc = 0;
6992 snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
6993 prompt[sizeof(prompt)-1] = '\0';
6995 help = NO_HELP;
6996 while(!no_prompt_user_id) {
6997 flags = OE_APPEND_CURRENT;
6998 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
6999 sizeof(answer),prompt,NULL,help,&flags);
7000 if(rc == 2)
7001 continue;
7003 if(rc == 3){
7004 help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
7005 continue;
7008 if(rc != 4)
7009 break;
7012 if(rc == 1 || (rc == 0 && !answer[0])) {
7013 q_status_message(SM_ORDER, 3, 4,
7014 _("Send cancelled (User-id must be provided before sending)"));
7015 return(0);
7018 /* save the name */
7019 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
7020 sizeof(prompt)-50, answer);
7021 prompt[sizeof(prompt)-1] = '\0';
7022 if(ps_global->blank_user_id
7023 && !no_prompt_user_id
7024 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7025 set_variable(V_USER_ID, answer, 1, 1, Main);
7027 else{
7028 fs_give((void **)&(ps_global->VAR_USER_ID));
7029 ps_global->VAR_USER_ID = cpystr(answer);
7033 /* query for personal name */
7034 if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
7035 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
7036 answer[0] = '\0';
7037 snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
7038 prompt[sizeof(prompt)-1] = '\0';
7040 help = NO_HELP;
7041 while(1) {
7042 flags = OE_APPEND_CURRENT;
7043 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7044 sizeof(answer),prompt,NULL,help,&flags);
7045 if(rc == 2)
7046 continue;
7048 if(rc == 3){
7049 help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
7050 continue;
7053 if(rc != 4)
7054 break;
7057 if(rc == 0 && answer){ /* save the name */
7058 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
7059 sizeof(prompt)-50, answer);
7060 prompt[sizeof(prompt)-1] = '\0';
7061 if(ps_global->blank_personal_name
7062 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7063 set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
7065 else{
7066 fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
7067 ps_global->VAR_PERSONAL_NAME = cpystr(answer);
7073 * query for host/domain portion of address, using IMAP
7074 * host as default
7076 if(ps_global->blank_user_domain
7077 || ps_global->maildomain == ps_global->localdomain
7078 || ps_global->maildomain == ps_global->hostname){
7079 if(ps_global->inbox_name[0] == '{'){
7080 for(i=0;
7081 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7082 answer[i] = ps_global->inbox_name[i+1];
7084 answer[i] = '\0';
7086 else
7087 answer[0] = '\0';
7089 snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
7090 prompt[sizeof(prompt)-1] = '\0';
7092 help = NO_HELP;
7093 while(1) {
7094 flags = OE_APPEND_CURRENT;
7095 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7096 sizeof(answer),prompt,NULL,help,&flags);
7097 if(rc == 2)
7098 continue;
7100 if(rc == 3){
7101 help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
7102 continue;
7105 if(rc != 4)
7106 break;
7109 if(rc == 1 || (rc == 0 && !answer[0])) {
7110 q_status_message(SM_ORDER, 3, 4,
7111 _("Send cancelled (Host/domain name must be provided before sending)"));
7112 return(0);
7115 /* save the name */
7116 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
7117 sizeof(prompt)-50, answer);
7118 prompt[sizeof(prompt)-1] = '\0';
7119 if(!ps_global->userdomain && !ps_global->blank_user_domain
7120 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7121 set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
7122 fs_give((void **)&(ps_global->maildomain)); /* blast old val */
7123 ps_global->userdomain = cpystr(answer);
7124 ps_global->maildomain = ps_global->userdomain;
7126 else{
7127 fs_give((void **)&(ps_global->maildomain));
7128 ps_global->userdomain = cpystr(answer);
7129 ps_global->maildomain = ps_global->userdomain;
7133 /* check for smtp server */
7134 if(!ps_global->VAR_SMTP_SERVER ||
7135 !ps_global->VAR_SMTP_SERVER[0] ||
7136 !ps_global->VAR_SMTP_SERVER[0][0]){
7137 char **list;
7139 if(ps_global->inbox_name[0] == '{'){
7140 for(i=0;
7141 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7142 answer[i] = ps_global->inbox_name[i+1];
7144 answer[i] = '\0';
7146 else
7147 answer[0] = '\0';
7149 snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
7150 prompt[sizeof(prompt)-1] = '\0';
7152 help = NO_HELP;
7153 while(1) {
7154 flags = OE_APPEND_CURRENT;
7155 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7156 sizeof(answer),prompt,NULL,help,&flags);
7157 if(rc == 2)
7158 continue;
7160 if(rc == 3){
7161 help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
7162 continue;
7165 if(rc != 4)
7166 break;
7169 if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
7170 q_status_message(SM_ORDER, 3, 4,
7171 _("Send cancelled (SMTP server must be provided before sending)"));
7172 return(0);
7175 /* save the name */
7176 list = (char **) fs_get(2 * sizeof(char *));
7177 list[0] = cpystr(answer);
7178 list[1] = NULL;
7179 set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
7180 fs_give((void *)&list[0]);
7181 fs_give((void *)list);
7184 return(1);
7187 #endif /* defined(DOS) || defined(OS2) */