* clear out some warnings by gcc 9.3.1.
[alpine.git] / alpine / send.c
blobbc70dadf85bb8f6cf6ea6cd04ec0b7f3a02dfa46
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;
607 /*================= Compose new message ===============*/
608 body = mail_newbody();
609 outgoing = mail_newenvelope();
611 if(given_to)
612 rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
615 * Setup possible role
617 if(role_arg)
618 role = copy_action(role_arg);
620 if(!role){
621 /* Setup possible compose role */
622 if(nonempty_patterns(rflags, &dummy)){
624 * setup default role
625 * Msgno = -1 means there is no msg.
626 * This will match roles which have the Compose Use turned
627 * on, and have no patterns set, and match the Current
628 * Folder Type.
630 role = set_role_from_msg(ps_global, rflags, -1L, NULL);
632 if(confirm_role(rflags, &role))
633 role = combine_inherited_role(role);
634 else{ /* cancel reply */
635 role = NULL;
636 cmd_cancelled("Composition");
637 return;
642 if(role)
643 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
644 role->nick);
646 outgoing->message_id = generate_message_id(role);
648 * The type of storage object allocated below is vitally
649 * important. See SIMPLIFYING ASSUMPTION #37
651 if((body->contents.text.data = (void *) so_get(PicoText,
652 NULL, EDIT_ACCESS)) != NULL){
653 char ch;
655 if(inc_text_getc){
656 while((*inc_text_getc)(&ch))
657 if(!so_writec(ch, (STORE_S *)body->contents.text.data)){
658 break;
662 else{
663 q_status_message(SM_ORDER | SM_DING, 3, 4,
664 _("Problem creating space for message text."));
665 return;
668 if(role && role->template){
669 char *filtered;
671 impl = 1; /* leave cursor in header if not explicit */
672 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
673 if(filtered){
674 if(*filtered){
675 so_puts((STORE_S *)body->contents.text.data, filtered);
676 if(impl == 1)
677 template_len = strlen(filtered);
680 fs_give((void **)&filtered);
683 else
684 impl = 1;
686 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
687 if(impl == 2)
688 redraft_pos->offset += template_len;
690 if(*sig)
691 so_puts((STORE_S *)body->contents.text.data, sig);
693 fs_give((void **)&sig);
696 body->type = TYPETEXT;
698 if(attach)
699 create_message_body(&body, attach, 0);
702 ps_global->prev_screen = compose_screen;
703 if(!(fcc_to_free = fcc) && !(role && role->fcc))
704 fcc = fcc_arg; /* Didn't pick up fcc, use given */
707 * check whether a build_address-produced fcc is different from
708 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
710 if(fcc){
711 char *tmp_fcc = NULL;
713 if(outgoing->to){
714 tmp_fcc = get_fcc_based_on_to(outgoing->to);
715 if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
716 fcc_is_sticky++; /* cause sticky bit to get set */
719 else if((tmp_fcc = get_fcc(NULL)) != NULL &&
720 !strcmp(fcc, tmp_fcc)){
721 /* not sticky */
723 else
724 fcc_is_sticky++;
726 if(tmp_fcc)
727 fs_give((void **)&tmp_fcc);
730 pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc,
731 reply, redraft_pos, lcc, custom,
732 (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0));
734 if(reply){
735 if(reply->mailbox)
736 fs_give((void **) &reply->mailbox);
737 if(reply->origmbox)
738 fs_give((void **) &reply->origmbox);
739 if(reply->prefix)
740 fs_give((void **) &reply->prefix);
741 if(reply->data.uid.msgs)
742 fs_give((void **) &reply->data.uid.msgs);
743 fs_give((void **) &reply);
746 if(fcc_to_free)
747 fs_give((void **)&fcc_to_free);
749 if(lcc)
750 fs_give((void **)&lcc);
752 mail_free_envelope(&outgoing);
753 pine_free_body(&body);
754 free_redraft_pos(&redraft_pos);
755 free_action(&role);
759 /*----------------------------------------------------------------------
760 Args: stream -- This is where we get the postponed messages from
761 We'll expunge and close it here unless it is mail_stream.
763 These are all return values:
764 ================
765 outgoing --
766 body --
767 fcc --
768 lcc --
769 reply --
770 redraft_pos --
771 custom --
772 role --
773 ================
775 flags --
777 ----*/
779 redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body,
780 char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos,
781 PINEFIELD **custom, ACTION_S **role, int flags)
783 MAILSTREAM *stream;
784 long cont_msg = 1L;
785 STORE_S *so;
787 if(!(streamp && *streamp))
788 return(0);
790 stream = *streamp;
793 * If we're manipulating the current folder, don't bother
794 * with index
796 if(!stream->nmsgs){
797 if(REDRAFT_PPND&flags)
798 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!"));
799 else
800 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!"));
802 return(redraft_cleanup(streamp, FALSE, flags));
804 else if(stream == ps_global->mail_stream
805 && ps_global->prev_screen == mail_index_screen){
807 * Since the user's got this folder already opened and they're
808 * on a selected message, pick that one rather than rebuild
809 * another index screen...
811 cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
813 else if(stream->nmsgs > 1L){ /* offer browser ? */
814 int rv;
816 if(REDRAFT_PPND&flags){ /* set to last message postponed */
817 mn_set_cur(sp_msgmap(stream),
818 mn_get_revsort(sp_msgmap(stream))
819 ? 1L : mn_get_total(sp_msgmap(stream)));
821 else{ /* set to top form letter */
822 mn_set_cur(sp_msgmap(stream), 1L);
825 clear_index_cache(stream, 0);
826 while(1){
827 void *ti;
829 ti = stop_threading_temporarily();
830 rv = index_lister(ps_global, NULL, stream->mailbox,
831 stream, sp_msgmap(stream));
832 restore_threading(&ti);
834 cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream)));
835 if(count_flagged(stream, F_DEL)
836 && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){
837 if(REDRAFT_PPND&flags)
838 q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
839 else
840 q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
842 continue;
845 break;
848 clear_index_cache(stream, 0);
850 if(rv){
851 q_status_message(SM_ORDER, 0, 3, _("Composition cancelled"));
852 (void) redraft_cleanup(streamp, FALSE, flags);
854 if(!*streamp && !ps_global->mail_stream){
855 q_status_message2(SM_ORDER, 3, 7,
856 "No more %.200s, returning to \"%.200s\"",
857 (REDRAFT_PPND&flags) ? "postponed messages"
858 : "form letters",
859 ps_global->inbox_name);
860 if(ps_global && ps_global->ttyo){
861 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
862 ps_global->mangled_footer = 1;
865 do_broach_folder(ps_global->inbox_name,
866 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
868 ps_global->next_screen = mail_index_screen;
871 return(0); /* special case */
875 if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL)
876 return(redraft_work(streamp, cont_msg, outgoing, body,
877 fcc, lcc, reply, redraft_pos, custom,
878 role, flags, so));
879 else
880 return(0);
885 redraft_prompt(char *type, char *prompt, int failure)
887 if(background_posting(FALSE)){
888 q_status_message1(SM_ORDER, 0, 3,
889 _("%s folder unavailable while background posting"),
890 type);
891 return(failure);
894 return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM));
898 /* this is for initializing the fixed header elements in pine_send() */
900 prompt::name::help::prwid::maxlen::realaddr::
901 builder::affected_entry::next_affected::selector::key_label::fileedit::
902 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
903 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
905 static struct headerentry he_template[]={
906 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
907 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
908 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
909 {"From : ", "From", h_composer_from, 10, 0, NULL,
910 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
911 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
912 {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
913 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
914 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
915 {"To : ", "To", h_composer_to, 10, 0, NULL,
916 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
917 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK},
918 {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL,
919 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
920 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
921 {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL,
922 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
923 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
924 {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL,
925 news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL,
926 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
927 {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL,
928 NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, fcc_tab_complete,
929 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE},
930 {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL,
931 build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete,
932 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
933 {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL,
934 NULL, NULL, NULL, NULL, "To Files", NULL, NULL,
935 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE},
936 {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL,
937 valid_subject, NULL, NULL, NULL, NULL, NULL, NULL,
938 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
939 {"", "References", NO_HELP, 10, 0, NULL,
940 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
941 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
942 {"", "Date", NO_HELP, 10, 0, NULL,
943 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
944 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
945 {"", "In-Reply-To", NO_HELP, 10, 0, NULL,
946 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
947 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
948 {"", "Message-ID", NO_HELP, 10, 0, NULL,
949 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
950 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
951 {"", "X-Priority", NO_HELP, 10, 0, NULL,
952 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
953 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
954 {"", "User-Agent", NO_HELP, 10, 0, NULL,
955 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
956 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
957 {"", "To", 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 {"", "X-Post-Error",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 {"", "X-Reply-UID", 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 {"", "X-Reply-Mbox", 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-SMTP-Server", 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 {"", "X-Cursor-Pos", 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 {"", "X-Our-ReplyTo", 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 {"", OUR_HDRS_LIST, 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-Auth-Received", 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 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
985 {"", "Sender", NO_HELP, 10, 0, NULL,
986 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
987 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
988 #endif
992 static struct headerentry he_custom_addr_templ={
993 NULL, NULL, h_composer_custom_addr,10, 0, NULL,
994 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
995 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK};
997 static struct headerentry he_custom_free_templ={
998 NULL, NULL, h_composer_custom_free,10, 0, NULL,
999 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1000 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE};
1003 /*----------------------------------------------------------------------
1004 Get addressee for message, then post message
1006 Args: outgoing -- Partially formatted outgoing ENVELOPE
1007 body -- Body of outgoing message
1008 prmpt_who -- Optional prompt for optionally_enter call
1009 prmpt_cnf -- Optional prompt for confirmation call
1010 used_tobufval -- The string that the to was eventually set equal to.
1011 This gets passed back if non-NULL on entry.
1012 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1013 SS_NULLRP - Use null return-path so we'll send an
1014 SMTP MAIL FROM: <>
1016 Result: message "To: " field is provided and message is sent or cancelled.
1018 Fields:
1019 remail -
1020 return_path -
1021 date added here
1022 from added here
1023 sender -
1024 reply_to -
1025 subject passed in, NOT edited but maybe canonized here
1026 to possibly passed in, edited and canonized here
1027 cc -
1028 bcc -
1029 in_reply_to -
1030 message_id -
1032 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1033 with the first part TYPETEXT! All newlines in the text here also end with
1034 CRLF.
1036 Returns 0 on success, -1 on failure.
1037 ----*/
1039 pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */
1040 struct mail_bodystruct **body,
1041 ACTION_S **rolep,
1042 char *prmpt_who,
1043 char *prmpt_cnf,
1044 char **used_tobufval,
1045 int flagsarg)
1047 char **tobufp, *p, tmp[MAILTMPLEN];
1048 void *messagebuf;
1049 int done = 0, retval = 0, x;
1050 int lastrc, rc = 0, ku, i, resize_len, result, fcc_result;
1051 int og2s_done = 0;
1052 HelpType help;
1053 static HISTORY_S *history = NULL;
1054 ESCKEY_S ekey[6];
1055 BUILDER_ARG ba_fcc;
1056 METAENV *header;
1057 ACTION_S *role = rolep ? *rolep : NULL;
1058 PAT_STATE pstate;
1060 dprint((1,"\n === simple send called === \n"));
1062 memset(&ba_fcc, 0, sizeof(BUILDER_ARG));
1064 init_hist(&history, HISTSIZE);
1066 header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp);
1068 /*----- Fill in a few general parts of the envelope ----*/
1069 if(!outgoing->date){
1070 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1071 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
1073 rfc822_date(tmp_20k_buf); /* format and copy new date */
1074 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1075 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
1077 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
1080 if(!outgoing->from){
1081 if(role && role->from){
1082 if(ps_global->never_allow_changing_from)
1083 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1084 else
1085 outgoing->from = copyaddrlist(role->from);
1087 else
1088 outgoing->from = generate_from();
1091 if(!(flagsarg & SS_NULLRP))
1092 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1094 ekey[i = 0].ch = ctrl('T');
1095 ekey[i].rval = 2;
1096 ekey[i].name = "^T";
1097 ekey[i++].label = N_("To AddrBk");
1099 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1100 ekey[i].ch = ctrl('I');
1101 ekey[i].rval = 11;
1102 ekey[i].name = "TAB";
1103 ekey[i++].label = N_("Complete");
1106 if(nonempty_patterns(ROLE_DO_ROLES, &pstate) && first_pattern(&pstate)){
1107 ekey[i].ch = ctrl('R');
1108 ekey[i].rval = 15;
1109 ekey[i].name = "^R";
1110 ekey[i++].label = "Set Role";
1113 ekey[i].ch = KEY_UP;
1114 ekey[i].rval = 30;
1115 ekey[i].name = "";
1116 ku = i;
1117 ekey[i++].label = "";
1119 ekey[i].ch = KEY_DOWN;
1120 ekey[i].rval = 31;
1121 ekey[i].name = "";
1122 ekey[i++].label = "";
1124 ekey[i].ch = -1;
1126 if(outgoing->remail == NULL)
1127 strcpy(tmp, _("FORWARD (as e-mail) to : "));
1129 /*----------------------------------------------------------------------
1130 Loop editing the "To: " field until everything goes well
1131 ----*/
1132 help = NO_HELP;
1134 while(!done){
1135 int flags;
1137 if(outgoing->message_id)
1138 fs_give((void **) &outgoing->message_id);
1140 outgoing->message_id = generate_message_id(role);
1142 if(outgoing->remail){
1143 if(role)
1144 snprintf(tmp, sizeof(tmp), _("BOUNCE (redirect) message using role \"%s\" to : "), role->nick);
1145 else
1146 strncpy(tmp, _("BOUNCE (redirect) message to : "), sizeof(tmp));
1147 tmp[sizeof(tmp)-1] = '\0';
1150 if(!og2s_done){
1151 og2s_done++;
1152 outgoing2strings(header, *body, &messagebuf, NULL, 1);
1155 lastrc = rc;
1156 if(flagsarg & SS_PROMPTFORTO){
1157 if(!*tobufp)
1158 *tobufp = cpystr("");
1160 resize_len = MAX(MAXPATH, strlen(*tobufp));
1161 fs_resize((void **) tobufp, resize_len+1);
1163 if(items_in_hist(history) > 0){
1164 ekey[ku].name = HISTORY_UP_KEYNAME;
1165 ekey[ku].label = HISTORY_KEYLABEL;
1166 ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
1167 ekey[ku+1].label = HISTORY_KEYLABEL;
1169 else{
1170 ekey[ku].name = "";
1171 ekey[ku].label = "";
1172 ekey[ku+1].name = "";
1173 ekey[ku+1].label = "";
1176 flags = OE_APPEND_CURRENT;
1178 rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
1179 0, resize_len,
1180 prmpt_who
1181 ? prmpt_who
1182 : tmp,
1183 ekey, help, &flags);
1185 else
1186 rc = 0;
1188 switch(rc){
1189 case -1:
1190 q_status_message(SM_ORDER | SM_DING, 3, 4,
1191 "Internal problem encountered");
1192 retval = -1;
1193 done++;
1194 break;
1196 case 15 : /* set a role */
1197 {void (*prev_screen)(struct pine *) = NULL, (*redraw)(void) = NULL;
1199 redraw = ps_global->redrawer;
1200 ps_global->redrawer = NULL;
1201 prev_screen = ps_global->prev_screen;
1202 role = NULL;
1203 ps_global->next_screen = SCREEN_FUN_NULL;
1205 if(role_select_screen(ps_global, &role,
1206 outgoing->remail ? MC_BOUNCE : MC_FORWARD) < 0)
1207 cmd_cancelled(_("Set Role"));
1208 else{
1209 if(role)
1210 role = combine_inherited_role(role);
1211 else{
1212 role = (ACTION_S *) fs_get(sizeof(*role));
1213 memset((void *) role, 0, sizeof(*role));
1214 role->nick = cpystr("Default Role");
1218 if(redraw)
1219 (*redraw)();
1221 ps_global->next_screen = prev_screen;
1222 ps_global->redrawer = redraw;
1223 ps_global->mangled_screen = 1;
1225 if(role && role->from && !ps_global->never_allow_changing_from){
1226 mail_free_address (&outgoing->from);
1227 outgoing->from = copyaddrlist(role->from);
1228 if(!(flagsarg & SS_NULLRP)){
1229 fs_give((void **) &outgoing->return_path);
1230 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1233 if(rolep) *rolep = role;
1235 break;
1237 case 30 :
1238 if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){
1239 strncpy(*tobufp, p, resize_len);
1240 (*tobufp)[resize_len-1] = '\0';
1242 else
1243 Writechar(BELL, 0);
1245 break;
1247 case 31 :
1248 if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){
1249 strncpy(*tobufp, p, resize_len);
1250 (*tobufp)[resize_len-1] = '\0';
1252 else
1253 Writechar(BELL, 0);
1255 break;
1257 case 2: /* ^T */
1258 case 0:
1259 {void (*redraw) (void) = ps_global->redrawer;
1260 char *returned_addr = NULL;
1261 int len, l;
1263 if(rc == 2){
1264 int got_something = 0;
1266 push_titlebar_state();
1267 returned_addr = addr_book_bounce();
1270 * Just make it look like user typed this list in.
1272 if(returned_addr){
1273 got_something++;
1274 if((l=resize_len) < (len = strlen(returned_addr)) + 1){
1275 l = len;
1276 fs_resize((void **) tobufp, (size_t) (l+1));
1279 strncpy(*tobufp, returned_addr, l);
1280 (*tobufp)[l] = '\0';
1281 fs_give((void **)&returned_addr);
1284 ClearScreen();
1285 pop_titlebar_state();
1286 redraw_titlebar();
1287 if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
1288 (*ps_global->redrawer)();
1290 if(!got_something)
1291 continue;
1294 if(*tobufp && **tobufp != '\0'){
1295 char *errbuf, *addr;
1296 int tolen;
1298 save_hist(history, *tobufp, 0, NULL);
1300 errbuf = NULL;
1303 * If role has an fcc, use it instead of what build_address
1304 * tells us.
1306 if(role && role->fcc){
1307 if(ba_fcc.tptr)
1308 fs_give((void **) &ba_fcc.tptr);
1310 ba_fcc.tptr = cpystr(role->fcc);
1313 if(build_address(*tobufp, &addr, &errbuf,
1314 (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){
1315 int sendit = 0;
1317 if(errbuf)
1318 fs_give((void **)&errbuf);
1320 if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){
1321 l = tolen;
1322 fs_resize((void **) tobufp, (size_t) (l+1));
1325 strncpy(*tobufp, addr, l);
1326 (*tobufp)[l] = '\0';
1327 if(used_tobufval)
1328 *used_tobufval = cpystr(addr);
1330 /* confirm address */
1331 if(flagsarg & SS_PROMPTFORTO){
1332 char dsn_string[30];
1333 int dsn_label = 0, dsn_show, i;
1334 int verbose_label = 0;
1335 ESCKEY_S opts[13];
1337 strings2outgoing(header, body, NULL, 0);
1339 if((flagsarg & SS_PROMPTFORTO)
1340 && ((x = check_addresses(header)) == CA_BAD
1341 || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE,
1342 ps_global))))
1343 /*--- Addresses didn't check out---*/
1344 continue;
1346 i = 0;
1347 opts[i].ch = 'y';
1348 opts[i].rval = 'y';
1349 opts[i].name = "Y";
1350 opts[i++].label = N_("Yes");
1352 opts[i].ch = 'n';
1353 opts[i].rval = 'n';
1354 opts[i].name = "N";
1355 opts[i++].label = N_("No");
1357 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
1358 if(F_ON(F_VERBOSE_POST, ps_global)){
1359 /* setup keymenu slot to toggle verbose mode */
1360 opts[i].ch = ctrl('W');
1361 opts[i].rval = 12;
1362 opts[i].name = "^W";
1363 verbose_label = i++;
1364 if(F_ON(F_DSN, ps_global)){
1365 opts[i].ch = 0;
1366 opts[i].rval = 0;
1367 opts[i].name = "";
1368 opts[i++].label = "";
1372 /* clear DSN flags */
1373 call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL);
1374 if(F_ON(F_DSN, ps_global)){
1375 /* setup keymenu slots to toggle dsn bits */
1376 opts[i].ch = 'd';
1377 opts[i].rval = 'd';
1378 opts[i].name = "D";
1379 opts[i].label = "DSNOpts";
1380 dsn_label = i++;
1381 opts[i].ch = -2;
1382 opts[i].rval = 's';
1383 opts[i].name = "S";
1384 opts[i++].label = "";
1385 opts[i].ch = -2;
1386 opts[i].rval = 'x';
1387 opts[i].name = "X";
1388 opts[i++].label = "";
1389 opts[i].ch = -2;
1390 opts[i].rval = 'h';
1391 opts[i].name = "H";
1392 opts[i++].label = "";
1395 opts[i].ch = -1;
1397 while(1){
1398 int rv;
1400 dsn_show = (call_mailer_flags & CM_DSN_SHOW);
1401 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
1402 "%s%s%s%s%s%sto \"%s\" ? ",
1403 prmpt_cnf ? prmpt_cnf : "Send message ",
1404 ((call_mailer_flags & CM_VERBOSE)
1405 || (dsn_show))
1406 ? "(" : "",
1407 (call_mailer_flags & CM_VERBOSE)
1408 ? "in verbose mode" : "",
1409 (dsn_show && (call_mailer_flags & CM_VERBOSE))
1410 ? ", " : "",
1411 (dsn_show) ? dsn_string : "",
1412 ((call_mailer_flags & CM_VERBOSE) || dsn_show)
1413 ? ") " : "",
1414 (addr && *addr)
1415 ? addr
1416 : (F_ON(F_FCC_ON_BOUNCE, ps_global)
1417 && ba_fcc.tptr && ba_fcc.tptr[0])
1418 ? ba_fcc.tptr
1419 : "");
1420 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1422 if((strlen(tmp_20k_buf) >
1423 ps_global->ttyo->screen_cols - 2) &&
1424 ps_global->ttyo->screen_cols >= 7)
1425 strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7,
1426 "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7);
1428 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1430 if(verbose_label)
1431 opts[verbose_label].label =
1432 /* TRANSLATORS: several possible key labels follow */
1433 (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
1435 if(F_ON(F_DSN, ps_global)){
1436 if(call_mailer_flags & CM_DSN_SHOW){
1437 opts[dsn_label].label =
1438 (call_mailer_flags & CM_DSN_DELAY)
1439 ? N_("NoDelay") : N_("Delay");
1440 opts[dsn_label+1].ch = 's';
1441 opts[dsn_label+1].label =
1442 (call_mailer_flags & CM_DSN_SUCCESS)
1443 ? N_("NoSuccess") : N_("Success");
1444 opts[dsn_label+2].ch = 'x';
1445 opts[dsn_label+2].label =
1446 (call_mailer_flags & CM_DSN_NEVER)
1447 ? N_("ErrRets") : N_("NoErrRets");
1448 opts[dsn_label+3].ch = 'h';
1449 opts[dsn_label+3].label =
1450 (call_mailer_flags & CM_DSN_FULL)
1451 ? N_("RetHdrs") : N_("RetFull");
1455 rv = radio_buttons(tmp_20k_buf,
1456 -FOOTER_ROWS(ps_global), opts,
1457 'y', 'z', NO_HELP, RB_NORM);
1458 if(rv == 'y'){ /* user ACCEPTS! */
1459 sendit = 1;
1460 break;
1462 else if(rv == 'n'){ /* Declined! */
1463 break;
1465 else if(rv == 'z'){ /* Cancelled! */
1466 break;
1468 else if(rv == 12){ /* flip verbose bit */
1469 if(call_mailer_flags & CM_VERBOSE)
1470 call_mailer_flags &= ~CM_VERBOSE;
1471 else
1472 call_mailer_flags |= CM_VERBOSE;
1474 else if(call_mailer_flags & CM_DSN_SHOW){
1475 if(rv == 's'){ /* flip success bit */
1476 call_mailer_flags ^= CM_DSN_SUCCESS;
1477 /* turn off related bits */
1478 if(call_mailer_flags & CM_DSN_SUCCESS)
1479 call_mailer_flags &= ~(CM_DSN_NEVER);
1481 else if(rv == 'd'){ /* flip delay bit */
1482 call_mailer_flags ^= CM_DSN_DELAY;
1483 /* turn off related bits */
1484 if(call_mailer_flags & CM_DSN_DELAY)
1485 call_mailer_flags &= ~(CM_DSN_NEVER);
1487 else if(rv == 'x'){ /* flip never bit */
1488 call_mailer_flags ^= CM_DSN_NEVER;
1489 /* turn off related bits */
1490 if(call_mailer_flags & CM_DSN_NEVER)
1491 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
1493 else if(rv == 'h'){ /* flip full bit */
1494 call_mailer_flags ^= CM_DSN_FULL;
1497 else if(rv == 'd'){ /* show dsn options */
1498 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
1501 snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"),
1502 (call_mailer_flags & CM_DSN_NEVER)
1503 ? _("Never") : "F",
1504 (call_mailer_flags & CM_DSN_DELAY)
1505 ? "D" : "",
1506 (call_mailer_flags & CM_DSN_SUCCESS)
1507 ? "S" : "",
1508 (call_mailer_flags & CM_DSN_NEVER)
1509 ? ""
1510 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
1511 : "-Hdrs");
1512 dsn_string[sizeof(dsn_string)-1] = '\0';
1516 if(addr)
1517 fs_give((void **)&addr);
1519 if(!(flagsarg & SS_PROMPTFORTO) || sendit){
1520 char *fcc = NULL;
1521 CONTEXT_S *fcc_cntxt = NULL;
1523 if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
1524 if(ba_fcc.tptr)
1525 fcc = cpystr(ba_fcc.tptr);
1527 set_last_fcc(fcc);
1530 * If special name "inbox" then replace it with the
1531 * real inbox path.
1533 if(ps_global->VAR_INBOX_PATH
1534 && strucmp(fcc, ps_global->inbox_name) == 0){
1535 char *replace_fcc;
1537 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
1538 fs_give((void **) &fcc);
1539 fcc = replace_fcc;
1543 /*---- Check out fcc -----*/
1544 if(fcc && *fcc){
1545 (void) commence_fcc(fcc, &fcc_cntxt, FALSE);
1546 if(!lmc.so){
1547 dprint((4,"can't open fcc, cont\n"));
1548 if(!(flagsarg & SS_PROMPTFORTO)){
1549 retval = -1;
1550 fs_give((void **)&fcc);
1551 fcc = NULL;
1552 goto finish;
1554 else
1555 continue;
1557 else
1558 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
1560 else
1561 lmc.so = NULL;
1563 if(!(outgoing->to || outgoing->cc || outgoing->bcc
1564 || lmc.so)){
1565 q_status_message(SM_ORDER, 3, 5, _("No recipients specified!"));
1566 continue;
1569 if(outgoing->to || outgoing->cc || outgoing->bcc){
1570 char **alt_smtp = NULL;
1572 if(role && role->smtp){
1573 if(ps_global->FIX_SMTP_SERVER
1574 && ps_global->FIX_SMTP_SERVER[0])
1575 q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1576 else
1577 alt_smtp = role->smtp;
1580 result = call_mailer(header, *body, alt_smtp,
1581 call_mailer_flags,
1582 call_mailer_file_result,
1583 pipe_callback);
1584 mark_address_failure_for_pico(header);
1586 else
1587 result = 0;
1589 if(result == 1 && !lmc.so)
1590 q_status_message(SM_ORDER, 0, 3, _("Message sent"));
1592 /*----- Was there an fcc involved? -----*/
1593 if(lmc.so){
1594 if(result == 1
1595 || (result == 0
1596 && pine_rfc822_output(header, *body, NULL, NULL))){
1597 char label[50];
1599 strncpy(label, "Fcc", sizeof(label));
1600 label[sizeof(label)-1] = '\0';
1601 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
1602 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
1603 label[sizeof(label)-1] = '\0';
1606 /* Now actually copy to fcc folder and close */
1607 fcc_result =
1608 write_fcc(fcc, fcc_cntxt, lmc.so, NULL,
1609 label,
1610 F_ON(F_MARK_FCC_SEEN, ps_global)
1611 ? "\\SEEN" : NULL);
1613 else if(result == 0){
1614 q_status_message(SM_ORDER,3,5,
1615 _("Fcc Failed!. No message saved."));
1616 retval = -1;
1617 dprint((1, "explicit fcc write failed!\n"));
1620 so_give(&lmc.so);
1623 if(result < 0){
1624 dprint((1, "Bounce failed\n"));
1625 if(!(flagsarg & SS_PROMPTFORTO))
1626 retval = -1;
1627 else
1628 continue;
1630 else if(result == 1){
1631 if(!fcc)
1632 q_status_message(SM_ORDER, 0, 3,
1633 _("Message sent"));
1634 else{
1635 int avail = ps_global->ttyo->screen_cols-2;
1636 int need, fcclen;
1637 char *part1 = "Message sent and ";
1638 char *part2 = fcc_result ? "" : "NOT ";
1639 char *part3 = "copied to ";
1640 fcclen = strlen(fcc);
1642 need = 2 + strlen(part1) + strlen(part2) +
1643 strlen(part3) + fcclen;
1645 if(need > avail && fcclen > 6)
1646 fcclen -= MIN(fcclen-6, need-avail);
1648 q_status_message4(SM_ORDER, 0, 3,
1649 "%s%s%s\"%s\"",
1650 part1, part2, part3,
1651 short_str(fcc,
1652 (char *)tmp_20k_buf,
1653 SIZEOF_20KBUF,
1654 fcclen, FrontDots));
1658 if(fcc)
1659 fs_give((void **)&fcc);
1661 else{
1662 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1663 retval = -1;
1666 else{
1667 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1668 _("Error in address: %s"), errbuf);
1669 if(errbuf)
1670 fs_give((void **)&errbuf);
1672 if(!(flagsarg & SS_PROMPTFORTO))
1673 retval = -1;
1674 else
1675 continue;
1679 else{
1680 q_status_message(SM_ORDER | SM_DING, 3, 5,
1681 _("No addressee! No e-mail sent."));
1682 retval = -1;
1686 done++;
1687 break;
1689 case 1:
1690 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1691 done++;
1692 retval = -1;
1693 break;
1695 case 3:
1696 help = (help == NO_HELP)
1697 ? (outgoing->remail == NULL
1698 ? h_anon_forward
1699 : h_bounce)
1700 : NO_HELP;
1701 break;
1703 case 11:
1704 if(**tobufp){
1705 char *new_nickname = NULL;
1706 int l;
1707 int ambiguity;
1709 ambiguity = abook_nickname_complete(*tobufp, &new_nickname,
1710 (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA);
1711 if(new_nickname){
1712 if(*new_nickname){
1713 if((l=strlen(new_nickname)) > resize_len){
1714 resize_len = l;
1715 fs_resize((void **) tobufp, resize_len+1);
1718 strncpy(*tobufp, new_nickname, l);
1719 (*tobufp)[l] = '\0';
1722 fs_give((void **) &new_nickname);
1725 if(ambiguity != 2)
1726 Writechar(BELL, 0);
1729 break;
1731 case 4: /* can't suspend */
1732 default:
1733 break;
1737 finish:
1738 if(ba_fcc.tptr)
1739 fs_give((void **)&ba_fcc.tptr);
1741 pine_free_env(&header);
1743 return(retval);
1748 * pine_simple_send_header - generate header suitable for simple_sending
1750 METAENV *
1751 pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp)
1753 METAENV *header;
1754 PINEFIELD *pf;
1755 static struct headerentry he_dummy;
1757 header = pine_new_env(outgoing, fccp, tobufpp, NULL);
1759 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1760 for(pf = header->local; pf && pf->name; pf = pf->next)
1761 if(pf->type == Address && !strucmp(pf->name, "to")){
1762 memset((void *) &he_dummy, 0, sizeof(he_dummy));
1763 pf->extdata = (void *) &he_dummy;
1764 HE(pf)->dirty = 1;
1765 break;
1768 return(header);
1773 /*----------------------------------------------------------------------
1774 Prepare data structures for pico, call pico, then post message
1776 Args: outgoing -- Partially formatted outgoing ENVELOPE
1777 body -- Body of outgoing message
1778 editor_title -- Title for anchor line in composer
1779 fcc_arg -- The file carbon copy field
1780 reply -- Struct describing set of msgs being replied-to
1781 lcc_arg --
1782 custom -- custom header list.
1783 sticky_fcc --
1785 Result: message is edited, then postponed, cancelled or sent.
1787 Fields:
1788 remail -
1789 return_path -
1790 date added here
1791 from added here
1792 sender -
1793 reply_to -
1794 subject passed in, edited and cannonized here
1795 to possibly passed in, edited and cannonized here
1796 cc possibly passed in, edited and cannonized here
1797 bcc edited and cannonized here
1798 in_reply_to generated in reply() and passed in
1799 message_id -
1801 Storage for these fields comes from anywhere outside. It is remalloced
1802 here so the composer can realloc them if needed. The copies here are also
1803 freed here.
1805 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1806 with the first part TYPETEXT! All newlines in the text here also end with
1807 CRLF.
1809 There's a further assumption that the text in the TYPETEXT part is
1810 stored in a storage object (see filter.c).
1811 ----*/
1812 void
1813 pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
1814 char *editor_title, ACTION_S *role, char *fcc_arg,
1815 REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg,
1816 PINEFIELD *custom, int flags)
1818 int i, fixed_cnt, total_cnt, index,
1819 editor_result = 0, body_start = 0, use_news_order = 0;
1820 char *p, *addr, *fcc, *fcc_to_free = NULL;
1821 char *start_here_name = NULL;
1822 char *suggested_nntp_server = NULL;
1823 char *title = NULL;
1824 struct headerentry *he, *headents, *he_to, *he_fcc, *he_news = NULL, *he_lcc = NULL,
1825 *he_from = NULL;
1826 PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL,
1827 *pf_smtp_server, *pf_nntp_server,
1828 *pf_fcc = NULL, *pf_err, *pf_uid, *pf_mbox, *pf_curpos,
1829 *pf_ourrep, *pf_ourhdrs, **sending_order;
1830 METAENV header;
1831 ADDRESS *lcc_addr = NULL;
1832 ADDRESS *nobody_addr = NULL;
1833 BODY_PARTICULARS_S *bp;
1834 STORE_S *orig_so = NULL;
1835 PICO pbuf1, *save_previous_pbuf;
1836 CustomType ct;
1837 REDRAFT_POS_S *local_redraft_pos = NULL;
1839 dprint((1,"\n=== send called ===\n"));
1841 save_previous_pbuf = pbf;
1842 pbf = &pbuf1;
1843 standard_picobuf_setup(pbf);
1846 * Cancel any pending initial commands since pico uses a different
1847 * input routine. If we didn't cancel them, they would happen after
1848 * we returned from the editor, which would be confusing.
1850 if(ps_global->in_init_seq){
1851 ps_global->in_init_seq = 0;
1852 ps_global->save_in_init_seq = 0;
1853 clear_cursor_pos();
1854 if(ps_global->initial_cmds){
1855 if(ps_global->free_initial_cmds)
1856 fs_give((void **)&(ps_global->free_initial_cmds));
1858 ps_global->initial_cmds = 0;
1861 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1864 #if defined(DOS) || defined(OS2)
1865 if(!dos_valid_from()){
1866 pbf = save_previous_pbuf;
1867 return;
1870 pbf->upload = NULL;
1871 #else
1872 pbf->upload = (ps_global->VAR_UPLOAD_CMD
1873 && ps_global->VAR_UPLOAD_CMD[0])
1874 ? upload_msg_to_pico : NULL;
1875 #endif
1877 pbf->msgntext = message_format_for_pico;
1878 pbf->mimetype = mime_type_for_pico;
1879 pbf->exittest = send_exit_for_pico;
1880 pbf->user_says_noflow = dont_flow_this_time;
1881 pbf->newthread = new_thread_on_blank_subject;
1882 ps_global->newthread = 0; /* reset this value */
1883 if(F_OFF(F_CANCEL_CONFIRM, ps_global))
1884 pbf->canceltest = cancel_for_pico;
1885 #ifdef _WINDOWS
1886 pbf->dict = (ps_global->VAR_DICTIONARY
1887 && ps_global->VAR_DICTIONARY[0]
1888 && ps_global->VAR_DICTIONARY[0][0])
1889 ? ps_global->VAR_DICTIONARY : NULL;
1890 pbf->chosen_dict = -1; /* not chosen yet */
1891 #endif /* _WINDOWS */
1892 pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] &&
1893 ps_global->VAR_EDITOR[0][0])
1894 ? ps_global->VAR_EDITOR : NULL;
1895 pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0])
1896 ? ps_global->VAR_SPELLER : NULL;
1897 pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
1898 pbf->quote_str = reply && reply->prefix ? reply->prefix : "> ";
1899 /* We actually want to set this only if message we're sending is flowed */
1900 pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
1901 pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
1902 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
1903 && (strcmp(pbf->quote_str, "> ") == 0
1904 || strcmp(pbf->quote_str, ">") == 0));
1905 pbf->edit_offset = 0;
1906 title = cpystr(set_titlebar(editor_title,
1907 ps_global->mail_stream,
1908 ps_global->context_current,
1909 ps_global->cur_folder,ps_global->msgmap,
1910 0, FolderName, 0, 0, NULL));
1911 pbf->pine_anchor = title;
1913 #if defined(DOS) || defined(OS2)
1914 if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){
1915 pbf->oper_dir = ps_global->VAR_FILE_DIR;
1917 #endif
1919 if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE))
1920 pbf->pine_flags |= P_CHKPTNOW;
1922 /* NOTE: initial cursor position set below */
1924 dprint((9, "flags: %x\n", pbf->pine_flags));
1927 * When user runs compose and the current folder is a newsgroup,
1928 * offer to post to the current newsgroup.
1930 if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups))
1931 && IS_NEWS(ps_global->mail_stream)){
1932 char prompt[200], news_group[MAILTMPLEN];
1934 pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group,
1935 sizeof(news_group));
1938 * Replies don't get this far because To or Newsgroups will already
1939 * be filled in. So must be either ordinary compose or forward.
1940 * Forward sets subject, so use that to tell the difference.
1942 if(news_group[0] && !outgoing->subject){
1943 int ch = 'y';
1944 int ret_val;
1945 char *errmsg = NULL;
1946 BUILDER_ARG *fcc_build = NULL;
1948 if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
1949 snprintf(prompt, sizeof(prompt),
1950 _("Post to current newsgroup (%s)"), news_group);
1951 prompt[sizeof(prompt)-1] = '\0';
1952 ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM);
1955 switch(ch){
1956 case 'y':
1957 if(outgoing->newsgroups)
1958 fs_give((void **)&outgoing->newsgroups);
1960 if(!fcc_arg && !(role && role->fcc)){
1961 fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
1962 memset((void *)fcc_build, 0, sizeof(BUILDER_ARG));
1963 fcc_build->tptr = fcc_to_free;
1966 ret_val = news_build(news_group, &outgoing->newsgroups,
1967 &errmsg, fcc_build, NULL);
1969 if(ret_val == -1){
1970 if(outgoing->newsgroups)
1971 fs_give((void **)&outgoing->newsgroups);
1973 outgoing->newsgroups = cpystr(news_group);
1976 if(!fcc_arg && !(role && role->fcc)){
1977 fcc_arg = fcc_to_free = fcc_build->tptr;
1978 fs_give((void **)&fcc_build);
1981 if(errmsg){
1982 if(*errmsg){
1983 q_status_message(SM_ORDER, 3, 3, errmsg);
1984 display_message(NO_OP_COMMAND);
1987 fs_give((void **)&errmsg);
1990 break;
1992 case 'x': /* ^C */
1993 q_status_message(SM_ORDER, 0, 3, _("Message cancelled"));
1994 dprint((4, "=== send: cancelled\n"));
1995 pbf = save_previous_pbuf;
1996 return;
1998 case 'n':
1999 break;
2001 default:
2002 break;
2006 if(F_ON(F_PREDICT_NNTP_SERVER, ps_global)
2007 && outgoing->newsgroups && *outgoing->newsgroups
2008 && IS_NEWS(ps_global->mail_stream)){
2009 NETMBX news_mb;
2011 if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox,
2012 &news_mb))
2013 if(!strucmp(news_mb.service, "nntp")){
2014 if(*ps_global->mail_stream->original_mailbox == '{'){
2015 char *svcp = NULL, *psvcp;
2017 suggested_nntp_server =
2018 cpystr(ps_global->mail_stream->original_mailbox + 1);
2019 if((p = strindex(suggested_nntp_server, '}')) != NULL)
2020 *p = '\0';
2021 for(p = strindex(suggested_nntp_server, '/'); p && *p;
2022 p = strindex(p, '/')){
2023 /* take out /nntp, which gets added in nntp_open */
2024 if(!struncmp(p, "/nntp", 5))
2025 svcp = p + 5;
2026 else if(!struncmp(p, "/service=nntp", 13))
2027 svcp = p + 13;
2028 else if(!struncmp(p, "/service=\"nntp\"", 15))
2029 svcp = p + 15;
2030 else
2031 p++;
2032 if(svcp){
2033 if(*svcp == '\0')
2034 *p = '\0';
2035 else if(*svcp == '/' || *svcp == ':'){
2036 for(psvcp = p; *svcp; svcp++, psvcp++)
2037 *psvcp = *svcp;
2038 *psvcp = '\0';
2040 svcp = NULL;
2044 else
2045 suggested_nntp_server = cpystr(news_mb.orighost);
2050 * If we don't already have custom headers set and the role has custom
2051 * headers, then incorporate those custom headers into "custom".
2053 if(!custom){
2054 PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL;
2056 dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
2058 * If we allow the Combine argument here, we're saying that we want to
2059 * combine the values from the envelope and the role for the fields To,
2060 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
2061 * we'll have a To in the envelope because we're replying. If our role also
2062 * has a To action, then Combine would combine those two and offer both
2063 * to the user. We've decided against doing this. Instead, we always use
2064 * Replace, and the role's header value replaces the value from the
2065 * envelope. It might also make sense in some cases to do the opposite,
2066 * which would be treating the role headers as defaults, just like
2067 * customized-hdrs.
2069 #ifdef WANT_TO_COMBINE_ADDRESSES
2070 if(role && role->cstm)
2071 rolehdrs = parse_custom_hdrs(role->cstm, Combine);
2072 #else
2073 if(role && role->cstm)
2074 rolehdrs = parse_custom_hdrs(role->cstm, Replace);
2075 #endif
2077 if(rolehdrs){
2078 custom = combine_custom_headers(dflthdrs, rolehdrs);
2079 if(dflthdrs){
2080 free_prompts(dflthdrs);
2081 free_customs(dflthdrs);
2084 if(rolehdrs){
2085 free_prompts(rolehdrs);
2086 free_customs(rolehdrs);
2089 else
2090 custom = dflthdrs;
2093 g_rolenick = role ? role->nick : NULL;
2095 /* how many fixed fields are there? */
2096 for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++)
2099 total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1);
2101 /* the fixed part of the PINEFIELDs */
2102 i = fixed_cnt * sizeof(PINEFIELD);
2103 pfields = (PINEFIELD *)fs_get((size_t) i);
2104 memset(pfields, 0, (size_t) i);
2106 /* temporary headerentry array for pico */
2107 i = (total_cnt + 1) * sizeof(struct headerentry);
2108 headents = (struct headerentry *)fs_get((size_t) i);
2109 memset(headents, 0, (size_t) i);
2111 i = total_cnt * sizeof(PINEFIELD *);
2112 sending_order = (PINEFIELD **)fs_get((size_t) i);
2113 memset(sending_order, 0, (size_t) i);
2115 pbf->headents = headents;
2116 header.env = outgoing;
2117 header.local = pfields;
2118 header.sending_order = sending_order;
2120 /* custom part of PINEFIELDs */
2121 header.custom = custom;
2123 he = headents;
2124 pf = pfields;
2127 * For Address types, pf->addr points to an ADDRESS *.
2128 * If that address is in the "outgoing" envelope, it will
2129 * be freed by the caller, otherwise, it should be freed here.
2130 * Pf->textbuf for an Address is used a little to set up a default,
2131 * but then is freed right away below. Pf->scratch is used for a
2132 * pointer to some alloced space for pico to edit in. Addresses in
2133 * the custom area are freed by free_customs().
2135 * For FreeText types, pf->addr is not used. Pf->text points to a
2136 * pointer that points to the text. Pf->textbuf points to a copy of
2137 * the text that must be freed before we leave, otherwise, it is
2138 * probably a pointer into the envelope and that gets freed by the
2139 * caller.
2141 * He->realaddr is the pointer to the text that pico actually edits.
2144 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2145 # define NN 4
2146 #else
2147 # define NN 3
2148 #endif
2150 if(outgoing->newsgroups && *outgoing->newsgroups)
2151 use_news_order++;
2153 /* initialize the fixed header elements of the two temp arrays */
2154 for(i=0; i < fixed_cnt; i++, pf++){
2155 static int news_order[] = {
2156 N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC,
2157 N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY,
2158 N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX,
2159 N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS
2160 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2161 , N_SENDER
2162 #endif
2165 index = i;
2166 /* slightly different editing order if sending to news */
2167 if(use_news_order &&
2168 index >= 0 && index < sizeof(news_order)/sizeof(news_order[0]))
2169 index = news_order[i];
2171 /* copy the templates */
2172 *he = he_template[index];
2174 pf->name = cpystr(pf_template[index].name);
2175 if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global)){
2176 /* slide string over so it is Sender instead of X-X-Sender */
2177 for(p = pf->name+4; *p != '\0'; p++)
2178 *(p-4) = *p;
2179 *(p-4) = '\0';
2181 pf->type = pf_template[index].type;
2182 pf->canedit = pf_template[index].canedit;
2183 pf->rcptto = pf_template[index].rcptto;
2184 pf->writehdr = pf_template[index].writehdr;
2185 pf->localcopy = pf_template[index].localcopy;
2186 pf->extdata = he;
2187 pf->next = pf + 1;
2189 he->rich_header = view_as_rich(pf->name, he->rich_header);
2190 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2191 he->nickcmpl = NULL;
2193 switch(pf->type){
2194 case FreeText: /* realaddr points to c-client env */
2195 if(index == N_NEWS){
2196 sending_order[1] = pf;
2197 he->realaddr = &outgoing->newsgroups;
2198 he_news = he;
2200 switch(set_default_hdrval(pf, custom)){
2201 case Replace:
2202 if(*he->realaddr)
2203 fs_give((void **)he->realaddr);
2205 *he->realaddr = pf->textbuf;
2206 pf->textbuf = NULL;
2207 he->sticky = 1;
2208 break;
2210 case Combine:
2211 if(*he->realaddr){ /* combine values */
2212 if(pf->textbuf && *pf->textbuf){
2213 char *combined_hdr;
2214 size_t l;
2216 l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
2217 combined_hdr = (char *) fs_get((l+1) * sizeof(char));
2218 strncpy(combined_hdr, *he->realaddr, l);
2219 combined_hdr[l] = '\0';
2220 strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
2221 combined_hdr[l] = '\0';
2222 strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
2223 combined_hdr[l] = '\0';
2225 fs_give((void **)he->realaddr);
2226 *he->realaddr = combined_hdr;
2227 q_status_message(SM_ORDER, 3, 3,
2228 "Adding newsgroup from role");
2229 he->sticky = 1;
2232 else{
2233 *he->realaddr = pf->textbuf;
2234 pf->textbuf = NULL;
2237 break;
2239 case UseAsDef:
2240 /* if no value, use default */
2241 if(!*he->realaddr){
2242 *he->realaddr = pf->textbuf;
2243 pf->textbuf = NULL;
2246 break;
2248 case NoMatch:
2249 break;
2252 /* If there is a newsgroup, we'd better show it */
2253 if(outgoing->newsgroups && *outgoing->newsgroups)
2254 he->rich_header = 0; /* force on by default */
2256 if(pf->textbuf)
2257 fs_give((void **)&pf->textbuf);
2259 pf->text = he->realaddr;
2261 else if(index == N_DATE){
2262 sending_order[2] = pf;
2263 pf->text = (char **) &outgoing->date;
2264 pf->extdata = NULL;
2266 else if(index == N_INREPLY){
2267 sending_order[NN+9] = pf;
2268 pf->text = &outgoing->in_reply_to;
2269 pf->extdata = NULL;
2271 else if(index == N_MSGID){
2272 sending_order[NN+10] = pf;
2273 pf->text = &outgoing->message_id;
2274 pf->extdata = NULL;
2276 else if(index == N_REF){
2277 sending_order[NN+11] = pf;
2278 pf->text = &outgoing->references;
2279 pf->extdata = NULL;
2281 else if(index == N_PRIORITY){
2282 sending_order[NN+12] = pf;
2283 pf->text = &pf->textbuf;
2284 pf->extdata = NULL;
2286 else if(index == N_USERAGENT){
2287 sending_order[NN+13] = pf;
2288 pf->text = &pf->textbuf;
2289 pf->textbuf = generate_user_agent();
2290 pf->extdata = NULL;
2292 else if(index == N_POSTERR){
2293 sending_order[NN+14] = pf;
2294 pf_err = pf;
2295 pf->text = &pf->textbuf;
2296 pf->extdata = NULL;
2298 else if(index == N_RPLUID){
2299 sending_order[NN+15] = pf;
2300 pf_uid = pf;
2301 pf->text = &pf->textbuf;
2302 pf->extdata = NULL;
2304 else if(index == N_RPLMBOX){
2305 sending_order[NN+16] = pf;
2306 pf_mbox = pf;
2307 pf->text = &pf->textbuf;
2308 pf->extdata = NULL;
2310 else if(index == N_SMTP){
2311 sending_order[NN+17] = pf;
2312 pf_smtp_server = pf;
2313 pf->text = &pf->textbuf;
2314 pf->extdata = NULL;
2316 else if(index == N_NNTP){
2317 sending_order[NN+18] = pf;
2318 pf_nntp_server = pf;
2319 pf->text = &pf->textbuf;
2320 pf->extdata = NULL;
2322 else if(index == N_CURPOS){
2323 sending_order[NN+19] = pf;
2324 pf_curpos = pf;
2325 pf->text = &pf->textbuf;
2326 pf->extdata = NULL;
2328 else if(index == N_OURREPLYTO){
2329 sending_order[NN+20] = pf;
2330 pf_ourrep = pf;
2331 pf->text = &pf->textbuf;
2332 pf->extdata = NULL;
2334 else if(index == N_OURHDRS){
2335 sending_order[NN+21] = pf;
2336 pf_ourhdrs = pf;
2337 pf->text = &pf->textbuf;
2338 pf->extdata = NULL;
2340 else if(index == N_AUTHRCVD){
2341 sending_order[0] = pf;
2342 pf_ourhdrs = pf;
2343 pf->text = &pf->textbuf;
2344 pf->extdata = NULL;
2346 else{
2347 q_status_message(SM_ORDER | SM_DING, 3, 7,
2348 "Botched: Unmatched FreeText header in pine_send");
2351 break;
2353 /* can't do a default for this one */
2354 case Attachment:
2355 /* If there is an attachment already, we'd better show them */
2356 if(body && *body && (*body)->type != TYPETEXT)
2357 he->rich_header = 0; /* force on by default */
2359 break;
2361 case Address:
2362 switch(index){
2363 case N_FROM:
2364 sending_order[3] = pf;
2365 pf->addr = &outgoing->from;
2366 if(role && role->from){
2367 if(ps_global->never_allow_changing_from)
2368 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2369 else{
2370 outgoing->from = copyaddrlist(role->from);
2371 he->display_it = 1; /* show it */
2372 he->rich_header = 0;
2376 he_from = he;
2377 break;
2379 case N_TO:
2380 sending_order[NN+2] = pf;
2381 pf->addr = &outgoing->to;
2382 /* If already set, make it act like we typed it in */
2383 if(outgoing->to
2384 && outgoing->to->mailbox
2385 && outgoing->to->mailbox[0]
2386 && flags & PS_STICKY_TO)
2387 he->sticky = 1;
2389 he_to = he;
2390 pf_to = pf;
2391 break;
2393 case N_NOBODY:
2394 sending_order[NN+5] = pf;
2395 pf_nobody = pf;
2396 if(ps_global->VAR_EMPTY_HDR_MSG
2397 && !ps_global->VAR_EMPTY_HDR_MSG[0]){
2398 pf->addr = NULL;
2400 else{
2401 nobody_addr = mail_newaddr();
2402 nobody_addr->next = mail_newaddr();
2403 nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
2404 SIZEOF_20KBUF,
2405 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
2406 ? ps_global->VAR_EMPTY_HDR_MSG
2407 : "undisclosed-recipients"),
2408 ps_global->posting_charmap));
2409 pf->addr = &nobody_addr;
2412 break;
2414 case N_CC:
2415 sending_order[NN+3] = pf;
2416 pf->addr = &outgoing->cc;
2417 break;
2419 case N_BCC:
2420 sending_order[NN+4] = pf;
2421 pf->addr = &outgoing->bcc;
2422 /* if bcc exists, make sure it's exposed so nothing's
2423 * sent by mistake...
2425 if(outgoing->bcc)
2426 he->display_it = 1;
2428 break;
2430 case N_REPLYTO:
2431 sending_order[NN+1] = pf;
2432 pf->addr = &outgoing->reply_to;
2433 if(role && role->replyto){
2434 if(outgoing->reply_to)
2435 mail_free_address(&outgoing->reply_to);
2437 outgoing->reply_to = copyaddrlist(role->replyto);
2438 he->display_it = 1; /* show it */
2439 he->rich_header = 0;
2442 break;
2444 case N_LCC:
2445 sending_order[NN+7] = pf;
2446 pf->addr = &lcc_addr;
2447 he_lcc = he;
2448 if(lcc_arg){
2449 build_address(lcc_arg, &addr, NULL, NULL, NULL);
2450 rfc822_parse_adrlist(&lcc_addr, addr,
2451 ps_global->maildomain);
2452 fs_give((void **)&addr);
2453 he->display_it = 1;
2456 break;
2458 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2459 case N_SENDER:
2460 sending_order[4] = pf;
2461 pf->addr = &outgoing->sender;
2462 break;
2463 #endif
2465 default:
2466 q_status_message1(SM_ORDER,3,7,
2467 "Internal error: Address header %s", comatose(index));
2468 break;
2472 * If this is a reply to news, don't show the regular email
2473 * recipient headers (unless they are non-empty).
2475 if((outgoing->newsgroups && *outgoing->newsgroups)
2476 && (index == N_TO || index == N_CC
2477 || index == N_BCC || index == N_LCC)
2478 && (pf->addr && !*pf->addr)){
2479 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2480 pf->textbuf && *pf->textbuf){
2481 removing_trailing_white_space(pf->textbuf);
2482 (void)removing_double_quotes(pf->textbuf);
2483 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2484 rfc822_parse_adrlist(pf->addr, addr,
2485 ps_global->maildomain);
2486 fs_give((void **)&addr);
2487 if(ct > UseAsDef)
2488 he->sticky = 1;
2490 else
2491 he->rich_header = 1; /* hide */
2495 * If this address doesn't already have a value, then we check
2496 * for a default value assigned by the user.
2498 else if(pf->addr && !*pf->addr){
2499 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2500 (index != N_FROM ||
2501 (!ps_global->never_allow_changing_from &&
2502 F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
2503 pf->textbuf && *pf->textbuf){
2505 removing_trailing_white_space(pf->textbuf);
2506 (void)removing_double_quotes(pf->textbuf);
2509 * Try to set To based on Lcc. Don't attempt Fcc.
2511 if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
2512 BUILDER_ARG *barg = NULL;
2513 char *ppp = NULL;
2515 if(*pf_to->addr)
2516 ppp = addr_list_string(*pf_to->addr, NULL, 1);
2518 if(!ppp)
2519 ppp = cpystr("");
2521 barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
2522 memset(barg, 0, sizeof(*barg));
2523 barg->me = &(he->bldr_private);
2524 barg->aff = &(he_to->bldr_private);
2525 barg->tptr = cpystr(ppp);
2527 build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
2528 he->display_it = 1;
2530 rfc822_parse_adrlist(pf->addr, addr,
2531 ps_global->maildomain);
2532 if(addr)
2533 fs_give((void **) &addr);
2535 if(ct > UseAsDef)
2536 he->sticky = 1;
2538 if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
2539 ADDRESS *a = NULL;
2541 rfc822_parse_adrlist(&a, barg->tptr,
2542 ps_global->maildomain);
2543 if(a){
2544 if(pf_to->addr)
2545 mail_free_address(pf_to->addr);
2547 *pf_to->addr = a;
2551 if(barg){
2552 if(barg->tptr)
2553 fs_give((void **) &barg->tptr);
2555 fs_give((void **) &barg);
2558 if(ppp)
2559 fs_give((void **) &ppp);
2561 else{
2562 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2563 rfc822_parse_adrlist(pf->addr, addr,
2564 ps_global->maildomain);
2565 if(addr)
2566 fs_give((void **) &addr);
2568 if(ct > UseAsDef)
2569 he->sticky = 1;
2573 /* if we still don't have a from */
2574 if(index == N_FROM && !*pf->addr)
2575 *pf->addr = generate_from();
2579 * Addr is already set in the rest of the cases.
2581 else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
2582 ADDRESS *adr = NULL;
2585 * We get to this case of the ifelse if the from or reply-to
2586 * addr was set by a role above.
2589 /* figure out the default value */
2590 (void)set_default_hdrval(pf, custom);
2591 if(pf->textbuf && *pf->textbuf){
2592 removing_trailing_white_space(pf->textbuf);
2593 (void)removing_double_quotes(pf->textbuf);
2594 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2595 rfc822_parse_adrlist(&adr, addr,
2596 ps_global->maildomain);
2597 fs_give((void **)&addr);
2600 /* if value set by role is different from default, show it */
2601 if(adr && !address_is_same(*pf->addr, adr))
2602 he->display_it = 1; /* start this off showing */
2604 /* malformed */
2605 if(!(*pf->addr)->mailbox){
2606 fs_give((void **)pf->addr);
2607 he->display_it = 1;
2610 if(adr)
2611 mail_free_address(&adr);
2613 else if((index == N_TO || index == N_CC || index == N_BCC)
2614 && pf->addr){
2615 ADDRESS *a = NULL, **tail;
2618 * These three are different from the others because we
2619 * might add the addresses to what is already there instead
2620 * of replacing.
2623 switch(set_default_hdrval(pf, custom)){
2624 case Replace:
2625 if(*pf->addr)
2626 mail_free_address(pf->addr);
2628 removing_trailing_white_space(pf->textbuf);
2629 (void)removing_double_quotes(pf->textbuf);
2630 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2631 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2632 fs_give((void **)&addr);
2633 he->sticky = 1;
2634 break;
2636 case Combine:
2637 removing_trailing_white_space(pf->textbuf);
2638 (void)removing_double_quotes(pf->textbuf);
2639 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2640 rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
2641 fs_give((void **)&addr);
2642 he->sticky = 1;
2643 if(a){
2644 for(tail = pf->addr; *tail; tail = &(*tail)->next)
2646 *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
2647 *pf->addr, NULL, a, RCA_ALL);
2648 q_status_message(SM_ORDER, 3, 3,
2649 "Adding addresses from role");
2650 mail_free_address(&a);
2653 break;
2655 case UseAsDef:
2656 case NoMatch:
2657 break;
2660 he->display_it = 1; /* start this off showing */
2662 else if(pf->addr){
2663 switch(set_default_hdrval(pf, custom)){
2664 case Replace:
2665 case Combine:
2666 if(*pf->addr)
2667 mail_free_address(pf->addr);
2669 removing_trailing_white_space(pf->textbuf);
2670 (void)removing_double_quotes(pf->textbuf);
2671 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2672 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2673 fs_give((void **)&addr);
2674 he->sticky = 1;
2675 break;
2677 case UseAsDef:
2678 case NoMatch:
2679 break;
2682 he->display_it = 1;
2685 if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
2686 mail_free_address(pf->addr);
2687 he->display_it = 1; /* start this off showing */
2690 if(pf->textbuf) /* free default value in any case */
2691 fs_give((void **)&pf->textbuf);
2693 /* outgoing2strings will alloc the string pf->scratch below */
2694 he->realaddr = &pf->scratch;
2695 break;
2697 case Fcc:
2698 sending_order[NN+8] = pf;
2699 pf_fcc = pf;
2700 if(role && role->fcc)
2701 fcc = role->fcc;
2702 else
2703 fcc = get_fcc(fcc_arg);
2705 if(fcc_to_free){
2706 fs_give((void **)&fcc_to_free);
2707 fcc_arg = NULL;
2710 if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
2711 he->sticky = 1;
2713 if(role)
2714 role->fcc = NULL;
2716 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
2717 he->display_it = 1; /* start this off showing */
2719 he->realaddr = &fcc;
2720 pf->text = &fcc;
2721 he_fcc = he;
2722 break;
2724 case Subject :
2725 sending_order[NN+6] = pf;
2727 switch(set_default_hdrval(pf, custom)){
2728 case Replace:
2729 case Combine:
2730 pf->scratch = pf->textbuf;
2731 pf->textbuf = NULL;
2732 he->sticky = 1;
2733 if(outgoing->subject)
2734 fs_give((void **)&outgoing->subject);
2736 break;
2738 case UseAsDef:
2739 case NoMatch:
2740 /* if no value, use default */
2741 if(outgoing->subject){
2742 pf->scratch = cpystr(outgoing->subject);
2744 else{
2745 pf->scratch = pf->textbuf;
2746 pf->textbuf = NULL;
2749 break;
2752 he->realaddr = &pf->scratch;
2753 pf->text = &outgoing->subject;
2754 break;
2756 default:
2757 q_status_message1(SM_ORDER,3,7,
2758 "Unknown header type %d in pine_send",
2759 (void *)pf->type);
2760 break;
2764 * We may or may not want to give the user the chance to edit
2765 * the From and Reply-To lines. If they are listed in either
2766 * Default-composer-hdrs or Customized-hdrs, then they can edit
2767 * them, else no.
2768 * If canedit is not set, that means that this header is not in
2769 * the user's customized-hdrs. If rich_header is set, that
2770 * means that this header is not in the user's
2771 * default-composer-hdrs (since From and Reply-To are rich
2772 * by default). So, don't give it an he to edit with in that case.
2774 * For other types, just not setting canedit will cause it to be
2775 * uneditable, regardless of what the user does.
2777 switch(index){
2778 case N_FROM:
2779 /* to allow it, we let this fall through to the reply-to case below */
2780 if(ps_global->never_allow_changing_from ||
2781 (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
2782 !(role && role->from))){
2783 if(pf->canedit || !he->rich_header)
2784 q_status_message(SM_ORDER, 3, 3,
2785 _("Not allowed to change header \"From\""));
2787 memset(he, 0, (size_t)sizeof(*he));
2788 pf->extdata = NULL;
2789 break;
2792 case N_REPLYTO:
2793 if(!pf->canedit && he->rich_header){
2794 memset(he, 0, (size_t)sizeof(*he));
2795 pf->extdata = NULL;
2797 else{
2798 pf->canedit = 1;
2799 he++;
2802 break;
2804 default:
2805 if(!pf->canedit){
2806 memset(he, 0, (size_t)sizeof(*he));
2807 pf->extdata = NULL;
2809 else
2810 he++;
2812 break;
2817 * This is so the builder can tell the composer to fill the affected
2818 * field based on the value in the field on the left.
2820 * Note that this mechanism isn't completely general. Each entry has
2821 * only a single next_affected, so if some other entry points an
2822 * affected entry at an entry with a next_affected, they all inherit
2823 * that next_affected. Since this isn't used much a careful ordering
2824 * of the affected fields should make it a sufficient mechanism.
2826 he_to->affected_entry = he_fcc;
2827 he_news->affected_entry = he_fcc;
2828 he_lcc->affected_entry = he_to;
2829 he_to->next_affected = he_fcc;
2831 (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
2833 i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2835 * Set up headerentries for custom fields.
2836 * NOTE: "i" is assumed to now index first custom field in sending
2837 * order.
2839 for(pf = pf->next; pf && pf->name; pf = pf->next){
2840 char *addr;
2842 if(pf->standard)
2843 continue;
2845 pf->extdata = he;
2846 pf->canedit = 1;
2847 pf->rcptto = 0;
2848 pf->writehdr = 1;
2849 pf->localcopy = 1;
2851 switch(pf->type){
2852 case Address:
2853 if(pf->addr){ /* better be set */
2854 sending_order[i++] = pf;
2855 *he = he_custom_addr_templ;
2856 /* change default text into an ADDRESS */
2857 /* strip quotes around whole default */
2858 removing_trailing_white_space(pf->textbuf);
2859 (void)removing_double_quotes(pf->textbuf);
2860 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2861 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2862 fs_give((void **)&addr);
2863 if(pf->textbuf)
2864 fs_give((void **)&pf->textbuf);
2866 he->realaddr = &pf->scratch;
2867 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2868 he->nickcmpl = NULL;
2871 break;
2873 case FreeText:
2874 sending_order[i++] = pf;
2875 *he = he_custom_free_templ;
2876 he->realaddr = &pf->textbuf;
2877 pf->text = &pf->textbuf;
2878 if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
2879 (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
2880 he->display_it = 1; /* show it */
2882 break;
2884 default:
2885 q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
2886 (void *)pf->type);
2887 break;
2890 he->name = pf->name;
2892 /* use first 8 characters for prompt */
2893 he->prompt = cpystr(" : ");
2894 strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
2896 he->rich_header = view_as_rich(he->name, he->rich_header);
2897 he++;
2901 * Make sure at least *one* field is displayable...
2903 for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
2904 if(HE(pf) && !HE(pf)->rich_header){
2905 index = i;
2906 break;
2910 * None displayable!!! Warn and display defaults.
2912 if(index == -1){
2913 q_status_message(SM_ORDER,0,5,
2914 "No default-composer-hdrs matched, displaying defaults");
2915 for(i = 0, pf = header.local; pf; pf = pf->next, i++)
2916 if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
2917 && HE(pf))
2918 HE(pf)->rich_header = 0;
2922 * Save information about body which set_mime_type_by_grope might change.
2923 * Then, if we get an error sending, we reset these things so that
2924 * grope can do it's thing again after we edit some more.
2926 if ((*body)->type == TYPEMULTIPART)
2927 bp = save_body_particulars(&(*body)->nested.part->body);
2928 else
2929 bp = save_body_particulars(*body);
2932 local_redraft_pos = redraft_pos;
2934 /*----------------------------------------------------------------------
2935 Loop calling the editor until everything goes well
2936 ----*/
2937 while(1){
2938 int saved_user_timeout;
2940 /* Reset body to what it was when we started. */
2941 if ((*body)->type == TYPEMULTIPART)
2942 reset_body_particulars(bp, &(*body)->nested.part->body);
2943 else
2944 reset_body_particulars(bp,*body);
2946 * set initial cursor position based on how many times we've been
2947 * thru the loop...
2949 if(reply && reply->pseudo){
2950 pbf->pine_flags |= reply->data.pico_flags;
2952 else if(body_start){
2953 pbf->pine_flags |= P_BODY;
2954 body_start = 0; /* maybe not next time */
2956 else if(local_redraft_pos){
2957 pbf->edit_offset = local_redraft_pos->offset;
2958 /* set the start_here bit in correct header */
2959 for(pf = header.local; pf && pf->name; pf = pf->next)
2960 if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
2961 && HE(pf)){
2962 HE(pf)->start_here = 1;
2963 break;
2966 /* If didn't find it, we start in body. */
2967 if(!pf || !pf->name)
2968 pbf->pine_flags |= P_BODY;
2970 else if(reply && (!reply->forw && !reply->forwarded)){
2971 pbf->pine_flags |= P_BODY;
2974 /* in case these were turned on in previous pass through loop */
2975 if(pf_nobody){
2976 pf_nobody->writehdr = 0;
2977 pf_nobody->localcopy = 0;
2980 if(pf_fcc)
2981 pf_fcc->localcopy = 0;
2984 * If a sending attempt failed after we passed the message text
2985 * thru a user-defined filter, "orig_so" points to the original
2986 * text. Replace the body's encoded data with the original...
2988 if(orig_so){
2989 STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
2990 ? &(*body)->nested.part->body.contents.text.data
2991 : &(*body)->contents.text.data);
2992 so_give(so);
2993 *so = orig_so;
2994 orig_so = NULL;
2998 * Convert the envelope and body to the string format that
2999 * pico can edit
3001 outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
3003 for(pf = header.local; pf && pf->name; pf = pf->next){
3005 * If this isn't the first time through this loop, we may have
3006 * freed some of the FreeText headers below so that they wouldn't
3007 * show up as empty headers in the finished message. Need to
3008 * alloc them again here so they can be edited.
3010 if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
3011 *HE(pf)->realaddr = cpystr("");
3013 if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
3014 HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
3018 * If From is exposed, probably by a role, then start the cursor
3019 * on the first line which isn't filled in. If it isn't, then we
3020 * don't move the cursor, mostly for back-compat.
3022 if((!reply || reply->forw || reply->forwarded) &&
3023 !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
3024 (he_from->display_it || !he_from->rich_header)){
3025 for(pf = header.local; pf && pf->name; pf = pf->next)
3026 if(HE(pf) &&
3027 (HE(pf)->display_it || !HE(pf)->rich_header) &&
3028 HE(pf)->realaddr &&
3029 (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
3030 HE(pf)->start_here = 1;
3031 break;
3035 #ifdef _WINDOWS
3036 mswin_setwindowmenu (MENU_COMPOSER);
3037 #endif
3039 cancel_busy_cue(-1);
3040 flush_status_messages(1);
3042 /* turn off user input timeout when in composer */
3043 saved_user_timeout = ps_global->hours_to_timeout;
3044 ps_global->hours_to_timeout = 0;
3045 dprint((1, "\n ---- COMPOSER ----\n"));
3046 editor_result = pico(pbf);
3047 dprint((4, "... composer returns (0x%x)\n", editor_result));
3048 ps_global->hours_to_timeout = saved_user_timeout;
3050 #ifdef _WINDOWS
3051 mswin_setwindowmenu (MENU_DEFAULT);
3052 #endif
3053 fix_windsize(ps_global);
3056 * Only reinitialize signals if we didn't receive an interesting
3057 * one while in pico, since pico's return is part of processing that
3058 * signal and it should continue to be ignored.
3060 if(!(editor_result & COMP_GOTHUP))
3061 init_signals(); /* Pico has it's own signal stuff */
3064 * We're going to save in DEADLETTER. Dump attachments first.
3066 if(editor_result & COMP_CANCEL)
3067 free_attachment_list(&pbf->attachments);
3069 /* Turn strings back into structures */
3070 strings2outgoing(&header, body, pbf->attachments, flowing_requested);
3072 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3073 if(outgoing->newsgroups){
3074 sqzspaces(outgoing->newsgroups);
3075 if(!outgoing->newsgroups[0])
3076 fs_give((void **)&(outgoing->newsgroups));
3079 /* Make subject NULL if it is "" (so won't show up in headers) */
3080 if(outgoing->subject && !outgoing->subject[0])
3081 fs_give((void **)&(outgoing->subject));
3083 /* remove custom fields that are empty */
3084 for(pf = header.local; pf && pf->name; pf = pf->next){
3085 if(pf->type == FreeText && pf->textbuf){
3086 if(pf->textbuf[0] == '\0'){
3087 fs_give((void **)&pf->textbuf);
3088 pf->text = NULL;
3093 removing_trailing_white_space(fcc);
3095 /*-------- Stamp it with a current date -------*/
3096 if(outgoing->date) /* update old date */
3097 fs_give((void **)&(outgoing->date));
3099 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3100 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
3102 rfc822_date(tmp_20k_buf); /* format and copy new date */
3103 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3104 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
3106 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
3108 /* Set return_path based on From which is going to be used */
3109 if(outgoing->return_path)
3110 mail_free_address(&outgoing->return_path);
3112 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
3115 * Don't ever believe the sender that is there.
3116 * If From doesn't look quite right, generate our own sender.
3118 if(outgoing->sender)
3119 mail_free_address(&outgoing->sender);
3122 * If the LHS of the address doesn't match, or the RHS
3123 * doesn't match one of localdomain or hostname,
3124 * then add a sender line (really X-X-Sender).
3126 * Don't add a personal_name since the user can change that.
3128 if(F_OFF(F_DISABLE_SENDER, ps_global)
3130 (!outgoing->from
3131 || !outgoing->from->mailbox
3132 || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
3133 || !outgoing->from->host
3134 || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
3135 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
3137 outgoing->sender = mail_newaddr();
3138 outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
3139 outgoing->sender->host = cpystr(ps_global->hostname);
3142 if(ps_global->newthread){
3143 if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
3144 if(outgoing->references) fs_give((void **)&outgoing->references);
3147 /*----- Message is edited, now decide what to do with it ----*/
3148 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
3149 /*=========== Postpone or Interrupted message ============*/
3150 CONTEXT_S *fcc_cntxt = NULL;
3151 char folder[MAXPATH+1];
3152 int fcc_result = 0;
3153 char label[50];
3155 dprint((4, "pine_send:%s handling\n",
3156 (editor_result & COMP_SUSPEND)
3157 ? "SUSPEND"
3158 : (editor_result & COMP_GOTHUP)
3159 ? "HUP"
3160 : (editor_result & COMP_CANCEL)
3161 ? "CANCEL" : "HUH?"));
3162 if((editor_result & COMP_CANCEL)
3163 && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
3164 || ps_global->deadlets == 0)){
3165 q_status_message(SM_ORDER, 0, 3, "Message cancelled");
3166 break;
3170 * The idea here is to use the Fcc: writing facility
3171 * to append to the special postponed message folder...
3173 * NOTE: the strategy now is to write the message and
3174 * all attachments as they exist at composition time.
3175 * In other words, attachments are postponed by value
3176 * and not reference. This may change later, but we'll
3177 * need a local "message/external-body" type that
3178 * outgoing2strings knows how to properly set up for
3179 * the composer. Maybe later...
3182 label[0] = '\0';
3184 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
3185 && (editor_result & COMP_SUSPEND)
3186 && (check_addresses(&header) == CA_BAD)){
3187 /*--- Addresses didn't check out---*/
3188 q_status_message(SM_ORDER, 7, 7,
3189 _("Not allowed to postpone message until addresses are qualified"));
3190 continue;
3194 * Build the local message copy so.
3196 * In the HUP case, we'll write the bezerk delimiter by
3197 * hand and output the message directly into the folder.
3198 * It's not only faster, we don't have to worry about
3199 * c-client reentrance and less hands paw over the data so
3200 * there's less chance of a problem.
3202 * In the Postpone case, just create it if the user wants to
3203 * and create a temporary storage object to write into. */
3204 fake_hup:
3205 lmc.all_written = lmc.text_only = lmc.text_written = 0;
3206 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3207 int newfile = 1;
3208 time_t now = time((time_t *)0);
3210 #if defined(DOS) || defined(OS2)
3212 * we can't assume anything about root or home dirs, so
3213 * just plunk it down in the same place as the pinerc
3215 if(!getenv("HOME")){
3216 char *lc = last_cmpnt(ps_global->pinerc);
3217 folder[0] = '\0';
3218 if(lc != NULL){
3219 strncpy(folder,ps_global->pinerc,
3220 MIN(lc-ps_global->pinerc,sizeof(folder)-1));
3221 folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
3224 strncat(folder, (editor_result & COMP_GOTHUP)
3225 ? INTERRUPTED_MAIL : DEADLETTER,
3226 sizeof(folder)-strlen(folder)-1);
3228 else
3229 #endif
3230 build_path(folder,
3231 ps_global->VAR_OPER_DIR
3232 ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
3233 (editor_result & COMP_GOTHUP)
3234 ? INTERRUPTED_MAIL : DEADLETTER,
3235 sizeof(folder));
3237 if(editor_result & COMP_CANCEL){
3238 char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
3240 if(strlen(folder) + 1 < sizeof(filename))
3241 for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
3242 strncpy(filename, folder, sizeof(filename));
3243 filename[sizeof(filename)-1] = '\0';
3244 strncpy(newfname, filename, sizeof(newfname));
3245 newfname[sizeof(newfname)-1] = '\0';
3247 if(i > 1){
3248 snprintf(nbuf, sizeof(nbuf), "%d", i);
3249 nbuf[sizeof(nbuf)-1] = '\0';
3250 strncat(filename, nbuf,
3251 sizeof(filename)-strlen(filename)-1);
3252 filename[sizeof(filename)-1] = '\0';
3255 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
3256 nbuf[sizeof(nbuf)-1] = '\0';
3257 strncat(newfname, nbuf,
3258 sizeof(newfname)-strlen(newfname)-1);
3259 newfname[sizeof(newfname)-1] = '\0';
3260 (void) rename_file(filename, newfname);
3263 our_unlink(folder);
3265 else
3266 newfile = can_access(folder, ACCESS_EXISTS);
3268 if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
3269 if (outgoing->from){
3270 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
3271 newfile ? "" : "\015\012",
3272 outgoing->from->mailbox,
3273 outgoing->from->host, ctime(&now));
3274 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3275 if(!so_puts(lmc.so, tmp_20k_buf)){
3276 if(editor_result & COMP_CANCEL)
3277 q_status_message2(SM_ORDER | SM_DING, 3, 3,
3278 "Can't write \"%s\": %s",
3279 folder, error_description(errno));
3280 else
3281 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3282 folder ? folder : "?",
3283 error_description(errno)));
3288 else{ /* Must be COMP_SUSPEND */
3289 if(!ps_global->VAR_POSTPONED_FOLDER
3290 || !ps_global->VAR_POSTPONED_FOLDER[0]){
3291 q_status_message(SM_ORDER | SM_DING, 3, 3,
3292 _("No postponed file defined"));
3293 continue;
3297 * Store the cursor position
3299 * First find the header entry with the start_here
3300 * bit set, if any. This means the editor is telling
3301 * us to start on this header field next time.
3303 start_here_name = NULL;
3304 for(pf = header.local; pf && pf->name; pf = pf->next)
3305 if(HE(pf) && HE(pf)->start_here){
3306 start_here_name = pf->name;
3307 break;
3310 /* If there wasn't one, ":" means we start in the body. */
3311 if(!start_here_name || !*start_here_name)
3312 start_here_name = ":";
3314 if(ps_global->VAR_FORM_FOLDER
3315 && ps_global->VAR_FORM_FOLDER[0]
3316 && postpone_prompt() == 'f'){
3317 strncpy(folder, ps_global->VAR_FORM_FOLDER,
3318 sizeof(folder)-1);
3319 folder[sizeof(folder)-1] = '\0';
3320 strncpy(label, "form letter", sizeof(label));
3321 label[sizeof(label)-1] = '\0';
3323 else{
3324 strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
3325 sizeof(folder)-1);
3326 folder[sizeof(folder)-1] = '\0';
3327 strncpy(label, "postponed message", sizeof(label));
3328 label[sizeof(label)-1] = '\0';
3331 lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
3334 if(lmc.so){
3335 size_t sz;
3336 char *lmq = NULL;
3338 /* copy fcc line to postponed or interrupted folder */
3339 if(pf_fcc)
3340 pf_fcc->localcopy = 1;
3342 /* plug error into header for later display to user */
3343 if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
3344 pf_err->writehdr = 1;
3345 pf_err->localcopy = 1;
3346 pf_err->textbuf = lmq;
3350 * if reply, write (UID)folder header field so we can
3351 * later flag the replied-to message \\ANSWERED
3352 * DON'T save MSGNO's.
3354 if(reply && reply->uid){
3355 char uidbuf[MAILTMPLEN], *p;
3356 long i;
3358 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
3359 if(i)
3360 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
3362 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
3365 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3367 pf_uid->writehdr = 1;
3368 pf_uid->localcopy = 1;
3369 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
3370 reply->prefix ? int2string(strlen(reply->prefix))
3371 : (reply->forwarded) ? "": "0 ",
3372 reply->prefix ? " " : "",
3373 reply->prefix ? reply->prefix : "",
3374 i, reply->data.uid.validity,
3375 tmp_20k_buf, reply->mailbox);
3376 uidbuf[sizeof(uidbuf)-1] = '\0';
3377 pf_uid->textbuf = cpystr(uidbuf);
3380 * Logically, this ought to be part of pf_uid, but this
3381 * was added later and so had to be in a separate header
3382 * for backwards compatibility.
3384 pf_mbox->writehdr = 1;
3385 pf_mbox->localcopy = 1;
3386 pf_mbox->textbuf = cpystr(reply->origmbox
3387 ? reply->origmbox
3388 : reply->mailbox);
3391 /* Save cursor position */
3392 if(start_here_name && *start_here_name){
3393 char curposbuf[MAILTMPLEN];
3395 pf_curpos->writehdr = 1;
3396 pf_curpos->localcopy = 1;
3397 snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
3398 pbf->edit_offset);
3399 curposbuf[sizeof(curposbuf)-1] = '\0';
3400 pf_curpos->textbuf = cpystr(curposbuf);
3404 * Work around c-client reply-to bug. C-client will
3405 * return a reply_to in an envelope even if there is
3406 * no reply-to header field. We want to note here whether
3407 * the reply-to is real or not.
3409 if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
3410 pf_ourrep->writehdr = 1;
3411 pf_ourrep->localcopy = 1;
3412 if(outgoing->reply_to)
3413 pf_ourrep->textbuf = cpystr("Full");
3414 else
3415 pf_ourrep->textbuf = cpystr("Empty");
3418 /* Save the role-specific smtp server */
3419 if(role && role->smtp && role->smtp[0]){
3420 char *q, *smtp = NULL;
3421 char **lp;
3422 size_t len = 0;
3425 * Turn the list of smtp servers into a space-
3426 * delimited list in a single string.
3428 for(lp = role->smtp; (q = *lp) != NULL; lp++)
3429 len += (strlen(q) + 1);
3431 if(len){
3432 smtp = (char *) fs_get(len * sizeof(char));
3433 smtp[0] = '\0';
3434 for(lp = role->smtp; (q = *lp) != NULL; lp++){
3435 if(lp != role->smtp)
3436 strncat(smtp, " ", len-strlen(smtp)-1);
3438 strncat(smtp, q, len-strlen(smtp)-1);
3441 smtp[len-1] = '\0';
3444 pf_smtp_server->writehdr = 1;
3445 pf_smtp_server->localcopy = 1;
3446 if(smtp)
3447 pf_smtp_server->textbuf = smtp;
3448 else
3449 pf_smtp_server->textbuf = cpystr("");
3452 /* Save the role-specific nntp server */
3453 if(suggested_nntp_server ||
3454 (role && role->nntp && role->nntp[0])){
3455 char *q, *nntp = NULL;
3456 char **lp;
3457 size_t len = 0;
3459 if(role && role->nntp && role->nntp[0]){
3461 * Turn the list of nntp servers into a space-
3462 * delimited list in a single string.
3464 for(lp = role->nntp; (q = *lp) != NULL; lp++)
3465 len += (strlen(q) + 1);
3467 if(len){
3468 nntp = (char *) fs_get(len * sizeof(char));
3469 nntp[0] = '\0';
3470 for(lp = role->nntp; (q = *lp) != NULL; lp++){
3471 if(lp != role->nntp)
3472 strncat(nntp, " ", len-strlen(nntp)-1);
3474 strncat(nntp, q, len-strlen(nntp)-1);
3477 nntp[len-1] = '\0';
3480 else
3481 nntp = cpystr(suggested_nntp_server);
3483 pf_nntp_server->writehdr = 1;
3484 pf_nntp_server->localcopy = 1;
3485 if(nntp)
3486 pf_nntp_server->textbuf = nntp;
3487 else
3488 pf_nntp_server->textbuf = cpystr("");
3492 * Write the list of custom headers to the
3493 * X-Our-Headers header so that we can recover the
3494 * list in redraft.
3496 sz = 0;
3497 for(pf = header.custom; pf && pf->name; pf = pf->next)
3498 sz += strlen(pf->name) + 1;
3500 if(sz){
3501 char *q;
3503 pf_ourhdrs->writehdr = 1;
3504 pf_ourhdrs->localcopy = 1;
3505 pf_ourhdrs->textbuf = (char *)fs_get(sz);
3506 memset(pf_ourhdrs->textbuf, 0, sz);
3507 q = pf_ourhdrs->textbuf;
3508 for(pf = header.custom; pf && pf->name; pf = pf->next){
3509 if(pf != header.custom)
3510 sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
3512 sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
3515 pf_ourhdrs->textbuf[sz-1] = '\0';;
3519 * We need to make sure any header values that got cleared
3520 * get written to the postponed message (they won't if
3521 * pf->text is NULL). Otherwise, we can't tell previously
3522 * non-existent custom headers or default values from
3523 * custom (or other) headers that got blanked in the
3524 * composer...
3526 for(pf = header.local; pf && pf->name; pf = pf->next)
3527 if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
3528 *(HE(pf)->realaddr) = cpystr("");
3531 * We're saving the message for use later. It may be that the
3532 * characters in the message are not all convertible to the
3533 * user's posting_charmap. We'll save it as UTF-8 instead
3534 * and worry about that the next time they try to send it.
3535 * Use a different save pointer just to be sure we don't
3536 * mess up the other stuff. We should probably make the
3537 * charset an argument.
3539 * We also need to fix the charset of the body part
3540 * the user is editing so that we can read it back
3541 * successfully when we resume the composition.
3543 ps_global->post_utf8 = 1;
3546 PARAMETER *pm;
3547 BODY *bp;
3548 if((*body)->type == TYPEMULTIPART)
3549 bp = &(*body)->nested.part->body;
3550 else
3551 bp = *body;
3553 for(pm = bp->parameter;
3554 pm && strucmp(pm->attribute, "charset") != 0;
3555 pm = pm->next)
3558 if(pm){
3559 if(pm->value)
3560 fs_give((void **) &pm->value);
3562 pm->value = cpystr("UTF-8");
3566 if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
3567 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3568 char *err;
3569 STORE_S *hup_so;
3570 gf_io_t gc, pc;
3571 int we_cancel = 0;
3573 if(editor_result & COMP_CANCEL){
3574 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3575 "Saving to \"%s\"", folder);
3576 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3577 we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
3580 if((hup_so =
3581 so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
3582 gf_set_so_readc(&gc, lmc.so);
3583 gf_set_so_writec(&pc, hup_so);
3584 so_seek(lmc.so, 0L, 0); /* read msg copy and */
3585 so_seek(hup_so, 0L, 2); /* append to folder */
3586 gf_filter_init();
3587 gf_link_filter(gf_nvtnl_local, NULL);
3588 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
3589 dprint((1, "*** PIPE FAILED: %s\n",
3590 err ? err : "?"));
3592 gf_clear_so_readc(lmc.so);
3593 gf_clear_so_writec(hup_so);
3594 so_give(&hup_so);
3596 else
3597 dprint((1, "*** CAN'T CREATE %s: %s\n",
3598 folder ? folder : "?",
3599 error_description(errno)));
3601 if(we_cancel)
3602 cancel_busy_cue(-1);
3604 else
3605 fcc_result = write_fcc(folder, fcc_cntxt,
3606 lmc.so, NULL, label, NULL);
3609 /* discontinue coerced UTF-8 posting */
3610 ps_global->post_utf8 = 0;
3612 so_give(&lmc.so);
3614 else
3615 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3616 error_description(errno)));
3618 if(editor_result & COMP_GOTHUP){
3620 * Special Hack #291: if any hi-byte bits are set in
3621 * editor's result, we put them there.
3623 if(editor_result & 0xff00)
3624 exit(editor_result >> 8);
3626 dprint((1, "Save composition on HUP %sED\n",
3627 fcc_result ? "SUCCEED" : "FAIL"));
3628 hup_signal(); /* Do what we normally do on SIGHUP */
3630 else if((editor_result & COMP_SUSPEND) && fcc_result){
3631 if(ps_global->VAR_FORM_FOLDER
3632 && ps_global->VAR_FORM_FOLDER[0]
3633 && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
3634 q_status_message(SM_ORDER, 0, 3,
3635 _("Composition saved to Form Letter Folder. Select Compose to send."));
3636 else
3637 q_status_message(SM_ORDER, 0, 3,
3638 _("Composition postponed. Select Compose to resume."));
3640 break; /* postpone went OK, get out of here */
3642 else if(editor_result & COMP_CANCEL){
3643 char *lc = NULL;
3645 if(fcc_result && folder)
3646 lc = last_cmpnt(folder);
3648 q_status_message3(SM_ORDER, 0, 3,
3649 _("Message cancelled%s%s%s"),
3650 (lc && *lc) ? " and copied to \"" : "",
3651 (lc && *lc) ? lc : "",
3652 (lc && *lc) ? "\" file" : "");
3653 break;
3655 else{
3656 q_status_message(SM_ORDER, 0, 4,
3657 _("Continuing composition. Message not postponed or sent"));
3658 body_start = 1;
3659 continue; /* postpone failed, jump back in to composer */
3662 else{
3663 /*------ Must be sending mail or posting ! -----*/
3664 int result, valid_addr, continue_with_only_fcc = 0;
3665 CONTEXT_S *fcc_cntxt = NULL;
3667 result = 0;
3668 dprint((4, "=== sending: "));
3670 /* --- If posting, confirm with user ----*/
3671 if(outgoing->newsgroups && *outgoing->newsgroups
3672 && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
3673 && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
3674 q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
3675 dprint((4, "no post, continuing\n"));
3676 continue;
3679 if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
3680 || outgoing->newsgroups)){
3681 if(fcc && fcc[0]){
3682 if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
3683 want_to(_("No recipients, really copy only to Fcc "),
3684 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
3685 continue;
3687 continue_with_only_fcc++;
3689 else{
3690 q_status_message(SM_ORDER, 3, 4,
3691 _("No recipients specified!"));
3692 dprint((4, "no recip, continuing\n"));
3693 continue;
3697 if((valid_addr = check_addresses(&header)) == CA_BAD){
3698 /*--- Addresses didn't check out---*/
3699 dprint((4, "addrs failed, continuing\n"));
3700 continue;
3703 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
3704 && !continue_with_only_fcc
3705 && !(outgoing->to || outgoing->cc || lcc_addr
3706 || outgoing->newsgroups)
3707 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3708 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
3709 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3710 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3711 free_redraft_pos(&local_redraft_pos);
3713 local_redraft_pos
3714 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3715 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3716 local_redraft_pos->hdrname = cpystr(TONAME);
3717 continue;
3720 if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
3721 && check_for_subject(&header) == CF_MISSING){
3722 dprint((4, "No subject, continuing\n"));
3723 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3724 free_redraft_pos(&local_redraft_pos);
3726 local_redraft_pos
3727 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3728 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3729 local_redraft_pos->hdrname = cpystr(SUBJNAME);
3730 continue;
3733 if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
3734 && check_for_fcc(fcc) == CF_MISSING){
3735 dprint((4, "No fcc, continuing\n"));
3736 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3737 free_redraft_pos(&local_redraft_pos);
3739 local_redraft_pos
3740 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3741 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3742 local_redraft_pos->hdrname = cpystr("Fcc");
3743 continue;
3746 set_last_fcc(fcc);
3748 /*---- Check out fcc -----*/
3749 if(fcc && *fcc){
3751 * If special name "inbox" then replace it with the
3752 * real inbox path.
3754 if(ps_global->VAR_INBOX_PATH
3755 && strucmp(fcc, ps_global->inbox_name) == 0){
3756 char *replace_fcc;
3758 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
3759 fs_give((void **)&fcc);
3760 fcc = replace_fcc;
3763 lmc.all_written = lmc.text_written = 0;
3764 /* lmc.text_only set on command line */
3765 if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
3766 /* ---- Open or allocation of fcc failed ----- */
3767 dprint((4,"can't open/allocate fcc, cont'g\n"));
3770 * Find field entry associated with fcc, and start
3771 * composer on it...
3773 for(pf = header.local; pf && pf->name; pf = pf->next)
3774 if(pf->type == Fcc && HE(pf))
3775 HE(pf)->start_here = 1;
3777 continue;
3779 else
3780 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
3782 else
3783 lmc.so = NULL;
3785 /*---- Take care of any requested prefiltering ----*/
3786 if(sending_filter_requested
3787 && !filter_message_text(sending_filter_requested, outgoing,
3788 *body, &orig_so, &header)){
3789 q_status_message1(SM_ORDER, 3, 3,
3790 _("Problem filtering! Nothing sent%s."),
3791 fcc ? " or saved to fcc" : "");
3792 continue;
3795 /*------ Actually post -------*/
3796 if(outgoing->newsgroups){
3797 char **alt_nntp = NULL, *alt_nntp_p[2];
3798 if(((role && role->nntp)
3799 || suggested_nntp_server)){
3800 if(ps_global->FIX_NNTP_SERVER
3801 && ps_global->FIX_NNTP_SERVER[0])
3802 q_status_message(SM_ORDER | SM_DING, 5, 5,
3803 "Using nntp-server that is administratively fixed");
3804 else if(role && role->nntp)
3805 alt_nntp = role->nntp;
3806 else{
3807 alt_nntp_p[0] = suggested_nntp_server;
3808 alt_nntp_p[1] = NULL;
3809 alt_nntp = alt_nntp_p;
3812 if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
3813 dprint((1, "Post failed, continuing\n"));
3814 if(outgoing->message_id)
3815 fs_give((void **) &outgoing->message_id);
3817 outgoing->message_id = generate_message_id(role);
3819 continue;
3821 else
3822 result |= P_NEWS_WIN;
3826 * BUG: IF we've posted the message *and* an fcc was specified
3827 * then we've already got a neatly formatted message in the
3828 * lmc.so. It'd be nice not to have to re-encode everything
3829 * to insert it into the smtp slot...
3833 * Turn on "undisclosed recipients" header if no To or cc.
3835 if(!(outgoing->to || outgoing->cc)
3836 && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
3837 pf_nobody->writehdr = 1;
3838 pf_nobody->localcopy = 1;
3841 if(priority_requested){
3842 (void) set_priority_header(&header, priority_requested);
3843 fs_give((void **) &priority_requested);
3846 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3848 * If requested, launch background posting...
3850 if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
3851 ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
3852 memset(ps_global->post, 0, sizeof(POST_S));
3853 if(fcc)
3854 ps_global->post->fcc = cpystr(fcc);
3856 if((ps_global->post->pid = fork()) == 0){
3858 * Put us in new process group...
3860 setpgrp(0, ps_global->post->pid);
3862 /* BUG: should fix argv[0] to indicate what we're up to */
3865 * If there are any live streams, pretend we never
3866 * knew them. Problem is two processes writing
3867 * same server process.
3868 * This is not clean but we're just going to exit
3869 * right away anyway. We just want to be sure to leave
3870 * the stuff that the parent is going to use alone.
3871 * The next three lines will disable the re-use of the
3872 * existing streams and cause us to open a new one if
3873 * needed.
3875 ps_global->mail_stream = NULL;
3876 ps_global->s_pool.streams = NULL;
3877 ps_global->s_pool.nstream = 0;
3879 /* quell any display output */
3880 ps_global->in_init_seq = 1;
3882 /*------- Actually mail the message ------*/
3883 if(valid_addr == CA_OK
3884 && (outgoing->to || outgoing->cc
3885 || outgoing->bcc || lcc_addr)){
3886 char **alt_smtp = NULL;
3888 if(role && role->smtp){
3889 if(ps_global->FIX_SMTP_SERVER
3890 && ps_global->FIX_SMTP_SERVER[0])
3891 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3892 else
3893 alt_smtp = role->smtp;
3896 result |= (call_mailer(&header, *body, alt_smtp,
3897 call_mailer_flags,
3898 call_mailer_file_result,
3899 pipe_callback) > 0)
3900 ? P_MAIL_WIN : P_MAIL_LOSE;
3902 if(result & P_MAIL_LOSE)
3903 mark_address_failure_for_pico(&header);
3906 /*----- Was there an fcc involved? -----*/
3907 if(lmc.so){
3908 /*------ Write it if at least something worked ------*/
3909 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3910 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3911 && pine_rfc822_output(&header, *body,
3912 NULL, NULL))){
3913 char label[50];
3915 strncpy(label, "Fcc", sizeof(label));
3916 label[sizeof(label)-1] = '\0';
3917 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3918 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3919 label[sizeof(label)-1] = '\0';
3922 /*-- Now actually copy to fcc folder and close --*/
3923 result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
3924 NULL, label,
3925 F_ON(F_MARK_FCC_SEEN, ps_global)
3926 ? "\\SEEN" : NULL))
3927 ? P_FCC_WIN : P_FCC_LOSE;
3929 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3930 q_status_message(SM_ORDER, 3, 5,
3931 _("Fcc Failed!. No message saved."));
3932 dprint((1,
3933 "explicit fcc write failed!\n"));
3934 result |= P_FCC_LOSE;
3937 so_give(&lmc.so);
3940 if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
3942 * Encode child's result in hi-byte of
3943 * editor's result
3945 editor_result = ((result << 8) | COMP_GOTHUP);
3946 goto fake_hup;
3949 exit(result);
3952 if(ps_global->post->pid > 0){
3953 q_status_message(SM_ORDER, 3, 3,
3954 _("Message handed off for posting"));
3955 break; /* up to our child now */
3957 else{
3958 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3959 "Can't fork for send: %s",
3960 error_description(errno));
3961 if(ps_global->post->fcc)
3962 fs_give((void **) &ps_global->post->fcc);
3964 fs_give((void **) &ps_global->post);
3967 if(lmc.so) /* throw away unused store obj */
3968 so_give(&lmc.so);
3970 if(outgoing->message_id)
3971 fs_give((void **) &outgoing->message_id);
3973 outgoing->message_id = generate_message_id(role);
3975 continue; /* if we got here, there was a prob */
3977 #endif /* BACKGROUND_POST */
3979 /*------- Actually mail the message ------*/
3980 if(valid_addr == CA_OK
3981 && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
3982 char **alt_smtp = NULL;
3984 if(role && role->smtp){
3985 if(ps_global->FIX_SMTP_SERVER
3986 && ps_global->FIX_SMTP_SERVER[0])
3987 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3988 else
3989 alt_smtp = role->smtp;
3992 result |= (call_mailer(&header, *body, alt_smtp,
3993 call_mailer_flags,
3994 call_mailer_file_result,
3995 pipe_callback) > 0)
3996 ? P_MAIL_WIN : P_MAIL_LOSE;
3998 if(result & P_MAIL_LOSE)
3999 mark_address_failure_for_pico(&header);
4002 /*----- Was there an fcc involved? -----*/
4003 if(lmc.so){
4004 /*------ Write it if at least something worked ------*/
4005 if((result & (P_MAIL_WIN | P_NEWS_WIN))
4006 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
4007 && pine_rfc822_output(&header, *body, NULL, NULL))){
4008 char label[50];
4010 strncpy(label, "Fcc", sizeof(label));
4011 label[sizeof(label)-1] = '\0';
4012 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
4013 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
4014 label[sizeof(label)-1] = '\0';
4017 /*-- Now actually copy to fcc folder and close --*/
4018 result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
4019 F_ON(F_MARK_FCC_SEEN, ps_global)
4020 ? "\\SEEN" : NULL))
4021 ? P_FCC_WIN : P_FCC_LOSE;
4023 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
4024 q_status_message(SM_ORDER,3,5,
4025 _("Fcc Failed!. No message saved."));
4026 dprint((1, "explicit fcc write failed!\n"));
4027 result |= P_FCC_LOSE;
4030 so_give(&lmc.so);
4033 /*----- Mail Post FAILED, back to composer -----*/
4034 if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
4035 dprint((1, "Send failed, continuing\n"));
4037 if(result & P_FCC_LOSE){
4039 * Find field entry associated with fcc, and start
4040 * composer on it...
4042 for(pf = header.local; pf && pf->name; pf = pf->next)
4043 if(pf->type == Fcc && HE(pf))
4044 HE(pf)->start_here = 1;
4046 q_status_message(SM_ORDER | SM_DING, 3, 3,
4047 pine_send_status(result, fcc,
4048 tmp_20k_buf, SIZEOF_20KBUF, NULL));
4051 if(outgoing->message_id)
4052 fs_give((void **) &outgoing->message_id);
4054 outgoing->message_id = generate_message_id(role);
4056 continue;
4060 * If message sent *completely* successfully, there's a
4061 * reply struct AND we're allowed to write back state, do it.
4062 * But also protect against shifted message numbers due
4063 * to new mail arrival. Since the number passed is based
4064 * on the real imap msg no, AND we're sure no expunge has
4065 * been done, just fix up the sorted number...
4067 update_answered_flags(reply);
4069 /*----- Signed, sealed, delivered! ------*/
4070 q_status_message(SM_ORDER, 0, 3,
4071 pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
4073 break; /* All's well, pop out of here */
4077 if(orig_so)
4078 so_give(&orig_so);
4080 if(fcc)
4081 fs_give((void **)&fcc);
4083 free_body_particulars(bp);
4085 free_attachment_list(&pbf->attachments);
4087 standard_picobuf_teardown(pbf);
4089 for(i=0; i < fixed_cnt; i++){
4090 if(pfields[i].textbuf)
4091 fs_give((void **)&pfields[i].textbuf);
4093 fs_give((void **)&pfields[i].name);
4096 if(lcc_addr)
4097 mail_free_address(&lcc_addr);
4099 if(nobody_addr)
4100 mail_free_address(&nobody_addr);
4102 free_prompts(header.custom);
4103 free_customs(header.custom);
4104 fs_give((void **)&pfields);
4105 free_headents(&headents);
4106 fs_give((void **)&sending_order);
4107 if(suggested_nntp_server)
4108 fs_give((void **)&suggested_nntp_server);
4109 if(title)
4110 fs_give((void **)&title);
4112 if(local_redraft_pos && local_redraft_pos != redraft_pos)
4113 free_redraft_pos(&local_redraft_pos);
4115 pbf = save_previous_pbuf;
4116 g_rolenick = NULL;
4118 dprint((4, "=== send returning ===\n"));
4123 * Check for subject in outgoing message.
4125 * Asks user whether to proceed with no subject.
4128 check_for_subject(METAENV *header)
4130 PINEFIELD *pf;
4131 int rv = CF_OK;
4133 for(pf = header->local; pf && pf->name; pf = pf->next)
4134 if(pf->type == Subject){
4135 if(pf->text && *pf->text && **pf->text)
4136 rv = CF_OK;
4137 else{
4138 if(want_to("No Subject, send anyway ",
4139 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
4140 rv = CF_OK;
4141 else
4142 rv = CF_MISSING;
4145 break;
4149 return(rv);
4154 * Check for fcc in outgoing message.
4156 * Asks user whether to proceed with no fcc.
4159 check_for_fcc(char *fcc)
4161 int rv = CF_OK;
4163 if(fcc && *fcc)
4164 rv = CF_OK;
4165 else{
4166 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
4167 rv = CF_OK;
4168 else
4169 rv = CF_MISSING;
4172 return(rv);
4177 * Confirm that the user wants to send to MAILER-DAEMON
4180 confirm_daemon_send(void)
4182 return(want_to("Really send this message to the MAILER-DAEMON",
4183 'n', 'n', NO_HELP, WT_NORM) == 'y');
4187 void
4188 free_prompts(PINEFIELD *head)
4190 PINEFIELD *pf;
4192 for(pf = head; pf && pf->name; pf = pf->next){
4193 if(HE(pf) && HE(pf)->prompt)
4194 fs_give((void **)& HE(pf)->prompt);
4200 postpone_prompt(void)
4202 static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
4203 {'f', 'f', "F", N_("Form Letter Folder")},
4204 {-1, 0, NULL, NULL} };
4206 return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
4207 pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
4212 * call__mailer_file_result - some results from call_mailer might be in a file.
4213 * dislplay that file.
4215 void
4216 call_mailer_file_result(char *filename, int style)
4218 if(filename){
4219 if(style & CM_BR_VERBOSE){
4220 display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
4222 else{
4223 display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
4228 void
4229 mark_address_failure_for_pico(METAENV *header)
4231 PINEFIELD *pf;
4232 ADDRESS *a;
4233 int error_count = 0;
4234 struct headerentry *last_he = NULL;
4236 for(pf = header->local; pf && pf->name; pf = pf->next)
4237 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
4238 for(a = *pf->addr; a != NULL; a = a->next)
4239 if(a->error != NULL
4240 && error_count++ < MAX_ADDR_ERROR
4241 && (HE(pf))){
4242 if(last_he) /* start last reported err */
4243 last_he->start_here = 0;
4245 (last_he = HE(pf))->start_here = 1;
4252 * This is specialized routine. It assumes that the only things that we
4253 * care about restoring are the body type, subtype, encoding and the
4254 * state of the charset parameter. It also assumes that if the charset
4255 * parameter exists when we save it, it won't be removed later.
4257 BODY_PARTICULARS_S *
4258 save_body_particulars(struct mail_bodystruct *body)
4260 BODY_PARTICULARS_S *bp;
4261 PARAMETER *pm;
4263 bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
4265 bp->type = body->type;
4266 bp->encoding = body->encoding;
4267 bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
4268 bp->parameter = body->parameter;
4269 for(pm = bp->parameter;
4270 pm && strucmp(pm->attribute, "charset") != 0;
4271 pm = pm->next)
4272 ;/* searching for possible charset parameter */
4274 if(pm){ /* found one */
4275 bp->had_csp = 1; /* saved body had charset parameter */
4276 bp->charset = pm->value ? cpystr(pm->value) : NULL;
4278 else{
4279 bp->had_csp = 0;
4280 bp->charset = NULL;
4283 return(bp);
4287 void
4288 reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
4290 body->type = bp->type;
4291 body->encoding = bp->encoding;
4292 if(body->subtype)
4293 fs_give((void **)&body->subtype);
4295 body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
4297 if(bp->parameter){
4298 PARAMETER *pm, *pm_prev = NULL;
4300 for(pm = body->parameter;
4301 pm && strucmp(pm->attribute, "charset") != 0;
4302 pm = pm->next)
4303 pm_prev = pm;
4305 if(pm){ /* body has charset parameter */
4306 if(bp->had_csp){ /* reset to what it used to be */
4307 if(pm->value)
4308 fs_give((void **)&pm->value);
4310 pm->value = bp->charset ? cpystr(bp->charset) : NULL;
4312 else{ /* remove charset parameter */
4313 if(pm_prev)
4314 pm_prev->next = pm->next;
4315 else
4316 body->parameter = pm->next;
4318 mail_free_body_parameter(&pm);
4321 else{
4322 if(bp->had_csp){
4324 * This can't happen because grope never removes
4325 * the charset parameter.
4327 q_status_message(SM_ORDER | SM_DING, 5, 5,
4328 "Programmer error: saved charset but no current charset param in pine_send");
4331 else{
4332 ok, still no parameter
4337 else{
4338 if(body->parameter)
4339 mail_free_body_parameter(&body->parameter);
4341 body->parameter = NULL;
4346 void
4347 free_body_particulars(BODY_PARTICULARS_S *bp)
4349 if(bp){
4350 if(bp->subtype)
4351 fs_give((void **)&bp->subtype);
4353 if(bp->charset)
4354 fs_give((void **)&bp->charset);
4356 fs_give((void **)&bp);
4361 /*----------------------------------------------------------------------
4362 Build a status message suitable for framing
4364 Returns: pointer to resulting buffer
4365 ---*/
4366 char *
4367 pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
4369 int avail = ps_global->ttyo->screen_cols - 2;
4370 int fixedneed, need, lenfcc;
4371 char *part1, *part2, *part3, *part4, *part5;
4372 char fbuf[MAILTMPLEN+1];
4374 part1 = (result & P_NEWS_WIN)
4375 ? "posted"
4376 : (result & P_NEWS_LOSE)
4377 ? "NOT POSTED"
4378 : "";
4379 part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
4380 && (result & P_FCC_BITS))
4381 ? ", "
4382 : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
4383 ? " and "
4384 : "";
4385 part3 = (result & P_MAIL_WIN)
4386 ? "sent"
4387 : (result & P_MAIL_LOSE)
4388 ? "NOT SENT"
4389 : "";
4390 part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
4391 ? " and "
4392 : "";
4393 part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
4394 ? "ONLY copied to "
4395 : (result & P_FCC_WIN)
4396 ? "copied to "
4397 : (result & P_FCC_LOSE)
4398 ? "NOT copied to "
4399 : "";
4400 lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
4402 fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
4403 strlen(part4) + strlen(part5);
4404 need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
4406 if(need > avail && fixedneed + 3 >= avail){
4407 /* dots on end of fixed, no fcc */
4408 snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
4409 part1, part2, part3, part4, part5);
4410 short_str(fbuf, buf, buflen, avail, EndDots);
4412 else if(need > avail){
4413 /* include whole fixed part, quotes and dots at end of fcc name */
4414 if(lenfcc > 0)
4415 lenfcc = MAX(1, lenfcc-(need-avail));
4417 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4418 part1, part2, part3, part4, part5,
4419 (result & P_FCC_BITS) ? "\"" : "",
4420 short_str((result & P_FCC_BITS) ? fcc_name : "",
4421 fbuf, sizeof(fbuf), lenfcc, FrontDots),
4422 (result & P_FCC_BITS) ? "\"" : "");
4424 else{
4425 /* whole thing */
4426 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4427 part1, part2, part3, part4, part5,
4428 (result & P_FCC_BITS) ? "\"" : "",
4429 (result & P_FCC_BITS) ? fcc_name : "",
4430 (result & P_FCC_BITS) ? "\"" : "");
4433 if(goodorbad)
4434 *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
4436 return(buf);
4439 /* Callback from Pico to set the conditions for Alpine to start a new thread
4442 void
4443 new_thread_on_blank_subject(void)
4445 ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
4450 /*----------------------------------------------------------------------
4451 Call back for pico to insert the specified message's text
4453 Args: n -- message number to format
4454 f -- function to use to output the formatted message
4457 Returns: returns msg number formatted on success, zero on error.
4458 ----*/
4459 long
4460 message_format_for_pico(long int n, int (*f) (int))
4462 ENVELOPE *e;
4463 BODY *b;
4464 char *old_quote = NULL;
4465 long rv = n;
4467 if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
4468 && (e = pine_mail_fetchstructure(ps_global->mail_stream,
4469 mn_m2raw(ps_global->msgmap, n), &b)))){
4470 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4471 flush_status_messages(0);
4472 return(0L);
4475 /* temporarily assign a new quote string */
4476 old_quote = pbf->quote_str;
4477 pbf->quote_str = reply_quote_str(e);
4479 /* build separator line */
4480 reply_delimiter(e, NULL, f);
4482 /* actually write message text */
4483 if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
4484 FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
4485 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4486 flush_status_messages(0);
4487 rv = 0L;
4490 fs_give((void **)&pbf->quote_str);
4491 pbf->quote_str = old_quote;
4492 return(rv);
4496 /*----------------------------------------------------------------------
4497 Call back for pico to prompt the user for exit confirmation
4499 Args: dflt -- default answer for confirmation prompt
4501 Returns: either NULL if the user accepts exit, or string containing
4502 reason why the user declined.
4503 ----*/
4505 send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
4506 char **result)
4508 int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
4509 int dsn_label = 0, fcc_label = 0, lparen;
4510 int flowing_label = 0, double_rad;
4511 char *rstr = NULL, *p, *lc, *optp;
4512 char dsn_string[30];
4513 void (*redraw)(void) = ps_global->redrawer;
4514 ESCKEY_S opts[18];
4515 struct filters {
4516 char *filter;
4517 int index;
4518 struct filters *prev, *next;
4519 } *filters = NULL, *fp;
4521 sending_filter_requested = NULL;
4522 call_mailer_flags = 0;
4523 background_requested = 0;
4524 flowing_requested = allow_flowed ? 1 : 0;
4525 lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
4526 if(priority_requested)
4527 fs_give((void **) &priority_requested);
4529 if(background_posting(FALSE)){
4530 if(result)
4531 *result = "Can't send while background posting. Use postpone.";
4533 return(1);
4536 if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
4537 if(result)
4538 *result = NULL;
4540 return(0);
4543 ps_global->redrawer = redraw_pico;
4545 if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
4546 (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
4549 * Build list of available filters...
4551 for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
4552 for(p = ps_global->VAR_SEND_FILTER[i];
4553 *p && !isspace((unsigned char)*p); p++)
4556 c = *p;
4557 *p = '\0';
4558 if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
4559 && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
4560 *p = c;
4561 continue;
4564 fp = (struct filters *)fs_get(sizeof(struct filters));
4565 fp->index = i;
4566 if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
4567 fp->filter = cpystr(lc);
4569 else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
4570 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
4571 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4572 fp->filter = cpystr(tmp_20k_buf);
4574 else
4575 fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
4577 *p = c;
4579 if(filters){
4580 fp->next = filters;
4581 fp->prev = filters->prev;
4582 fp->prev->next = filters->prev = fp;
4584 else{
4585 filters = (struct filters *)fs_get(sizeof(struct filters));
4586 filters->index = -1;
4587 filters->filter = NULL;
4588 filters->next = filters->prev = fp;
4589 fp->next = fp->prev = filters;
4593 i = 0;
4594 opts[i].ch = 'y';
4595 opts[i].rval = 'y';
4596 opts[i].name = "Y";
4597 opts[i++].label = N_("Yes");
4599 opts[i].ch = 'n';
4600 opts[i].rval = 'n';
4601 opts[i].name = "N";
4602 opts[i++].label = N_("No");
4604 if(filters){
4605 /* set global_filter_pointer to desired filter or NULL if none */
4606 /* prepare two keymenu slots for selecting filter */
4607 opts[i].ch = ctrl('P');
4608 opts[i].rval = 10;
4609 opts[i].name = "^P";
4610 opts[i++].label = N_("Prev Filter");
4612 opts[i].ch = ctrl('N');
4613 opts[i].rval = 11;
4614 opts[i].name = "^N";
4615 opts[i++].label = N_("Next Filter");
4617 if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
4618 filters = filters->next;
4621 if(F_ON(F_VERBOSE_POST, ps_global)){
4622 /* setup keymenu slot to toggle verbose mode */
4623 opts[i].ch = ctrl('W');
4624 opts[i].rval = 12;
4625 opts[i].name = "^W";
4626 verbose_label = i++;
4629 if(allow_flowed){
4630 /* setup keymenu slot to toggle flowed mode */
4631 opts[i].ch = ctrl('V');
4632 opts[i].rval = 22;
4633 opts[i].name = "^V";
4634 flowing_label = i++;
4635 flowing_requested = 1;
4638 if(F_ON(F_NO_FCC_ATTACH, ps_global)){
4639 /* setup keymenu slot to toggle attacment on fcc */
4640 opts[i].ch = ctrl('F');
4641 opts[i].rval = 21;
4642 opts[i].name = "^F";
4643 fcc_label = i++;
4646 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4647 if(F_ON(F_BACKGROUND_POST, ps_global)){
4648 opts[i].ch = ctrl('R');
4649 opts[i].rval = 15;
4650 opts[i].name = "^R";
4651 bg_label = i++;
4653 #endif
4655 opts[i].ch = 'p';
4656 opts[i].rval = 'p';
4657 opts[i].name = "P";
4658 opts[i++].label = N_("Priority");
4660 #ifdef SMIME
4661 if(F_OFF(F_DONT_DO_SMIME, ps_global)){
4662 opts[i].ch = 'e';
4663 opts[i].rval = 'e';
4664 opts[i].name = "E";
4665 opts[i++].label = "Encrypt";
4667 opts[i].ch = 'g';
4668 opts[i].rval = 'g';
4669 opts[i].name = "G";
4670 opts[i++].label = "Sign";
4672 if(ps_global->smime){
4673 ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
4674 ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
4677 #endif
4679 double_rad = i;
4681 if(F_ON(F_DSN, ps_global)){
4682 /* setup keymenu slots to toggle dsn bits */
4683 opts[i].ch = 'd';
4684 opts[i].rval = 'd';
4685 opts[i].name = "D";
4686 opts[i].label = N_("DSNOpts");
4687 dsn_label = i++;
4688 opts[i].ch = -2;
4689 opts[i].rval = 's';
4690 opts[i].name = "S";
4691 opts[i++].label = "";
4692 opts[i].ch = -2;
4693 opts[i].rval = 'x';
4694 opts[i].name = "X";
4695 opts[i++].label = "";
4696 opts[i].ch = -2;
4697 opts[i].rval = 'h';
4698 opts[i].name = "H";
4699 opts[i++].label = "";
4702 if(filters){
4703 opts[i].ch = KEY_UP;
4704 opts[i].rval = 10;
4705 opts[i].name = "";
4706 opts[i++].label = "";
4708 opts[i].ch = KEY_DOWN;
4709 opts[i].rval = 11;
4710 opts[i].name = "";
4711 opts[i++].label = "";
4714 opts[i].ch = -1;
4716 fix_windsize(ps_global);
4718 while(1){
4719 if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
4720 *p = '\0';
4721 else
4722 p = NULL;
4724 lparen = 0;
4725 strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
4726 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4727 optp = tmp_20k_buf + strlen(tmp_20k_buf);
4729 if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
4730 sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4732 if(allow_flowed && !flowing_requested){
4733 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4734 if(!lparen){
4735 *optp++ = ' ';
4736 *optp++ = '(';
4737 lparen++;
4739 else
4740 *optp++ = ' ';
4743 sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4746 if(filters){
4747 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4748 if(!lparen){
4749 *optp++ = ' ';
4750 *optp++ = '(';
4751 lparen++;
4753 else{
4754 *optp++ = ',';
4755 *optp++ = ' ';
4759 if(filters->filter){
4760 sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4761 sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4762 if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
4763 *optp++ = '\"';
4765 else
4766 sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4769 if((call_mailer_flags & CM_VERBOSE) || background_requested){
4770 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4771 if(!lparen){
4772 *optp++ = ' ';
4773 *optp++ = '(';
4774 lparen++;
4776 else
4777 *optp++ = ' ';
4780 sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4781 if(call_mailer_flags & CM_VERBOSE)
4782 sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4784 if(background_requested)
4785 sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4787 sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4790 if(g_rolenick && !(he && he[N_FROM].dirty)){
4791 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4792 if(!lparen){
4793 *optp++ = ' ';
4794 *optp++ = '(';
4795 lparen++;
4797 else
4798 *optp++ = ' ';
4801 sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4802 sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4803 sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4806 if(call_mailer_flags & CM_DSN_SHOW){
4807 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4808 if(!lparen){
4809 *optp++ = ' ';
4810 *optp++ = '(';
4811 lparen++;
4813 else{
4814 *optp++ = ',';
4815 *optp++ = ' ';
4819 sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4822 #ifdef SMIME
4823 if(ps_global->smime && ps_global->smime->do_encrypt){
4824 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4825 if(!lparen){
4826 *optp++ = ' ';
4827 *optp++ = '(';
4828 lparen++;
4830 else{
4831 *optp++ = ',';
4832 *optp++ = ' ';
4836 sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4839 if(ps_global->smime && ps_global->smime->do_sign){
4840 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4841 if(!lparen){
4842 *optp++ = ' ';
4843 *optp++ = '(';
4844 lparen++;
4846 else{
4847 *optp++ = ',';
4848 *optp++ = ' ';
4852 sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4854 #endif
4856 if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
4857 *optp++ = ')';
4859 sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4860 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4862 if(p)
4863 *p = ' ';
4865 if(flowing_label)
4866 opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
4868 if(verbose_label)
4869 opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
4871 if(bg_label)
4872 opts[bg_label].label = background_requested
4873 ? N_("Foreground") : N_("Background");
4875 if(fcc_label)
4876 opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
4877 : N_("No Fcc Atmts ");
4879 if(F_ON(F_DSN, ps_global)){
4880 if(call_mailer_flags & CM_DSN_SHOW){
4881 opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
4882 ? N_("NoDelay") : N_("Delay");
4883 opts[dsn_label+1].ch = 's';
4884 opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
4885 ? N_("NoSuccess") : N_("Success");
4886 opts[dsn_label+2].ch = 'x';
4887 opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
4888 ? N_("ErrRets") : N_("NoErrRets");
4889 opts[dsn_label+3].ch = 'h';
4890 opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
4891 ? N_("RetHdrs") : N_("RetFull");
4895 if(double_rad +
4896 ((call_mailer_flags & CM_DSN_SHOW)
4897 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
4898 rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4899 'y', 'z',
4900 (F_ON(F_DSN, ps_global) && allow_flowed)
4901 ? h_send_prompt_dsn_flowed :
4902 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4903 allow_flowed ? h_send_prompt_flowed :
4904 h_send_prompt,
4905 RB_NORM);
4906 else
4907 rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4908 'y', 'z',
4909 (double_rad +
4910 ((call_mailer_flags & CM_DSN_SHOW)
4911 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
4912 ? NO_HELP :
4913 (F_ON(F_DSN, ps_global) && allow_flowed)
4914 ? h_send_prompt_dsn_flowed :
4915 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4916 allow_flowed ? h_send_prompt_flowed :
4917 h_send_prompt,
4918 RB_NORM);
4920 if(rv == 'y'){ /* user ACCEPTS! */
4921 break;
4923 else if(rv == 'n'){ /* Declined! */
4924 rstr = _("No Message Sent");
4925 break;
4927 else if(rv == 'z'){ /* Cancelled! */
4928 rstr = _("Send Cancelled");
4929 break;
4931 else if(rv == 10){ /* PREVIOUS filter */
4932 filters = filters->prev;
4934 else if(rv == 11){ /* NEXT filter */
4935 filters = filters->next;
4937 else if(rv == 21){
4938 lmc.text_only = !lmc.text_only;
4940 else if(rv == 12){ /* flip verbose bit */
4941 if(call_mailer_flags & CM_VERBOSE)
4942 call_mailer_flags &= ~CM_VERBOSE;
4943 else
4944 call_mailer_flags |= CM_VERBOSE;
4946 if((call_mailer_flags & CM_VERBOSE) && background_requested)
4947 background_requested = 0;
4949 else if(rv == 22){ /* flip flowing bit */
4950 flowing_requested = !flowing_requested;
4952 else if(rv == 15){
4953 if((background_requested = !background_requested)
4954 && (call_mailer_flags & CM_VERBOSE))
4955 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
4957 else if(call_mailer_flags & CM_DSN_SHOW){
4958 if(rv == 's'){ /* flip success bit */
4959 call_mailer_flags ^= CM_DSN_SUCCESS;
4960 /* turn off related bits */
4961 if(call_mailer_flags & CM_DSN_SUCCESS)
4962 call_mailer_flags &= ~(CM_DSN_NEVER);
4964 else if(rv == 'd'){ /* flip delay bit */
4965 call_mailer_flags ^= CM_DSN_DELAY;
4966 /* turn off related bits */
4967 if(call_mailer_flags & CM_DSN_DELAY)
4968 call_mailer_flags &= ~(CM_DSN_NEVER);
4970 else if(rv == 'x'){ /* flip never bit */
4971 call_mailer_flags ^= CM_DSN_NEVER;
4972 /* turn off related bits */
4973 if(call_mailer_flags & CM_DSN_NEVER)
4974 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
4976 else if(rv == 'h'){ /* flip full bit */
4977 call_mailer_flags ^= CM_DSN_FULL;
4980 else if(rv == 'd'){ /* show dsn options */
4982 * When you turn on DSN, the default is to notify on
4983 * failure, success, or delay; and to return the whole
4984 * body on failure.
4986 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
4988 else if(rv == 'p'){ /* choose X-Priority */
4989 char *prio;
4991 prio = choose_a_priority(priority_requested);
4992 if((ps_global->redrawer = redraw_pico) != NULL){
4993 (*ps_global->redrawer)();
4994 fix_windsize(ps_global);
4997 if(prio){
4998 if(priority_requested)
4999 fs_give((void **) &priority_requested);
5001 if(prio[0])
5002 priority_requested = prio;
5003 else
5004 fs_give((void **) &prio);
5007 #ifdef SMIME
5008 else if(rv=='e'){
5009 if(ps_global->smime)
5010 ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
5012 else if(rv=='g'){
5013 if(ps_global->smime)
5014 ps_global->smime->do_sign = !ps_global->smime->do_sign;
5016 #endif
5018 snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
5019 (call_mailer_flags & CM_DSN_NEVER)
5020 ? "Never" : "F",
5021 (call_mailer_flags & CM_DSN_DELAY)
5022 ? "D" : "",
5023 (call_mailer_flags & CM_DSN_SUCCESS)
5024 ? "S" : "",
5025 (call_mailer_flags & CM_DSN_NEVER)
5026 ? ""
5027 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
5028 : "-Hdrs");
5029 dsn_string[sizeof(dsn_string)-1] = '\0';
5032 /* remember selection */
5033 if(filters && filters->index > -1)
5034 sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
5036 if(filters){
5037 filters->prev->next = NULL; /* tie off list */
5038 while(filters){ /* then free it */
5039 fp = filters->next;
5040 if(filters->filter)
5041 fs_give((void **)&filters->filter);
5043 fs_give((void **)&filters);
5044 filters = fp;
5048 if(old_suspend)
5049 (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
5051 if(result)
5052 *result = rstr;
5054 ps_global->redrawer = redraw;
5056 return((rstr == NULL) ? 0 : 1);
5061 * Allow user to choose a priority for sending.
5063 * Returns an allocated priority on success, NULL otherwise.
5065 char *
5066 choose_a_priority(char *default_val)
5068 char *choice = NULL;
5069 char **priority_list, **lp;
5070 char *starting_val = NULL;
5071 char *none;
5072 int i, cnt;
5073 PRIORITY_S *p;
5075 for(cnt = 0, p = priorities; p && p->desc; p++)
5076 cnt++;
5078 cnt++; /* for NONE entry */
5079 lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
5080 memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
5082 for(i = 0, p = priorities; p && p->desc; p++){
5083 *lp = cpystr(p->desc);
5084 if(default_val && !strcmp(default_val, p->desc))
5085 starting_val = (*lp);
5087 lp++;
5090 none = _("NONE - No X-Priority header included");
5091 *lp = cpystr(none);
5092 if(!starting_val)
5093 starting_val = (*lp);
5095 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5096 TRANSLATORS: Print something1 using something2.
5097 "priorities" is something1 */
5098 choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
5099 _("priorities"), h_select_priority_screen,
5100 _("HELP FOR SELECTING A PRIORITY"),
5101 starting_val);
5103 if(!choice)
5104 q_status_message(SM_ORDER, 1, 4, _("No change"));
5105 else if(!strcmp(choice, none))
5106 choice[0] = '\0';
5108 free_list_array(&priority_list);
5110 return(choice);
5115 dont_flow_this_time(void)
5117 return(flowing_requested ? 0 : 1);
5121 /*----------------------------------------------------------------------
5122 Call back for pico to display mime type of attachment
5124 Args: file -- filename being attached
5126 Returns: returns 1 on success (message queued), zero otherwise (don't know
5127 type so nothing queued).
5128 ----*/
5130 mime_type_for_pico(char *file)
5132 BODY *body;
5133 int rv;
5134 void *file_contents;
5136 body = mail_newbody();
5137 body->type = TYPEOTHER;
5138 body->encoding = ENCOTHER;
5140 /* don't know where the cursor's been, reset it */
5141 clear_cursor_pos();
5142 if(!set_mime_type_by_extension(body, file)){
5143 if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5144 body->contents.text.data = file_contents;
5145 set_mime_type_by_grope(body);
5149 if(body->type != TYPEOTHER){
5150 rv = 1;
5151 q_status_message3(SM_ORDER, 0, 3,
5152 _("File %s attached as type %s/%s"), file,
5153 body_types[body->type],
5154 body->subtype ? body->subtype : rfc822_default_subtype(body->type));
5156 else
5157 rv = 0;
5159 pine_free_body(&body);
5160 return(rv);
5164 /*----------------------------------------------------------------------
5165 Call back for pico to receive an uploaded message
5167 Args: fname -- name for uploaded file (empty if they want us to assign it)
5168 size -- pointer to long to hold the attachment's size
5170 Notes: the attachment is uploaded to a temp file, and
5172 Returns: TRUE on success, FALSE otherwise
5173 ----*/
5175 upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
5177 char cmd[MAXPATH+1], *fnp = NULL;
5178 char *locale_name = NULL;
5179 long l;
5180 PIPE_S *syspipe;
5182 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5183 fname ? fname : "<NO FILE>"));
5185 if(!fname) /* no place for file name */
5186 return(0);
5188 if(!*fname){ /* caller wants temp file */
5189 if((fnp = temp_nam(NULL, "pu")) != NULL){
5190 strncpy(fname, fnp, fnlen);
5191 fname[fnlen-1] = '\0';
5192 our_unlink(fnp);
5193 fs_give((void **)&fnp);
5196 else
5197 locale_name = convert_to_locale(fname);
5199 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
5200 ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
5201 if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
5202 0, pipe_callback, pipe_report_error)) != NULL){
5203 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
5204 if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
5205 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5206 "Error determining size of %s: %s", fname,
5207 fnp = error_description(errno));
5208 dprint((1,
5209 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5210 cmd ? cmd : "?",
5211 fname ? fname : "?",
5212 fnp ? fnp : "?"));
5214 else if(size)
5215 *size = l;
5217 if(locale_name)
5218 fs_give((void **) &locale_name);
5220 return(l >= 0);
5222 else
5223 q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
5225 if(locale_name)
5226 fs_give((void **) &locale_name);
5228 return(0);
5232 char *
5233 cancel_for_pico(void (*redraw_pico)(void))
5235 int rv;
5236 char *rstr = NULL;
5237 char *prompt =
5238 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5239 void (*redraw)(void) = ps_global->redrawer;
5240 static ESCKEY_S opts[] = {
5241 {'c', 'c', "C", N_("Confirm")},
5242 {'n', 'n', "N", N_("No")},
5243 {'y', 'y', "", ""},
5244 {-1, 0, NULL, NULL}
5247 ps_global->redrawer = redraw_pico;
5248 fix_windsize(ps_global);
5250 while(1){
5251 rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
5252 'n', 'x', h_confirm_cancel, RB_NORM);
5253 if(rv == 'c'){ /* user ACCEPTS! */
5254 rstr = "";
5255 break;
5257 else if(rv == 'y'){
5258 q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
5259 display_message('x');
5261 else
5262 break;
5265 ps_global->redrawer = redraw;
5266 return(rstr);
5270 /*----------------------------------------------------------------------
5271 Pass the first text segment of the message thru the "send filter"
5273 Args: body pointer and address for storage object of old data
5275 Returns: returns 1 on success, zero on error.
5276 ----*/
5278 filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
5279 STORE_S **old, METAENV *header)
5281 char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
5282 int key = 0, include_hdrs = 0;
5283 gf_io_t gc, pc;
5284 STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
5285 ? &body->nested.part->body.contents.text.data
5286 : &body->contents.text.data),
5287 *tmp_so = NULL, *tmpf_so,
5288 *save_local_so, *readthis_so, *our_tmpf_so = NULL;
5289 #define DO_HEADERS 1
5291 if(fcmd
5292 && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
5293 &key, &include_hdrs, NULL))){
5294 if(tmpf){
5296 * We need WRITE_TO_LOCALE here because the user is going to
5297 * be operating on tmpf. We need to change it back after they
5298 * are done.
5300 if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
5301 if(key){
5302 so_puts(tmpf_so, filter_session_key());
5303 so_puts(tmpf_so, NEWLINE);
5307 * If the headers are wanted for filtering, we can just
5308 * stick them in the tmpf file that is already there before
5309 * putting the body in.
5311 if(include_hdrs){
5312 save_local_so = lmc.so;
5313 lmc.so = tmpf_so; /* write it to tmpf_so */
5314 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5315 pine_rfc822_header(header, body, NULL, NULL);
5316 lmc.so = save_local_so;
5319 so_seek(*so, 0L, 0);
5320 gf_set_so_readc(&gc, *so);
5321 gf_set_so_writec(&pc, tmpf_so);
5322 gf_filter_init();
5323 errstr = gf_pipe(gc, pc);
5324 gf_clear_so_readc(*so);
5325 gf_clear_so_writec(tmpf_so);
5326 so_give(&tmpf_so);
5328 else
5329 errstr = "Can't create space for filter temporary file.";
5331 else if(include_hdrs){
5333 * Gf_filter wants a single storage object to read from.
5334 * If headers are wanted for filtering we'll have to put them
5335 * and the body into a temp file first and then use that
5336 * as the storage object for gf_filter.
5337 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5338 * takes care of that.
5340 if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
5341 /* put headers in our_tmpf_so */
5342 save_local_so = lmc.so;
5343 lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
5344 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5345 pine_rfc822_header(header, body, NULL, NULL);
5346 lmc.so = save_local_so;
5348 /* put body in our_tmpf_so */
5349 so_seek(*so, 0L, 0);
5350 gf_set_so_readc(&gc, *so);
5351 gf_set_so_writec(&pc, our_tmpf_so);
5352 gf_filter_init();
5353 errstr = gf_pipe(gc, pc);
5354 gf_clear_so_readc(*so);
5355 gf_clear_so_writec(our_tmpf_so);
5357 /* tell gf_filter to read from our_tmpf_so instead of *so */
5358 readthis_so = our_tmpf_so;
5360 else
5361 errstr = "Can't create space for temporary file.";
5363 else
5364 readthis_so = *so;
5366 if(!errstr){
5367 if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
5368 gf_set_so_writec(&pc, tmp_so);
5369 ps_global->mangled_screen = 1;
5370 suspend_busy_cue();
5371 ClearScreen();
5372 fflush(stdout);
5373 if(tmpf){
5374 PIPE_S *fpipe;
5376 if((fpipe = open_system_pipe(cmd, NULL, NULL,
5377 PIPE_NOSHELL | PIPE_RESET,
5378 0, pipe_callback, pipe_report_error)) != NULL){
5379 if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
5381 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5382 if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5383 gf_set_so_readc(&gc, tmpf_so);
5384 gf_filter_init();
5385 errstr = gf_pipe(gc, pc);
5386 gf_clear_so_readc(tmpf_so);
5387 so_give(&tmpf_so);
5389 else
5390 errstr = "Can't open temp file filter wrote.";
5392 else
5393 errstr = "Filter command returned error.";
5395 else
5396 errstr = "Can't exec filter text.";
5398 else
5399 errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
5400 readthis_so, pc, NULL, 0, 0,
5401 pipe_callback);
5403 if(our_tmpf_so)
5404 so_give(&our_tmpf_so);
5406 gf_clear_so_writec(tmp_so);
5408 #ifdef _WINDOWS
5409 if(!errstr){
5411 * Can't really be using stdout, so don't print message that
5412 * gets printed in the else. Ideally the program being called
5413 * will wait after showing the message, we might want to look
5414 * into doing the waiting in console based apps... or not.
5416 #else
5417 if(errstr){
5418 unsigned long ch;
5420 fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
5421 fflush(stdout);
5422 while((ch = read_char(300)) != ctrl('M')
5423 && ch != NO_OP_IDLE)
5424 putchar(BELL);
5426 else{
5427 #endif /* _WINDOWS */
5428 BODY *b = (body->type == TYPEMULTIPART)
5429 ? &body->nested.part->body : body;
5431 *old = *so; /* save old so */
5432 *so = tmp_so; /* return new one */
5433 (*so)->attr = copy_parameters((*old)->attr);
5436 * If the command said it would return new MIME
5437 * mime type data, check it out...
5439 if(mtf){
5440 char buf[MAILTMPLEN], *s;
5441 FILE *fp;
5443 if((fp = our_fopen(mtf, "rb")) != NULL){
5444 if(fgets(buf, sizeof(buf), fp)
5445 && !struncmp(buf, "content-", 8)
5446 && (s = strchr(buf+8, ':'))){
5447 BODY *nb = mail_newbody();
5449 for(*s++ = '\0'; *s == ' '; s++)
5452 rfc822_parse_content_header(nb,
5453 (char *) ucase((unsigned char *) buf+8),s);
5454 if(nb->type == TYPETEXT
5455 && nb->subtype
5456 && (!b->subtype
5457 || strucmp(b->subtype, nb->subtype))){
5458 if(b->subtype)
5459 fs_give((void **) &b->subtype);
5461 b->subtype = nb->subtype;
5462 nb->subtype = NULL;
5464 mail_free_body_parameter(&b->parameter);
5465 b->parameter = nb->parameter;
5466 nb->parameter = NULL;
5467 mail_free_body_parameter(&nb->parameter);
5470 mail_free_body(&nb);
5473 fclose(fp);
5478 * Reevaluate the encoding in case form's changed...
5480 b->encoding = ENCOTHER;
5481 set_mime_type_by_grope(b);
5484 ClearScreen();
5485 resume_busy_cue(0);
5487 else
5488 errstr = "Can't create space for filtered text.";
5491 fs_give((void **)&cmd);
5493 else
5494 return(0);
5496 if(tmpf){
5497 our_unlink(tmpf);
5498 fs_give((void **)&tmpf);
5501 if(mtf){
5502 our_unlink(mtf);
5503 fs_give((void **) &mtf);
5506 if(resultf){
5507 if(name_file_size(resultf) > 0L)
5508 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
5509 our_unlink(resultf);
5510 fs_give((void **)&resultf);
5512 else if(errstr){
5513 if(tmp_so)
5514 so_give(&tmp_so);
5516 q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
5517 errstr);
5518 dprint((1, "Filter FAILED: %s\n",
5519 errstr ? errstr : "?"));
5522 return(errstr == NULL);
5526 /*----------------------------------------------------------------------
5527 Copy the newsgroup name of the given mailbox into the given buffer
5529 Args:
5531 Returns:
5532 ----*/
5533 void
5534 pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
5536 NETMBX mb;
5538 if(*mailbox == '#'){ /* Strip the leading "#news." */
5539 strncpy(group_name, mailbox + 6, len-1);
5540 group_name[len-1] = '\0';
5542 else if(mail_valid_net_parse(mailbox, &mb)){
5543 pine_send_newsgroup_name(mb.mailbox, group_name, len);
5545 else
5546 *group_name = '\0';
5550 /*----------------------------------------------------------------------
5551 Set up fields for passing to pico. Assumes first text part is
5552 intended to be passed along for editing, and is in the form of
5553 of a storage object brought into existence sometime before pico_send().
5554 -----*/
5555 void
5556 outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
5557 PATMT **pico_a, int from_bounce)
5559 PINEFIELD *pf;
5562 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5563 * is guaranteed to be of type PicoText!
5565 if(bod->type == TYPETEXT){
5566 *text = so_text((STORE_S *) bod->contents.text.data);
5568 /* mark storage object as user edited */
5569 if(!from_bounce)
5570 (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
5572 else if(bod->type == TYPEMULTIPART){
5573 PART *part;
5574 PATMT **ppa;
5575 char *type, *name, *p;
5576 int name_l;
5579 * We used to jump out the window if the first part wasn't text,
5580 * but that may not be the case when bouncing a message with
5581 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5582 * contents of the first part to send is still ALWAYS in a
5583 * PicoText storage object, *AND* if that object doesn't contain
5584 * data of type text, then it must contain THE ENCODED NON-TEXT
5585 * DATA of the piece being sent.
5587 * It's up to the programmer to make sure that such a message is
5588 * sent via pine_simple_send and never get to the composer via
5589 * pine_send.
5591 * Make sense?
5593 *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
5595 /* mark storage object as user edited */
5596 if(!from_bounce)
5597 (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
5600 * If we already had a list, blast it now, so we can build a new
5601 * attachment list that reflects what's really there...
5603 if(pico_a){
5604 free_attachment_list(pico_a);
5607 /* Simplifyihg assumption #28e. (see cross reference)
5608 All parts in the body passed in here that are not already
5609 in the attachments list are added to the end of the attachments
5610 list. Attachment items not in the body list will be taken care
5611 of in strings2outgoing, but they are unlikely to occur
5614 for(part = bod->nested.part->next; part != NULL; part = part->next) {
5615 /* Already in list? */
5616 for(ppa = pico_a;
5617 *ppa && strcmp((*ppa)->id, part->body.id);
5618 ppa = &(*ppa)->next)
5621 if(!*ppa){ /* Not in the list! append it... */
5622 *ppa = (PATMT *)fs_get(sizeof(PATMT));
5624 if(part->body.description){
5625 char *p;
5626 size_t len;
5628 len = 4*strlen(part->body.description)+1;
5629 p = (char *)fs_get(len*sizeof(char));
5630 if(rfc1522_decode_to_utf8((unsigned char *)p,
5631 len, part->body.description) == (unsigned char *) p){
5632 (*ppa)->description = p;
5634 else{
5635 fs_give((void **)&p);
5636 (*ppa)->description = cpystr(part->body.description);
5639 else
5640 (*ppa)->description = cpystr("");
5642 type = type_desc(part->body.type, part->body.subtype,
5643 part->body.parameter, NULL, 0);
5646 * If we can find a "name" parm, display that too...
5648 if((name = parameter_val(part->body.parameter, "name")) != NULL){
5649 /* Convert any [ or ]'s the name contained */
5650 for(p = name; *p ; p++)
5651 if(*p == '[')
5652 *p = '(';
5653 else if(*p == ']')
5654 *p = ')';
5656 name_l = p - name;
5658 else
5659 name_l = 0;
5661 (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
5663 snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
5664 name ? ": " : "", name ? name : "");
5665 (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
5667 if(name)
5668 fs_give((void **) &name);
5670 (*ppa)->flags = A_FLIT;
5671 (*ppa)->size = cpystr(byte_string(
5672 send_body_size(&part->body)));
5673 if(!part->body.id)
5674 part->body.id = generate_message_id(NULL);
5676 (*ppa)->id = cpystr(part->body.id);
5677 (*ppa)->next = NULL;
5684 /*------------------------------------------------------------------
5685 Malloc strings to pass to composer editor because it expects
5686 such strings so it can realloc them
5687 -----------------------------------------------------------------*/
5689 * turn any address fields into text strings
5692 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5693 * NOT to be RFC1522 decoded. Said differently, they're understood
5694 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5695 * original charset tagging as far into the compose/send pipe as
5696 * we can.
5698 for(pf = header->local; pf && pf->name; pf = pf->next)
5699 if(pf->canedit)
5700 switch(pf->type){
5701 case Address :
5702 if(pf->addr){
5703 char *p, *t, *u;
5704 long l;
5706 pf->scratch = addr_list_string(*pf->addr, NULL, 1);
5709 * Scan for and fix-up patently bogus fields.
5711 * NOTE: collaboration with this code and what's done in
5712 * reply.c:reply_cp_addr to package up the bogus stuff
5713 * is required.
5715 for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
5716 for(t = p; ; t--)
5717 if(*t == '&'){ /* find "leading" token */
5718 int replacelen;
5721 * Rfc822_cat has been changed so that it now quotes
5722 * this sometimes. So we have to look out for quotes
5723 * which confuse the decoder. It was only quoting
5724 * because we were putting \r \n in the input, I think.
5726 if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
5727 t[-1] = p[-1] = ' ';
5729 *t++ = ' '; /* replace token */
5730 *p = '\0'; /* tie off string */
5731 u = rfc822_base64((unsigned char *) t,
5732 (unsigned long) strlen(t),
5733 (unsigned long *) &l);
5734 if(!u)
5735 u = "";
5737 replacelen = strlen(t);
5738 *p = '@'; /* restore 'p' */
5739 rplstr(p, strlen(p), 12, ""); /* clear special token */
5740 rplstr(t, strlen(u)-replacelen+1, replacelen, u);
5741 if(u)
5742 fs_give((void **) &u);
5744 if(HE(pf))
5745 HE(pf)->start_here = 1;
5747 break;
5749 else if(t == pf->scratch)
5750 break;
5752 removing_leading_white_space(pf->scratch);
5753 if(pf->scratch){
5754 size_t l;
5757 * Replace control characters with ^C notation, unless
5758 * some conditions are met (see istrncpy).
5759 * If user doesn't edit, then we switch back to the
5760 * original version. If user does edit, then all bets
5761 * are off.
5763 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5764 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5765 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5766 fs_give((void **)&pf->scratch);
5767 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5770 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5774 break;
5776 case Subject :
5777 if(pf->text){
5778 char *p, *src;
5779 size_t len;
5781 src = pf->scratch ? pf->scratch
5782 : (*pf->text) ? *pf->text : "";
5784 len = 4*strlen(src)+1;
5785 p = (char *)fs_get(len * sizeof(char));
5786 if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
5787 if(pf->scratch)
5788 fs_give((void **)&pf->scratch);
5790 pf->scratch = p;
5792 else{
5793 fs_give((void **)&p);
5794 if(!pf->scratch)
5795 pf->scratch = cpystr(src);
5798 if(pf->scratch){
5799 size_t l;
5802 * Replace control characters with ^C notation, unless
5803 * some conditions are met (see istrncpy).
5804 * If user doesn't edit, then we switch back to the
5805 * original version. If user does edit, then all bets
5806 * are off.
5808 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5809 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5810 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5811 fs_give((void **)&pf->scratch);
5812 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5815 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5819 break;
5821 default :
5822 break;
5827 /*----------------------------------------------------------------------
5828 Restore fields returned from pico to form useful to sending
5829 routines.
5830 -----*/
5831 void
5832 strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
5834 PINEFIELD *pf;
5835 int we_cancel = 0;
5837 we_cancel = busy_cue(NULL, NULL, 1);
5840 * turn any local address strings into address lists
5842 for(pf = header->local; pf && pf->name; pf = pf->next)
5843 if(pf->scratch){
5844 char *the_address = NULL;
5846 switch(pf->type){
5847 case Address :
5848 removing_trailing_white_space(pf->scratch);
5850 if((the_address || *pf->scratch) && pf->addr){
5851 ADDRESS *new_addr = NULL;
5852 static char *fakedomain = "@";
5854 if(!the_address)
5855 the_address = pf->scratch;
5857 rfc822_parse_adrlist(&new_addr, the_address,
5858 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
5859 ? fakedomain : ps_global->maildomain);
5860 mail_free_address(pf->addr); /* free old addrs */
5861 *pf->addr = new_addr; /* assign new addr */
5863 else if(pf->addr)
5864 mail_free_address(pf->addr); /* free old addrs */
5866 break;
5868 case Subject :
5869 if(*pf->text)
5870 fs_give((void **)pf->text);
5872 if(*pf->scratch){
5873 *pf->text = cpystr(pf->scratch);
5876 break;
5878 default :
5879 break;
5882 fs_give((void **)&pf->scratch); /* free now useless text */
5885 create_message_body(bod, attach, flow_it);
5887 if(we_cancel)
5888 cancel_busy_cue(-1);
5892 /*----------------------------------------------------------------------
5894 The head of the body list here is always either TEXT or MULTIPART. It may be
5895 changed from TEXT to MULTIPART if there are attachments to be added
5896 and it is not already multipart.
5897 ----*/
5898 void
5899 create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
5901 PART *p, **pp;
5902 PATMT *pa;
5903 BODY *tmp_body, *text_body = NULL;
5904 void *file_contents;
5905 PARAMETER **parmp;
5906 char *lc;
5908 TIME_STAMP("create_body start.", 1);
5910 * if conditions are met short circuit MIME wrapping
5912 if((*b)->type != TYPEMULTIPART && !attach){
5914 /* only override assigned encoding if it might need upgrading */
5915 if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
5916 (*b)->encoding = ENCOTHER;
5918 create_message_body_text(*b, flow_it);
5920 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
5921 || !((*b)->encoding == ENC8BIT
5922 || (*b)->encoding == ENCBINARY)){
5923 TIME_STAMP("create_body end.", 1);
5924 return;
5926 else /* protect 8bit in multipart */
5927 text_body = *b;
5930 if((*b)->type == TYPETEXT) {
5931 /*-- Current type is text, but there are attachments to add --*/
5932 /*-- Upgrade to a TYPEMULTIPART --*/
5933 tmp_body = (BODY *)mail_newbody();
5934 tmp_body->type = TYPEMULTIPART;
5935 tmp_body->nested.part = mail_newbody_part();
5936 if(text_body){
5938 * Why do we do this?
5939 * The problem is that base64 or quoted-printable encoding is
5940 * sensitive to having random data appended to it's end. If
5941 * we use a single part TEXT message and something in between
5942 * us and the end appends advertising without adjusting for
5943 * the encoding, the message is screwed up. So we wrap the
5944 * text part inside a multipart and then the appended data
5945 * will come after the boundary.
5947 * We wish we could do this on the way out the door in a
5948 * child of post_rfc822_output because at that point we know
5949 * the character set and the encoding being used. For example,
5950 * iso-2022-jp is an encoding that is not sensitive to data
5951 * appended to the end, so it wouldn't need to be wrapped.
5952 * We could conceivably have post_rfc822_body inspect the
5953 * body and change it before doing the output. It would work
5954 * but would be very fragile. We'd be passed a body from
5955 * c-client to output and instead of just doing the output
5956 * we'd change the body and then output it. Not worth it
5957 * since the multipart wrapping is completely correct for
5958 * MIME-aware mailers.
5960 (void) copy_body(&(tmp_body->nested.part->body), *b);
5961 /* move contents which were NOT copied */
5962 tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
5963 (*b)->contents.text.data = NULL;
5965 else{
5966 tmp_body->nested.part->body = **b;
5968 (*b)->subtype = (*b)->id = (*b)->description = NULL;
5969 (*b)->parameter = NULL;
5970 (*b)->contents.text.data = NULL;
5973 pine_free_body(b);
5974 *b = tmp_body;
5977 if(!text_body){
5978 /*-- Now type must be MULTIPART with first part text --*/
5979 (*b)->nested.part->body.encoding = ENCOTHER;
5980 create_message_body_text(&((*b)->nested.part->body), flow_it);
5983 /*------ Go through the parts list remove those to be deleted -----*/
5984 for(pp = &(*b)->nested.part->next; *pp;){
5985 for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
5986 /* already existed? */
5987 if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
5988 char *orig_descp = NULL, *cs = NULL;
5991 * decode original to see if it matches what was decoded
5992 * when we sent it in.
5995 if((*pp)->body.description)
5996 orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
5997 SIZEOF_20KBUF, (*pp)->body.description);
5999 if(!(*pp)->body.description /* update description? */
6000 || (pa->description && strcmp(pa->description, orig_descp))){
6001 if((*pp)->body.description)
6002 fs_give((void **) &(*pp)->body.description);
6004 /* encoding happens as msg text is written */
6005 (*pp)->body.description = cpystr(pa->description);
6008 if(cs)
6009 fs_give((void **) &cs);
6011 break;
6014 if(pa == NULL){
6015 p = *pp; /* prepare to zap *pp */
6016 *pp = p->next; /* pull next one in list up */
6017 p->next = NULL; /* tie off removed node */
6019 pine_free_body_data(&p->body); /* clean up contained data */
6020 mail_free_body_part(&p); /* free up the part */
6022 else
6023 pp = &(*pp)->next;
6026 /*---------- Now add any new attachments ---------*/
6027 for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
6028 for(pa = attach; pa != NULL; pa = pa->next) {
6029 if(pa->id != NULL)
6030 continue; /* Has an ID, it's old */
6033 * the idea is handle ALL attachments as open FILE *'s. Actual
6034 * encoding and such is handled at the time the message
6035 * is shoved into the mail slot or written to disk...
6037 * Also, we never unlink a file, so it's up to whoever opens
6038 * it to deal with tmpfile issues.
6040 if((file_contents = (void *)so_get(FileStar, pa->filename,
6041 READ_ACCESS)) == NULL){
6042 q_status_message2(SM_ORDER | SM_DING, 3, 4,
6043 _("Error \"%s\", couldn't attach file \"%s\""),
6044 error_description(errno), pa->filename);
6045 display_message('x');
6046 continue;
6049 p->next = mail_newbody_part();
6050 p = p->next;
6051 p->body.id = generate_message_id(NULL);
6052 p->body.contents.text.data = file_contents;
6055 * Set type to unknown and let set_mime_type_by_* figure it out.
6056 * Always encode attachments we add as BINARY.
6058 p->body.type = TYPEOTHER;
6059 p->body.encoding = ENCBINARY;
6060 p->body.size.bytes = name_file_size(pa->filename);
6061 if(!set_mime_type_by_extension(&p->body, pa->filename)){
6062 set_mime_type_by_grope(&p->body);
6063 set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
6066 so_release((STORE_S *)p->body.contents.text.data);
6068 if(pa->description) /* encoding happens when msg written */
6069 p->body.description = cpystr(pa->description);
6071 /* Add name attribute for backward compatibility */
6072 for(parmp = &p->body.parameter; *parmp; )
6073 if(!struncmp((*parmp)->attribute, "name", 4)
6074 && (!*((*parmp)->attribute + 4)
6075 || *((*parmp)->attribute + 4) == '*')){
6076 PARAMETER *free_me = *parmp;
6077 *parmp = (*parmp)->next;
6078 free_me->next = NULL;
6079 mail_free_body_parameter(&free_me);
6081 else
6082 parmp = &(*parmp)->next;
6084 set_parameter(parmp, "name",
6085 pa->filename
6086 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6087 : NULL);
6089 /* Then set the Content-Disposition ala RFC1806 */
6090 if(!p->body.disposition.type){
6091 p->body.disposition.type = cpystr("attachment");
6092 for(parmp = &p->body.disposition.parameter; *parmp; )
6093 if(!struncmp((*parmp)->attribute, "filename", 4)
6094 && (!*((*parmp)->attribute + 4)
6095 || *((*parmp)->attribute + 4) == '*')){
6096 PARAMETER *free_me = *parmp;
6097 *parmp = (*parmp)->next;
6098 free_me->next = NULL;
6099 mail_free_body_parameter(&free_me);
6101 else
6102 parmp = &(*parmp)->next;
6104 set_parameter(parmp, "filename",
6105 pa->filename
6106 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6107 : NULL);
6110 p->next = NULL;
6111 pa->id = cpystr(p->body.id);
6115 * Now, if this multipart has but one text piece (that is, no
6116 * attachments), then downgrade from a composite type to a discrete
6117 * text/plain message if CTE is not 8bit.
6119 if(!(*b)->nested.part->next
6120 && (*b)->nested.part->body.type == TYPETEXT
6121 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
6122 || !((*b)->nested.part->body.encoding == ENC8BIT
6123 || (*b)->nested.part->body.encoding == ENCBINARY))){
6124 /* Clone the interesting body part */
6125 tmp_body = mail_newbody();
6126 *tmp_body = (*b)->nested.part->body;
6127 /* and rub out what we don't want cleaned up when it's free'd */
6128 mail_initbody(&(*b)->nested.part->body);
6129 mail_free_body(b);
6130 *b = tmp_body;
6134 TIME_STAMP("create_body end.", 1);
6139 * Fill in text BODY part's structure
6142 void
6143 create_message_body_text(struct mail_bodystruct *b, int flow_it)
6145 set_mime_type_by_grope(b);
6146 if(b != NULL){
6147 remove_parameter(&b->parameter, "format"); /* we will set it up below */
6148 remove_parameter(&b->parameter, "delsp"); /* we never set this up */
6150 if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
6151 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
6152 && flow_it)
6153 set_parameter(b ? &b->parameter : NULL, "format", "flowed");
6155 set_body_size(b);
6160 * free_attachment_list - free attachments in given list
6162 void
6163 free_attachment_list(PATMT **alist)
6165 PATMT *leading;
6167 while(alist && *alist){ /* pointer pointing to something */
6168 leading = (*alist)->next;
6169 if((*alist)->description)
6170 fs_give((void **)&(*alist)->description);
6172 if((*alist)->filename){
6173 if((*alist)->flags & A_TMP)
6174 if(our_unlink((*alist)->filename) < 0)
6175 dprint((1, "-- Can't unlink(%s): %s\n",
6176 (*alist)->filename ? (*alist)->filename : "?",
6177 error_description(errno)));
6179 fs_give((void **)&(*alist)->filename);
6182 if((*alist)->size)
6183 fs_give((void **)&(*alist)->size);
6185 if((*alist)->id)
6186 fs_give((void **)&(*alist)->id);
6188 fs_give((void **)alist);
6190 *alist = leading;
6195 void
6196 set_body_size(struct mail_bodystruct *b)
6198 unsigned char c;
6199 int we_cancel = 0;
6201 we_cancel = busy_cue(NULL, NULL, 1);
6202 so_seek((STORE_S *)b->contents.text.data, 0L, 0);
6203 b->size.bytes = 0L;
6204 while(so_readc(&c, (STORE_S *)b->contents.text.data))
6205 b->size.bytes++;
6207 if(we_cancel)
6208 cancel_busy_cue(-1);
6213 * view_as_rich - set the rich_header flag
6215 * name - name of the header field
6216 * deflt - default value to return if user didn't set it
6218 * Note: if the user tries to turn them all off with "", then
6219 * we take that to mean default, since otherwise there is no
6220 * way to get to the headers.
6223 view_as_rich(char *name, int deflt)
6225 char **p;
6226 char *q;
6228 p = ps_global->VAR_COMP_HDRS;
6230 if(p && *p && **p){
6231 for(; (q = *p) != NULL; p++){
6232 if(!struncmp(q, name, strlen(name)))
6233 return 0; /* 0 means we *do* view it by default */
6236 return 1; /* 1 means it starts out hidden */
6238 return(deflt);
6243 * background_posting - return whether or not we're already in the process
6244 * of posting
6247 background_posting(int gripe)
6249 if(ps_global->post){
6250 if(gripe)
6251 q_status_message(SM_ORDER|SM_DING, 3, 3,
6252 _("Can't post while posting!"));
6253 return(1);
6256 return(0);
6260 /*----------------------------------------------------------------------
6261 Validate the given subject relative to any news groups.
6263 Args: none
6265 Returns: always returns 1, but also returns error if
6266 ----*/
6268 valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
6270 struct headerentry *hp;
6272 if(expanded)
6273 *expanded = cpystr(given);
6275 if(error){
6277 * Now look for any header entry we passed to pico that has to do
6278 * with news. If there's no subject, gripe.
6280 for(hp = pbf->headents; hp->prompt; hp++)
6281 if(hp->help == h_composer_news){
6282 if(hp->hd_text->text[0] && !*given)
6283 *error = cpystr(
6284 _("News postings MUST have a subject! Please add one!"));
6286 break;
6290 return(0);
6295 * This is the build_address used by the composer to check for an address
6296 * in the addrbook.
6298 * Args: to -- the passed in line to parse
6299 * full_to -- Address of a pointer to return the full address in.
6300 * This will be allocated here and freed by the caller.
6301 * error -- Address of a pointer to return an error message in.
6302 * This will be allocated here and freed by the caller.
6303 * barg -- Address of a pointer to return the fcc in is in
6304 * fcc->tptr. It will have already been allocated by the
6305 * caller but we may free it and reallocate if we wish.
6306 * Caller will free it.
6308 * Result: 0 is returned if address was OK,
6309 * -1 if address wasn't OK.
6311 * Side effect: Can flush addrbook entry cache entries so they need to be
6312 * re-fetched afterwords.
6315 build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
6317 char *p;
6318 int ret_val, no_repo = 0, *save_nesting_level;
6319 BuildTo bldto;
6320 PrivateTop *pt = NULL;
6321 PrivateAffector *af = NULL;
6322 char *fcc_local = NULL;
6323 jmp_buf save_jmp_buf;
6325 dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
6327 /* check to see if to string is empty to avoid work */
6328 for(p = to; p && *p && isspace((unsigned char)*p); p++)
6329 ;/* do nothing */
6331 if(!p || !*p){
6332 if(full_to)
6333 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
6335 return 0;
6338 if(full_to != NULL)
6339 *full_to = (char *)NULL;
6341 if(error != NULL)
6342 *error = (char *)NULL;
6344 /* No guarantee cursor or status line is how we saved it */
6345 clear_cursor_pos();
6346 mark_status_unknown();
6348 if(ps_global->remote_abook_validity > 0 &&
6349 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6350 *mangled |= BUILDER_SCREEN_MANGLED;
6353 * If we end up jumping back here because somebody else changed one of
6354 * our addrbooks out from underneath us, we may well leak some memory.
6355 * That's probably ok since this will be very rare.
6357 * The reason for the memcpy of the jmp_buf is that we may actually
6358 * be indirectly calling this function from within the address book.
6359 * For example, we may be in the address book screen and then run
6360 * the ComposeTo command which puts us in the composer, then we call
6361 * build_address from there which resets addrbook_changed_unexpectedly.
6362 * Once we leave build_address we need to reset addrbook_changed_un...
6363 * because this position on the stack will no longer be valid.
6364 * Same is true of the other setjmp's in this file which are wrapped
6365 * in memcpy calls.
6367 save_nesting_level = cpyint(ab_nesting_level);
6368 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6369 if(setjmp(addrbook_changed_unexpectedly)){
6370 no_repo = 0;
6371 pt = NULL;
6372 af = NULL;
6373 fcc_local = NULL;
6374 if(error != NULL)
6375 *error = (char *)NULL;
6377 if(full_to && *full_to)
6378 fs_give((void **)full_to);
6380 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6381 dprint((1,
6382 "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
6383 addrbook_reset();
6384 ab_nesting_level = *save_nesting_level;
6387 ab_nesting_level++;
6388 bldto.type = Str;
6389 bldto.arg.str = to;
6390 ret_val = build_address_internal(bldto, full_to, error,
6391 barg ? &fcc_local : NULL,
6392 &no_repo, NULL, save_and_restore,
6393 0, mangled);
6394 ab_nesting_level--;
6395 if(save_nesting_level)
6396 fs_give((void **)&save_nesting_level);
6399 * Have to rfc1522_decode the full_to string before sending it back.
6401 if(full_to && *full_to ){
6402 char *q;
6403 size_t len;
6405 len = 4*strlen(*full_to)+1;
6406 q = (char *)fs_get(len * sizeof(char));
6407 p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
6409 /* p == q means that decoding happened, p is decoded *full_to */
6410 if(p == q){
6411 fs_give((void **)full_to);
6412 *full_to = p;
6414 else
6415 fs_give((void **)&q);
6418 if(fcc_local){
6419 unsigned long csum;
6421 /* Pt will point to headents[Fcc].bldr_private */
6422 pt = NULL;
6423 if(barg && barg->aff)
6424 pt = (PrivateTop *)(*barg->aff);
6427 * If *barg->aff is set, that means fcc was set from a list
6428 * during some previous builder call.
6429 * If the current To line contains the old expansion as a prefix, then
6430 * we should leave things as they are. In order to decide that,
6431 * we look at a hash value computed from the strings.
6433 if(pt && (af=pt->affector) && af->who == BP_To){
6434 int len;
6436 len = strlen(to);
6437 if(len >= af->cksumlen){
6438 int save;
6440 save = to[af->cksumlen];
6441 to[af->cksumlen] = '\0';
6442 csum = line_hash(to);
6443 to[af->cksumlen] = save;
6445 else
6446 csum = af->cksumval + 1; /* something not equal to cksumval */
6449 if(!pt ||
6450 !pt->affector ||
6451 (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
6453 /* replace fcc value */
6454 if(barg->tptr)
6455 fs_give((void **)&barg->tptr);
6457 barg->tptr = fcc_local;
6459 if(barg->aff){
6460 if(!pt){
6461 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6462 pt = (PrivateTop *)(*barg->aff);
6463 memset((void *)pt, 0, sizeof(PrivateTop));
6466 if(no_repo){
6467 if(!pt->affector)
6468 pt->affector =
6469 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6471 af = pt->affector;
6472 af->who = BP_To;
6473 af->cksumlen = strlen(((full_to && *full_to)
6474 ? *full_to : ""));
6475 af->cksumval = line_hash(((full_to && *full_to)
6476 ? *full_to : ""));
6478 else{
6480 * If result is reproducible, we don't keep track here.
6482 if(pt->affector)
6483 fs_give((void **)&pt->affector);
6487 else
6488 fs_give((void **)&fcc_local); /* unused in this case */
6491 /* This is so pico will erase the old message */
6492 if(error != NULL && *error == NULL)
6493 *error = cpystr("");
6495 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6496 flush_status_messages(1);
6497 return(ret_val);
6502 * This is the builder used by the composer for the Lcc line.
6504 * Args: lcc -- the passed in Lcc line to parse
6505 * full_lcc -- Address of a pointer to return the full address in.
6506 * This will be allocated here and freed by the caller.
6507 * error -- Address of a pointer to return an error message in.
6508 * This is not allocated so should not be freed by the caller.
6509 * barg -- This is a pointer to text for affected entries which
6510 * we may be changing. The first one in the list is the
6511 * To entry. We may put the name of the list in empty
6512 * group syntax form there (like List Name: ;).
6513 * The second one in the list is the fcc field.
6514 * The tptr members already point to text allocated in the
6515 * caller. We may free and reallocate here, caller will
6516 * free the result in any case.
6518 * Result: 0 is returned if address was OK,
6519 * -1 if address wasn't OK.
6521 * Side effect: Can flush addrbook entry cache entries so they need to be
6522 * re-fetched afterwords.
6525 build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
6527 int ret_val,
6528 no_repo = 0; /* fcc or lcc not reproducible */
6529 int *save_nesting_level;
6530 BuildTo bldlcc;
6531 PrivateTop *pt = NULL;
6532 PrivateAffector *af = NULL;
6533 char *p,
6534 *fcc_local = NULL,
6535 *to = NULL,
6536 *dummy;
6537 jmp_buf save_jmp_buf;
6539 dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
6541 /* check to see if to string is empty to avoid work */
6542 for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
6543 ;/* do nothing */
6545 if(!p || !*p){
6546 if(full_lcc)
6547 *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
6549 return 0;
6552 if(error != NULL)
6553 *error = (char *)NULL;
6555 if(ps_global->remote_abook_validity > 0 &&
6556 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6557 *mangled |= BUILDER_SCREEN_MANGLED;
6560 * If we end up jumping back here because somebody else changed one of
6561 * our addrbooks out from underneath us, we may well leak some memory.
6562 * That's probably ok since this will be very rare.
6564 save_nesting_level = cpyint(ab_nesting_level);
6565 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6566 if(setjmp(addrbook_changed_unexpectedly)){
6567 no_repo = 0;
6568 pt = NULL;
6569 af = NULL;
6570 fcc_local = NULL;
6571 to = NULL;
6572 if(error != NULL)
6573 *error = (char *)NULL;
6575 if(full_lcc && *full_lcc)
6576 fs_give((void **)full_lcc);
6578 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6579 dprint((1,
6580 "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
6581 addrbook_reset();
6582 ab_nesting_level = *save_nesting_level;
6585 ab_nesting_level++;
6586 bldlcc.type = Str;
6587 bldlcc.arg.str = lcc;
6590 * To is first affected_entry and Fcc is second.
6591 * The conditional stuff for the fcc argument says to only change the
6592 * fcc if the fcc pointer is passed in non-null, and the To pointer
6593 * is also non-null. If they are null, that means they've already been
6594 * entered (are sticky). We don't affect fcc if either fcc or To has
6595 * been typed in.
6597 ret_val = build_address_internal(bldlcc,
6598 full_lcc,
6599 error,
6600 (barg && barg->next && barg->next->tptr && barg->tptr)
6601 ? &fcc_local : NULL,
6602 &no_repo,
6603 (barg && barg->tptr) ? &to : NULL,
6604 save_and_restore, 0, mangled);
6606 ab_nesting_level--;
6607 if(save_nesting_level)
6608 fs_give((void **)&save_nesting_level);
6610 /* full_lcc is what ends up in the Lcc: line */
6611 if(full_lcc && *full_lcc){
6612 size_t len;
6615 * Have to rfc1522_decode the full_lcc string before sending it back.
6617 len = 4*strlen(*full_lcc)+1;
6618 p = (char *)fs_get(len * sizeof(char));
6619 if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
6620 fs_give((void **)full_lcc);
6621 *full_lcc = p;
6623 else
6624 fs_give((void **)&p);
6627 /* to is what ends up in the To: line */
6628 if(to && *to){
6629 unsigned long csum;
6630 size_t len;
6633 * Have to rfc1522_decode the full_to string before sending it back.
6635 len = 4*strlen(to)+1;
6636 p = (char *)fs_get(len * sizeof(char));
6637 dummy = NULL;
6638 if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
6640 * If the caller wants us to try to preserve the charset
6641 * information (they set aff) we copy it into encoded->etext.
6642 * We don't have to worry about pasting together pieces of
6643 * etext like we do in build_address because whenever the
6644 * Lcc line is setting the To line it will be setting the
6645 * whole line, not modifying it.
6646 * Pt will point to headents[To].bldr_private.
6648 if(barg && barg->aff){
6649 pt = (PrivateTop *)(*barg->aff);
6651 if(!pt){
6652 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6653 pt = (PrivateTop *)(*barg->aff);
6654 memset((void *)pt, 0, sizeof(PrivateTop));
6658 fs_give((void **)&to);
6659 to = p;
6661 else
6662 fs_give((void **)&p);
6664 if(dummy)
6665 fs_give((void **)&dummy);
6669 * This part is recording the fact that the To line was set to
6670 * what it is by entering something on the Lcc line. In particular,
6671 * if a list alias was entered here then the fullname of the list
6672 * goes in the To line. We save this affector information so that
6673 * we can tell it shouldn't be modified if we call build_addr_lcc
6674 * again unless we actually modified what's in the Lcc line so that
6675 * it doesn't start with the same thing. The problem we're solving
6676 * is that the contents of the Lcc line no longer look like the
6677 * list they were derived from.
6678 * Pt will point to headents[To].bldr_private.
6680 if(barg && barg->aff)
6681 pt = (PrivateTop *)(*barg->aff);
6683 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6684 int len;
6686 len = strlen(lcc);
6687 if(len >= af->cksumlen){
6688 int save;
6690 save = lcc[af->cksumlen];
6691 lcc[af->cksumlen] = '\0';
6692 csum = line_hash(lcc);
6693 lcc[af->cksumlen] = save;
6695 else
6696 csum = af->cksumval + 1; /* so they aren't equal */
6699 if(!pt ||
6700 !pt->affector ||
6701 pt->affector->who != BP_Lcc ||
6702 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6704 /* replace to value */
6705 if(barg->tptr && barg->tptr[0]){
6706 size_t l;
6707 char *t;
6709 l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
6710 t = (char *)fs_get((l+1) * sizeof(char));
6711 snprintf(t, l+1, "%s%s%s",
6712 barg->tptr,
6713 (to && *to) ? ", " : "",
6714 (to && *to) ? to : "");
6715 fs_give((void **)&barg->tptr);
6716 if(to)
6717 fs_give((void **)&to);
6719 barg->tptr = t;
6721 else{
6722 if(barg->tptr)
6723 fs_give((void **)&barg->tptr);
6725 barg->tptr = to;
6728 if(barg->aff){
6729 if(!pt){
6730 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6731 pt = (PrivateTop *)(*barg->aff);
6732 memset((void *)pt, 0, sizeof(PrivateTop));
6735 if(no_repo){
6736 if(!pt->affector)
6737 pt->affector =
6738 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6740 af = pt->affector;
6741 af->who = BP_Lcc;
6742 af->cksumlen = strlen(((full_lcc && *full_lcc)
6743 ? *full_lcc : ""));
6744 af->cksumval = line_hash(((full_lcc && *full_lcc)
6745 ? *full_lcc : ""));
6747 else{
6749 * If result is reproducible, we don't keep track here.
6751 if(pt->affector)
6752 fs_give((void **)&pt->affector);
6756 else
6757 fs_give((void **)&to); /* unused in this case */
6760 if(fcc_local){
6761 unsigned long csum;
6764 * If *barg->next->aff is set, that means fcc was set from a list
6765 * during some previous builder call. If the current Lcc line
6766 * contains the old expansion as a prefix, then we should leave
6767 * things as they are. In order to decide that we look at a hash
6768 * value computed from the strings.
6769 * Pt will point to headents[Fcc].bldr_private
6771 pt = NULL;
6772 if(barg && barg->next && barg->next->aff)
6773 pt = (PrivateTop *)(*barg->next->aff);
6775 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6776 int len;
6778 len = strlen(lcc);
6779 if(len >= af->cksumlen){
6780 int save;
6782 save = lcc[af->cksumlen];
6783 lcc[af->cksumlen] = '\0';
6784 csum = line_hash(lcc);
6785 lcc[af->cksumlen] = save;
6787 else
6788 csum = af->cksumval + 1; /* something not equal to cksumval */
6791 if(!pt ||
6792 !pt->affector ||
6793 pt->affector->who != BP_Lcc ||
6794 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6796 /* replace fcc value */
6797 if(barg->next->tptr)
6798 fs_give((void **)&barg->next->tptr);
6800 barg->next->tptr = fcc_local;
6802 if(barg->next->aff){
6803 if(!pt){
6804 *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
6805 pt = (PrivateTop *)(*barg->next->aff);
6806 memset((void *)pt, 0, sizeof(PrivateTop));
6809 if(no_repo){
6810 if(!pt->affector)
6811 pt->affector =
6812 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6814 af = pt->affector;
6815 af->who = BP_Lcc;
6816 af->cksumlen = strlen(((full_lcc && *full_lcc)
6817 ? *full_lcc : ""));
6818 af->cksumval = line_hash(((full_lcc && *full_lcc)
6819 ? *full_lcc : ""));
6821 else{
6823 * If result is reproducible, we don't keep track here.
6825 if(pt->affector)
6826 fs_give((void **)&pt->affector);
6830 else
6831 fs_give((void **)&fcc_local); /* unused in this case */
6835 if(error != NULL && *error == NULL)
6836 *error = cpystr("");
6838 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6839 flush_status_messages(0);
6840 return(ret_val);
6844 /*----------------------------------------------------------------------
6845 Verify and canonicalize news groups names.
6846 Called from the message composer
6848 Args: given_group -- List of groups typed by user
6849 expanded_group -- pointer to point to expanded list, which will be
6850 allocated here and freed in caller. If this is
6851 NULL, don't attempt to validate.
6852 error -- pointer to store error message
6853 fcc -- pointer to point to fcc, which will be
6854 allocated here and freed in caller
6856 Returns: 0 if all is OK
6857 -1 if addresses weren't valid
6859 Test the given list of newstroups against those recognized by our nntp
6860 servers. Testing by actually trying to open the list is much cheaper, both
6861 in bandwidth and memory, than yanking the whole list across the wire.
6862 ----*/
6864 news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
6866 int rv;
6867 char *fccptr = NULL;
6869 if(fcc && fcc->tptr)
6870 fccptr = cpystr(fcc->tptr);
6872 clear_cursor_pos();
6874 rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
6876 /* assign any new fcc to the BUILDER_ARG */
6877 if(fccptr){
6878 if(fcc){
6879 /* it changed */
6880 if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
6881 fs_give((void **) &fcc->tptr);
6882 fcc->tptr = fccptr;
6883 fccptr = NULL;
6887 if(fccptr)
6888 fs_give((void **) &fccptr);
6891 /* deal with any busy indicator */
6892 if(news_busy_cue){
6893 news_busy_cue = 0;
6894 cancel_busy_cue(0);
6895 mark_status_dirty();
6896 display_message('x');
6897 if(mangled)
6898 *mangled |= BUILDER_MESSAGE_DISPLAYED;
6902 return(rv);
6906 void
6907 news_build_busy(void)
6909 news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
6913 #if defined(DOS) || defined(OS2)
6915 /*----------------------------------------------------------------------
6916 Verify that the necessary pieces are around to allow for
6917 message sending under DOS
6919 Args: strict -- tells us if a remote stream is required before
6920 sending is permitted.
6922 The idea is to make sure pine knows enough to put together a valid
6923 from line. The things we MUST know are a user-id, user-domain and
6924 smtp server to dump the message off on. Typically these are
6925 provided in pine's configuration file, but if not, the user is
6926 queried here.
6927 ----*/
6929 dos_valid_from()
6931 char prompt[100], answer[80];
6932 int rc, i, flags;
6933 HelpType help;
6936 * query for user name portion of address, use IMAP login
6937 * name as default
6939 if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
6940 NETMBX mb;
6941 int no_prompt_user_id = 0;
6943 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6944 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6945 && *mb.user){
6946 strncpy(answer, mb.user, sizeof(answer)-1);
6947 answer[sizeof(answer)-1] = '\0';
6949 else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
6950 /* no user-id prompting if set */
6951 no_prompt_user_id = 1;
6952 rc = 0;
6953 if(!ps_global->mail_stream)
6954 do_broach_folder(ps_global->inbox_name,
6955 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
6956 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6957 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6958 && *mb.user){
6959 strncpy(answer, mb.user, sizeof(answer)-1);
6960 answer[sizeof(answer)-1] = '\0';
6962 else
6963 answer[0] = '\0';
6965 else
6966 answer[0] = '\0';
6968 if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
6969 /* No prompt, just assume mailbox login is user-id */
6970 no_prompt_user_id = 1;
6971 rc = 0;
6974 snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
6975 prompt[sizeof(prompt)-1] = '\0';
6977 help = NO_HELP;
6978 while(!no_prompt_user_id) {
6979 flags = OE_APPEND_CURRENT;
6980 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
6981 sizeof(answer),prompt,NULL,help,&flags);
6982 if(rc == 2)
6983 continue;
6985 if(rc == 3){
6986 help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
6987 continue;
6990 if(rc != 4)
6991 break;
6994 if(rc == 1 || (rc == 0 && !answer[0])) {
6995 q_status_message(SM_ORDER, 3, 4,
6996 _("Send cancelled (User-id must be provided before sending)"));
6997 return(0);
7000 /* save the name */
7001 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
7002 sizeof(prompt)-50, answer);
7003 prompt[sizeof(prompt)-1] = '\0';
7004 if(ps_global->blank_user_id
7005 && !no_prompt_user_id
7006 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7007 set_variable(V_USER_ID, answer, 1, 1, Main);
7009 else{
7010 fs_give((void **)&(ps_global->VAR_USER_ID));
7011 ps_global->VAR_USER_ID = cpystr(answer);
7015 /* query for personal name */
7016 if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
7017 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
7018 answer[0] = '\0';
7019 snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
7020 prompt[sizeof(prompt)-1] = '\0';
7022 help = NO_HELP;
7023 while(1) {
7024 flags = OE_APPEND_CURRENT;
7025 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7026 sizeof(answer),prompt,NULL,help,&flags);
7027 if(rc == 2)
7028 continue;
7030 if(rc == 3){
7031 help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
7032 continue;
7035 if(rc != 4)
7036 break;
7039 if(rc == 0 && answer){ /* save the name */
7040 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
7041 sizeof(prompt)-50, answer);
7042 prompt[sizeof(prompt)-1] = '\0';
7043 if(ps_global->blank_personal_name
7044 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7045 set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
7047 else{
7048 fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
7049 ps_global->VAR_PERSONAL_NAME = cpystr(answer);
7055 * query for host/domain portion of address, using IMAP
7056 * host as default
7058 if(ps_global->blank_user_domain
7059 || ps_global->maildomain == ps_global->localdomain
7060 || ps_global->maildomain == ps_global->hostname){
7061 if(ps_global->inbox_name[0] == '{'){
7062 for(i=0;
7063 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7064 answer[i] = ps_global->inbox_name[i+1];
7066 answer[i] = '\0';
7068 else
7069 answer[0] = '\0';
7071 snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
7072 prompt[sizeof(prompt)-1] = '\0';
7074 help = NO_HELP;
7075 while(1) {
7076 flags = OE_APPEND_CURRENT;
7077 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7078 sizeof(answer),prompt,NULL,help,&flags);
7079 if(rc == 2)
7080 continue;
7082 if(rc == 3){
7083 help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
7084 continue;
7087 if(rc != 4)
7088 break;
7091 if(rc == 1 || (rc == 0 && !answer[0])) {
7092 q_status_message(SM_ORDER, 3, 4,
7093 _("Send cancelled (Host/domain name must be provided before sending)"));
7094 return(0);
7097 /* save the name */
7098 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
7099 sizeof(prompt)-50, answer);
7100 prompt[sizeof(prompt)-1] = '\0';
7101 if(!ps_global->userdomain && !ps_global->blank_user_domain
7102 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7103 set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
7104 fs_give((void **)&(ps_global->maildomain)); /* blast old val */
7105 ps_global->userdomain = cpystr(answer);
7106 ps_global->maildomain = ps_global->userdomain;
7108 else{
7109 fs_give((void **)&(ps_global->maildomain));
7110 ps_global->userdomain = cpystr(answer);
7111 ps_global->maildomain = ps_global->userdomain;
7115 /* check for smtp server */
7116 if(!ps_global->VAR_SMTP_SERVER ||
7117 !ps_global->VAR_SMTP_SERVER[0] ||
7118 !ps_global->VAR_SMTP_SERVER[0][0]){
7119 char **list;
7121 if(ps_global->inbox_name[0] == '{'){
7122 for(i=0;
7123 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7124 answer[i] = ps_global->inbox_name[i+1];
7126 answer[i] = '\0';
7128 else
7129 answer[0] = '\0';
7131 snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
7132 prompt[sizeof(prompt)-1] = '\0';
7134 help = NO_HELP;
7135 while(1) {
7136 flags = OE_APPEND_CURRENT;
7137 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7138 sizeof(answer),prompt,NULL,help,&flags);
7139 if(rc == 2)
7140 continue;
7142 if(rc == 3){
7143 help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
7144 continue;
7147 if(rc != 4)
7148 break;
7151 if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
7152 q_status_message(SM_ORDER, 3, 4,
7153 _("Send cancelled (SMTP server must be provided before sending)"));
7154 return(0);
7157 /* save the name */
7158 list = (char **) fs_get(2 * sizeof(char *));
7159 list[0] = cpystr(answer);
7160 list[1] = NULL;
7161 set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
7162 fs_give((void *)&list[0]);
7163 fs_give((void *)list);
7166 return(1);
7169 #endif /* defined(DOS) || defined(OS2) */