* Avoid error messages or tcp timeouts when cancelling imap authentication.
[alpine.git] / alpine / send.c
blobe21d8009595472f6b9a3a012e149a2186b5c9912
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);
614 outgoing->message_id = generate_message_id();
617 * Setup possible role
619 if(role_arg)
620 role = copy_action(role_arg);
622 if(!role){
623 /* Setup possible compose role */
624 if(nonempty_patterns(rflags, &dummy)){
626 * setup default role
627 * Msgno = -1 means there is no msg.
628 * This will match roles which have the Compose Use turned
629 * on, and have no patterns set, and match the Current
630 * Folder Type.
632 role = set_role_from_msg(ps_global, rflags, -1L, NULL);
634 if(confirm_role(rflags, &role))
635 role = combine_inherited_role(role);
636 else{ /* cancel reply */
637 role = NULL;
638 cmd_cancelled("Composition");
639 return;
644 if(role)
645 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
646 role->nick);
649 * The type of storage object allocated below is vitally
650 * important. See SIMPLIFYING ASSUMPTION #37
652 if((body->contents.text.data = (void *) so_get(PicoText,
653 NULL, EDIT_ACCESS)) != NULL){
654 char ch;
656 if(inc_text_getc){
657 while((*inc_text_getc)(&ch))
658 if(!so_writec(ch, (STORE_S *)body->contents.text.data)){
659 break;
663 else{
664 q_status_message(SM_ORDER | SM_DING, 3, 4,
665 _("Problem creating space for message text."));
666 return;
669 if(role && role->template){
670 char *filtered;
672 impl = 1; /* leave cursor in header if not explicit */
673 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
674 if(filtered){
675 if(*filtered){
676 so_puts((STORE_S *)body->contents.text.data, filtered);
677 if(impl == 1)
678 template_len = strlen(filtered);
681 fs_give((void **)&filtered);
684 else
685 impl = 1;
687 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
688 if(impl == 2)
689 redraft_pos->offset += template_len;
691 if(*sig)
692 so_puts((STORE_S *)body->contents.text.data, sig);
694 fs_give((void **)&sig);
697 body->type = TYPETEXT;
699 if(attach)
700 create_message_body(&body, attach, 0);
703 ps_global->prev_screen = compose_screen;
704 if(!(fcc_to_free = fcc) && !(role && role->fcc))
705 fcc = fcc_arg; /* Didn't pick up fcc, use given */
708 * check whether a build_address-produced fcc is different from
709 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
711 if(fcc){
712 char *tmp_fcc = NULL;
714 if(outgoing->to){
715 tmp_fcc = get_fcc_based_on_to(outgoing->to);
716 if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
717 fcc_is_sticky++; /* cause sticky bit to get set */
720 else if((tmp_fcc = get_fcc(NULL)) != NULL &&
721 !strcmp(fcc, tmp_fcc)){
722 /* not sticky */
724 else
725 fcc_is_sticky++;
727 if(tmp_fcc)
728 fs_give((void **)&tmp_fcc);
731 pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc,
732 reply, redraft_pos, lcc, custom,
733 (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0));
735 if(reply){
736 if(reply->mailbox)
737 fs_give((void **) &reply->mailbox);
738 if(reply->origmbox)
739 fs_give((void **) &reply->origmbox);
740 if(reply->prefix)
741 fs_give((void **) &reply->prefix);
742 if(reply->data.uid.msgs)
743 fs_give((void **) &reply->data.uid.msgs);
744 fs_give((void **) &reply);
747 if(fcc_to_free)
748 fs_give((void **)&fcc_to_free);
750 if(lcc)
751 fs_give((void **)&lcc);
753 mail_free_envelope(&outgoing);
754 pine_free_body(&body);
755 free_redraft_pos(&redraft_pos);
756 free_action(&role);
760 /*----------------------------------------------------------------------
761 Args: stream -- This is where we get the postponed messages from
762 We'll expunge and close it here unless it is mail_stream.
764 These are all return values:
765 ================
766 outgoing --
767 body --
768 fcc --
769 lcc --
770 reply --
771 redraft_pos --
772 custom --
773 role --
774 ================
776 flags --
778 ----*/
780 redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body,
781 char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos,
782 PINEFIELD **custom, ACTION_S **role, int flags)
784 MAILSTREAM *stream;
785 long cont_msg = 1L;
786 STORE_S *so;
788 if(!(streamp && *streamp))
789 return(0);
791 stream = *streamp;
794 * If we're manipulating the current folder, don't bother
795 * with index
797 if(!stream->nmsgs){
798 if(REDRAFT_PPND&flags)
799 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!"));
800 else
801 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!"));
803 return(redraft_cleanup(streamp, FALSE, flags));
805 else if(stream == ps_global->mail_stream
806 && ps_global->prev_screen == mail_index_screen){
808 * Since the user's got this folder already opened and they're
809 * on a selected message, pick that one rather than rebuild
810 * another index screen...
812 cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
814 else if(stream->nmsgs > 1L){ /* offer browser ? */
815 int rv;
817 if(REDRAFT_PPND&flags){ /* set to last message postponed */
818 mn_set_cur(sp_msgmap(stream),
819 mn_get_revsort(sp_msgmap(stream))
820 ? 1L : mn_get_total(sp_msgmap(stream)));
822 else{ /* set to top form letter */
823 mn_set_cur(sp_msgmap(stream), 1L);
826 clear_index_cache(stream, 0);
827 while(1){
828 void *ti;
830 ti = stop_threading_temporarily();
831 rv = index_lister(ps_global, NULL, stream->mailbox,
832 stream, sp_msgmap(stream));
833 restore_threading(&ti);
835 cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream)));
836 if(count_flagged(stream, F_DEL)
837 && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){
838 if(REDRAFT_PPND&flags)
839 q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
840 else
841 q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
843 continue;
846 break;
849 clear_index_cache(stream, 0);
851 if(rv){
852 q_status_message(SM_ORDER, 0, 3, _("Composition cancelled"));
853 (void) redraft_cleanup(streamp, FALSE, flags);
855 if(!*streamp && !ps_global->mail_stream){
856 q_status_message2(SM_ORDER, 3, 7,
857 "No more %.200s, returning to \"%.200s\"",
858 (REDRAFT_PPND&flags) ? "postponed messages"
859 : "form letters",
860 ps_global->inbox_name);
861 if(ps_global && ps_global->ttyo){
862 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
863 ps_global->mangled_footer = 1;
866 do_broach_folder(ps_global->inbox_name,
867 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
869 ps_global->next_screen = mail_index_screen;
872 return(0); /* special case */
876 if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL)
877 return(redraft_work(streamp, cont_msg, outgoing, body,
878 fcc, lcc, reply, redraft_pos, custom,
879 role, flags, so));
880 else
881 return(0);
886 redraft_prompt(char *type, char *prompt, int failure)
888 if(background_posting(FALSE)){
889 q_status_message1(SM_ORDER, 0, 3,
890 _("%s folder unavailable while background posting"),
891 type);
892 return(failure);
895 return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM));
899 /* this is for initializing the fixed header elements in pine_send() */
901 prompt::name::help::prwid::maxlen::realaddr::
902 builder::affected_entry::next_affected::selector::key_label::fileedit::
903 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
904 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
906 static struct headerentry he_template[]={
907 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
908 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
909 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
910 {"From : ", "From", h_composer_from, 10, 0, NULL,
911 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
912 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
913 {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
914 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
915 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
916 {"To : ", "To", h_composer_to, 10, 0, NULL,
917 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
918 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK},
919 {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL,
920 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
921 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
922 {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL,
923 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
924 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
925 {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL,
926 news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL,
927 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
928 {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL,
929 NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, fcc_tab_complete,
930 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE},
931 {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL,
932 build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete,
933 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
934 {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL,
935 NULL, NULL, NULL, NULL, "To Files", NULL, NULL,
936 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE},
937 {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL,
938 valid_subject, NULL, NULL, NULL, NULL, NULL, NULL,
939 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
940 {"", "References", NO_HELP, 10, 0, NULL,
941 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
942 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
943 {"", "Date", NO_HELP, 10, 0, NULL,
944 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
945 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
946 {"", "In-Reply-To", NO_HELP, 10, 0, NULL,
947 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
948 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
949 {"", "Message-ID", NO_HELP, 10, 0, NULL,
950 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
951 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
952 {"", "X-Priority", NO_HELP, 10, 0, NULL,
953 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
954 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
955 {"", "User-Agent", NO_HELP, 10, 0, NULL,
956 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
957 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
958 {"", "To", NO_HELP, 10, 0, NULL,
959 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
960 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
961 {"", "X-Post-Error",NO_HELP, 10, 0, NULL,
962 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
963 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
964 {"", "X-Reply-UID", NO_HELP, 10, 0, NULL,
965 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
966 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
967 {"", "X-Reply-Mbox", NO_HELP, 10, 0, NULL,
968 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
969 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
970 {"", "X-SMTP-Server", NO_HELP, 10, 0, NULL,
971 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
972 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
973 {"", "X-Cursor-Pos", NO_HELP, 10, 0, NULL,
974 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
975 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
976 {"", "X-Our-ReplyTo", NO_HELP, 10, 0, NULL,
977 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
978 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
979 {"", OUR_HDRS_LIST, NO_HELP, 10, 0, NULL,
980 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
981 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
982 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
983 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
984 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
985 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
986 {"", "Sender", NO_HELP, 10, 0, NULL,
987 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
988 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
989 #endif
993 static struct headerentry he_custom_addr_templ={
994 NULL, NULL, h_composer_custom_addr,10, 0, NULL,
995 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
996 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK};
998 static struct headerentry he_custom_free_templ={
999 NULL, NULL, h_composer_custom_free,10, 0, NULL,
1000 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1001 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE};
1004 /*----------------------------------------------------------------------
1005 Get addressee for message, then post message
1007 Args: outgoing -- Partially formatted outgoing ENVELOPE
1008 body -- Body of outgoing message
1009 prmpt_who -- Optional prompt for optionally_enter call
1010 prmpt_cnf -- Optional prompt for confirmation call
1011 used_tobufval -- The string that the to was eventually set equal to.
1012 This gets passed back if non-NULL on entry.
1013 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1014 SS_NULLRP - Use null return-path so we'll send an
1015 SMTP MAIL FROM: <>
1017 Result: message "To: " field is provided and message is sent or cancelled.
1019 Fields:
1020 remail -
1021 return_path -
1022 date added here
1023 from added here
1024 sender -
1025 reply_to -
1026 subject passed in, NOT edited but maybe canonized here
1027 to possibly passed in, edited and canonized here
1028 cc -
1029 bcc -
1030 in_reply_to -
1031 message_id -
1033 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1034 with the first part TYPETEXT! All newlines in the text here also end with
1035 CRLF.
1037 Returns 0 on success, -1 on failure.
1038 ----*/
1040 pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */
1041 struct mail_bodystruct **body,
1042 ACTION_S **rolep,
1043 char *prmpt_who,
1044 char *prmpt_cnf,
1045 char **used_tobufval,
1046 int flagsarg)
1048 char **tobufp, *p, tmp[MAILTMPLEN];
1049 void *messagebuf;
1050 int done = 0, retval = 0, x;
1051 int lastrc, rc = 0, ku, i, resize_len, result, fcc_result;
1052 int og2s_done = 0;
1053 HelpType help;
1054 static HISTORY_S *history = NULL;
1055 ESCKEY_S ekey[6];
1056 BUILDER_ARG ba_fcc;
1057 METAENV *header;
1058 ACTION_S *role = rolep ? *rolep : NULL;
1059 PAT_STATE pstate;
1061 dprint((1,"\n === simple send called === \n"));
1063 memset(&ba_fcc, 0, sizeof(BUILDER_ARG));
1065 init_hist(&history, HISTSIZE);
1067 header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp);
1069 /*----- Fill in a few general parts of the envelope ----*/
1070 if(!outgoing->date){
1071 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1072 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
1074 rfc822_date(tmp_20k_buf); /* format and copy new date */
1075 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1076 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
1078 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
1081 if(!outgoing->from){
1082 if(role && role->from){
1083 if(ps_global->never_allow_changing_from)
1084 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1085 else
1086 outgoing->from = copyaddrlist(role->from);
1088 else
1089 outgoing->from = generate_from();
1092 if(!(flagsarg & SS_NULLRP))
1093 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1095 ekey[i = 0].ch = ctrl('T');
1096 ekey[i].rval = 2;
1097 ekey[i].name = "^T";
1098 ekey[i++].label = N_("To AddrBk");
1100 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1101 ekey[i].ch = ctrl('I');
1102 ekey[i].rval = 11;
1103 ekey[i].name = "TAB";
1104 ekey[i++].label = N_("Complete");
1107 if(nonempty_patterns(ROLE_DO_ROLES, &pstate) && first_pattern(&pstate)){
1108 ekey[i].ch = ctrl('R');
1109 ekey[i].rval = 15;
1110 ekey[i].name = "^R";
1111 ekey[i++].label = "Set Role";
1114 ekey[i].ch = KEY_UP;
1115 ekey[i].rval = 30;
1116 ekey[i].name = "";
1117 ku = i;
1118 ekey[i++].label = "";
1120 ekey[i].ch = KEY_DOWN;
1121 ekey[i].rval = 31;
1122 ekey[i].name = "";
1123 ekey[i++].label = "";
1125 ekey[i].ch = -1;
1127 if(outgoing->remail == NULL)
1128 strcpy(tmp, _("FORWARD (as e-mail) to : "));
1130 /*----------------------------------------------------------------------
1131 Loop editing the "To: " field until everything goes well
1132 ----*/
1133 help = NO_HELP;
1135 while(!done){
1136 int flags;
1138 if(outgoing->remail){
1139 if(role)
1140 snprintf(tmp, sizeof(tmp), _("BOUNCE (redirect) message using role \"%s\" to : "), role->nick);
1141 else
1142 strncpy(tmp, _("BOUNCE (redirect) message to : "), sizeof(tmp));
1143 tmp[sizeof(tmp)-1] = '\0';
1146 if(!og2s_done){
1147 og2s_done++;
1148 outgoing2strings(header, *body, &messagebuf, NULL, 1);
1151 lastrc = rc;
1152 if(flagsarg & SS_PROMPTFORTO){
1153 if(!*tobufp)
1154 *tobufp = cpystr("");
1156 resize_len = MAX(MAXPATH, strlen(*tobufp));
1157 fs_resize((void **) tobufp, resize_len+1);
1159 if(items_in_hist(history) > 0){
1160 ekey[ku].name = HISTORY_UP_KEYNAME;
1161 ekey[ku].label = HISTORY_KEYLABEL;
1162 ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
1163 ekey[ku+1].label = HISTORY_KEYLABEL;
1165 else{
1166 ekey[ku].name = "";
1167 ekey[ku].label = "";
1168 ekey[ku+1].name = "";
1169 ekey[ku+1].label = "";
1172 flags = OE_APPEND_CURRENT;
1174 rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
1175 0, resize_len,
1176 prmpt_who
1177 ? prmpt_who
1178 : tmp,
1179 ekey, help, &flags);
1181 else
1182 rc = 0;
1184 switch(rc){
1185 case -1:
1186 q_status_message(SM_ORDER | SM_DING, 3, 4,
1187 "Internal problem encountered");
1188 retval = -1;
1189 done++;
1190 break;
1192 case 15 : /* set a role */
1193 {void (*prev_screen)(struct pine *) = NULL, (*redraw)(void) = NULL;
1195 redraw = ps_global->redrawer;
1196 ps_global->redrawer = NULL;
1197 prev_screen = ps_global->prev_screen;
1198 role = NULL;
1199 ps_global->next_screen = SCREEN_FUN_NULL;
1201 if(role_select_screen(ps_global, &role,
1202 outgoing->remail ? MC_BOUNCE : MC_FORWARD) < 0)
1203 cmd_cancelled(_("Set Role"));
1204 else{
1205 if(role)
1206 role = combine_inherited_role(role);
1207 else{
1208 role = (ACTION_S *) fs_get(sizeof(*role));
1209 memset((void *) role, 0, sizeof(*role));
1210 role->nick = cpystr("Default Role");
1214 if(redraw)
1215 (*redraw)();
1217 ps_global->next_screen = prev_screen;
1218 ps_global->redrawer = redraw;
1219 ps_global->mangled_screen = 1;
1221 if(role && role->from && !ps_global->never_allow_changing_from){
1222 mail_free_address (&outgoing->from);
1223 outgoing->from = copyaddrlist(role->from);
1225 if(rolep) *rolep = role;
1227 break;
1229 case 30 :
1230 if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){
1231 strncpy(*tobufp, p, resize_len);
1232 (*tobufp)[resize_len-1] = '\0';
1234 else
1235 Writechar(BELL, 0);
1237 break;
1239 case 31 :
1240 if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){
1241 strncpy(*tobufp, p, resize_len);
1242 (*tobufp)[resize_len-1] = '\0';
1244 else
1245 Writechar(BELL, 0);
1247 break;
1249 case 2: /* ^T */
1250 case 0:
1251 {void (*redraw) (void) = ps_global->redrawer;
1252 char *returned_addr = NULL;
1253 int len, l;
1255 if(rc == 2){
1256 int got_something = 0;
1258 push_titlebar_state();
1259 returned_addr = addr_book_bounce();
1262 * Just make it look like user typed this list in.
1264 if(returned_addr){
1265 got_something++;
1266 if((l=resize_len) < (len = strlen(returned_addr)) + 1){
1267 l = len;
1268 fs_resize((void **) tobufp, (size_t) (l+1));
1271 strncpy(*tobufp, returned_addr, l);
1272 (*tobufp)[l] = '\0';
1273 fs_give((void **)&returned_addr);
1276 ClearScreen();
1277 pop_titlebar_state();
1278 redraw_titlebar();
1279 if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
1280 (*ps_global->redrawer)();
1282 if(!got_something)
1283 continue;
1286 if(*tobufp && **tobufp != '\0'){
1287 char *errbuf, *addr;
1288 int tolen;
1290 save_hist(history, *tobufp, 0, NULL);
1292 errbuf = NULL;
1295 * If role has an fcc, use it instead of what build_address
1296 * tells us.
1298 if(role && role->fcc){
1299 if(ba_fcc.tptr)
1300 fs_give((void **) &ba_fcc.tptr);
1302 ba_fcc.tptr = cpystr(role->fcc);
1305 if(build_address(*tobufp, &addr, &errbuf,
1306 (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){
1307 int sendit = 0;
1309 if(errbuf)
1310 fs_give((void **)&errbuf);
1312 if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){
1313 l = tolen;
1314 fs_resize((void **) tobufp, (size_t) (l+1));
1317 strncpy(*tobufp, addr, l);
1318 (*tobufp)[l] = '\0';
1319 if(used_tobufval)
1320 *used_tobufval = cpystr(addr);
1322 /* confirm address */
1323 if(flagsarg & SS_PROMPTFORTO){
1324 char dsn_string[30];
1325 int dsn_label = 0, dsn_show, i;
1326 int verbose_label = 0;
1327 ESCKEY_S opts[13];
1329 strings2outgoing(header, body, NULL, 0);
1331 if((flagsarg & SS_PROMPTFORTO)
1332 && ((x = check_addresses(header)) == CA_BAD
1333 || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE,
1334 ps_global))))
1335 /*--- Addresses didn't check out---*/
1336 continue;
1338 i = 0;
1339 opts[i].ch = 'y';
1340 opts[i].rval = 'y';
1341 opts[i].name = "Y";
1342 opts[i++].label = N_("Yes");
1344 opts[i].ch = 'n';
1345 opts[i].rval = 'n';
1346 opts[i].name = "N";
1347 opts[i++].label = N_("No");
1349 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
1350 if(F_ON(F_VERBOSE_POST, ps_global)){
1351 /* setup keymenu slot to toggle verbose mode */
1352 opts[i].ch = ctrl('W');
1353 opts[i].rval = 12;
1354 opts[i].name = "^W";
1355 verbose_label = i++;
1356 if(F_ON(F_DSN, ps_global)){
1357 opts[i].ch = 0;
1358 opts[i].rval = 0;
1359 opts[i].name = "";
1360 opts[i++].label = "";
1364 /* clear DSN flags */
1365 call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL);
1366 if(F_ON(F_DSN, ps_global)){
1367 /* setup keymenu slots to toggle dsn bits */
1368 opts[i].ch = 'd';
1369 opts[i].rval = 'd';
1370 opts[i].name = "D";
1371 opts[i].label = "DSNOpts";
1372 dsn_label = i++;
1373 opts[i].ch = -2;
1374 opts[i].rval = 's';
1375 opts[i].name = "S";
1376 opts[i++].label = "";
1377 opts[i].ch = -2;
1378 opts[i].rval = 'x';
1379 opts[i].name = "X";
1380 opts[i++].label = "";
1381 opts[i].ch = -2;
1382 opts[i].rval = 'h';
1383 opts[i].name = "H";
1384 opts[i++].label = "";
1387 opts[i].ch = -1;
1389 while(1){
1390 int rv;
1392 dsn_show = (call_mailer_flags & CM_DSN_SHOW);
1393 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
1394 "%s%s%s%s%s%sto \"%s\" ? ",
1395 prmpt_cnf ? prmpt_cnf : "Send message ",
1396 ((call_mailer_flags & CM_VERBOSE)
1397 || (dsn_show))
1398 ? "(" : "",
1399 (call_mailer_flags & CM_VERBOSE)
1400 ? "in verbose mode" : "",
1401 (dsn_show && (call_mailer_flags & CM_VERBOSE))
1402 ? ", " : "",
1403 (dsn_show) ? dsn_string : "",
1404 ((call_mailer_flags & CM_VERBOSE) || dsn_show)
1405 ? ") " : "",
1406 (addr && *addr)
1407 ? addr
1408 : (F_ON(F_FCC_ON_BOUNCE, ps_global)
1409 && ba_fcc.tptr && ba_fcc.tptr[0])
1410 ? ba_fcc.tptr
1411 : "");
1412 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1414 if((strlen(tmp_20k_buf) >
1415 ps_global->ttyo->screen_cols - 2) &&
1416 ps_global->ttyo->screen_cols >= 7)
1417 strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7,
1418 "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7);
1420 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1422 if(verbose_label)
1423 opts[verbose_label].label =
1424 /* TRANSLATORS: several possible key labels follow */
1425 (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
1427 if(F_ON(F_DSN, ps_global)){
1428 if(call_mailer_flags & CM_DSN_SHOW){
1429 opts[dsn_label].label =
1430 (call_mailer_flags & CM_DSN_DELAY)
1431 ? N_("NoDelay") : N_("Delay");
1432 opts[dsn_label+1].ch = 's';
1433 opts[dsn_label+1].label =
1434 (call_mailer_flags & CM_DSN_SUCCESS)
1435 ? N_("NoSuccess") : N_("Success");
1436 opts[dsn_label+2].ch = 'x';
1437 opts[dsn_label+2].label =
1438 (call_mailer_flags & CM_DSN_NEVER)
1439 ? N_("ErrRets") : N_("NoErrRets");
1440 opts[dsn_label+3].ch = 'h';
1441 opts[dsn_label+3].label =
1442 (call_mailer_flags & CM_DSN_FULL)
1443 ? N_("RetHdrs") : N_("RetFull");
1447 rv = radio_buttons(tmp_20k_buf,
1448 -FOOTER_ROWS(ps_global), opts,
1449 'y', 'z', NO_HELP, RB_NORM);
1450 if(rv == 'y'){ /* user ACCEPTS! */
1451 sendit = 1;
1452 break;
1454 else if(rv == 'n'){ /* Declined! */
1455 break;
1457 else if(rv == 'z'){ /* Cancelled! */
1458 break;
1460 else if(rv == 12){ /* flip verbose bit */
1461 if(call_mailer_flags & CM_VERBOSE)
1462 call_mailer_flags &= ~CM_VERBOSE;
1463 else
1464 call_mailer_flags |= CM_VERBOSE;
1466 else if(call_mailer_flags & CM_DSN_SHOW){
1467 if(rv == 's'){ /* flip success bit */
1468 call_mailer_flags ^= CM_DSN_SUCCESS;
1469 /* turn off related bits */
1470 if(call_mailer_flags & CM_DSN_SUCCESS)
1471 call_mailer_flags &= ~(CM_DSN_NEVER);
1473 else if(rv == 'd'){ /* flip delay bit */
1474 call_mailer_flags ^= CM_DSN_DELAY;
1475 /* turn off related bits */
1476 if(call_mailer_flags & CM_DSN_DELAY)
1477 call_mailer_flags &= ~(CM_DSN_NEVER);
1479 else if(rv == 'x'){ /* flip never bit */
1480 call_mailer_flags ^= CM_DSN_NEVER;
1481 /* turn off related bits */
1482 if(call_mailer_flags & CM_DSN_NEVER)
1483 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
1485 else if(rv == 'h'){ /* flip full bit */
1486 call_mailer_flags ^= CM_DSN_FULL;
1489 else if(rv == 'd'){ /* show dsn options */
1490 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
1493 snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"),
1494 (call_mailer_flags & CM_DSN_NEVER)
1495 ? _("Never") : "F",
1496 (call_mailer_flags & CM_DSN_DELAY)
1497 ? "D" : "",
1498 (call_mailer_flags & CM_DSN_SUCCESS)
1499 ? "S" : "",
1500 (call_mailer_flags & CM_DSN_NEVER)
1501 ? ""
1502 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
1503 : "-Hdrs");
1504 dsn_string[sizeof(dsn_string)-1] = '\0';
1508 if(addr)
1509 fs_give((void **)&addr);
1511 if(!(flagsarg & SS_PROMPTFORTO) || sendit){
1512 char *fcc = NULL;
1513 CONTEXT_S *fcc_cntxt = NULL;
1515 if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
1516 if(ba_fcc.tptr)
1517 fcc = cpystr(ba_fcc.tptr);
1519 set_last_fcc(fcc);
1522 * If special name "inbox" then replace it with the
1523 * real inbox path.
1525 if(ps_global->VAR_INBOX_PATH
1526 && strucmp(fcc, ps_global->inbox_name) == 0){
1527 char *replace_fcc;
1529 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
1530 fs_give((void **) &fcc);
1531 fcc = replace_fcc;
1535 /*---- Check out fcc -----*/
1536 if(fcc && *fcc){
1537 (void) commence_fcc(fcc, &fcc_cntxt, FALSE);
1538 if(!lmc.so){
1539 dprint((4,"can't open fcc, cont\n"));
1540 if(!(flagsarg & SS_PROMPTFORTO)){
1541 retval = -1;
1542 fs_give((void **)&fcc);
1543 fcc = NULL;
1544 goto finish;
1546 else
1547 continue;
1549 else
1550 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
1552 else
1553 lmc.so = NULL;
1555 if(!(outgoing->to || outgoing->cc || outgoing->bcc
1556 || lmc.so)){
1557 q_status_message(SM_ORDER, 3, 5, _("No recipients specified!"));
1558 continue;
1561 if(outgoing->to || outgoing->cc || outgoing->bcc){
1562 char **alt_smtp = NULL;
1564 if(role && role->smtp){
1565 if(ps_global->FIX_SMTP_SERVER
1566 && ps_global->FIX_SMTP_SERVER[0])
1567 q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1568 else
1569 alt_smtp = role->smtp;
1572 result = call_mailer(header, *body, alt_smtp,
1573 call_mailer_flags,
1574 call_mailer_file_result,
1575 pipe_callback);
1576 mark_address_failure_for_pico(header);
1578 else
1579 result = 0;
1581 if(result == 1 && !lmc.so)
1582 q_status_message(SM_ORDER, 0, 3, _("Message sent"));
1584 /*----- Was there an fcc involved? -----*/
1585 if(lmc.so){
1586 if(result == 1
1587 || (result == 0
1588 && pine_rfc822_output(header, *body, NULL, NULL))){
1589 char label[50];
1591 strncpy(label, "Fcc", sizeof(label));
1592 label[sizeof(label)-1] = '\0';
1593 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
1594 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
1595 label[sizeof(label)-1] = '\0';
1598 /* Now actually copy to fcc folder and close */
1599 fcc_result =
1600 write_fcc(fcc, fcc_cntxt, lmc.so, NULL,
1601 label,
1602 F_ON(F_MARK_FCC_SEEN, ps_global)
1603 ? "\\SEEN" : NULL);
1605 else if(result == 0){
1606 q_status_message(SM_ORDER,3,5,
1607 _("Fcc Failed!. No message saved."));
1608 retval = -1;
1609 dprint((1, "explicit fcc write failed!\n"));
1612 so_give(&lmc.so);
1615 if(result < 0){
1616 dprint((1, "Bounce failed\n"));
1617 if(!(flagsarg & SS_PROMPTFORTO))
1618 retval = -1;
1619 else
1620 continue;
1622 else if(result == 1){
1623 if(!fcc)
1624 q_status_message(SM_ORDER, 0, 3,
1625 _("Message sent"));
1626 else{
1627 int avail = ps_global->ttyo->screen_cols-2;
1628 int need, fcclen;
1629 char *part1 = "Message sent and ";
1630 char *part2 = fcc_result ? "" : "NOT ";
1631 char *part3 = "copied to ";
1632 fcclen = strlen(fcc);
1634 need = 2 + strlen(part1) + strlen(part2) +
1635 strlen(part3) + fcclen;
1637 if(need > avail && fcclen > 6)
1638 fcclen -= MIN(fcclen-6, need-avail);
1640 q_status_message4(SM_ORDER, 0, 3,
1641 "%s%s%s\"%s\"",
1642 part1, part2, part3,
1643 short_str(fcc,
1644 (char *)tmp_20k_buf,
1645 SIZEOF_20KBUF,
1646 fcclen, FrontDots));
1650 if(fcc)
1651 fs_give((void **)&fcc);
1653 else{
1654 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1655 retval = -1;
1658 else{
1659 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1660 _("Error in address: %s"), errbuf);
1661 if(errbuf)
1662 fs_give((void **)&errbuf);
1664 if(!(flagsarg & SS_PROMPTFORTO))
1665 retval = -1;
1666 else
1667 continue;
1671 else{
1672 q_status_message(SM_ORDER | SM_DING, 3, 5,
1673 _("No addressee! No e-mail sent."));
1674 retval = -1;
1678 done++;
1679 break;
1681 case 1:
1682 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1683 done++;
1684 retval = -1;
1685 break;
1687 case 3:
1688 help = (help == NO_HELP)
1689 ? (outgoing->remail == NULL
1690 ? h_anon_forward
1691 : h_bounce)
1692 : NO_HELP;
1693 break;
1695 case 11:
1696 if(**tobufp){
1697 char *new_nickname = NULL;
1698 int l;
1699 int ambiguity;
1701 ambiguity = abook_nickname_complete(*tobufp, &new_nickname,
1702 (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA);
1703 if(new_nickname){
1704 if(*new_nickname){
1705 if((l=strlen(new_nickname)) > resize_len){
1706 resize_len = l;
1707 fs_resize((void **) tobufp, resize_len+1);
1710 strncpy(*tobufp, new_nickname, l);
1711 (*tobufp)[l] = '\0';
1714 fs_give((void **) &new_nickname);
1717 if(ambiguity != 2)
1718 Writechar(BELL, 0);
1721 break;
1723 case 4: /* can't suspend */
1724 default:
1725 break;
1729 finish:
1730 if(ba_fcc.tptr)
1731 fs_give((void **)&ba_fcc.tptr);
1733 pine_free_env(&header);
1735 return(retval);
1740 * pine_simple_send_header - generate header suitable for simple_sending
1742 METAENV *
1743 pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp)
1745 METAENV *header;
1746 PINEFIELD *pf;
1747 static struct headerentry he_dummy;
1749 header = pine_new_env(outgoing, fccp, tobufpp, NULL);
1751 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1752 for(pf = header->local; pf && pf->name; pf = pf->next)
1753 if(pf->type == Address && !strucmp(pf->name, "to")){
1754 memset((void *) &he_dummy, 0, sizeof(he_dummy));
1755 pf->extdata = (void *) &he_dummy;
1756 HE(pf)->dirty = 1;
1757 break;
1760 return(header);
1765 /*----------------------------------------------------------------------
1766 Prepare data structures for pico, call pico, then post message
1768 Args: outgoing -- Partially formatted outgoing ENVELOPE
1769 body -- Body of outgoing message
1770 editor_title -- Title for anchor line in composer
1771 fcc_arg -- The file carbon copy field
1772 reply -- Struct describing set of msgs being replied-to
1773 lcc_arg --
1774 custom -- custom header list.
1775 sticky_fcc --
1777 Result: message is edited, then postponed, cancelled or sent.
1779 Fields:
1780 remail -
1781 return_path -
1782 date added here
1783 from added here
1784 sender -
1785 reply_to -
1786 subject passed in, edited and cannonized here
1787 to possibly passed in, edited and cannonized here
1788 cc possibly passed in, edited and cannonized here
1789 bcc edited and cannonized here
1790 in_reply_to generated in reply() and passed in
1791 message_id -
1793 Storage for these fields comes from anywhere outside. It is remalloced
1794 here so the composer can realloc them if needed. The copies here are also
1795 freed here.
1797 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1798 with the first part TYPETEXT! All newlines in the text here also end with
1799 CRLF.
1801 There's a further assumption that the text in the TYPETEXT part is
1802 stored in a storage object (see filter.c).
1803 ----*/
1804 void
1805 pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
1806 char *editor_title, ACTION_S *role, char *fcc_arg,
1807 REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg,
1808 PINEFIELD *custom, int flags)
1810 int i, fixed_cnt, total_cnt, index,
1811 editor_result = 0, body_start = 0, use_news_order = 0;
1812 char *p, *addr, *fcc, *fcc_to_free = NULL;
1813 char *start_here_name = NULL;
1814 char *suggested_nntp_server = NULL;
1815 char *title = NULL;
1816 struct headerentry *he, *headents, *he_to, *he_fcc, *he_news = NULL, *he_lcc = NULL,
1817 *he_from = NULL;
1818 PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL,
1819 *pf_smtp_server, *pf_nntp_server,
1820 *pf_fcc = NULL, *pf_err, *pf_uid, *pf_mbox, *pf_curpos,
1821 *pf_ourrep, *pf_ourhdrs, **sending_order;
1822 METAENV header;
1823 ADDRESS *lcc_addr = NULL;
1824 ADDRESS *nobody_addr = NULL;
1825 BODY_PARTICULARS_S *bp;
1826 STORE_S *orig_so = NULL;
1827 PICO pbuf1, *save_previous_pbuf;
1828 CustomType ct;
1829 REDRAFT_POS_S *local_redraft_pos = NULL;
1831 dprint((1,"\n=== send called ===\n"));
1833 save_previous_pbuf = pbf;
1834 pbf = &pbuf1;
1835 standard_picobuf_setup(pbf);
1838 * Cancel any pending initial commands since pico uses a different
1839 * input routine. If we didn't cancel them, they would happen after
1840 * we returned from the editor, which would be confusing.
1842 if(ps_global->in_init_seq){
1843 ps_global->in_init_seq = 0;
1844 ps_global->save_in_init_seq = 0;
1845 clear_cursor_pos();
1846 if(ps_global->initial_cmds){
1847 if(ps_global->free_initial_cmds)
1848 fs_give((void **)&(ps_global->free_initial_cmds));
1850 ps_global->initial_cmds = 0;
1853 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1856 #if defined(DOS) || defined(OS2)
1857 if(!dos_valid_from()){
1858 pbf = save_previous_pbuf;
1859 return;
1862 pbf->upload = NULL;
1863 #else
1864 pbf->upload = (ps_global->VAR_UPLOAD_CMD
1865 && ps_global->VAR_UPLOAD_CMD[0])
1866 ? upload_msg_to_pico : NULL;
1867 #endif
1869 pbf->msgntext = message_format_for_pico;
1870 pbf->mimetype = mime_type_for_pico;
1871 pbf->exittest = send_exit_for_pico;
1872 pbf->user_says_noflow = dont_flow_this_time;
1873 pbf->newthread = new_thread_on_blank_subject;
1874 ps_global->newthread = 0; /* reset this value */
1875 if(F_OFF(F_CANCEL_CONFIRM, ps_global))
1876 pbf->canceltest = cancel_for_pico;
1877 #ifdef _WINDOWS
1878 pbf->dict = (ps_global->VAR_DICTIONARY
1879 && ps_global->VAR_DICTIONARY[0]
1880 && ps_global->VAR_DICTIONARY[0][0])
1881 ? ps_global->VAR_DICTIONARY : NULL;
1882 pbf->chosen_dict = -1; /* not chosen yet */
1883 #endif /* _WINDOWS */
1884 pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] &&
1885 ps_global->VAR_EDITOR[0][0])
1886 ? ps_global->VAR_EDITOR : NULL;
1887 pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0])
1888 ? ps_global->VAR_SPELLER : NULL;
1889 pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
1890 pbf->quote_str = reply && reply->prefix ? reply->prefix : "> ";
1891 /* We actually want to set this only if message we're sending is flowed */
1892 pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
1893 pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
1894 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
1895 && (strcmp(pbf->quote_str, "> ") == 0
1896 || strcmp(pbf->quote_str, ">") == 0));
1897 pbf->edit_offset = 0;
1898 title = cpystr(set_titlebar(editor_title,
1899 ps_global->mail_stream,
1900 ps_global->context_current,
1901 ps_global->cur_folder,ps_global->msgmap,
1902 0, FolderName, 0, 0, NULL));
1903 pbf->pine_anchor = title;
1905 #if defined(DOS) || defined(OS2)
1906 if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){
1907 pbf->oper_dir = ps_global->VAR_FILE_DIR;
1909 #endif
1911 if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE))
1912 pbf->pine_flags |= P_CHKPTNOW;
1914 /* NOTE: initial cursor position set below */
1916 dprint((9, "flags: %x\n", pbf->pine_flags));
1919 * When user runs compose and the current folder is a newsgroup,
1920 * offer to post to the current newsgroup.
1922 if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups))
1923 && IS_NEWS(ps_global->mail_stream)){
1924 char prompt[200], news_group[MAILTMPLEN];
1926 pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group,
1927 sizeof(news_group));
1930 * Replies don't get this far because To or Newsgroups will already
1931 * be filled in. So must be either ordinary compose or forward.
1932 * Forward sets subject, so use that to tell the difference.
1934 if(news_group[0] && !outgoing->subject){
1935 int ch = 'y';
1936 int ret_val;
1937 char *errmsg = NULL;
1938 BUILDER_ARG *fcc_build = NULL;
1940 if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
1941 snprintf(prompt, sizeof(prompt),
1942 _("Post to current newsgroup (%s)"), news_group);
1943 prompt[sizeof(prompt)-1] = '\0';
1944 ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM);
1947 switch(ch){
1948 case 'y':
1949 if(outgoing->newsgroups)
1950 fs_give((void **)&outgoing->newsgroups);
1952 if(!fcc_arg && !(role && role->fcc)){
1953 fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
1954 memset((void *)fcc_build, 0, sizeof(BUILDER_ARG));
1955 fcc_build->tptr = fcc_to_free;
1958 ret_val = news_build(news_group, &outgoing->newsgroups,
1959 &errmsg, fcc_build, NULL);
1961 if(ret_val == -1){
1962 if(outgoing->newsgroups)
1963 fs_give((void **)&outgoing->newsgroups);
1965 outgoing->newsgroups = cpystr(news_group);
1968 if(!fcc_arg && !(role && role->fcc)){
1969 fcc_arg = fcc_to_free = fcc_build->tptr;
1970 fs_give((void **)&fcc_build);
1973 if(errmsg){
1974 if(*errmsg){
1975 q_status_message(SM_ORDER, 3, 3, errmsg);
1976 display_message(NO_OP_COMMAND);
1979 fs_give((void **)&errmsg);
1982 break;
1984 case 'x': /* ^C */
1985 q_status_message(SM_ORDER, 0, 3, _("Message cancelled"));
1986 dprint((4, "=== send: cancelled\n"));
1987 pbf = save_previous_pbuf;
1988 return;
1990 case 'n':
1991 break;
1993 default:
1994 break;
1998 if(F_ON(F_PREDICT_NNTP_SERVER, ps_global)
1999 && outgoing->newsgroups && *outgoing->newsgroups
2000 && IS_NEWS(ps_global->mail_stream)){
2001 NETMBX news_mb;
2003 if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox,
2004 &news_mb))
2005 if(!strucmp(news_mb.service, "nntp")){
2006 if(*ps_global->mail_stream->original_mailbox == '{'){
2007 char *svcp = NULL, *psvcp;
2009 suggested_nntp_server =
2010 cpystr(ps_global->mail_stream->original_mailbox + 1);
2011 if((p = strindex(suggested_nntp_server, '}')) != NULL)
2012 *p = '\0';
2013 for(p = strindex(suggested_nntp_server, '/'); p && *p;
2014 p = strindex(p, '/')){
2015 /* take out /nntp, which gets added in nntp_open */
2016 if(!struncmp(p, "/nntp", 5))
2017 svcp = p + 5;
2018 else if(!struncmp(p, "/service=nntp", 13))
2019 svcp = p + 13;
2020 else if(!struncmp(p, "/service=\"nntp\"", 15))
2021 svcp = p + 15;
2022 else
2023 p++;
2024 if(svcp){
2025 if(*svcp == '\0')
2026 *p = '\0';
2027 else if(*svcp == '/' || *svcp == ':'){
2028 for(psvcp = p; *svcp; svcp++, psvcp++)
2029 *psvcp = *svcp;
2030 *psvcp = '\0';
2032 svcp = NULL;
2036 else
2037 suggested_nntp_server = cpystr(news_mb.orighost);
2042 * If we don't already have custom headers set and the role has custom
2043 * headers, then incorporate those custom headers into "custom".
2045 if(!custom){
2046 PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL;
2048 dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
2050 * If we allow the Combine argument here, we're saying that we want to
2051 * combine the values from the envelope and the role for the fields To,
2052 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
2053 * we'll have a To in the envelope because we're replying. If our role also
2054 * has a To action, then Combine would combine those two and offer both
2055 * to the user. We've decided against doing this. Instead, we always use
2056 * Replace, and the role's header value replaces the value from the
2057 * envelope. It might also make sense in some cases to do the opposite,
2058 * which would be treating the role headers as defaults, just like
2059 * customized-hdrs.
2061 #ifdef WANT_TO_COMBINE_ADDRESSES
2062 if(role && role->cstm)
2063 rolehdrs = parse_custom_hdrs(role->cstm, Combine);
2064 #else
2065 if(role && role->cstm)
2066 rolehdrs = parse_custom_hdrs(role->cstm, Replace);
2067 #endif
2069 if(rolehdrs){
2070 custom = combine_custom_headers(dflthdrs, rolehdrs);
2071 if(dflthdrs){
2072 free_prompts(dflthdrs);
2073 free_customs(dflthdrs);
2076 if(rolehdrs){
2077 free_prompts(rolehdrs);
2078 free_customs(rolehdrs);
2081 else
2082 custom = dflthdrs;
2085 g_rolenick = role ? role->nick : NULL;
2087 /* how many fixed fields are there? */
2088 for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++)
2091 total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1);
2093 /* the fixed part of the PINEFIELDs */
2094 i = fixed_cnt * sizeof(PINEFIELD);
2095 pfields = (PINEFIELD *)fs_get((size_t) i);
2096 memset(pfields, 0, (size_t) i);
2098 /* temporary headerentry array for pico */
2099 i = (total_cnt + 1) * sizeof(struct headerentry);
2100 headents = (struct headerentry *)fs_get((size_t) i);
2101 memset(headents, 0, (size_t) i);
2103 i = total_cnt * sizeof(PINEFIELD *);
2104 sending_order = (PINEFIELD **)fs_get((size_t) i);
2105 memset(sending_order, 0, (size_t) i);
2107 pbf->headents = headents;
2108 header.env = outgoing;
2109 header.local = pfields;
2110 header.sending_order = sending_order;
2112 /* custom part of PINEFIELDs */
2113 header.custom = custom;
2115 he = headents;
2116 pf = pfields;
2119 * For Address types, pf->addr points to an ADDRESS *.
2120 * If that address is in the "outgoing" envelope, it will
2121 * be freed by the caller, otherwise, it should be freed here.
2122 * Pf->textbuf for an Address is used a little to set up a default,
2123 * but then is freed right away below. Pf->scratch is used for a
2124 * pointer to some alloced space for pico to edit in. Addresses in
2125 * the custom area are freed by free_customs().
2127 * For FreeText types, pf->addr is not used. Pf->text points to a
2128 * pointer that points to the text. Pf->textbuf points to a copy of
2129 * the text that must be freed before we leave, otherwise, it is
2130 * probably a pointer into the envelope and that gets freed by the
2131 * caller.
2133 * He->realaddr is the pointer to the text that pico actually edits.
2136 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2137 # define NN 4
2138 #else
2139 # define NN 3
2140 #endif
2142 if(outgoing->newsgroups && *outgoing->newsgroups)
2143 use_news_order++;
2145 /* initialize the fixed header elements of the two temp arrays */
2146 for(i=0; i < fixed_cnt; i++, pf++){
2147 static int news_order[] = {
2148 N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC,
2149 N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY,
2150 N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX,
2151 N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS
2152 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2153 , N_SENDER
2154 #endif
2157 index = i;
2158 /* slightly different editing order if sending to news */
2159 if(use_news_order &&
2160 index >= 0 && index < sizeof(news_order)/sizeof(news_order[0]))
2161 index = news_order[i];
2163 /* copy the templates */
2164 *he = he_template[index];
2166 pf->name = cpystr(pf_template[index].name);
2167 if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global)){
2168 /* slide string over so it is Sender instead of X-X-Sender */
2169 for(p = pf->name+4; *p != '\0'; p++)
2170 *(p-4) = *p;
2171 *(p-4) = '\0';
2173 pf->type = pf_template[index].type;
2174 pf->canedit = pf_template[index].canedit;
2175 pf->rcptto = pf_template[index].rcptto;
2176 pf->writehdr = pf_template[index].writehdr;
2177 pf->localcopy = pf_template[index].localcopy;
2178 pf->extdata = he;
2179 pf->next = pf + 1;
2181 he->rich_header = view_as_rich(pf->name, he->rich_header);
2182 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2183 he->nickcmpl = NULL;
2185 switch(pf->type){
2186 case FreeText: /* realaddr points to c-client env */
2187 if(index == N_NEWS){
2188 sending_order[1] = pf;
2189 he->realaddr = &outgoing->newsgroups;
2190 he_news = he;
2192 switch(set_default_hdrval(pf, custom)){
2193 case Replace:
2194 if(*he->realaddr)
2195 fs_give((void **)he->realaddr);
2197 *he->realaddr = pf->textbuf;
2198 pf->textbuf = NULL;
2199 he->sticky = 1;
2200 break;
2202 case Combine:
2203 if(*he->realaddr){ /* combine values */
2204 if(pf->textbuf && *pf->textbuf){
2205 char *combined_hdr;
2206 size_t l;
2208 l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
2209 combined_hdr = (char *) fs_get((l+1) * sizeof(char));
2210 strncpy(combined_hdr, *he->realaddr, l);
2211 combined_hdr[l] = '\0';
2212 strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
2213 combined_hdr[l] = '\0';
2214 strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
2215 combined_hdr[l] = '\0';
2217 fs_give((void **)he->realaddr);
2218 *he->realaddr = combined_hdr;
2219 q_status_message(SM_ORDER, 3, 3,
2220 "Adding newsgroup from role");
2221 he->sticky = 1;
2224 else{
2225 *he->realaddr = pf->textbuf;
2226 pf->textbuf = NULL;
2229 break;
2231 case UseAsDef:
2232 /* if no value, use default */
2233 if(!*he->realaddr){
2234 *he->realaddr = pf->textbuf;
2235 pf->textbuf = NULL;
2238 break;
2240 case NoMatch:
2241 break;
2244 /* If there is a newsgroup, we'd better show it */
2245 if(outgoing->newsgroups && *outgoing->newsgroups)
2246 he->rich_header = 0; /* force on by default */
2248 if(pf->textbuf)
2249 fs_give((void **)&pf->textbuf);
2251 pf->text = he->realaddr;
2253 else if(index == N_DATE){
2254 sending_order[2] = pf;
2255 pf->text = (char **) &outgoing->date;
2256 pf->extdata = NULL;
2258 else if(index == N_INREPLY){
2259 sending_order[NN+9] = pf;
2260 pf->text = &outgoing->in_reply_to;
2261 pf->extdata = NULL;
2263 else if(index == N_MSGID){
2264 sending_order[NN+10] = pf;
2265 pf->text = &outgoing->message_id;
2266 pf->extdata = NULL;
2268 else if(index == N_REF){
2269 sending_order[NN+11] = pf;
2270 pf->text = &outgoing->references;
2271 pf->extdata = NULL;
2273 else if(index == N_PRIORITY){
2274 sending_order[NN+12] = pf;
2275 pf->text = &pf->textbuf;
2276 pf->extdata = NULL;
2278 else if(index == N_USERAGENT){
2279 sending_order[NN+13] = pf;
2280 pf->text = &pf->textbuf;
2281 pf->textbuf = generate_user_agent();
2282 pf->extdata = NULL;
2284 else if(index == N_POSTERR){
2285 sending_order[NN+14] = pf;
2286 pf_err = pf;
2287 pf->text = &pf->textbuf;
2288 pf->extdata = NULL;
2290 else if(index == N_RPLUID){
2291 sending_order[NN+15] = pf;
2292 pf_uid = pf;
2293 pf->text = &pf->textbuf;
2294 pf->extdata = NULL;
2296 else if(index == N_RPLMBOX){
2297 sending_order[NN+16] = pf;
2298 pf_mbox = pf;
2299 pf->text = &pf->textbuf;
2300 pf->extdata = NULL;
2302 else if(index == N_SMTP){
2303 sending_order[NN+17] = pf;
2304 pf_smtp_server = pf;
2305 pf->text = &pf->textbuf;
2306 pf->extdata = NULL;
2308 else if(index == N_NNTP){
2309 sending_order[NN+18] = pf;
2310 pf_nntp_server = pf;
2311 pf->text = &pf->textbuf;
2312 pf->extdata = NULL;
2314 else if(index == N_CURPOS){
2315 sending_order[NN+19] = pf;
2316 pf_curpos = pf;
2317 pf->text = &pf->textbuf;
2318 pf->extdata = NULL;
2320 else if(index == N_OURREPLYTO){
2321 sending_order[NN+20] = pf;
2322 pf_ourrep = pf;
2323 pf->text = &pf->textbuf;
2324 pf->extdata = NULL;
2326 else if(index == N_OURHDRS){
2327 sending_order[NN+21] = pf;
2328 pf_ourhdrs = pf;
2329 pf->text = &pf->textbuf;
2330 pf->extdata = NULL;
2332 else if(index == N_AUTHRCVD){
2333 sending_order[0] = pf;
2334 pf_ourhdrs = pf;
2335 pf->text = &pf->textbuf;
2336 pf->extdata = NULL;
2338 else{
2339 q_status_message(SM_ORDER | SM_DING, 3, 7,
2340 "Botched: Unmatched FreeText header in pine_send");
2343 break;
2345 /* can't do a default for this one */
2346 case Attachment:
2347 /* If there is an attachment already, we'd better show them */
2348 if(body && *body && (*body)->type != TYPETEXT)
2349 he->rich_header = 0; /* force on by default */
2351 break;
2353 case Address:
2354 switch(index){
2355 case N_FROM:
2356 sending_order[3] = pf;
2357 pf->addr = &outgoing->from;
2358 if(role && role->from){
2359 if(ps_global->never_allow_changing_from)
2360 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2361 else{
2362 outgoing->from = copyaddrlist(role->from);
2363 he->display_it = 1; /* show it */
2364 he->rich_header = 0;
2368 he_from = he;
2369 break;
2371 case N_TO:
2372 sending_order[NN+2] = pf;
2373 pf->addr = &outgoing->to;
2374 /* If already set, make it act like we typed it in */
2375 if(outgoing->to
2376 && outgoing->to->mailbox
2377 && outgoing->to->mailbox[0]
2378 && flags & PS_STICKY_TO)
2379 he->sticky = 1;
2381 he_to = he;
2382 pf_to = pf;
2383 break;
2385 case N_NOBODY:
2386 sending_order[NN+5] = pf;
2387 pf_nobody = pf;
2388 if(ps_global->VAR_EMPTY_HDR_MSG
2389 && !ps_global->VAR_EMPTY_HDR_MSG[0]){
2390 pf->addr = NULL;
2392 else{
2393 nobody_addr = mail_newaddr();
2394 nobody_addr->next = mail_newaddr();
2395 nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
2396 SIZEOF_20KBUF,
2397 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
2398 ? ps_global->VAR_EMPTY_HDR_MSG
2399 : "undisclosed-recipients"),
2400 ps_global->posting_charmap));
2401 pf->addr = &nobody_addr;
2404 break;
2406 case N_CC:
2407 sending_order[NN+3] = pf;
2408 pf->addr = &outgoing->cc;
2409 break;
2411 case N_BCC:
2412 sending_order[NN+4] = pf;
2413 pf->addr = &outgoing->bcc;
2414 /* if bcc exists, make sure it's exposed so nothing's
2415 * sent by mistake...
2417 if(outgoing->bcc)
2418 he->display_it = 1;
2420 break;
2422 case N_REPLYTO:
2423 sending_order[NN+1] = pf;
2424 pf->addr = &outgoing->reply_to;
2425 if(role && role->replyto){
2426 if(outgoing->reply_to)
2427 mail_free_address(&outgoing->reply_to);
2429 outgoing->reply_to = copyaddrlist(role->replyto);
2430 he->display_it = 1; /* show it */
2431 he->rich_header = 0;
2434 break;
2436 case N_LCC:
2437 sending_order[NN+7] = pf;
2438 pf->addr = &lcc_addr;
2439 he_lcc = he;
2440 if(lcc_arg){
2441 build_address(lcc_arg, &addr, NULL, NULL, NULL);
2442 rfc822_parse_adrlist(&lcc_addr, addr,
2443 ps_global->maildomain);
2444 fs_give((void **)&addr);
2445 he->display_it = 1;
2448 break;
2450 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2451 case N_SENDER:
2452 sending_order[4] = pf;
2453 pf->addr = &outgoing->sender;
2454 break;
2455 #endif
2457 default:
2458 q_status_message1(SM_ORDER,3,7,
2459 "Internal error: Address header %s", comatose(index));
2460 break;
2464 * If this is a reply to news, don't show the regular email
2465 * recipient headers (unless they are non-empty).
2467 if((outgoing->newsgroups && *outgoing->newsgroups)
2468 && (index == N_TO || index == N_CC
2469 || index == N_BCC || index == N_LCC)
2470 && (pf->addr && !*pf->addr)){
2471 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2472 pf->textbuf && *pf->textbuf){
2473 removing_trailing_white_space(pf->textbuf);
2474 (void)removing_double_quotes(pf->textbuf);
2475 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2476 rfc822_parse_adrlist(pf->addr, addr,
2477 ps_global->maildomain);
2478 fs_give((void **)&addr);
2479 if(ct > UseAsDef)
2480 he->sticky = 1;
2482 else
2483 he->rich_header = 1; /* hide */
2487 * If this address doesn't already have a value, then we check
2488 * for a default value assigned by the user.
2490 else if(pf->addr && !*pf->addr){
2491 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2492 (index != N_FROM ||
2493 (!ps_global->never_allow_changing_from &&
2494 F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
2495 pf->textbuf && *pf->textbuf){
2497 removing_trailing_white_space(pf->textbuf);
2498 (void)removing_double_quotes(pf->textbuf);
2501 * Try to set To based on Lcc. Don't attempt Fcc.
2503 if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
2504 BUILDER_ARG *barg = NULL;
2505 char *ppp = NULL;
2507 if(*pf_to->addr)
2508 ppp = addr_list_string(*pf_to->addr, NULL, 1);
2510 if(!ppp)
2511 ppp = cpystr("");
2513 barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
2514 memset(barg, 0, sizeof(*barg));
2515 barg->me = &(he->bldr_private);
2516 barg->aff = &(he_to->bldr_private);
2517 barg->tptr = cpystr(ppp);
2519 build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
2520 he->display_it = 1;
2522 rfc822_parse_adrlist(pf->addr, addr,
2523 ps_global->maildomain);
2524 if(addr)
2525 fs_give((void **) &addr);
2527 if(ct > UseAsDef)
2528 he->sticky = 1;
2530 if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
2531 ADDRESS *a = NULL;
2533 rfc822_parse_adrlist(&a, barg->tptr,
2534 ps_global->maildomain);
2535 if(a){
2536 if(pf_to->addr)
2537 mail_free_address(pf_to->addr);
2539 *pf_to->addr = a;
2543 if(barg){
2544 if(barg->tptr)
2545 fs_give((void **) &barg->tptr);
2547 fs_give((void **) &barg);
2550 if(ppp)
2551 fs_give((void **) &ppp);
2553 else{
2554 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2555 rfc822_parse_adrlist(pf->addr, addr,
2556 ps_global->maildomain);
2557 if(addr)
2558 fs_give((void **) &addr);
2560 if(ct > UseAsDef)
2561 he->sticky = 1;
2565 /* if we still don't have a from */
2566 if(index == N_FROM && !*pf->addr)
2567 *pf->addr = generate_from();
2571 * Addr is already set in the rest of the cases.
2573 else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
2574 ADDRESS *adr = NULL;
2577 * We get to this case of the ifelse if the from or reply-to
2578 * addr was set by a role above.
2581 /* figure out the default value */
2582 (void)set_default_hdrval(pf, custom);
2583 if(pf->textbuf && *pf->textbuf){
2584 removing_trailing_white_space(pf->textbuf);
2585 (void)removing_double_quotes(pf->textbuf);
2586 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2587 rfc822_parse_adrlist(&adr, addr,
2588 ps_global->maildomain);
2589 fs_give((void **)&addr);
2592 /* if value set by role is different from default, show it */
2593 if(adr && !address_is_same(*pf->addr, adr))
2594 he->display_it = 1; /* start this off showing */
2596 /* malformed */
2597 if(!(*pf->addr)->mailbox){
2598 fs_give((void **)pf->addr);
2599 he->display_it = 1;
2602 if(adr)
2603 mail_free_address(&adr);
2605 else if((index == N_TO || index == N_CC || index == N_BCC)
2606 && pf->addr){
2607 ADDRESS *a = NULL, **tail;
2610 * These three are different from the others because we
2611 * might add the addresses to what is already there instead
2612 * of replacing.
2615 switch(set_default_hdrval(pf, custom)){
2616 case Replace:
2617 if(*pf->addr)
2618 mail_free_address(pf->addr);
2620 removing_trailing_white_space(pf->textbuf);
2621 (void)removing_double_quotes(pf->textbuf);
2622 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2623 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2624 fs_give((void **)&addr);
2625 he->sticky = 1;
2626 break;
2628 case Combine:
2629 removing_trailing_white_space(pf->textbuf);
2630 (void)removing_double_quotes(pf->textbuf);
2631 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2632 rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
2633 fs_give((void **)&addr);
2634 he->sticky = 1;
2635 if(a){
2636 for(tail = pf->addr; *tail; tail = &(*tail)->next)
2638 *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
2639 *pf->addr, NULL, a, RCA_ALL);
2640 q_status_message(SM_ORDER, 3, 3,
2641 "Adding addresses from role");
2642 mail_free_address(&a);
2645 break;
2647 case UseAsDef:
2648 case NoMatch:
2649 break;
2652 he->display_it = 1; /* start this off showing */
2654 else if(pf->addr){
2655 switch(set_default_hdrval(pf, custom)){
2656 case Replace:
2657 case Combine:
2658 if(*pf->addr)
2659 mail_free_address(pf->addr);
2661 removing_trailing_white_space(pf->textbuf);
2662 (void)removing_double_quotes(pf->textbuf);
2663 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2664 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2665 fs_give((void **)&addr);
2666 he->sticky = 1;
2667 break;
2669 case UseAsDef:
2670 case NoMatch:
2671 break;
2674 he->display_it = 1;
2677 if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
2678 mail_free_address(pf->addr);
2679 he->display_it = 1; /* start this off showing */
2682 if(pf->textbuf) /* free default value in any case */
2683 fs_give((void **)&pf->textbuf);
2685 /* outgoing2strings will alloc the string pf->scratch below */
2686 he->realaddr = &pf->scratch;
2687 break;
2689 case Fcc:
2690 sending_order[NN+8] = pf;
2691 pf_fcc = pf;
2692 if(role && role->fcc)
2693 fcc = role->fcc;
2694 else
2695 fcc = get_fcc(fcc_arg);
2697 if(fcc_to_free){
2698 fs_give((void **)&fcc_to_free);
2699 fcc_arg = NULL;
2702 if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
2703 he->sticky = 1;
2705 if(role)
2706 role->fcc = NULL;
2708 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
2709 he->display_it = 1; /* start this off showing */
2711 he->realaddr = &fcc;
2712 pf->text = &fcc;
2713 he_fcc = he;
2714 break;
2716 case Subject :
2717 sending_order[NN+6] = pf;
2719 switch(set_default_hdrval(pf, custom)){
2720 case Replace:
2721 case Combine:
2722 pf->scratch = pf->textbuf;
2723 pf->textbuf = NULL;
2724 he->sticky = 1;
2725 if(outgoing->subject)
2726 fs_give((void **)&outgoing->subject);
2728 break;
2730 case UseAsDef:
2731 case NoMatch:
2732 /* if no value, use default */
2733 if(outgoing->subject){
2734 pf->scratch = cpystr(outgoing->subject);
2736 else{
2737 pf->scratch = pf->textbuf;
2738 pf->textbuf = NULL;
2741 break;
2744 he->realaddr = &pf->scratch;
2745 pf->text = &outgoing->subject;
2746 break;
2748 default:
2749 q_status_message1(SM_ORDER,3,7,
2750 "Unknown header type %d in pine_send",
2751 (void *)pf->type);
2752 break;
2756 * We may or may not want to give the user the chance to edit
2757 * the From and Reply-To lines. If they are listed in either
2758 * Default-composer-hdrs or Customized-hdrs, then they can edit
2759 * them, else no.
2760 * If canedit is not set, that means that this header is not in
2761 * the user's customized-hdrs. If rich_header is set, that
2762 * means that this header is not in the user's
2763 * default-composer-hdrs (since From and Reply-To are rich
2764 * by default). So, don't give it an he to edit with in that case.
2766 * For other types, just not setting canedit will cause it to be
2767 * uneditable, regardless of what the user does.
2769 switch(index){
2770 case N_FROM:
2771 /* to allow it, we let this fall through to the reply-to case below */
2772 if(ps_global->never_allow_changing_from ||
2773 (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
2774 !(role && role->from))){
2775 if(pf->canedit || !he->rich_header)
2776 q_status_message(SM_ORDER, 3, 3,
2777 _("Not allowed to change header \"From\""));
2779 memset(he, 0, (size_t)sizeof(*he));
2780 pf->extdata = NULL;
2781 break;
2784 case N_REPLYTO:
2785 if(!pf->canedit && he->rich_header){
2786 memset(he, 0, (size_t)sizeof(*he));
2787 pf->extdata = NULL;
2789 else{
2790 pf->canedit = 1;
2791 he++;
2794 break;
2796 default:
2797 if(!pf->canedit){
2798 memset(he, 0, (size_t)sizeof(*he));
2799 pf->extdata = NULL;
2801 else
2802 he++;
2804 break;
2809 * This is so the builder can tell the composer to fill the affected
2810 * field based on the value in the field on the left.
2812 * Note that this mechanism isn't completely general. Each entry has
2813 * only a single next_affected, so if some other entry points an
2814 * affected entry at an entry with a next_affected, they all inherit
2815 * that next_affected. Since this isn't used much a careful ordering
2816 * of the affected fields should make it a sufficient mechanism.
2818 he_to->affected_entry = he_fcc;
2819 he_news->affected_entry = he_fcc;
2820 he_lcc->affected_entry = he_to;
2821 he_to->next_affected = he_fcc;
2823 (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
2825 i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2827 * Set up headerentries for custom fields.
2828 * NOTE: "i" is assumed to now index first custom field in sending
2829 * order.
2831 for(pf = pf->next; pf && pf->name; pf = pf->next){
2832 char *addr;
2834 if(pf->standard)
2835 continue;
2837 pf->extdata = he;
2838 pf->canedit = 1;
2839 pf->rcptto = 0;
2840 pf->writehdr = 1;
2841 pf->localcopy = 1;
2843 switch(pf->type){
2844 case Address:
2845 if(pf->addr){ /* better be set */
2846 sending_order[i++] = pf;
2847 *he = he_custom_addr_templ;
2848 /* change default text into an ADDRESS */
2849 /* strip quotes around whole default */
2850 removing_trailing_white_space(pf->textbuf);
2851 (void)removing_double_quotes(pf->textbuf);
2852 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2853 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2854 fs_give((void **)&addr);
2855 if(pf->textbuf)
2856 fs_give((void **)&pf->textbuf);
2858 he->realaddr = &pf->scratch;
2859 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2860 he->nickcmpl = NULL;
2863 break;
2865 case FreeText:
2866 sending_order[i++] = pf;
2867 *he = he_custom_free_templ;
2868 he->realaddr = &pf->textbuf;
2869 pf->text = &pf->textbuf;
2870 if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
2871 (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
2872 he->display_it = 1; /* show it */
2874 break;
2876 default:
2877 q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
2878 (void *)pf->type);
2879 break;
2882 he->name = pf->name;
2884 /* use first 8 characters for prompt */
2885 he->prompt = cpystr(" : ");
2886 strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
2888 he->rich_header = view_as_rich(he->name, he->rich_header);
2889 he++;
2893 * Make sure at least *one* field is displayable...
2895 for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
2896 if(HE(pf) && !HE(pf)->rich_header){
2897 index = i;
2898 break;
2902 * None displayable!!! Warn and display defaults.
2904 if(index == -1){
2905 q_status_message(SM_ORDER,0,5,
2906 "No default-composer-hdrs matched, displaying defaults");
2907 for(i = 0, pf = header.local; pf; pf = pf->next, i++)
2908 if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
2909 && HE(pf))
2910 HE(pf)->rich_header = 0;
2914 * Save information about body which set_mime_type_by_grope might change.
2915 * Then, if we get an error sending, we reset these things so that
2916 * grope can do it's thing again after we edit some more.
2918 if ((*body)->type == TYPEMULTIPART)
2919 bp = save_body_particulars(&(*body)->nested.part->body);
2920 else
2921 bp = save_body_particulars(*body);
2924 local_redraft_pos = redraft_pos;
2926 /*----------------------------------------------------------------------
2927 Loop calling the editor until everything goes well
2928 ----*/
2929 while(1){
2930 int saved_user_timeout;
2932 /* Reset body to what it was when we started. */
2933 if ((*body)->type == TYPEMULTIPART)
2934 reset_body_particulars(bp, &(*body)->nested.part->body);
2935 else
2936 reset_body_particulars(bp,*body);
2938 * set initial cursor position based on how many times we've been
2939 * thru the loop...
2941 if(reply && reply->pseudo){
2942 pbf->pine_flags |= reply->data.pico_flags;
2944 else if(body_start){
2945 pbf->pine_flags |= P_BODY;
2946 body_start = 0; /* maybe not next time */
2948 else if(local_redraft_pos){
2949 pbf->edit_offset = local_redraft_pos->offset;
2950 /* set the start_here bit in correct header */
2951 for(pf = header.local; pf && pf->name; pf = pf->next)
2952 if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
2953 && HE(pf)){
2954 HE(pf)->start_here = 1;
2955 break;
2958 /* If didn't find it, we start in body. */
2959 if(!pf || !pf->name)
2960 pbf->pine_flags |= P_BODY;
2962 else if(reply && (!reply->forw && !reply->forwarded)){
2963 pbf->pine_flags |= P_BODY;
2966 /* in case these were turned on in previous pass through loop */
2967 if(pf_nobody){
2968 pf_nobody->writehdr = 0;
2969 pf_nobody->localcopy = 0;
2972 if(pf_fcc)
2973 pf_fcc->localcopy = 0;
2976 * If a sending attempt failed after we passed the message text
2977 * thru a user-defined filter, "orig_so" points to the original
2978 * text. Replace the body's encoded data with the original...
2980 if(orig_so){
2981 STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
2982 ? &(*body)->nested.part->body.contents.text.data
2983 : &(*body)->contents.text.data);
2984 so_give(so);
2985 *so = orig_so;
2986 orig_so = NULL;
2990 * Convert the envelope and body to the string format that
2991 * pico can edit
2993 outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
2995 for(pf = header.local; pf && pf->name; pf = pf->next){
2997 * If this isn't the first time through this loop, we may have
2998 * freed some of the FreeText headers below so that they wouldn't
2999 * show up as empty headers in the finished message. Need to
3000 * alloc them again here so they can be edited.
3002 if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
3003 *HE(pf)->realaddr = cpystr("");
3005 if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
3006 HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
3010 * If From is exposed, probably by a role, then start the cursor
3011 * on the first line which isn't filled in. If it isn't, then we
3012 * don't move the cursor, mostly for back-compat.
3014 if((!reply || reply->forw || reply->forwarded) &&
3015 !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
3016 (he_from->display_it || !he_from->rich_header)){
3017 for(pf = header.local; pf && pf->name; pf = pf->next)
3018 if(HE(pf) &&
3019 (HE(pf)->display_it || !HE(pf)->rich_header) &&
3020 HE(pf)->realaddr &&
3021 (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
3022 HE(pf)->start_here = 1;
3023 break;
3027 #ifdef _WINDOWS
3028 mswin_setwindowmenu (MENU_COMPOSER);
3029 #endif
3031 cancel_busy_cue(-1);
3032 flush_status_messages(1);
3034 /* turn off user input timeout when in composer */
3035 saved_user_timeout = ps_global->hours_to_timeout;
3036 ps_global->hours_to_timeout = 0;
3037 dprint((1, "\n ---- COMPOSER ----\n"));
3038 editor_result = pico(pbf);
3039 dprint((4, "... composer returns (0x%x)\n", editor_result));
3040 ps_global->hours_to_timeout = saved_user_timeout;
3042 #ifdef _WINDOWS
3043 mswin_setwindowmenu (MENU_DEFAULT);
3044 #endif
3045 fix_windsize(ps_global);
3048 * Only reinitialize signals if we didn't receive an interesting
3049 * one while in pico, since pico's return is part of processing that
3050 * signal and it should continue to be ignored.
3052 if(!(editor_result & COMP_GOTHUP))
3053 init_signals(); /* Pico has it's own signal stuff */
3056 * We're going to save in DEADLETTER. Dump attachments first.
3058 if(editor_result & COMP_CANCEL)
3059 free_attachment_list(&pbf->attachments);
3061 /* Turn strings back into structures */
3062 strings2outgoing(&header, body, pbf->attachments, flowing_requested);
3064 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3065 if(outgoing->newsgroups){
3066 sqzspaces(outgoing->newsgroups);
3067 if(!outgoing->newsgroups[0])
3068 fs_give((void **)&(outgoing->newsgroups));
3071 /* Make subject NULL if it is "" (so won't show up in headers) */
3072 if(outgoing->subject && !outgoing->subject[0])
3073 fs_give((void **)&(outgoing->subject));
3075 /* remove custom fields that are empty */
3076 for(pf = header.local; pf && pf->name; pf = pf->next){
3077 if(pf->type == FreeText && pf->textbuf){
3078 if(pf->textbuf[0] == '\0'){
3079 fs_give((void **)&pf->textbuf);
3080 pf->text = NULL;
3085 removing_trailing_white_space(fcc);
3087 /*-------- Stamp it with a current date -------*/
3088 if(outgoing->date) /* update old date */
3089 fs_give((void **)&(outgoing->date));
3091 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3092 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
3094 rfc822_date(tmp_20k_buf); /* format and copy new date */
3095 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3096 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
3098 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
3100 /* Set return_path based on From which is going to be used */
3101 if(outgoing->return_path)
3102 mail_free_address(&outgoing->return_path);
3104 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
3107 * Don't ever believe the sender that is there.
3108 * If From doesn't look quite right, generate our own sender.
3110 if(outgoing->sender)
3111 mail_free_address(&outgoing->sender);
3114 * If the LHS of the address doesn't match, or the RHS
3115 * doesn't match one of localdomain or hostname,
3116 * then add a sender line (really X-X-Sender).
3118 * Don't add a personal_name since the user can change that.
3120 if(F_OFF(F_DISABLE_SENDER, ps_global)
3122 (!outgoing->from
3123 || !outgoing->from->mailbox
3124 || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
3125 || !outgoing->from->host
3126 || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
3127 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
3129 outgoing->sender = mail_newaddr();
3130 outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
3131 outgoing->sender->host = cpystr(ps_global->hostname);
3134 if(ps_global->newthread){
3135 if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
3136 if(outgoing->references) fs_give((void **)&outgoing->references);
3139 /*----- Message is edited, now decide what to do with it ----*/
3140 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
3141 /*=========== Postpone or Interrupted message ============*/
3142 CONTEXT_S *fcc_cntxt = NULL;
3143 char folder[MAXPATH+1];
3144 int fcc_result = 0;
3145 char label[50];
3147 dprint((4, "pine_send:%s handling\n",
3148 (editor_result & COMP_SUSPEND)
3149 ? "SUSPEND"
3150 : (editor_result & COMP_GOTHUP)
3151 ? "HUP"
3152 : (editor_result & COMP_CANCEL)
3153 ? "CANCEL" : "HUH?"));
3154 if((editor_result & COMP_CANCEL)
3155 && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
3156 || ps_global->deadlets == 0)){
3157 q_status_message(SM_ORDER, 0, 3, "Message cancelled");
3158 break;
3162 * The idea here is to use the Fcc: writing facility
3163 * to append to the special postponed message folder...
3165 * NOTE: the strategy now is to write the message and
3166 * all attachments as they exist at composition time.
3167 * In other words, attachments are postponed by value
3168 * and not reference. This may change later, but we'll
3169 * need a local "message/external-body" type that
3170 * outgoing2strings knows how to properly set up for
3171 * the composer. Maybe later...
3174 label[0] = '\0';
3176 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
3177 && (editor_result & COMP_SUSPEND)
3178 && (check_addresses(&header) == CA_BAD)){
3179 /*--- Addresses didn't check out---*/
3180 q_status_message(SM_ORDER, 7, 7,
3181 _("Not allowed to postpone message until addresses are qualified"));
3182 continue;
3186 * Build the local message copy so.
3188 * In the HUP case, we'll write the bezerk delimiter by
3189 * hand and output the message directly into the folder.
3190 * It's not only faster, we don't have to worry about
3191 * c-client reentrance and less hands paw over the data so
3192 * there's less chance of a problem.
3194 * In the Postpone case, just create it if the user wants to
3195 * and create a temporary storage object to write into. */
3196 fake_hup:
3197 lmc.all_written = lmc.text_only = lmc.text_written = 0;
3198 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3199 int newfile = 1;
3200 time_t now = time((time_t *)0);
3202 #if defined(DOS) || defined(OS2)
3204 * we can't assume anything about root or home dirs, so
3205 * just plunk it down in the same place as the pinerc
3207 if(!getenv("HOME")){
3208 char *lc = last_cmpnt(ps_global->pinerc);
3209 folder[0] = '\0';
3210 if(lc != NULL){
3211 strncpy(folder,ps_global->pinerc,
3212 MIN(lc-ps_global->pinerc,sizeof(folder)-1));
3213 folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
3216 strncat(folder, (editor_result & COMP_GOTHUP)
3217 ? INTERRUPTED_MAIL : DEADLETTER,
3218 sizeof(folder)-strlen(folder)-1);
3220 else
3221 #endif
3222 build_path(folder,
3223 ps_global->VAR_OPER_DIR
3224 ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
3225 (editor_result & COMP_GOTHUP)
3226 ? INTERRUPTED_MAIL : DEADLETTER,
3227 sizeof(folder));
3229 if(editor_result & COMP_CANCEL){
3230 char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
3232 if(strlen(folder) + 1 < sizeof(filename))
3233 for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
3234 strncpy(filename, folder, sizeof(filename));
3235 filename[sizeof(filename)-1] = '\0';
3236 strncpy(newfname, filename, sizeof(newfname));
3237 newfname[sizeof(newfname)-1] = '\0';
3239 if(i > 1){
3240 snprintf(nbuf, sizeof(nbuf), "%d", i);
3241 nbuf[sizeof(nbuf)-1] = '\0';
3242 strncat(filename, nbuf,
3243 sizeof(filename)-strlen(filename)-1);
3244 filename[sizeof(filename)-1] = '\0';
3247 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
3248 nbuf[sizeof(nbuf)-1] = '\0';
3249 strncat(newfname, nbuf,
3250 sizeof(newfname)-strlen(newfname)-1);
3251 newfname[sizeof(newfname)-1] = '\0';
3252 (void) rename_file(filename, newfname);
3255 our_unlink(folder);
3257 else
3258 newfile = can_access(folder, ACCESS_EXISTS);
3260 if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
3261 if (outgoing->from){
3262 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
3263 newfile ? "" : "\015\012",
3264 outgoing->from->mailbox,
3265 outgoing->from->host, ctime(&now));
3266 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3267 if(!so_puts(lmc.so, tmp_20k_buf)){
3268 if(editor_result & COMP_CANCEL)
3269 q_status_message2(SM_ORDER | SM_DING, 3, 3,
3270 "Can't write \"%s\": %s",
3271 folder, error_description(errno));
3272 else
3273 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3274 folder ? folder : "?",
3275 error_description(errno)));
3280 else{ /* Must be COMP_SUSPEND */
3281 if(!ps_global->VAR_POSTPONED_FOLDER
3282 || !ps_global->VAR_POSTPONED_FOLDER[0]){
3283 q_status_message(SM_ORDER | SM_DING, 3, 3,
3284 _("No postponed file defined"));
3285 continue;
3289 * Store the cursor position
3291 * First find the header entry with the start_here
3292 * bit set, if any. This means the editor is telling
3293 * us to start on this header field next time.
3295 start_here_name = NULL;
3296 for(pf = header.local; pf && pf->name; pf = pf->next)
3297 if(HE(pf) && HE(pf)->start_here){
3298 start_here_name = pf->name;
3299 break;
3302 /* If there wasn't one, ":" means we start in the body. */
3303 if(!start_here_name || !*start_here_name)
3304 start_here_name = ":";
3306 if(ps_global->VAR_FORM_FOLDER
3307 && ps_global->VAR_FORM_FOLDER[0]
3308 && postpone_prompt() == 'f'){
3309 strncpy(folder, ps_global->VAR_FORM_FOLDER,
3310 sizeof(folder)-1);
3311 folder[sizeof(folder)-1] = '\0';
3312 strncpy(label, "form letter", sizeof(label));
3313 label[sizeof(label)-1] = '\0';
3315 else{
3316 strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
3317 sizeof(folder)-1);
3318 folder[sizeof(folder)-1] = '\0';
3319 strncpy(label, "postponed message", sizeof(label));
3320 label[sizeof(label)-1] = '\0';
3323 lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
3326 if(lmc.so){
3327 size_t sz;
3328 char *lmq = NULL;
3330 /* copy fcc line to postponed or interrupted folder */
3331 if(pf_fcc)
3332 pf_fcc->localcopy = 1;
3334 /* plug error into header for later display to user */
3335 if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
3336 pf_err->writehdr = 1;
3337 pf_err->localcopy = 1;
3338 pf_err->textbuf = lmq;
3342 * if reply, write (UID)folder header field so we can
3343 * later flag the replied-to message \\ANSWERED
3344 * DON'T save MSGNO's.
3346 if(reply && reply->uid){
3347 char uidbuf[MAILTMPLEN], *p;
3348 long i;
3350 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
3351 if(i)
3352 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
3354 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
3357 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3359 pf_uid->writehdr = 1;
3360 pf_uid->localcopy = 1;
3361 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
3362 reply->prefix ? int2string(strlen(reply->prefix))
3363 : (reply->forwarded) ? "": "0 ",
3364 reply->prefix ? " " : "",
3365 reply->prefix ? reply->prefix : "",
3366 i, reply->data.uid.validity,
3367 tmp_20k_buf, reply->mailbox);
3368 uidbuf[sizeof(uidbuf)-1] = '\0';
3369 pf_uid->textbuf = cpystr(uidbuf);
3372 * Logically, this ought to be part of pf_uid, but this
3373 * was added later and so had to be in a separate header
3374 * for backwards compatibility.
3376 pf_mbox->writehdr = 1;
3377 pf_mbox->localcopy = 1;
3378 pf_mbox->textbuf = cpystr(reply->origmbox
3379 ? reply->origmbox
3380 : reply->mailbox);
3383 /* Save cursor position */
3384 if(start_here_name && *start_here_name){
3385 char curposbuf[MAILTMPLEN];
3387 pf_curpos->writehdr = 1;
3388 pf_curpos->localcopy = 1;
3389 snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
3390 pbf->edit_offset);
3391 curposbuf[sizeof(curposbuf)-1] = '\0';
3392 pf_curpos->textbuf = cpystr(curposbuf);
3396 * Work around c-client reply-to bug. C-client will
3397 * return a reply_to in an envelope even if there is
3398 * no reply-to header field. We want to note here whether
3399 * the reply-to is real or not.
3401 if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
3402 pf_ourrep->writehdr = 1;
3403 pf_ourrep->localcopy = 1;
3404 if(outgoing->reply_to)
3405 pf_ourrep->textbuf = cpystr("Full");
3406 else
3407 pf_ourrep->textbuf = cpystr("Empty");
3410 /* Save the role-specific smtp server */
3411 if(role && role->smtp && role->smtp[0]){
3412 char *q, *smtp = NULL;
3413 char **lp;
3414 size_t len = 0;
3417 * Turn the list of smtp servers into a space-
3418 * delimited list in a single string.
3420 for(lp = role->smtp; (q = *lp) != NULL; lp++)
3421 len += (strlen(q) + 1);
3423 if(len){
3424 smtp = (char *) fs_get(len * sizeof(char));
3425 smtp[0] = '\0';
3426 for(lp = role->smtp; (q = *lp) != NULL; lp++){
3427 if(lp != role->smtp)
3428 strncat(smtp, " ", len-strlen(smtp)-1);
3430 strncat(smtp, q, len-strlen(smtp)-1);
3433 smtp[len-1] = '\0';
3436 pf_smtp_server->writehdr = 1;
3437 pf_smtp_server->localcopy = 1;
3438 if(smtp)
3439 pf_smtp_server->textbuf = smtp;
3440 else
3441 pf_smtp_server->textbuf = cpystr("");
3444 /* Save the role-specific nntp server */
3445 if(suggested_nntp_server ||
3446 (role && role->nntp && role->nntp[0])){
3447 char *q, *nntp = NULL;
3448 char **lp;
3449 size_t len = 0;
3451 if(role && role->nntp && role->nntp[0]){
3453 * Turn the list of nntp servers into a space-
3454 * delimited list in a single string.
3456 for(lp = role->nntp; (q = *lp) != NULL; lp++)
3457 len += (strlen(q) + 1);
3459 if(len){
3460 nntp = (char *) fs_get(len * sizeof(char));
3461 nntp[0] = '\0';
3462 for(lp = role->nntp; (q = *lp) != NULL; lp++){
3463 if(lp != role->nntp)
3464 strncat(nntp, " ", len-strlen(nntp)-1);
3466 strncat(nntp, q, len-strlen(nntp)-1);
3469 nntp[len-1] = '\0';
3472 else
3473 nntp = cpystr(suggested_nntp_server);
3475 pf_nntp_server->writehdr = 1;
3476 pf_nntp_server->localcopy = 1;
3477 if(nntp)
3478 pf_nntp_server->textbuf = nntp;
3479 else
3480 pf_nntp_server->textbuf = cpystr("");
3484 * Write the list of custom headers to the
3485 * X-Our-Headers header so that we can recover the
3486 * list in redraft.
3488 sz = 0;
3489 for(pf = header.custom; pf && pf->name; pf = pf->next)
3490 sz += strlen(pf->name) + 1;
3492 if(sz){
3493 char *q;
3495 pf_ourhdrs->writehdr = 1;
3496 pf_ourhdrs->localcopy = 1;
3497 pf_ourhdrs->textbuf = (char *)fs_get(sz);
3498 memset(pf_ourhdrs->textbuf, 0, sz);
3499 q = pf_ourhdrs->textbuf;
3500 for(pf = header.custom; pf && pf->name; pf = pf->next){
3501 if(pf != header.custom)
3502 sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
3504 sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
3507 pf_ourhdrs->textbuf[sz-1] = '\0';;
3511 * We need to make sure any header values that got cleared
3512 * get written to the postponed message (they won't if
3513 * pf->text is NULL). Otherwise, we can't tell previously
3514 * non-existent custom headers or default values from
3515 * custom (or other) headers that got blanked in the
3516 * composer...
3518 for(pf = header.local; pf && pf->name; pf = pf->next)
3519 if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
3520 *(HE(pf)->realaddr) = cpystr("");
3523 * We're saving the message for use later. It may be that the
3524 * characters in the message are not all convertible to the
3525 * user's posting_charmap. We'll save it as UTF-8 instead
3526 * and worry about that the next time they try to send it.
3527 * Use a different save pointer just to be sure we don't
3528 * mess up the other stuff. We should probably make the
3529 * charset an argument.
3531 * We also need to fix the charset of the body part
3532 * the user is editing so that we can read it back
3533 * successfully when we resume the composition.
3535 ps_global->post_utf8 = 1;
3538 PARAMETER *pm;
3539 BODY *bp;
3540 if((*body)->type == TYPEMULTIPART)
3541 bp = &(*body)->nested.part->body;
3542 else
3543 bp = *body;
3545 for(pm = bp->parameter;
3546 pm && strucmp(pm->attribute, "charset") != 0;
3547 pm = pm->next)
3550 if(pm){
3551 if(pm->value)
3552 fs_give((void **) &pm->value);
3554 pm->value = cpystr("UTF-8");
3558 if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
3559 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3560 char *err;
3561 STORE_S *hup_so;
3562 gf_io_t gc, pc;
3563 int we_cancel = 0;
3565 if(editor_result & COMP_CANCEL){
3566 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3567 "Saving to \"%s\"", folder);
3568 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3569 we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
3572 if((hup_so =
3573 so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
3574 gf_set_so_readc(&gc, lmc.so);
3575 gf_set_so_writec(&pc, hup_so);
3576 so_seek(lmc.so, 0L, 0); /* read msg copy and */
3577 so_seek(hup_so, 0L, 2); /* append to folder */
3578 gf_filter_init();
3579 gf_link_filter(gf_nvtnl_local, NULL);
3580 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
3581 dprint((1, "*** PIPE FAILED: %s\n",
3582 err ? err : "?"));
3584 gf_clear_so_readc(lmc.so);
3585 gf_clear_so_writec(hup_so);
3586 so_give(&hup_so);
3588 else
3589 dprint((1, "*** CAN'T CREATE %s: %s\n",
3590 folder ? folder : "?",
3591 error_description(errno)));
3593 if(we_cancel)
3594 cancel_busy_cue(-1);
3596 else
3597 fcc_result = write_fcc(folder, fcc_cntxt,
3598 lmc.so, NULL, label, NULL);
3601 /* discontinue coerced UTF-8 posting */
3602 ps_global->post_utf8 = 0;
3604 so_give(&lmc.so);
3606 else
3607 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3608 error_description(errno)));
3610 if(editor_result & COMP_GOTHUP){
3612 * Special Hack #291: if any hi-byte bits are set in
3613 * editor's result, we put them there.
3615 if(editor_result & 0xff00)
3616 exit(editor_result >> 8);
3618 dprint((1, "Save composition on HUP %sED\n",
3619 fcc_result ? "SUCCEED" : "FAIL"));
3620 hup_signal(); /* Do what we normally do on SIGHUP */
3622 else if((editor_result & COMP_SUSPEND) && fcc_result){
3623 if(ps_global->VAR_FORM_FOLDER
3624 && ps_global->VAR_FORM_FOLDER[0]
3625 && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
3626 q_status_message(SM_ORDER, 0, 3,
3627 _("Composition saved to Form Letter Folder. Select Compose to send."));
3628 else
3629 q_status_message(SM_ORDER, 0, 3,
3630 _("Composition postponed. Select Compose to resume."));
3632 break; /* postpone went OK, get out of here */
3634 else if(editor_result & COMP_CANCEL){
3635 char *lc = NULL;
3637 if(fcc_result && folder)
3638 lc = last_cmpnt(folder);
3640 q_status_message3(SM_ORDER, 0, 3,
3641 _("Message cancelled%s%s%s"),
3642 (lc && *lc) ? " and copied to \"" : "",
3643 (lc && *lc) ? lc : "",
3644 (lc && *lc) ? "\" file" : "");
3645 break;
3647 else{
3648 q_status_message(SM_ORDER, 0, 4,
3649 _("Continuing composition. Message not postponed or sent"));
3650 body_start = 1;
3651 continue; /* postpone failed, jump back in to composer */
3654 else{
3655 /*------ Must be sending mail or posting ! -----*/
3656 int result, valid_addr, continue_with_only_fcc = 0;
3657 CONTEXT_S *fcc_cntxt = NULL;
3659 result = 0;
3660 dprint((4, "=== sending: "));
3662 /* --- If posting, confirm with user ----*/
3663 if(outgoing->newsgroups && *outgoing->newsgroups
3664 && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
3665 && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
3666 q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
3667 dprint((4, "no post, continuing\n"));
3668 continue;
3671 if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
3672 || outgoing->newsgroups)){
3673 if(fcc && fcc[0]){
3674 if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
3675 want_to(_("No recipients, really copy only to Fcc "),
3676 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
3677 continue;
3679 continue_with_only_fcc++;
3681 else{
3682 q_status_message(SM_ORDER, 3, 4,
3683 _("No recipients specified!"));
3684 dprint((4, "no recip, continuing\n"));
3685 continue;
3689 if((valid_addr = check_addresses(&header)) == CA_BAD){
3690 /*--- Addresses didn't check out---*/
3691 dprint((4, "addrs failed, continuing\n"));
3692 continue;
3695 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
3696 && !continue_with_only_fcc
3697 && !(outgoing->to || outgoing->cc || lcc_addr
3698 || outgoing->newsgroups)
3699 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3700 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
3701 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3702 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3703 free_redraft_pos(&local_redraft_pos);
3705 local_redraft_pos
3706 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3707 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3708 local_redraft_pos->hdrname = cpystr(TONAME);
3709 continue;
3712 if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
3713 && check_for_subject(&header) == CF_MISSING){
3714 dprint((4, "No subject, continuing\n"));
3715 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3716 free_redraft_pos(&local_redraft_pos);
3718 local_redraft_pos
3719 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3720 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3721 local_redraft_pos->hdrname = cpystr(SUBJNAME);
3722 continue;
3725 if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
3726 && check_for_fcc(fcc) == CF_MISSING){
3727 dprint((4, "No fcc, continuing\n"));
3728 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3729 free_redraft_pos(&local_redraft_pos);
3731 local_redraft_pos
3732 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3733 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3734 local_redraft_pos->hdrname = cpystr("Fcc");
3735 continue;
3738 set_last_fcc(fcc);
3740 /*---- Check out fcc -----*/
3741 if(fcc && *fcc){
3743 * If special name "inbox" then replace it with the
3744 * real inbox path.
3746 if(ps_global->VAR_INBOX_PATH
3747 && strucmp(fcc, ps_global->inbox_name) == 0){
3748 char *replace_fcc;
3750 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
3751 fs_give((void **)&fcc);
3752 fcc = replace_fcc;
3755 lmc.all_written = lmc.text_written = 0;
3756 /* lmc.text_only set on command line */
3757 if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
3758 /* ---- Open or allocation of fcc failed ----- */
3759 dprint((4,"can't open/allocate fcc, cont'g\n"));
3762 * Find field entry associated with fcc, and start
3763 * composer on it...
3765 for(pf = header.local; pf && pf->name; pf = pf->next)
3766 if(pf->type == Fcc && HE(pf))
3767 HE(pf)->start_here = 1;
3769 continue;
3771 else
3772 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
3774 else
3775 lmc.so = NULL;
3777 /*---- Take care of any requested prefiltering ----*/
3778 if(sending_filter_requested
3779 && !filter_message_text(sending_filter_requested, outgoing,
3780 *body, &orig_so, &header)){
3781 q_status_message1(SM_ORDER, 3, 3,
3782 _("Problem filtering! Nothing sent%s."),
3783 fcc ? " or saved to fcc" : "");
3784 continue;
3787 /*------ Actually post -------*/
3788 if(outgoing->newsgroups){
3789 char **alt_nntp = NULL, *alt_nntp_p[2];
3790 if(((role && role->nntp)
3791 || suggested_nntp_server)){
3792 if(ps_global->FIX_NNTP_SERVER
3793 && ps_global->FIX_NNTP_SERVER[0])
3794 q_status_message(SM_ORDER | SM_DING, 5, 5,
3795 "Using nntp-server that is administratively fixed");
3796 else if(role && role->nntp)
3797 alt_nntp = role->nntp;
3798 else{
3799 alt_nntp_p[0] = suggested_nntp_server;
3800 alt_nntp_p[1] = NULL;
3801 alt_nntp = alt_nntp_p;
3804 if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
3805 dprint((1, "Post failed, continuing\n"));
3806 if(outgoing->message_id)
3807 fs_give((void **) &outgoing->message_id);
3809 outgoing->message_id = generate_message_id();
3811 continue;
3813 else
3814 result |= P_NEWS_WIN;
3818 * BUG: IF we've posted the message *and* an fcc was specified
3819 * then we've already got a neatly formatted message in the
3820 * lmc.so. It'd be nice not to have to re-encode everything
3821 * to insert it into the smtp slot...
3825 * Turn on "undisclosed recipients" header if no To or cc.
3827 if(!(outgoing->to || outgoing->cc)
3828 && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
3829 pf_nobody->writehdr = 1;
3830 pf_nobody->localcopy = 1;
3833 if(priority_requested){
3834 (void) set_priority_header(&header, priority_requested);
3835 fs_give((void **) &priority_requested);
3838 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3840 * If requested, launch background posting...
3842 if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
3843 ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
3844 memset(ps_global->post, 0, sizeof(POST_S));
3845 if(fcc)
3846 ps_global->post->fcc = cpystr(fcc);
3848 if((ps_global->post->pid = fork()) == 0){
3850 * Put us in new process group...
3852 setpgrp(0, ps_global->post->pid);
3854 /* BUG: should fix argv[0] to indicate what we're up to */
3857 * If there are any live streams, pretend we never
3858 * knew them. Problem is two processes writing
3859 * same server process.
3860 * This is not clean but we're just going to exit
3861 * right away anyway. We just want to be sure to leave
3862 * the stuff that the parent is going to use alone.
3863 * The next three lines will disable the re-use of the
3864 * existing streams and cause us to open a new one if
3865 * needed.
3867 ps_global->mail_stream = NULL;
3868 ps_global->s_pool.streams = NULL;
3869 ps_global->s_pool.nstream = 0;
3871 /* quell any display output */
3872 ps_global->in_init_seq = 1;
3874 /*------- Actually mail the message ------*/
3875 if(valid_addr == CA_OK
3876 && (outgoing->to || outgoing->cc
3877 || outgoing->bcc || lcc_addr)){
3878 char **alt_smtp = NULL;
3880 if(role && role->smtp){
3881 if(ps_global->FIX_SMTP_SERVER
3882 && ps_global->FIX_SMTP_SERVER[0])
3883 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3884 else
3885 alt_smtp = role->smtp;
3888 result |= (call_mailer(&header, *body, alt_smtp,
3889 call_mailer_flags,
3890 call_mailer_file_result,
3891 pipe_callback) > 0)
3892 ? P_MAIL_WIN : P_MAIL_LOSE;
3894 if(result & P_MAIL_LOSE)
3895 mark_address_failure_for_pico(&header);
3898 /*----- Was there an fcc involved? -----*/
3899 if(lmc.so){
3900 /*------ Write it if at least something worked ------*/
3901 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3902 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3903 && pine_rfc822_output(&header, *body,
3904 NULL, NULL))){
3905 char label[50];
3907 strncpy(label, "Fcc", sizeof(label));
3908 label[sizeof(label)-1] = '\0';
3909 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3910 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3911 label[sizeof(label)-1] = '\0';
3914 /*-- Now actually copy to fcc folder and close --*/
3915 result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
3916 NULL, label,
3917 F_ON(F_MARK_FCC_SEEN, ps_global)
3918 ? "\\SEEN" : NULL))
3919 ? P_FCC_WIN : P_FCC_LOSE;
3921 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3922 q_status_message(SM_ORDER, 3, 5,
3923 _("Fcc Failed!. No message saved."));
3924 dprint((1,
3925 "explicit fcc write failed!\n"));
3926 result |= P_FCC_LOSE;
3929 so_give(&lmc.so);
3932 if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
3934 * Encode child's result in hi-byte of
3935 * editor's result
3937 editor_result = ((result << 8) | COMP_GOTHUP);
3938 goto fake_hup;
3941 exit(result);
3944 if(ps_global->post->pid > 0){
3945 q_status_message(SM_ORDER, 3, 3,
3946 _("Message handed off for posting"));
3947 break; /* up to our child now */
3949 else{
3950 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3951 "Can't fork for send: %s",
3952 error_description(errno));
3953 if(ps_global->post->fcc)
3954 fs_give((void **) &ps_global->post->fcc);
3956 fs_give((void **) &ps_global->post);
3959 if(lmc.so) /* throw away unused store obj */
3960 so_give(&lmc.so);
3962 if(outgoing->message_id)
3963 fs_give((void **) &outgoing->message_id);
3965 outgoing->message_id = generate_message_id();
3967 continue; /* if we got here, there was a prob */
3969 #endif /* BACKGROUND_POST */
3971 /*------- Actually mail the message ------*/
3972 if(valid_addr == CA_OK
3973 && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
3974 char **alt_smtp = NULL;
3976 if(role && role->smtp){
3977 if(ps_global->FIX_SMTP_SERVER
3978 && ps_global->FIX_SMTP_SERVER[0])
3979 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3980 else
3981 alt_smtp = role->smtp;
3984 result |= (call_mailer(&header, *body, alt_smtp,
3985 call_mailer_flags,
3986 call_mailer_file_result,
3987 pipe_callback) > 0)
3988 ? P_MAIL_WIN : P_MAIL_LOSE;
3990 if(result & P_MAIL_LOSE)
3991 mark_address_failure_for_pico(&header);
3994 /*----- Was there an fcc involved? -----*/
3995 if(lmc.so){
3996 /*------ Write it if at least something worked ------*/
3997 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3998 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3999 && pine_rfc822_output(&header, *body, NULL, NULL))){
4000 char label[50];
4002 strncpy(label, "Fcc", sizeof(label));
4003 label[sizeof(label)-1] = '\0';
4004 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
4005 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
4006 label[sizeof(label)-1] = '\0';
4009 /*-- Now actually copy to fcc folder and close --*/
4010 result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
4011 F_ON(F_MARK_FCC_SEEN, ps_global)
4012 ? "\\SEEN" : NULL))
4013 ? P_FCC_WIN : P_FCC_LOSE;
4015 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
4016 q_status_message(SM_ORDER,3,5,
4017 _("Fcc Failed!. No message saved."));
4018 dprint((1, "explicit fcc write failed!\n"));
4019 result |= P_FCC_LOSE;
4022 so_give(&lmc.so);
4025 /*----- Mail Post FAILED, back to composer -----*/
4026 if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
4027 dprint((1, "Send failed, continuing\n"));
4029 if(result & P_FCC_LOSE){
4031 * Find field entry associated with fcc, and start
4032 * composer on it...
4034 for(pf = header.local; pf && pf->name; pf = pf->next)
4035 if(pf->type == Fcc && HE(pf))
4036 HE(pf)->start_here = 1;
4038 q_status_message(SM_ORDER | SM_DING, 3, 3,
4039 pine_send_status(result, fcc,
4040 tmp_20k_buf, SIZEOF_20KBUF, NULL));
4043 if(outgoing->message_id)
4044 fs_give((void **) &outgoing->message_id);
4046 outgoing->message_id = generate_message_id();
4048 continue;
4052 * If message sent *completely* successfully, there's a
4053 * reply struct AND we're allowed to write back state, do it.
4054 * But also protect against shifted message numbers due
4055 * to new mail arrival. Since the number passed is based
4056 * on the real imap msg no, AND we're sure no expunge has
4057 * been done, just fix up the sorted number...
4059 update_answered_flags(reply);
4061 /*----- Signed, sealed, delivered! ------*/
4062 q_status_message(SM_ORDER, 0, 3,
4063 pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
4065 break; /* All's well, pop out of here */
4069 if(orig_so)
4070 so_give(&orig_so);
4072 if(fcc)
4073 fs_give((void **)&fcc);
4075 free_body_particulars(bp);
4077 free_attachment_list(&pbf->attachments);
4079 standard_picobuf_teardown(pbf);
4081 for(i=0; i < fixed_cnt; i++){
4082 if(pfields[i].textbuf)
4083 fs_give((void **)&pfields[i].textbuf);
4085 fs_give((void **)&pfields[i].name);
4088 if(lcc_addr)
4089 mail_free_address(&lcc_addr);
4091 if(nobody_addr)
4092 mail_free_address(&nobody_addr);
4094 free_prompts(header.custom);
4095 free_customs(header.custom);
4096 fs_give((void **)&pfields);
4097 free_headents(&headents);
4098 fs_give((void **)&sending_order);
4099 if(suggested_nntp_server)
4100 fs_give((void **)&suggested_nntp_server);
4101 if(title)
4102 fs_give((void **)&title);
4104 if(local_redraft_pos && local_redraft_pos != redraft_pos)
4105 free_redraft_pos(&local_redraft_pos);
4107 pbf = save_previous_pbuf;
4108 g_rolenick = NULL;
4110 dprint((4, "=== send returning ===\n"));
4115 * Check for subject in outgoing message.
4117 * Asks user whether to proceed with no subject.
4120 check_for_subject(METAENV *header)
4122 PINEFIELD *pf;
4123 int rv = CF_OK;
4125 for(pf = header->local; pf && pf->name; pf = pf->next)
4126 if(pf->type == Subject){
4127 if(pf->text && *pf->text && **pf->text)
4128 rv = CF_OK;
4129 else{
4130 if(want_to("No Subject, send anyway ",
4131 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
4132 rv = CF_OK;
4133 else
4134 rv = CF_MISSING;
4137 break;
4141 return(rv);
4146 * Check for fcc in outgoing message.
4148 * Asks user whether to proceed with no fcc.
4151 check_for_fcc(char *fcc)
4153 int rv = CF_OK;
4155 if(fcc && *fcc)
4156 rv = CF_OK;
4157 else{
4158 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
4159 rv = CF_OK;
4160 else
4161 rv = CF_MISSING;
4164 return(rv);
4169 * Confirm that the user wants to send to MAILER-DAEMON
4172 confirm_daemon_send(void)
4174 return(want_to("Really send this message to the MAILER-DAEMON",
4175 'n', 'n', NO_HELP, WT_NORM) == 'y');
4179 void
4180 free_prompts(PINEFIELD *head)
4182 PINEFIELD *pf;
4184 for(pf = head; pf && pf->name; pf = pf->next){
4185 if(HE(pf) && HE(pf)->prompt)
4186 fs_give((void **)& HE(pf)->prompt);
4192 postpone_prompt(void)
4194 static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
4195 {'f', 'f', "F", N_("Form Letter Folder")},
4196 {-1, 0, NULL, NULL} };
4198 return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
4199 pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
4204 * call__mailer_file_result - some results from call_mailer might be in a file.
4205 * dislplay that file.
4207 void
4208 call_mailer_file_result(char *filename, int style)
4210 if(filename){
4211 if(style & CM_BR_VERBOSE){
4212 display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
4214 else{
4215 display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
4220 void
4221 mark_address_failure_for_pico(METAENV *header)
4223 PINEFIELD *pf;
4224 ADDRESS *a;
4225 int error_count = 0;
4226 struct headerentry *last_he = NULL;
4228 for(pf = header->local; pf && pf->name; pf = pf->next)
4229 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
4230 for(a = *pf->addr; a != NULL; a = a->next)
4231 if(a->error != NULL
4232 && error_count++ < MAX_ADDR_ERROR
4233 && (HE(pf))){
4234 if(last_he) /* start last reported err */
4235 last_he->start_here = 0;
4237 (last_he = HE(pf))->start_here = 1;
4244 * This is specialized routine. It assumes that the only things that we
4245 * care about restoring are the body type, subtype, encoding and the
4246 * state of the charset parameter. It also assumes that if the charset
4247 * parameter exists when we save it, it won't be removed later.
4249 BODY_PARTICULARS_S *
4250 save_body_particulars(struct mail_bodystruct *body)
4252 BODY_PARTICULARS_S *bp;
4253 PARAMETER *pm;
4255 bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
4257 bp->type = body->type;
4258 bp->encoding = body->encoding;
4259 bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
4260 bp->parameter = body->parameter;
4261 for(pm = bp->parameter;
4262 pm && strucmp(pm->attribute, "charset") != 0;
4263 pm = pm->next)
4264 ;/* searching for possible charset parameter */
4266 if(pm){ /* found one */
4267 bp->had_csp = 1; /* saved body had charset parameter */
4268 bp->charset = pm->value ? cpystr(pm->value) : NULL;
4270 else{
4271 bp->had_csp = 0;
4272 bp->charset = NULL;
4275 return(bp);
4279 void
4280 reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
4282 body->type = bp->type;
4283 body->encoding = bp->encoding;
4284 if(body->subtype)
4285 fs_give((void **)&body->subtype);
4287 body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
4289 if(bp->parameter){
4290 PARAMETER *pm, *pm_prev = NULL;
4292 for(pm = body->parameter;
4293 pm && strucmp(pm->attribute, "charset") != 0;
4294 pm = pm->next)
4295 pm_prev = pm;
4297 if(pm){ /* body has charset parameter */
4298 if(bp->had_csp){ /* reset to what it used to be */
4299 if(pm->value)
4300 fs_give((void **)&pm->value);
4302 pm->value = bp->charset ? cpystr(bp->charset) : NULL;
4304 else{ /* remove charset parameter */
4305 if(pm_prev)
4306 pm_prev->next = pm->next;
4307 else
4308 body->parameter = pm->next;
4310 mail_free_body_parameter(&pm);
4313 else{
4314 if(bp->had_csp){
4316 * This can't happen because grope never removes
4317 * the charset parameter.
4319 q_status_message(SM_ORDER | SM_DING, 5, 5,
4320 "Programmer error: saved charset but no current charset param in pine_send");
4323 else{
4324 ok, still no parameter
4329 else{
4330 if(body->parameter)
4331 mail_free_body_parameter(&body->parameter);
4333 body->parameter = NULL;
4338 void
4339 free_body_particulars(BODY_PARTICULARS_S *bp)
4341 if(bp){
4342 if(bp->subtype)
4343 fs_give((void **)&bp->subtype);
4345 if(bp->charset)
4346 fs_give((void **)&bp->charset);
4348 fs_give((void **)&bp);
4353 /*----------------------------------------------------------------------
4354 Build a status message suitable for framing
4356 Returns: pointer to resulting buffer
4357 ---*/
4358 char *
4359 pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
4361 int avail = ps_global->ttyo->screen_cols - 2;
4362 int fixedneed, need, lenfcc;
4363 char *part1, *part2, *part3, *part4, *part5;
4364 char fbuf[MAILTMPLEN+1];
4366 part1 = (result & P_NEWS_WIN)
4367 ? "posted"
4368 : (result & P_NEWS_LOSE)
4369 ? "NOT POSTED"
4370 : "";
4371 part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
4372 && (result & P_FCC_BITS))
4373 ? ", "
4374 : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
4375 ? " and "
4376 : "";
4377 part3 = (result & P_MAIL_WIN)
4378 ? "sent"
4379 : (result & P_MAIL_LOSE)
4380 ? "NOT SENT"
4381 : "";
4382 part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
4383 ? " and "
4384 : "";
4385 part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
4386 ? "ONLY copied to "
4387 : (result & P_FCC_WIN)
4388 ? "copied to "
4389 : (result & P_FCC_LOSE)
4390 ? "NOT copied to "
4391 : "";
4392 lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
4394 fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
4395 strlen(part4) + strlen(part5);
4396 need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
4398 if(need > avail && fixedneed + 3 >= avail){
4399 /* dots on end of fixed, no fcc */
4400 snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
4401 part1, part2, part3, part4, part5);
4402 short_str(fbuf, buf, buflen, avail, EndDots);
4404 else if(need > avail){
4405 /* include whole fixed part, quotes and dots at end of fcc name */
4406 if(lenfcc > 0)
4407 lenfcc = MAX(1, lenfcc-(need-avail));
4409 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4410 part1, part2, part3, part4, part5,
4411 (result & P_FCC_BITS) ? "\"" : "",
4412 short_str((result & P_FCC_BITS) ? fcc_name : "",
4413 fbuf, sizeof(fbuf), lenfcc, FrontDots),
4414 (result & P_FCC_BITS) ? "\"" : "");
4416 else{
4417 /* whole thing */
4418 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4419 part1, part2, part3, part4, part5,
4420 (result & P_FCC_BITS) ? "\"" : "",
4421 (result & P_FCC_BITS) ? fcc_name : "",
4422 (result & P_FCC_BITS) ? "\"" : "");
4425 if(goodorbad)
4426 *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
4428 return(buf);
4431 /* Callback from Pico to set the conditions for Alpine to start a new thread
4434 void
4435 new_thread_on_blank_subject(void)
4437 ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
4442 /*----------------------------------------------------------------------
4443 Call back for pico to insert the specified message's text
4445 Args: n -- message number to format
4446 f -- function to use to output the formatted message
4449 Returns: returns msg number formatted on success, zero on error.
4450 ----*/
4451 long
4452 message_format_for_pico(long int n, int (*f) (int))
4454 ENVELOPE *e;
4455 BODY *b;
4456 char *old_quote = NULL;
4457 long rv = n;
4459 if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
4460 && (e = pine_mail_fetchstructure(ps_global->mail_stream,
4461 mn_m2raw(ps_global->msgmap, n), &b)))){
4462 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4463 flush_status_messages(0);
4464 return(0L);
4467 /* temporarily assign a new quote string */
4468 old_quote = pbf->quote_str;
4469 pbf->quote_str = reply_quote_str(e);
4471 /* build separator line */
4472 reply_delimiter(e, NULL, f);
4474 /* actually write message text */
4475 if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
4476 FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
4477 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4478 flush_status_messages(0);
4479 rv = 0L;
4482 fs_give((void **)&pbf->quote_str);
4483 pbf->quote_str = old_quote;
4484 return(rv);
4488 /*----------------------------------------------------------------------
4489 Call back for pico to prompt the user for exit confirmation
4491 Args: dflt -- default answer for confirmation prompt
4493 Returns: either NULL if the user accepts exit, or string containing
4494 reason why the user declined.
4495 ----*/
4497 send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
4498 char **result)
4500 int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
4501 int dsn_label = 0, fcc_label = 0, lparen;
4502 int flowing_label = 0, double_rad;
4503 char *rstr = NULL, *p, *lc, *optp;
4504 char dsn_string[30];
4505 void (*redraw)(void) = ps_global->redrawer;
4506 ESCKEY_S opts[18];
4507 struct filters {
4508 char *filter;
4509 int index;
4510 struct filters *prev, *next;
4511 } *filters = NULL, *fp;
4513 sending_filter_requested = NULL;
4514 call_mailer_flags = 0;
4515 background_requested = 0;
4516 flowing_requested = allow_flowed ? 1 : 0;
4517 lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
4518 if(priority_requested)
4519 fs_give((void **) &priority_requested);
4521 if(background_posting(FALSE)){
4522 if(result)
4523 *result = "Can't send while background posting. Use postpone.";
4525 return(1);
4528 if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
4529 if(result)
4530 *result = NULL;
4532 return(0);
4535 ps_global->redrawer = redraw_pico;
4537 if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
4538 (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
4541 * Build list of available filters...
4543 for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
4544 for(p = ps_global->VAR_SEND_FILTER[i];
4545 *p && !isspace((unsigned char)*p); p++)
4548 c = *p;
4549 *p = '\0';
4550 if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
4551 && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
4552 *p = c;
4553 continue;
4556 fp = (struct filters *)fs_get(sizeof(struct filters));
4557 fp->index = i;
4558 if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
4559 fp->filter = cpystr(lc);
4561 else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
4562 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
4563 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4564 fp->filter = cpystr(tmp_20k_buf);
4566 else
4567 fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
4569 *p = c;
4571 if(filters){
4572 fp->next = filters;
4573 fp->prev = filters->prev;
4574 fp->prev->next = filters->prev = fp;
4576 else{
4577 filters = (struct filters *)fs_get(sizeof(struct filters));
4578 filters->index = -1;
4579 filters->filter = NULL;
4580 filters->next = filters->prev = fp;
4581 fp->next = fp->prev = filters;
4585 i = 0;
4586 opts[i].ch = 'y';
4587 opts[i].rval = 'y';
4588 opts[i].name = "Y";
4589 opts[i++].label = N_("Yes");
4591 opts[i].ch = 'n';
4592 opts[i].rval = 'n';
4593 opts[i].name = "N";
4594 opts[i++].label = N_("No");
4596 if(filters){
4597 /* set global_filter_pointer to desired filter or NULL if none */
4598 /* prepare two keymenu slots for selecting filter */
4599 opts[i].ch = ctrl('P');
4600 opts[i].rval = 10;
4601 opts[i].name = "^P";
4602 opts[i++].label = N_("Prev Filter");
4604 opts[i].ch = ctrl('N');
4605 opts[i].rval = 11;
4606 opts[i].name = "^N";
4607 opts[i++].label = N_("Next Filter");
4609 if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
4610 filters = filters->next;
4613 if(F_ON(F_VERBOSE_POST, ps_global)){
4614 /* setup keymenu slot to toggle verbose mode */
4615 opts[i].ch = ctrl('W');
4616 opts[i].rval = 12;
4617 opts[i].name = "^W";
4618 verbose_label = i++;
4621 if(allow_flowed){
4622 /* setup keymenu slot to toggle flowed mode */
4623 opts[i].ch = ctrl('V');
4624 opts[i].rval = 22;
4625 opts[i].name = "^V";
4626 flowing_label = i++;
4627 flowing_requested = 1;
4630 if(F_ON(F_NO_FCC_ATTACH, ps_global)){
4631 /* setup keymenu slot to toggle attacment on fcc */
4632 opts[i].ch = ctrl('F');
4633 opts[i].rval = 21;
4634 opts[i].name = "^F";
4635 fcc_label = i++;
4638 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4639 if(F_ON(F_BACKGROUND_POST, ps_global)){
4640 opts[i].ch = ctrl('R');
4641 opts[i].rval = 15;
4642 opts[i].name = "^R";
4643 bg_label = i++;
4645 #endif
4647 opts[i].ch = 'p';
4648 opts[i].rval = 'p';
4649 opts[i].name = "P";
4650 opts[i++].label = N_("Priority");
4652 #ifdef SMIME
4653 if(F_OFF(F_DONT_DO_SMIME, ps_global)){
4654 opts[i].ch = 'e';
4655 opts[i].rval = 'e';
4656 opts[i].name = "E";
4657 opts[i++].label = "Encrypt";
4659 opts[i].ch = 'g';
4660 opts[i].rval = 'g';
4661 opts[i].name = "G";
4662 opts[i++].label = "Sign";
4664 if(ps_global->smime){
4665 ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
4666 ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
4669 #endif
4671 double_rad = i;
4673 if(F_ON(F_DSN, ps_global)){
4674 /* setup keymenu slots to toggle dsn bits */
4675 opts[i].ch = 'd';
4676 opts[i].rval = 'd';
4677 opts[i].name = "D";
4678 opts[i].label = N_("DSNOpts");
4679 dsn_label = i++;
4680 opts[i].ch = -2;
4681 opts[i].rval = 's';
4682 opts[i].name = "S";
4683 opts[i++].label = "";
4684 opts[i].ch = -2;
4685 opts[i].rval = 'x';
4686 opts[i].name = "X";
4687 opts[i++].label = "";
4688 opts[i].ch = -2;
4689 opts[i].rval = 'h';
4690 opts[i].name = "H";
4691 opts[i++].label = "";
4694 if(filters){
4695 opts[i].ch = KEY_UP;
4696 opts[i].rval = 10;
4697 opts[i].name = "";
4698 opts[i++].label = "";
4700 opts[i].ch = KEY_DOWN;
4701 opts[i].rval = 11;
4702 opts[i].name = "";
4703 opts[i++].label = "";
4706 opts[i].ch = -1;
4708 fix_windsize(ps_global);
4710 while(1){
4711 if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
4712 *p = '\0';
4713 else
4714 p = NULL;
4716 lparen = 0;
4717 strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
4718 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4719 optp = tmp_20k_buf + strlen(tmp_20k_buf);
4721 if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
4722 sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4724 if(allow_flowed && !flowing_requested){
4725 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4726 if(!lparen){
4727 *optp++ = ' ';
4728 *optp++ = '(';
4729 lparen++;
4731 else
4732 *optp++ = ' ';
4735 sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4738 if(filters){
4739 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4740 if(!lparen){
4741 *optp++ = ' ';
4742 *optp++ = '(';
4743 lparen++;
4745 else{
4746 *optp++ = ',';
4747 *optp++ = ' ';
4751 if(filters->filter){
4752 sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4753 sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4754 if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
4755 *optp++ = '\"';
4757 else
4758 sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4761 if((call_mailer_flags & CM_VERBOSE) || background_requested){
4762 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4763 if(!lparen){
4764 *optp++ = ' ';
4765 *optp++ = '(';
4766 lparen++;
4768 else
4769 *optp++ = ' ';
4772 sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4773 if(call_mailer_flags & CM_VERBOSE)
4774 sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4776 if(background_requested)
4777 sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4779 sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4782 if(g_rolenick && !(he && he[N_FROM].dirty)){
4783 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4784 if(!lparen){
4785 *optp++ = ' ';
4786 *optp++ = '(';
4787 lparen++;
4789 else
4790 *optp++ = ' ';
4793 sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4794 sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4795 sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4798 if(call_mailer_flags & CM_DSN_SHOW){
4799 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4800 if(!lparen){
4801 *optp++ = ' ';
4802 *optp++ = '(';
4803 lparen++;
4805 else{
4806 *optp++ = ',';
4807 *optp++ = ' ';
4811 sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4814 #ifdef SMIME
4815 if(ps_global->smime && ps_global->smime->do_encrypt){
4816 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4817 if(!lparen){
4818 *optp++ = ' ';
4819 *optp++ = '(';
4820 lparen++;
4822 else{
4823 *optp++ = ',';
4824 *optp++ = ' ';
4828 sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4831 if(ps_global->smime && ps_global->smime->do_sign){
4832 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4833 if(!lparen){
4834 *optp++ = ' ';
4835 *optp++ = '(';
4836 lparen++;
4838 else{
4839 *optp++ = ',';
4840 *optp++ = ' ';
4844 sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4846 #endif
4848 if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
4849 *optp++ = ')';
4851 sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4852 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4854 if(p)
4855 *p = ' ';
4857 if(flowing_label)
4858 opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
4860 if(verbose_label)
4861 opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
4863 if(bg_label)
4864 opts[bg_label].label = background_requested
4865 ? N_("Foreground") : N_("Background");
4867 if(fcc_label)
4868 opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
4869 : N_("No Fcc Atmts ");
4871 if(F_ON(F_DSN, ps_global)){
4872 if(call_mailer_flags & CM_DSN_SHOW){
4873 opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
4874 ? N_("NoDelay") : N_("Delay");
4875 opts[dsn_label+1].ch = 's';
4876 opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
4877 ? N_("NoSuccess") : N_("Success");
4878 opts[dsn_label+2].ch = 'x';
4879 opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
4880 ? N_("ErrRets") : N_("NoErrRets");
4881 opts[dsn_label+3].ch = 'h';
4882 opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
4883 ? N_("RetHdrs") : N_("RetFull");
4887 if(double_rad +
4888 ((call_mailer_flags & CM_DSN_SHOW)
4889 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
4890 rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4891 'y', 'z',
4892 (F_ON(F_DSN, ps_global) && allow_flowed)
4893 ? h_send_prompt_dsn_flowed :
4894 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4895 allow_flowed ? h_send_prompt_flowed :
4896 h_send_prompt,
4897 RB_NORM);
4898 else
4899 rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4900 'y', 'z',
4901 (double_rad +
4902 ((call_mailer_flags & CM_DSN_SHOW)
4903 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
4904 ? NO_HELP :
4905 (F_ON(F_DSN, ps_global) && allow_flowed)
4906 ? h_send_prompt_dsn_flowed :
4907 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4908 allow_flowed ? h_send_prompt_flowed :
4909 h_send_prompt,
4910 RB_NORM);
4912 if(rv == 'y'){ /* user ACCEPTS! */
4913 break;
4915 else if(rv == 'n'){ /* Declined! */
4916 rstr = _("No Message Sent");
4917 break;
4919 else if(rv == 'z'){ /* Cancelled! */
4920 rstr = _("Send Cancelled");
4921 break;
4923 else if(rv == 10){ /* PREVIOUS filter */
4924 filters = filters->prev;
4926 else if(rv == 11){ /* NEXT filter */
4927 filters = filters->next;
4929 else if(rv == 21){
4930 lmc.text_only = !lmc.text_only;
4932 else if(rv == 12){ /* flip verbose bit */
4933 if(call_mailer_flags & CM_VERBOSE)
4934 call_mailer_flags &= ~CM_VERBOSE;
4935 else
4936 call_mailer_flags |= CM_VERBOSE;
4938 if((call_mailer_flags & CM_VERBOSE) && background_requested)
4939 background_requested = 0;
4941 else if(rv == 22){ /* flip flowing bit */
4942 flowing_requested = !flowing_requested;
4944 else if(rv == 15){
4945 if((background_requested = !background_requested)
4946 && (call_mailer_flags & CM_VERBOSE))
4947 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
4949 else if(call_mailer_flags & CM_DSN_SHOW){
4950 if(rv == 's'){ /* flip success bit */
4951 call_mailer_flags ^= CM_DSN_SUCCESS;
4952 /* turn off related bits */
4953 if(call_mailer_flags & CM_DSN_SUCCESS)
4954 call_mailer_flags &= ~(CM_DSN_NEVER);
4956 else if(rv == 'd'){ /* flip delay bit */
4957 call_mailer_flags ^= CM_DSN_DELAY;
4958 /* turn off related bits */
4959 if(call_mailer_flags & CM_DSN_DELAY)
4960 call_mailer_flags &= ~(CM_DSN_NEVER);
4962 else if(rv == 'x'){ /* flip never bit */
4963 call_mailer_flags ^= CM_DSN_NEVER;
4964 /* turn off related bits */
4965 if(call_mailer_flags & CM_DSN_NEVER)
4966 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
4968 else if(rv == 'h'){ /* flip full bit */
4969 call_mailer_flags ^= CM_DSN_FULL;
4972 else if(rv == 'd'){ /* show dsn options */
4974 * When you turn on DSN, the default is to notify on
4975 * failure, success, or delay; and to return the whole
4976 * body on failure.
4978 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
4980 else if(rv == 'p'){ /* choose X-Priority */
4981 char *prio;
4983 prio = choose_a_priority(priority_requested);
4984 if((ps_global->redrawer = redraw_pico) != NULL){
4985 (*ps_global->redrawer)();
4986 fix_windsize(ps_global);
4989 if(prio){
4990 if(priority_requested)
4991 fs_give((void **) &priority_requested);
4993 if(prio[0])
4994 priority_requested = prio;
4995 else
4996 fs_give((void **) &prio);
4999 #ifdef SMIME
5000 else if(rv=='e'){
5001 if(ps_global->smime)
5002 ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
5004 else if(rv=='g'){
5005 if(ps_global->smime)
5006 ps_global->smime->do_sign = !ps_global->smime->do_sign;
5008 #endif
5010 snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
5011 (call_mailer_flags & CM_DSN_NEVER)
5012 ? "Never" : "F",
5013 (call_mailer_flags & CM_DSN_DELAY)
5014 ? "D" : "",
5015 (call_mailer_flags & CM_DSN_SUCCESS)
5016 ? "S" : "",
5017 (call_mailer_flags & CM_DSN_NEVER)
5018 ? ""
5019 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
5020 : "-Hdrs");
5021 dsn_string[sizeof(dsn_string)-1] = '\0';
5024 /* remember selection */
5025 if(filters && filters->index > -1)
5026 sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
5028 if(filters){
5029 filters->prev->next = NULL; /* tie off list */
5030 while(filters){ /* then free it */
5031 fp = filters->next;
5032 if(filters->filter)
5033 fs_give((void **)&filters->filter);
5035 fs_give((void **)&filters);
5036 filters = fp;
5040 if(old_suspend)
5041 (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
5043 if(result)
5044 *result = rstr;
5046 ps_global->redrawer = redraw;
5048 return((rstr == NULL) ? 0 : 1);
5053 * Allow user to choose a priority for sending.
5055 * Returns an allocated priority on success, NULL otherwise.
5057 char *
5058 choose_a_priority(char *default_val)
5060 char *choice = NULL;
5061 char **priority_list, **lp;
5062 char *starting_val = NULL;
5063 char *none;
5064 int i, cnt;
5065 PRIORITY_S *p;
5067 for(cnt = 0, p = priorities; p && p->desc; p++)
5068 cnt++;
5070 cnt++; /* for NONE entry */
5071 lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
5072 memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
5074 for(i = 0, p = priorities; p && p->desc; p++){
5075 *lp = cpystr(p->desc);
5076 if(default_val && !strcmp(default_val, p->desc))
5077 starting_val = (*lp);
5079 lp++;
5082 none = _("NONE - No X-Priority header included");
5083 *lp = cpystr(none);
5084 if(!starting_val)
5085 starting_val = (*lp);
5087 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5088 TRANSLATORS: Print something1 using something2.
5089 "priorities" is something1 */
5090 choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
5091 _("priorities"), h_select_priority_screen,
5092 _("HELP FOR SELECTING A PRIORITY"),
5093 starting_val);
5095 if(!choice)
5096 q_status_message(SM_ORDER, 1, 4, _("No change"));
5097 else if(!strcmp(choice, none))
5098 choice[0] = '\0';
5100 free_list_array(&priority_list);
5102 return(choice);
5107 dont_flow_this_time(void)
5109 return(flowing_requested ? 0 : 1);
5113 /*----------------------------------------------------------------------
5114 Call back for pico to display mime type of attachment
5116 Args: file -- filename being attached
5118 Returns: returns 1 on success (message queued), zero otherwise (don't know
5119 type so nothing queued).
5120 ----*/
5122 mime_type_for_pico(char *file)
5124 BODY *body;
5125 int rv;
5126 void *file_contents;
5128 body = mail_newbody();
5129 body->type = TYPEOTHER;
5130 body->encoding = ENCOTHER;
5132 /* don't know where the cursor's been, reset it */
5133 clear_cursor_pos();
5134 if(!set_mime_type_by_extension(body, file)){
5135 if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5136 body->contents.text.data = file_contents;
5137 set_mime_type_by_grope(body);
5141 if(body->type != TYPEOTHER){
5142 rv = 1;
5143 q_status_message3(SM_ORDER, 0, 3,
5144 _("File %s attached as type %s/%s"), file,
5145 body_types[body->type],
5146 body->subtype ? body->subtype : rfc822_default_subtype(body->type));
5148 else
5149 rv = 0;
5151 pine_free_body(&body);
5152 return(rv);
5156 /*----------------------------------------------------------------------
5157 Call back for pico to receive an uploaded message
5159 Args: fname -- name for uploaded file (empty if they want us to assign it)
5160 size -- pointer to long to hold the attachment's size
5162 Notes: the attachment is uploaded to a temp file, and
5164 Returns: TRUE on success, FALSE otherwise
5165 ----*/
5167 upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
5169 char cmd[MAXPATH+1], *fnp = NULL;
5170 char *locale_name = NULL;
5171 long l;
5172 PIPE_S *syspipe;
5174 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5175 fname ? fname : "<NO FILE>"));
5177 if(!fname) /* no place for file name */
5178 return(0);
5180 if(!*fname){ /* caller wants temp file */
5181 if((fnp = temp_nam(NULL, "pu")) != NULL){
5182 strncpy(fname, fnp, fnlen);
5183 fname[fnlen-1] = '\0';
5184 our_unlink(fnp);
5185 fs_give((void **)&fnp);
5188 else
5189 locale_name = convert_to_locale(fname);
5191 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
5192 ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
5193 if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
5194 0, pipe_callback, pipe_report_error)) != NULL){
5195 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
5196 if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
5197 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5198 "Error determining size of %s: %s", fname,
5199 fnp = error_description(errno));
5200 dprint((1,
5201 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5202 cmd ? cmd : "?",
5203 fname ? fname : "?",
5204 fnp ? fnp : "?"));
5206 else if(size)
5207 *size = l;
5209 if(locale_name)
5210 fs_give((void **) &locale_name);
5212 return(l >= 0);
5214 else
5215 q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
5217 if(locale_name)
5218 fs_give((void **) &locale_name);
5220 return(0);
5224 char *
5225 cancel_for_pico(void (*redraw_pico)(void))
5227 int rv;
5228 char *rstr = NULL;
5229 char *prompt =
5230 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5231 void (*redraw)(void) = ps_global->redrawer;
5232 static ESCKEY_S opts[] = {
5233 {'c', 'c', "C", N_("Confirm")},
5234 {'n', 'n', "N", N_("No")},
5235 {'y', 'y', "", ""},
5236 {-1, 0, NULL, NULL}
5239 ps_global->redrawer = redraw_pico;
5240 fix_windsize(ps_global);
5242 while(1){
5243 rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
5244 'n', 'x', h_confirm_cancel, RB_NORM);
5245 if(rv == 'c'){ /* user ACCEPTS! */
5246 rstr = "";
5247 break;
5249 else if(rv == 'y'){
5250 q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
5251 display_message('x');
5253 else
5254 break;
5257 ps_global->redrawer = redraw;
5258 return(rstr);
5262 /*----------------------------------------------------------------------
5263 Pass the first text segment of the message thru the "send filter"
5265 Args: body pointer and address for storage object of old data
5267 Returns: returns 1 on success, zero on error.
5268 ----*/
5270 filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
5271 STORE_S **old, METAENV *header)
5273 char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
5274 int key = 0, include_hdrs = 0;
5275 gf_io_t gc, pc;
5276 STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
5277 ? &body->nested.part->body.contents.text.data
5278 : &body->contents.text.data),
5279 *tmp_so = NULL, *tmpf_so,
5280 *save_local_so, *readthis_so, *our_tmpf_so = NULL;
5281 #define DO_HEADERS 1
5283 if(fcmd
5284 && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
5285 &key, &include_hdrs, NULL))){
5286 if(tmpf){
5288 * We need WRITE_TO_LOCALE here because the user is going to
5289 * be operating on tmpf. We need to change it back after they
5290 * are done.
5292 if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
5293 if(key){
5294 so_puts(tmpf_so, filter_session_key());
5295 so_puts(tmpf_so, NEWLINE);
5299 * If the headers are wanted for filtering, we can just
5300 * stick them in the tmpf file that is already there before
5301 * putting the body in.
5303 if(include_hdrs){
5304 save_local_so = lmc.so;
5305 lmc.so = tmpf_so; /* write it to tmpf_so */
5306 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5307 pine_rfc822_header(header, body, NULL, NULL);
5308 lmc.so = save_local_so;
5311 so_seek(*so, 0L, 0);
5312 gf_set_so_readc(&gc, *so);
5313 gf_set_so_writec(&pc, tmpf_so);
5314 gf_filter_init();
5315 errstr = gf_pipe(gc, pc);
5316 gf_clear_so_readc(*so);
5317 gf_clear_so_writec(tmpf_so);
5318 so_give(&tmpf_so);
5320 else
5321 errstr = "Can't create space for filter temporary file.";
5323 else if(include_hdrs){
5325 * Gf_filter wants a single storage object to read from.
5326 * If headers are wanted for filtering we'll have to put them
5327 * and the body into a temp file first and then use that
5328 * as the storage object for gf_filter.
5329 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5330 * takes care of that.
5332 if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
5333 /* put headers in our_tmpf_so */
5334 save_local_so = lmc.so;
5335 lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
5336 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5337 pine_rfc822_header(header, body, NULL, NULL);
5338 lmc.so = save_local_so;
5340 /* put body in our_tmpf_so */
5341 so_seek(*so, 0L, 0);
5342 gf_set_so_readc(&gc, *so);
5343 gf_set_so_writec(&pc, our_tmpf_so);
5344 gf_filter_init();
5345 errstr = gf_pipe(gc, pc);
5346 gf_clear_so_readc(*so);
5347 gf_clear_so_writec(our_tmpf_so);
5349 /* tell gf_filter to read from our_tmpf_so instead of *so */
5350 readthis_so = our_tmpf_so;
5352 else
5353 errstr = "Can't create space for temporary file.";
5355 else
5356 readthis_so = *so;
5358 if(!errstr){
5359 if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
5360 gf_set_so_writec(&pc, tmp_so);
5361 ps_global->mangled_screen = 1;
5362 suspend_busy_cue();
5363 ClearScreen();
5364 fflush(stdout);
5365 if(tmpf){
5366 PIPE_S *fpipe;
5368 if((fpipe = open_system_pipe(cmd, NULL, NULL,
5369 PIPE_NOSHELL | PIPE_RESET,
5370 0, pipe_callback, pipe_report_error)) != NULL){
5371 if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
5373 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5374 if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5375 gf_set_so_readc(&gc, tmpf_so);
5376 gf_filter_init();
5377 errstr = gf_pipe(gc, pc);
5378 gf_clear_so_readc(tmpf_so);
5379 so_give(&tmpf_so);
5381 else
5382 errstr = "Can't open temp file filter wrote.";
5384 else
5385 errstr = "Filter command returned error.";
5387 else
5388 errstr = "Can't exec filter text.";
5390 else
5391 errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
5392 readthis_so, pc, NULL, 0, 0,
5393 pipe_callback);
5395 if(our_tmpf_so)
5396 so_give(&our_tmpf_so);
5398 gf_clear_so_writec(tmp_so);
5400 #ifdef _WINDOWS
5401 if(!errstr){
5403 * Can't really be using stdout, so don't print message that
5404 * gets printed in the else. Ideally the program being called
5405 * will wait after showing the message, we might want to look
5406 * into doing the waiting in console based apps... or not.
5408 #else
5409 if(errstr){
5410 unsigned long ch;
5412 fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
5413 fflush(stdout);
5414 while((ch = read_char(300)) != ctrl('M')
5415 && ch != NO_OP_IDLE)
5416 putchar(BELL);
5418 else{
5419 #endif /* _WINDOWS */
5420 BODY *b = (body->type == TYPEMULTIPART)
5421 ? &body->nested.part->body : body;
5423 *old = *so; /* save old so */
5424 *so = tmp_so; /* return new one */
5425 (*so)->attr = copy_parameters((*old)->attr);
5428 * If the command said it would return new MIME
5429 * mime type data, check it out...
5431 if(mtf){
5432 char buf[MAILTMPLEN], *s;
5433 FILE *fp;
5435 if((fp = our_fopen(mtf, "rb")) != NULL){
5436 if(fgets(buf, sizeof(buf), fp)
5437 && !struncmp(buf, "content-", 8)
5438 && (s = strchr(buf+8, ':'))){
5439 BODY *nb = mail_newbody();
5441 for(*s++ = '\0'; *s == ' '; s++)
5444 rfc822_parse_content_header(nb,
5445 (char *) ucase((unsigned char *) buf+8),s);
5446 if(nb->type == TYPETEXT
5447 && nb->subtype
5448 && (!b->subtype
5449 || strucmp(b->subtype, nb->subtype))){
5450 if(b->subtype)
5451 fs_give((void **) &b->subtype);
5453 b->subtype = nb->subtype;
5454 nb->subtype = NULL;
5456 mail_free_body_parameter(&b->parameter);
5457 b->parameter = nb->parameter;
5458 nb->parameter = NULL;
5459 mail_free_body_parameter(&nb->parameter);
5462 mail_free_body(&nb);
5465 fclose(fp);
5470 * Reevaluate the encoding in case form's changed...
5472 b->encoding = ENCOTHER;
5473 set_mime_type_by_grope(b);
5476 ClearScreen();
5477 resume_busy_cue(0);
5479 else
5480 errstr = "Can't create space for filtered text.";
5483 fs_give((void **)&cmd);
5485 else
5486 return(0);
5488 if(tmpf){
5489 our_unlink(tmpf);
5490 fs_give((void **)&tmpf);
5493 if(mtf){
5494 our_unlink(mtf);
5495 fs_give((void **) &mtf);
5498 if(resultf){
5499 if(name_file_size(resultf) > 0L)
5500 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
5501 our_unlink(resultf);
5502 fs_give((void **)&resultf);
5504 else if(errstr){
5505 if(tmp_so)
5506 so_give(&tmp_so);
5508 q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
5509 errstr);
5510 dprint((1, "Filter FAILED: %s\n",
5511 errstr ? errstr : "?"));
5514 return(errstr == NULL);
5518 /*----------------------------------------------------------------------
5519 Copy the newsgroup name of the given mailbox into the given buffer
5521 Args:
5523 Returns:
5524 ----*/
5525 void
5526 pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
5528 NETMBX mb;
5530 if(*mailbox == '#'){ /* Strip the leading "#news." */
5531 strncpy(group_name, mailbox + 6, len-1);
5532 group_name[len-1] = '\0';
5534 else if(mail_valid_net_parse(mailbox, &mb)){
5535 pine_send_newsgroup_name(mb.mailbox, group_name, len);
5537 else
5538 *group_name = '\0';
5542 /*----------------------------------------------------------------------
5543 Set up fields for passing to pico. Assumes first text part is
5544 intended to be passed along for editing, and is in the form of
5545 of a storage object brought into existence sometime before pico_send().
5546 -----*/
5547 void
5548 outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
5549 PATMT **pico_a, int from_bounce)
5551 PINEFIELD *pf;
5554 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5555 * is guaranteed to be of type PicoText!
5557 if(bod->type == TYPETEXT){
5558 *text = so_text((STORE_S *) bod->contents.text.data);
5560 /* mark storage object as user edited */
5561 if(!from_bounce)
5562 (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
5564 else if(bod->type == TYPEMULTIPART){
5565 PART *part;
5566 PATMT **ppa;
5567 char *type, *name, *p;
5568 int name_l;
5571 * We used to jump out the window if the first part wasn't text,
5572 * but that may not be the case when bouncing a message with
5573 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5574 * contents of the first part to send is still ALWAYS in a
5575 * PicoText storage object, *AND* if that object doesn't contain
5576 * data of type text, then it must contain THE ENCODED NON-TEXT
5577 * DATA of the piece being sent.
5579 * It's up to the programmer to make sure that such a message is
5580 * sent via pine_simple_send and never get to the composer via
5581 * pine_send.
5583 * Make sense?
5585 *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
5587 /* mark storage object as user edited */
5588 if(!from_bounce)
5589 (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
5592 * If we already had a list, blast it now, so we can build a new
5593 * attachment list that reflects what's really there...
5595 if(pico_a){
5596 free_attachment_list(pico_a);
5599 /* Simplifyihg assumption #28e. (see cross reference)
5600 All parts in the body passed in here that are not already
5601 in the attachments list are added to the end of the attachments
5602 list. Attachment items not in the body list will be taken care
5603 of in strings2outgoing, but they are unlikely to occur
5606 for(part = bod->nested.part->next; part != NULL; part = part->next) {
5607 /* Already in list? */
5608 for(ppa = pico_a;
5609 *ppa && strcmp((*ppa)->id, part->body.id);
5610 ppa = &(*ppa)->next)
5613 if(!*ppa){ /* Not in the list! append it... */
5614 *ppa = (PATMT *)fs_get(sizeof(PATMT));
5616 if(part->body.description){
5617 char *p;
5618 size_t len;
5620 len = 4*strlen(part->body.description)+1;
5621 p = (char *)fs_get(len*sizeof(char));
5622 if(rfc1522_decode_to_utf8((unsigned char *)p,
5623 len, part->body.description) == (unsigned char *) p){
5624 (*ppa)->description = p;
5626 else{
5627 fs_give((void **)&p);
5628 (*ppa)->description = cpystr(part->body.description);
5631 else
5632 (*ppa)->description = cpystr("");
5634 type = type_desc(part->body.type, part->body.subtype,
5635 part->body.parameter, NULL, 0);
5638 * If we can find a "name" parm, display that too...
5640 if((name = parameter_val(part->body.parameter, "name")) != NULL){
5641 /* Convert any [ or ]'s the name contained */
5642 for(p = name; *p ; p++)
5643 if(*p == '[')
5644 *p = '(';
5645 else if(*p == ']')
5646 *p = ')';
5648 name_l = p - name;
5650 else
5651 name_l = 0;
5653 (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
5655 snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
5656 name ? ": " : "", name ? name : "");
5657 (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
5659 if(name)
5660 fs_give((void **) &name);
5662 (*ppa)->flags = A_FLIT;
5663 (*ppa)->size = cpystr(byte_string(
5664 send_body_size(&part->body)));
5665 if(!part->body.id)
5666 part->body.id = generate_message_id();
5668 (*ppa)->id = cpystr(part->body.id);
5669 (*ppa)->next = NULL;
5676 /*------------------------------------------------------------------
5677 Malloc strings to pass to composer editor because it expects
5678 such strings so it can realloc them
5679 -----------------------------------------------------------------*/
5681 * turn any address fields into text strings
5684 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5685 * NOT to be RFC1522 decoded. Said differently, they're understood
5686 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5687 * original charset tagging as far into the compose/send pipe as
5688 * we can.
5690 for(pf = header->local; pf && pf->name; pf = pf->next)
5691 if(pf->canedit)
5692 switch(pf->type){
5693 case Address :
5694 if(pf->addr){
5695 char *p, *t, *u;
5696 long l;
5698 pf->scratch = addr_list_string(*pf->addr, NULL, 1);
5701 * Scan for and fix-up patently bogus fields.
5703 * NOTE: collaboration with this code and what's done in
5704 * reply.c:reply_cp_addr to package up the bogus stuff
5705 * is required.
5707 for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
5708 for(t = p; ; t--)
5709 if(*t == '&'){ /* find "leading" token */
5710 int replacelen;
5713 * Rfc822_cat has been changed so that it now quotes
5714 * this sometimes. So we have to look out for quotes
5715 * which confuse the decoder. It was only quoting
5716 * because we were putting \r \n in the input, I think.
5718 if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
5719 t[-1] = p[-1] = ' ';
5721 *t++ = ' '; /* replace token */
5722 *p = '\0'; /* tie off string */
5723 u = rfc822_base64((unsigned char *) t,
5724 (unsigned long) strlen(t),
5725 (unsigned long *) &l);
5726 if(!u)
5727 u = "";
5729 replacelen = strlen(t);
5730 *p = '@'; /* restore 'p' */
5731 rplstr(p, strlen(p), 12, ""); /* clear special token */
5732 rplstr(t, strlen(u)-replacelen+1, replacelen, u);
5733 if(u)
5734 fs_give((void **) &u);
5736 if(HE(pf))
5737 HE(pf)->start_here = 1;
5739 break;
5741 else if(t == pf->scratch)
5742 break;
5744 removing_leading_white_space(pf->scratch);
5745 if(pf->scratch){
5746 size_t l;
5749 * Replace control characters with ^C notation, unless
5750 * some conditions are met (see istrncpy).
5751 * If user doesn't edit, then we switch back to the
5752 * original version. If user does edit, then all bets
5753 * are off.
5755 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5756 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5757 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5758 fs_give((void **)&pf->scratch);
5759 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5762 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5766 break;
5768 case Subject :
5769 if(pf->text){
5770 char *p, *src;
5771 size_t len;
5773 src = pf->scratch ? pf->scratch
5774 : (*pf->text) ? *pf->text : "";
5776 len = 4*strlen(src)+1;
5777 p = (char *)fs_get(len * sizeof(char));
5778 if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
5779 if(pf->scratch)
5780 fs_give((void **)&pf->scratch);
5782 pf->scratch = p;
5784 else{
5785 fs_give((void **)&p);
5786 if(!pf->scratch)
5787 pf->scratch = cpystr(src);
5790 if(pf->scratch){
5791 size_t l;
5794 * Replace control characters with ^C notation, unless
5795 * some conditions are met (see istrncpy).
5796 * If user doesn't edit, then we switch back to the
5797 * original version. If user does edit, then all bets
5798 * are off.
5800 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5801 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5802 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5803 fs_give((void **)&pf->scratch);
5804 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5807 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5811 break;
5813 default :
5814 break;
5819 /*----------------------------------------------------------------------
5820 Restore fields returned from pico to form useful to sending
5821 routines.
5822 -----*/
5823 void
5824 strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
5826 PINEFIELD *pf;
5827 int we_cancel = 0;
5829 we_cancel = busy_cue(NULL, NULL, 1);
5832 * turn any local address strings into address lists
5834 for(pf = header->local; pf && pf->name; pf = pf->next)
5835 if(pf->scratch){
5836 char *the_address = NULL;
5838 switch(pf->type){
5839 case Address :
5840 removing_trailing_white_space(pf->scratch);
5842 if((the_address || *pf->scratch) && pf->addr){
5843 ADDRESS *new_addr = NULL;
5844 static char *fakedomain = "@";
5846 if(!the_address)
5847 the_address = pf->scratch;
5849 rfc822_parse_adrlist(&new_addr, the_address,
5850 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
5851 ? fakedomain : ps_global->maildomain);
5852 mail_free_address(pf->addr); /* free old addrs */
5853 *pf->addr = new_addr; /* assign new addr */
5855 else if(pf->addr)
5856 mail_free_address(pf->addr); /* free old addrs */
5858 break;
5860 case Subject :
5861 if(*pf->text)
5862 fs_give((void **)pf->text);
5864 if(*pf->scratch){
5865 *pf->text = cpystr(pf->scratch);
5868 break;
5870 default :
5871 break;
5874 fs_give((void **)&pf->scratch); /* free now useless text */
5877 create_message_body(bod, attach, flow_it);
5879 if(we_cancel)
5880 cancel_busy_cue(-1);
5884 /*----------------------------------------------------------------------
5886 The head of the body list here is always either TEXT or MULTIPART. It may be
5887 changed from TEXT to MULTIPART if there are attachments to be added
5888 and it is not already multipart.
5889 ----*/
5890 void
5891 create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
5893 PART *p, **pp;
5894 PATMT *pa;
5895 BODY *tmp_body, *text_body = NULL;
5896 void *file_contents;
5897 PARAMETER **parmp;
5898 char *lc;
5900 TIME_STAMP("create_body start.", 1);
5902 * if conditions are met short circuit MIME wrapping
5904 if((*b)->type != TYPEMULTIPART && !attach){
5906 /* only override assigned encoding if it might need upgrading */
5907 if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
5908 (*b)->encoding = ENCOTHER;
5910 create_message_body_text(*b, flow_it);
5912 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
5913 || !((*b)->encoding == ENC8BIT
5914 || (*b)->encoding == ENCBINARY)){
5915 TIME_STAMP("create_body end.", 1);
5916 return;
5918 else /* protect 8bit in multipart */
5919 text_body = *b;
5922 if((*b)->type == TYPETEXT) {
5923 /*-- Current type is text, but there are attachments to add --*/
5924 /*-- Upgrade to a TYPEMULTIPART --*/
5925 tmp_body = (BODY *)mail_newbody();
5926 tmp_body->type = TYPEMULTIPART;
5927 tmp_body->nested.part = mail_newbody_part();
5928 if(text_body){
5930 * Why do we do this?
5931 * The problem is that base64 or quoted-printable encoding is
5932 * sensitive to having random data appended to it's end. If
5933 * we use a single part TEXT message and something in between
5934 * us and the end appends advertising without adjusting for
5935 * the encoding, the message is screwed up. So we wrap the
5936 * text part inside a multipart and then the appended data
5937 * will come after the boundary.
5939 * We wish we could do this on the way out the door in a
5940 * child of post_rfc822_output because at that point we know
5941 * the character set and the encoding being used. For example,
5942 * iso-2022-jp is an encoding that is not sensitive to data
5943 * appended to the end, so it wouldn't need to be wrapped.
5944 * We could conceivably have post_rfc822_body inspect the
5945 * body and change it before doing the output. It would work
5946 * but would be very fragile. We'd be passed a body from
5947 * c-client to output and instead of just doing the output
5948 * we'd change the body and then output it. Not worth it
5949 * since the multipart wrapping is completely correct for
5950 * MIME-aware mailers.
5952 (void) copy_body(&(tmp_body->nested.part->body), *b);
5953 /* move contents which were NOT copied */
5954 tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
5955 (*b)->contents.text.data = NULL;
5957 else{
5958 tmp_body->nested.part->body = **b;
5960 (*b)->subtype = (*b)->id = (*b)->description = NULL;
5961 (*b)->parameter = NULL;
5962 (*b)->contents.text.data = NULL;
5965 pine_free_body(b);
5966 *b = tmp_body;
5969 if(!text_body){
5970 /*-- Now type must be MULTIPART with first part text --*/
5971 (*b)->nested.part->body.encoding = ENCOTHER;
5972 create_message_body_text(&((*b)->nested.part->body), flow_it);
5975 /*------ Go through the parts list remove those to be deleted -----*/
5976 for(pp = &(*b)->nested.part->next; *pp;){
5977 for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
5978 /* already existed? */
5979 if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
5980 char *orig_descp = NULL, *cs = NULL;
5983 * decode original to see if it matches what was decoded
5984 * when we sent it in.
5987 if((*pp)->body.description)
5988 orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
5989 SIZEOF_20KBUF, (*pp)->body.description);
5991 if(!(*pp)->body.description /* update description? */
5992 || (pa->description && strcmp(pa->description, orig_descp))){
5993 if((*pp)->body.description)
5994 fs_give((void **) &(*pp)->body.description);
5996 /* encoding happens as msg text is written */
5997 (*pp)->body.description = cpystr(pa->description);
6000 if(cs)
6001 fs_give((void **) &cs);
6003 break;
6006 if(pa == NULL){
6007 p = *pp; /* prepare to zap *pp */
6008 *pp = p->next; /* pull next one in list up */
6009 p->next = NULL; /* tie off removed node */
6011 pine_free_body_data(&p->body); /* clean up contained data */
6012 mail_free_body_part(&p); /* free up the part */
6014 else
6015 pp = &(*pp)->next;
6018 /*---------- Now add any new attachments ---------*/
6019 for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
6020 for(pa = attach; pa != NULL; pa = pa->next) {
6021 if(pa->id != NULL)
6022 continue; /* Has an ID, it's old */
6025 * the idea is handle ALL attachments as open FILE *'s. Actual
6026 * encoding and such is handled at the time the message
6027 * is shoved into the mail slot or written to disk...
6029 * Also, we never unlink a file, so it's up to whoever opens
6030 * it to deal with tmpfile issues.
6032 if((file_contents = (void *)so_get(FileStar, pa->filename,
6033 READ_ACCESS)) == NULL){
6034 q_status_message2(SM_ORDER | SM_DING, 3, 4,
6035 _("Error \"%s\", couldn't attach file \"%s\""),
6036 error_description(errno), pa->filename);
6037 display_message('x');
6038 continue;
6041 p->next = mail_newbody_part();
6042 p = p->next;
6043 p->body.id = generate_message_id();
6044 p->body.contents.text.data = file_contents;
6047 * Set type to unknown and let set_mime_type_by_* figure it out.
6048 * Always encode attachments we add as BINARY.
6050 p->body.type = TYPEOTHER;
6051 p->body.encoding = ENCBINARY;
6052 p->body.size.bytes = name_file_size(pa->filename);
6053 if(!set_mime_type_by_extension(&p->body, pa->filename)){
6054 set_mime_type_by_grope(&p->body);
6055 set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
6058 so_release((STORE_S *)p->body.contents.text.data);
6060 if(pa->description) /* encoding happens when msg written */
6061 p->body.description = cpystr(pa->description);
6063 /* Add name attribute for backward compatibility */
6064 for(parmp = &p->body.parameter; *parmp; )
6065 if(!struncmp((*parmp)->attribute, "name", 4)
6066 && (!*((*parmp)->attribute + 4)
6067 || *((*parmp)->attribute + 4) == '*')){
6068 PARAMETER *free_me = *parmp;
6069 *parmp = (*parmp)->next;
6070 free_me->next = NULL;
6071 mail_free_body_parameter(&free_me);
6073 else
6074 parmp = &(*parmp)->next;
6076 set_parameter(parmp, "name",
6077 pa->filename
6078 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6079 : NULL);
6081 /* Then set the Content-Disposition ala RFC1806 */
6082 if(!p->body.disposition.type){
6083 p->body.disposition.type = cpystr("attachment");
6084 for(parmp = &p->body.disposition.parameter; *parmp; )
6085 if(!struncmp((*parmp)->attribute, "filename", 4)
6086 && (!*((*parmp)->attribute + 4)
6087 || *((*parmp)->attribute + 4) == '*')){
6088 PARAMETER *free_me = *parmp;
6089 *parmp = (*parmp)->next;
6090 free_me->next = NULL;
6091 mail_free_body_parameter(&free_me);
6093 else
6094 parmp = &(*parmp)->next;
6096 set_parameter(parmp, "filename",
6097 pa->filename
6098 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6099 : NULL);
6102 p->next = NULL;
6103 pa->id = cpystr(p->body.id);
6107 * Now, if this multipart has but one text piece (that is, no
6108 * attachments), then downgrade from a composite type to a discrete
6109 * text/plain message if CTE is not 8bit.
6111 if(!(*b)->nested.part->next
6112 && (*b)->nested.part->body.type == TYPETEXT
6113 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
6114 || !((*b)->nested.part->body.encoding == ENC8BIT
6115 || (*b)->nested.part->body.encoding == ENCBINARY))){
6116 /* Clone the interesting body part */
6117 tmp_body = mail_newbody();
6118 *tmp_body = (*b)->nested.part->body;
6119 /* and rub out what we don't want cleaned up when it's free'd */
6120 mail_initbody(&(*b)->nested.part->body);
6121 mail_free_body(b);
6122 *b = tmp_body;
6126 TIME_STAMP("create_body end.", 1);
6131 * Fill in text BODY part's structure
6134 void
6135 create_message_body_text(struct mail_bodystruct *b, int flow_it)
6137 set_mime_type_by_grope(b);
6138 if(b != NULL){
6139 remove_parameter(&b->parameter, "format"); /* we will set it up below */
6140 remove_parameter(&b->parameter, "delsp"); /* we never set this up */
6142 if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
6143 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
6144 && flow_it)
6145 set_parameter(b ? &b->parameter : NULL, "format", "flowed");
6147 set_body_size(b);
6152 * free_attachment_list - free attachments in given list
6154 void
6155 free_attachment_list(PATMT **alist)
6157 PATMT *leading;
6159 while(alist && *alist){ /* pointer pointing to something */
6160 leading = (*alist)->next;
6161 if((*alist)->description)
6162 fs_give((void **)&(*alist)->description);
6164 if((*alist)->filename){
6165 if((*alist)->flags & A_TMP)
6166 if(our_unlink((*alist)->filename) < 0)
6167 dprint((1, "-- Can't unlink(%s): %s\n",
6168 (*alist)->filename ? (*alist)->filename : "?",
6169 error_description(errno)));
6171 fs_give((void **)&(*alist)->filename);
6174 if((*alist)->size)
6175 fs_give((void **)&(*alist)->size);
6177 if((*alist)->id)
6178 fs_give((void **)&(*alist)->id);
6180 fs_give((void **)alist);
6182 *alist = leading;
6187 void
6188 set_body_size(struct mail_bodystruct *b)
6190 unsigned char c;
6191 int we_cancel = 0;
6193 we_cancel = busy_cue(NULL, NULL, 1);
6194 so_seek((STORE_S *)b->contents.text.data, 0L, 0);
6195 b->size.bytes = 0L;
6196 while(so_readc(&c, (STORE_S *)b->contents.text.data))
6197 b->size.bytes++;
6199 if(we_cancel)
6200 cancel_busy_cue(-1);
6205 * view_as_rich - set the rich_header flag
6207 * name - name of the header field
6208 * deflt - default value to return if user didn't set it
6210 * Note: if the user tries to turn them all off with "", then
6211 * we take that to mean default, since otherwise there is no
6212 * way to get to the headers.
6215 view_as_rich(char *name, int deflt)
6217 char **p;
6218 char *q;
6220 p = ps_global->VAR_COMP_HDRS;
6222 if(p && *p && **p){
6223 for(; (q = *p) != NULL; p++){
6224 if(!struncmp(q, name, strlen(name)))
6225 return 0; /* 0 means we *do* view it by default */
6228 return 1; /* 1 means it starts out hidden */
6230 return(deflt);
6235 * background_posting - return whether or not we're already in the process
6236 * of posting
6239 background_posting(int gripe)
6241 if(ps_global->post){
6242 if(gripe)
6243 q_status_message(SM_ORDER|SM_DING, 3, 3,
6244 _("Can't post while posting!"));
6245 return(1);
6248 return(0);
6252 /*----------------------------------------------------------------------
6253 Validate the given subject relative to any news groups.
6255 Args: none
6257 Returns: always returns 1, but also returns error if
6258 ----*/
6260 valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
6262 struct headerentry *hp;
6264 if(expanded)
6265 *expanded = cpystr(given);
6267 if(error){
6269 * Now look for any header entry we passed to pico that has to do
6270 * with news. If there's no subject, gripe.
6272 for(hp = pbf->headents; hp->prompt; hp++)
6273 if(hp->help == h_composer_news){
6274 if(hp->hd_text->text[0] && !*given)
6275 *error = cpystr(
6276 _("News postings MUST have a subject! Please add one!"));
6278 break;
6282 return(0);
6287 * This is the build_address used by the composer to check for an address
6288 * in the addrbook.
6290 * Args: to -- the passed in line to parse
6291 * full_to -- Address of a pointer to return the full address in.
6292 * This will be allocated here and freed by the caller.
6293 * error -- Address of a pointer to return an error message in.
6294 * This will be allocated here and freed by the caller.
6295 * barg -- Address of a pointer to return the fcc in is in
6296 * fcc->tptr. It will have already been allocated by the
6297 * caller but we may free it and reallocate if we wish.
6298 * Caller will free it.
6300 * Result: 0 is returned if address was OK,
6301 * -1 if address wasn't OK.
6303 * Side effect: Can flush addrbook entry cache entries so they need to be
6304 * re-fetched afterwords.
6307 build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
6309 char *p;
6310 int ret_val, no_repo = 0, *save_nesting_level;
6311 BuildTo bldto;
6312 PrivateTop *pt = NULL;
6313 PrivateAffector *af = NULL;
6314 char *fcc_local = NULL;
6315 jmp_buf save_jmp_buf;
6317 dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
6319 /* check to see if to string is empty to avoid work */
6320 for(p = to; p && *p && isspace((unsigned char)*p); p++)
6321 ;/* do nothing */
6323 if(!p || !*p){
6324 if(full_to)
6325 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
6327 return 0;
6330 if(full_to != NULL)
6331 *full_to = (char *)NULL;
6333 if(error != NULL)
6334 *error = (char *)NULL;
6336 /* No guarantee cursor or status line is how we saved it */
6337 clear_cursor_pos();
6338 mark_status_unknown();
6340 if(ps_global->remote_abook_validity > 0 &&
6341 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6342 *mangled |= BUILDER_SCREEN_MANGLED;
6345 * If we end up jumping back here because somebody else changed one of
6346 * our addrbooks out from underneath us, we may well leak some memory.
6347 * That's probably ok since this will be very rare.
6349 * The reason for the memcpy of the jmp_buf is that we may actually
6350 * be indirectly calling this function from within the address book.
6351 * For example, we may be in the address book screen and then run
6352 * the ComposeTo command which puts us in the composer, then we call
6353 * build_address from there which resets addrbook_changed_unexpectedly.
6354 * Once we leave build_address we need to reset addrbook_changed_un...
6355 * because this position on the stack will no longer be valid.
6356 * Same is true of the other setjmp's in this file which are wrapped
6357 * in memcpy calls.
6359 save_nesting_level = cpyint(ab_nesting_level);
6360 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6361 if(setjmp(addrbook_changed_unexpectedly)){
6362 no_repo = 0;
6363 pt = NULL;
6364 af = NULL;
6365 fcc_local = NULL;
6366 if(error != NULL)
6367 *error = (char *)NULL;
6369 if(full_to && *full_to)
6370 fs_give((void **)full_to);
6372 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6373 dprint((1,
6374 "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
6375 addrbook_reset();
6376 ab_nesting_level = *save_nesting_level;
6379 ab_nesting_level++;
6380 bldto.type = Str;
6381 bldto.arg.str = to;
6382 ret_val = build_address_internal(bldto, full_to, error,
6383 barg ? &fcc_local : NULL,
6384 &no_repo, NULL, save_and_restore,
6385 0, mangled);
6386 ab_nesting_level--;
6387 if(save_nesting_level)
6388 fs_give((void **)&save_nesting_level);
6391 * Have to rfc1522_decode the full_to string before sending it back.
6393 if(full_to && *full_to ){
6394 char *q;
6395 size_t len;
6397 len = 4*strlen(*full_to)+1;
6398 q = (char *)fs_get(len * sizeof(char));
6399 p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
6401 /* p == q means that decoding happened, p is decoded *full_to */
6402 if(p == q){
6403 fs_give((void **)full_to);
6404 *full_to = p;
6406 else
6407 fs_give((void **)&q);
6410 if(fcc_local){
6411 unsigned long csum;
6413 /* Pt will point to headents[Fcc].bldr_private */
6414 pt = NULL;
6415 if(barg && barg->aff)
6416 pt = (PrivateTop *)(*barg->aff);
6419 * If *barg->aff is set, that means fcc was set from a list
6420 * during some previous builder call.
6421 * If the current To line contains the old expansion as a prefix, then
6422 * we should leave things as they are. In order to decide that,
6423 * we look at a hash value computed from the strings.
6425 if(pt && (af=pt->affector) && af->who == BP_To){
6426 int len;
6428 len = strlen(to);
6429 if(len >= af->cksumlen){
6430 int save;
6432 save = to[af->cksumlen];
6433 to[af->cksumlen] = '\0';
6434 csum = line_hash(to);
6435 to[af->cksumlen] = save;
6437 else
6438 csum = af->cksumval + 1; /* something not equal to cksumval */
6441 if(!pt ||
6442 !pt->affector ||
6443 (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
6445 /* replace fcc value */
6446 if(barg->tptr)
6447 fs_give((void **)&barg->tptr);
6449 barg->tptr = fcc_local;
6451 if(barg->aff){
6452 if(!pt){
6453 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6454 pt = (PrivateTop *)(*barg->aff);
6455 memset((void *)pt, 0, sizeof(PrivateTop));
6458 if(no_repo){
6459 if(!pt->affector)
6460 pt->affector =
6461 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6463 af = pt->affector;
6464 af->who = BP_To;
6465 af->cksumlen = strlen(((full_to && *full_to)
6466 ? *full_to : ""));
6467 af->cksumval = line_hash(((full_to && *full_to)
6468 ? *full_to : ""));
6470 else{
6472 * If result is reproducible, we don't keep track here.
6474 if(pt->affector)
6475 fs_give((void **)&pt->affector);
6479 else
6480 fs_give((void **)&fcc_local); /* unused in this case */
6483 /* This is so pico will erase the old message */
6484 if(error != NULL && *error == NULL)
6485 *error = cpystr("");
6487 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6488 flush_status_messages(1);
6489 return(ret_val);
6494 * This is the builder used by the composer for the Lcc line.
6496 * Args: lcc -- the passed in Lcc line to parse
6497 * full_lcc -- Address of a pointer to return the full address in.
6498 * This will be allocated here and freed by the caller.
6499 * error -- Address of a pointer to return an error message in.
6500 * This is not allocated so should not be freed by the caller.
6501 * barg -- This is a pointer to text for affected entries which
6502 * we may be changing. The first one in the list is the
6503 * To entry. We may put the name of the list in empty
6504 * group syntax form there (like List Name: ;).
6505 * The second one in the list is the fcc field.
6506 * The tptr members already point to text allocated in the
6507 * caller. We may free and reallocate here, caller will
6508 * free the result in any case.
6510 * Result: 0 is returned if address was OK,
6511 * -1 if address wasn't OK.
6513 * Side effect: Can flush addrbook entry cache entries so they need to be
6514 * re-fetched afterwords.
6517 build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
6519 int ret_val,
6520 no_repo = 0; /* fcc or lcc not reproducible */
6521 int *save_nesting_level;
6522 BuildTo bldlcc;
6523 PrivateTop *pt = NULL;
6524 PrivateAffector *af = NULL;
6525 char *p,
6526 *fcc_local = NULL,
6527 *to = NULL,
6528 *dummy;
6529 jmp_buf save_jmp_buf;
6531 dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
6533 /* check to see if to string is empty to avoid work */
6534 for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
6535 ;/* do nothing */
6537 if(!p || !*p){
6538 if(full_lcc)
6539 *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
6541 return 0;
6544 if(error != NULL)
6545 *error = (char *)NULL;
6547 if(ps_global->remote_abook_validity > 0 &&
6548 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6549 *mangled |= BUILDER_SCREEN_MANGLED;
6552 * If we end up jumping back here because somebody else changed one of
6553 * our addrbooks out from underneath us, we may well leak some memory.
6554 * That's probably ok since this will be very rare.
6556 save_nesting_level = cpyint(ab_nesting_level);
6557 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6558 if(setjmp(addrbook_changed_unexpectedly)){
6559 no_repo = 0;
6560 pt = NULL;
6561 af = NULL;
6562 fcc_local = NULL;
6563 to = NULL;
6564 if(error != NULL)
6565 *error = (char *)NULL;
6567 if(full_lcc && *full_lcc)
6568 fs_give((void **)full_lcc);
6570 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6571 dprint((1,
6572 "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
6573 addrbook_reset();
6574 ab_nesting_level = *save_nesting_level;
6577 ab_nesting_level++;
6578 bldlcc.type = Str;
6579 bldlcc.arg.str = lcc;
6582 * To is first affected_entry and Fcc is second.
6583 * The conditional stuff for the fcc argument says to only change the
6584 * fcc if the fcc pointer is passed in non-null, and the To pointer
6585 * is also non-null. If they are null, that means they've already been
6586 * entered (are sticky). We don't affect fcc if either fcc or To has
6587 * been typed in.
6589 ret_val = build_address_internal(bldlcc,
6590 full_lcc,
6591 error,
6592 (barg && barg->next && barg->next->tptr && barg->tptr)
6593 ? &fcc_local : NULL,
6594 &no_repo,
6595 (barg && barg->tptr) ? &to : NULL,
6596 save_and_restore, 0, mangled);
6598 ab_nesting_level--;
6599 if(save_nesting_level)
6600 fs_give((void **)&save_nesting_level);
6602 /* full_lcc is what ends up in the Lcc: line */
6603 if(full_lcc && *full_lcc){
6604 size_t len;
6607 * Have to rfc1522_decode the full_lcc string before sending it back.
6609 len = 4*strlen(*full_lcc)+1;
6610 p = (char *)fs_get(len * sizeof(char));
6611 if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
6612 fs_give((void **)full_lcc);
6613 *full_lcc = p;
6615 else
6616 fs_give((void **)&p);
6619 /* to is what ends up in the To: line */
6620 if(to && *to){
6621 unsigned long csum;
6622 size_t len;
6625 * Have to rfc1522_decode the full_to string before sending it back.
6627 len = 4*strlen(to)+1;
6628 p = (char *)fs_get(len * sizeof(char));
6629 dummy = NULL;
6630 if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
6632 * If the caller wants us to try to preserve the charset
6633 * information (they set aff) we copy it into encoded->etext.
6634 * We don't have to worry about pasting together pieces of
6635 * etext like we do in build_address because whenever the
6636 * Lcc line is setting the To line it will be setting the
6637 * whole line, not modifying it.
6638 * Pt will point to headents[To].bldr_private.
6640 if(barg && barg->aff){
6641 pt = (PrivateTop *)(*barg->aff);
6643 if(!pt){
6644 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6645 pt = (PrivateTop *)(*barg->aff);
6646 memset((void *)pt, 0, sizeof(PrivateTop));
6650 fs_give((void **)&to);
6651 to = p;
6653 else
6654 fs_give((void **)&p);
6656 if(dummy)
6657 fs_give((void **)&dummy);
6661 * This part is recording the fact that the To line was set to
6662 * what it is by entering something on the Lcc line. In particular,
6663 * if a list alias was entered here then the fullname of the list
6664 * goes in the To line. We save this affector information so that
6665 * we can tell it shouldn't be modified if we call build_addr_lcc
6666 * again unless we actually modified what's in the Lcc line so that
6667 * it doesn't start with the same thing. The problem we're solving
6668 * is that the contents of the Lcc line no longer look like the
6669 * list they were derived from.
6670 * Pt will point to headents[To].bldr_private.
6672 if(barg && barg->aff)
6673 pt = (PrivateTop *)(*barg->aff);
6675 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6676 int len;
6678 len = strlen(lcc);
6679 if(len >= af->cksumlen){
6680 int save;
6682 save = lcc[af->cksumlen];
6683 lcc[af->cksumlen] = '\0';
6684 csum = line_hash(lcc);
6685 lcc[af->cksumlen] = save;
6687 else
6688 csum = af->cksumval + 1; /* so they aren't equal */
6691 if(!pt ||
6692 !pt->affector ||
6693 pt->affector->who != BP_Lcc ||
6694 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6696 /* replace to value */
6697 if(barg->tptr && barg->tptr[0]){
6698 size_t l;
6699 char *t;
6701 l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
6702 t = (char *)fs_get((l+1) * sizeof(char));
6703 snprintf(t, l+1, "%s%s%s",
6704 barg->tptr,
6705 (to && *to) ? ", " : "",
6706 (to && *to) ? to : "");
6707 fs_give((void **)&barg->tptr);
6708 if(to)
6709 fs_give((void **)&to);
6711 barg->tptr = t;
6713 else{
6714 if(barg->tptr)
6715 fs_give((void **)&barg->tptr);
6717 barg->tptr = to;
6720 if(barg->aff){
6721 if(!pt){
6722 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6723 pt = (PrivateTop *)(*barg->aff);
6724 memset((void *)pt, 0, sizeof(PrivateTop));
6727 if(no_repo){
6728 if(!pt->affector)
6729 pt->affector =
6730 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6732 af = pt->affector;
6733 af->who = BP_Lcc;
6734 af->cksumlen = strlen(((full_lcc && *full_lcc)
6735 ? *full_lcc : ""));
6736 af->cksumval = line_hash(((full_lcc && *full_lcc)
6737 ? *full_lcc : ""));
6739 else{
6741 * If result is reproducible, we don't keep track here.
6743 if(pt->affector)
6744 fs_give((void **)&pt->affector);
6748 else
6749 fs_give((void **)&to); /* unused in this case */
6752 if(fcc_local){
6753 unsigned long csum;
6756 * If *barg->next->aff is set, that means fcc was set from a list
6757 * during some previous builder call. If the current Lcc line
6758 * contains the old expansion as a prefix, then we should leave
6759 * things as they are. In order to decide that we look at a hash
6760 * value computed from the strings.
6761 * Pt will point to headents[Fcc].bldr_private
6763 pt = NULL;
6764 if(barg && barg->next && barg->next->aff)
6765 pt = (PrivateTop *)(*barg->next->aff);
6767 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6768 int len;
6770 len = strlen(lcc);
6771 if(len >= af->cksumlen){
6772 int save;
6774 save = lcc[af->cksumlen];
6775 lcc[af->cksumlen] = '\0';
6776 csum = line_hash(lcc);
6777 lcc[af->cksumlen] = save;
6779 else
6780 csum = af->cksumval + 1; /* something not equal to cksumval */
6783 if(!pt ||
6784 !pt->affector ||
6785 pt->affector->who != BP_Lcc ||
6786 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6788 /* replace fcc value */
6789 if(barg->next->tptr)
6790 fs_give((void **)&barg->next->tptr);
6792 barg->next->tptr = fcc_local;
6794 if(barg->next->aff){
6795 if(!pt){
6796 *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
6797 pt = (PrivateTop *)(*barg->next->aff);
6798 memset((void *)pt, 0, sizeof(PrivateTop));
6801 if(no_repo){
6802 if(!pt->affector)
6803 pt->affector =
6804 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6806 af = pt->affector;
6807 af->who = BP_Lcc;
6808 af->cksumlen = strlen(((full_lcc && *full_lcc)
6809 ? *full_lcc : ""));
6810 af->cksumval = line_hash(((full_lcc && *full_lcc)
6811 ? *full_lcc : ""));
6813 else{
6815 * If result is reproducible, we don't keep track here.
6817 if(pt->affector)
6818 fs_give((void **)&pt->affector);
6822 else
6823 fs_give((void **)&fcc_local); /* unused in this case */
6827 if(error != NULL && *error == NULL)
6828 *error = cpystr("");
6830 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6831 flush_status_messages(0);
6832 return(ret_val);
6836 /*----------------------------------------------------------------------
6837 Verify and canonicalize news groups names.
6838 Called from the message composer
6840 Args: given_group -- List of groups typed by user
6841 expanded_group -- pointer to point to expanded list, which will be
6842 allocated here and freed in caller. If this is
6843 NULL, don't attempt to validate.
6844 error -- pointer to store error message
6845 fcc -- pointer to point to fcc, which will be
6846 allocated here and freed in caller
6848 Returns: 0 if all is OK
6849 -1 if addresses weren't valid
6851 Test the given list of newstroups against those recognized by our nntp
6852 servers. Testing by actually trying to open the list is much cheaper, both
6853 in bandwidth and memory, than yanking the whole list across the wire.
6854 ----*/
6856 news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
6858 int rv;
6859 char *fccptr = NULL;
6861 if(fcc && fcc->tptr)
6862 fccptr = cpystr(fcc->tptr);
6864 clear_cursor_pos();
6866 rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
6868 /* assign any new fcc to the BUILDER_ARG */
6869 if(fccptr){
6870 if(fcc){
6871 /* it changed */
6872 if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
6873 fs_give((void **) &fcc->tptr);
6874 fcc->tptr = fccptr;
6875 fccptr = NULL;
6879 if(fccptr)
6880 fs_give((void **) &fccptr);
6883 /* deal with any busy indicator */
6884 if(news_busy_cue){
6885 news_busy_cue = 0;
6886 cancel_busy_cue(0);
6887 mark_status_dirty();
6888 display_message('x');
6889 if(mangled)
6890 *mangled |= BUILDER_MESSAGE_DISPLAYED;
6894 return(rv);
6898 void
6899 news_build_busy(void)
6901 news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
6905 #if defined(DOS) || defined(OS2)
6907 /*----------------------------------------------------------------------
6908 Verify that the necessary pieces are around to allow for
6909 message sending under DOS
6911 Args: strict -- tells us if a remote stream is required before
6912 sending is permitted.
6914 The idea is to make sure pine knows enough to put together a valid
6915 from line. The things we MUST know are a user-id, user-domain and
6916 smtp server to dump the message off on. Typically these are
6917 provided in pine's configuration file, but if not, the user is
6918 queried here.
6919 ----*/
6921 dos_valid_from()
6923 char prompt[100], answer[80];
6924 int rc, i, flags;
6925 HelpType help;
6928 * query for user name portion of address, use IMAP login
6929 * name as default
6931 if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
6932 NETMBX mb;
6933 int no_prompt_user_id = 0;
6935 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6936 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6937 && *mb.user){
6938 strncpy(answer, mb.user, sizeof(answer)-1);
6939 answer[sizeof(answer)-1] = '\0';
6941 else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
6942 /* no user-id prompting if set */
6943 no_prompt_user_id = 1;
6944 rc = 0;
6945 if(!ps_global->mail_stream)
6946 do_broach_folder(ps_global->inbox_name,
6947 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
6948 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6949 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6950 && *mb.user){
6951 strncpy(answer, mb.user, sizeof(answer)-1);
6952 answer[sizeof(answer)-1] = '\0';
6954 else
6955 answer[0] = '\0';
6957 else
6958 answer[0] = '\0';
6960 if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
6961 /* No prompt, just assume mailbox login is user-id */
6962 no_prompt_user_id = 1;
6963 rc = 0;
6966 snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
6967 prompt[sizeof(prompt)-1] = '\0';
6969 help = NO_HELP;
6970 while(!no_prompt_user_id) {
6971 flags = OE_APPEND_CURRENT;
6972 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
6973 sizeof(answer),prompt,NULL,help,&flags);
6974 if(rc == 2)
6975 continue;
6977 if(rc == 3){
6978 help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
6979 continue;
6982 if(rc != 4)
6983 break;
6986 if(rc == 1 || (rc == 0 && !answer[0])) {
6987 q_status_message(SM_ORDER, 3, 4,
6988 _("Send cancelled (User-id must be provided before sending)"));
6989 return(0);
6992 /* save the name */
6993 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
6994 sizeof(prompt)-50, answer);
6995 prompt[sizeof(prompt)-1] = '\0';
6996 if(ps_global->blank_user_id
6997 && !no_prompt_user_id
6998 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
6999 set_variable(V_USER_ID, answer, 1, 1, Main);
7001 else{
7002 fs_give((void **)&(ps_global->VAR_USER_ID));
7003 ps_global->VAR_USER_ID = cpystr(answer);
7007 /* query for personal name */
7008 if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
7009 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
7010 answer[0] = '\0';
7011 snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
7012 prompt[sizeof(prompt)-1] = '\0';
7014 help = NO_HELP;
7015 while(1) {
7016 flags = OE_APPEND_CURRENT;
7017 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7018 sizeof(answer),prompt,NULL,help,&flags);
7019 if(rc == 2)
7020 continue;
7022 if(rc == 3){
7023 help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
7024 continue;
7027 if(rc != 4)
7028 break;
7031 if(rc == 0 && answer){ /* save the name */
7032 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
7033 sizeof(prompt)-50, answer);
7034 prompt[sizeof(prompt)-1] = '\0';
7035 if(ps_global->blank_personal_name
7036 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7037 set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
7039 else{
7040 fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
7041 ps_global->VAR_PERSONAL_NAME = cpystr(answer);
7047 * query for host/domain portion of address, using IMAP
7048 * host as default
7050 if(ps_global->blank_user_domain
7051 || ps_global->maildomain == ps_global->localdomain
7052 || ps_global->maildomain == ps_global->hostname){
7053 if(ps_global->inbox_name[0] == '{'){
7054 for(i=0;
7055 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7056 answer[i] = ps_global->inbox_name[i+1];
7058 answer[i] = '\0';
7060 else
7061 answer[0] = '\0';
7063 snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
7064 prompt[sizeof(prompt)-1] = '\0';
7066 help = NO_HELP;
7067 while(1) {
7068 flags = OE_APPEND_CURRENT;
7069 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7070 sizeof(answer),prompt,NULL,help,&flags);
7071 if(rc == 2)
7072 continue;
7074 if(rc == 3){
7075 help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
7076 continue;
7079 if(rc != 4)
7080 break;
7083 if(rc == 1 || (rc == 0 && !answer[0])) {
7084 q_status_message(SM_ORDER, 3, 4,
7085 _("Send cancelled (Host/domain name must be provided before sending)"));
7086 return(0);
7089 /* save the name */
7090 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
7091 sizeof(prompt)-50, answer);
7092 prompt[sizeof(prompt)-1] = '\0';
7093 if(!ps_global->userdomain && !ps_global->blank_user_domain
7094 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7095 set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
7096 fs_give((void **)&(ps_global->maildomain)); /* blast old val */
7097 ps_global->userdomain = cpystr(answer);
7098 ps_global->maildomain = ps_global->userdomain;
7100 else{
7101 fs_give((void **)&(ps_global->maildomain));
7102 ps_global->userdomain = cpystr(answer);
7103 ps_global->maildomain = ps_global->userdomain;
7107 /* check for smtp server */
7108 if(!ps_global->VAR_SMTP_SERVER ||
7109 !ps_global->VAR_SMTP_SERVER[0] ||
7110 !ps_global->VAR_SMTP_SERVER[0][0]){
7111 char **list;
7113 if(ps_global->inbox_name[0] == '{'){
7114 for(i=0;
7115 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7116 answer[i] = ps_global->inbox_name[i+1];
7118 answer[i] = '\0';
7120 else
7121 answer[0] = '\0';
7123 snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
7124 prompt[sizeof(prompt)-1] = '\0';
7126 help = NO_HELP;
7127 while(1) {
7128 flags = OE_APPEND_CURRENT;
7129 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7130 sizeof(answer),prompt,NULL,help,&flags);
7131 if(rc == 2)
7132 continue;
7134 if(rc == 3){
7135 help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
7136 continue;
7139 if(rc != 4)
7140 break;
7143 if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
7144 q_status_message(SM_ORDER, 3, 4,
7145 _("Send cancelled (SMTP server must be provided before sending)"));
7146 return(0);
7149 /* save the name */
7150 list = (char **) fs_get(2 * sizeof(char *));
7151 list[0] = cpystr(answer);
7152 list[1] = NULL;
7153 set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
7154 fs_give((void *)&list[0]);
7155 fs_give((void *)list);
7158 return(1);
7161 #endif /* defined(DOS) || defined(OS2) */