* new version 2.20.2
[alpine.git] / alpine / send.c
blob5725b4825851be58b80eed3cd220892ea1d7bab5
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 2006-2008 University of Washington
8 * Copyright 2013-2015 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 Functions for composing and sending mail
22 ====*/
25 #include "headers.h"
26 #include "send.h"
27 #include "status.h"
28 #include "mailview.h"
29 #include "mailindx.h"
30 #include "dispfilt.h"
31 #include "keymenu.h"
32 #include "folder.h"
33 #include "radio.h"
34 #include "addrbook.h"
35 #include "reply.h"
36 #include "titlebar.h"
37 #include "signal.h"
38 #include "mailcmd.h"
39 #include "roleconf.h"
40 #include "adrbkcmd.h"
41 #include "busy.h"
42 #include "../pith/debug.h"
43 #include "../pith/state.h"
44 #include "../pith/conf.h"
45 #include "../pith/flag.h"
46 #include "../pith/bldaddr.h"
47 #include "../pith/copyaddr.h"
48 #include "../pith/detach.h"
49 #include "../pith/mimedesc.h"
50 #include "../pith/pipe.h"
51 #include "../pith/addrstring.h"
52 #include "../pith/news.h"
53 #include "../pith/detoken.h"
54 #include "../pith/util.h"
55 #include "../pith/init.h"
56 #include "../pith/mailcmd.h"
57 #include "../pith/ablookup.h"
58 #include "../pith/reply.h"
59 #include "../pith/hist.h"
60 #include "../pith/list.h"
61 #include "../pith/icache.h"
62 #include "../pith/busy.h"
63 #include "../pith/mimetype.h"
64 #include "../pith/send.h"
65 #include "../pith/smime.h"
68 typedef struct body_particulars {
69 unsigned short type, encoding, had_csp;
70 char *subtype, *charset;
71 PARAMETER *parameter;
72 } BODY_PARTICULARS_S;
74 #define PHONE_HOME_VERSION "-count"
75 #define PHONE_HOME_HOST "patches.freeiz.com"
78 * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook
80 #define HE(PF) ((struct headerentry *)((PF)->extdata))
84 * Internal Prototypes
86 int redraft(MAILSTREAM **, ENVELOPE **, BODY **, char **, char **, REPLY_S **,
87 REDRAFT_POS_S **, PINEFIELD **, ACTION_S **, int);
88 int redraft_prompt(char *, char *, int);
89 int check_for_subject(METAENV *);
90 int check_for_fcc(char *);
91 void free_prompts(PINEFIELD *);
92 int postpone_prompt(void);
93 METAENV *pine_simple_send_header(ENVELOPE *, char **, char ***);
94 void call_mailer_file_result(char *, int);
95 void mark_address_failure_for_pico(METAENV *);
96 BODY_PARTICULARS_S
97 *save_body_particulars(BODY *);
98 void reset_body_particulars(BODY_PARTICULARS_S *, BODY *);
99 void free_body_particulars(BODY_PARTICULARS_S *);
100 long message_format_for_pico(long, int (*)(int));
101 int send_exit_for_pico(struct headerentry *, void (*)(void), int, char **);
102 void new_thread_on_blank_subject(void);
103 char *choose_a_priority(char *);
104 int dont_flow_this_time(void);
105 int mime_type_for_pico(char *);
106 char *cancel_for_pico(void (*)(void));
107 int filter_message_text(char *, ENVELOPE *, BODY *, STORE_S **, METAENV *);
108 void pine_send_newsgroup_name(char *, char*, size_t);
109 void outgoing2strings(METAENV *, BODY *, void **, PATMT **, int);
110 void strings2outgoing(METAENV *, BODY **, PATMT *, int);
111 void create_message_body_text(BODY *, int);
112 void set_body_size(BODY *);
113 int view_as_rich(char *, int);
114 int background_posting(int);
115 int valid_subject(char *, char **, char **,BUILDER_ARG *,int *);
116 int build_addr_lcc(char *, char **, char **, BUILDER_ARG *, int *);
117 int news_build(char *, char **, char **, BUILDER_ARG *, int *);
118 void news_build_busy(void);
119 #if defined(DOS) || defined(OS2)
120 int dos_valid_from(void);
121 #endif /* defined(DOS) || defined(OS2) */
125 * Pointer to buffer to hold pointers into pine data that's needed by pico.
127 static PICO *pbf;
130 static char *g_rolenick = NULL;
133 static char *sending_filter_requested;
134 static char background_requested, flowing_requested;
135 static unsigned call_mailer_flags;
136 static char *priority_requested;
138 /* local global to save busy_cue state */
139 static int news_busy_cue = 0;
143 * Various useful strings
145 #define INTRPT_PMT \
146 _("Continue INTERRUPTED composition (answering \"n\" won't erase it)")
147 #define PSTPND_PMT \
148 _("Continue postponed composition (answering \"No\" won't erase it)")
149 #define FORM_PMT \
150 _("Start composition from Form Letter Folder")
151 #define PSTPN_FORM_PMT \
152 _("Save to Postponed or Form letter folder? ")
153 #define POST_PMT \
154 _("Posted message may go to thousands of readers. Really post")
155 #define INTR_DEL_PMT \
156 _("Deleted messages will be removed from folder after use. Proceed")
160 * Macros to help sort out posting results
162 #define P_MAIL_WIN 0x01
163 #define P_MAIL_LOSE 0x02
164 #define P_MAIL_BITS 0x03
165 #define P_NEWS_WIN 0x04
166 #define P_NEWS_LOSE 0x08
167 #define P_NEWS_BITS 0x0C
168 #define P_FCC_WIN 0x10
169 #define P_FCC_LOSE 0x20
170 #define P_FCC_BITS 0x30
173 #define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE"
177 * For check_for_subject and check_for_fcc
179 #define CF_OK 0x1
180 #define CF_MISSING 0x2
183 /*----------------------------------------------------------------------
184 Compose screen (not forward or reply). Set up envelope, call composer
186 Args: pine_state -- The usual pine structure
188 Little front end for the compose screen
189 ---*/
190 void
191 compose_screen(struct pine *pine_state)
193 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
194 (*redraw)(void) = pine_state->redrawer;
196 pine_state->redrawer = NULL;
197 ps_global->next_screen = SCREEN_FUN_NULL;
198 mailcap_free(); /* free resources we won't be using for a while */
199 compose_mail(NULL, NULL, NULL, NULL, NULL);
200 pine_state->next_screen = prev_screen;
201 pine_state->redrawer = redraw;
205 /*----------------------------------------------------------------------
206 Alternate compose screen. Set up role and call regular compose.
208 Args: pine_state -- The usual pine structure
209 ---*/
210 void
211 alt_compose_screen(struct pine *pine_state)
213 ACTION_S *role = NULL;
214 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
215 (*redraw)(void) = pine_state->redrawer;
217 pine_state->redrawer = NULL;
218 ps_global->next_screen = SCREEN_FUN_NULL;
219 mailcap_free(); /* free resources we won't be using for a while */
221 /* Setup role */
222 if(role_select_screen(pine_state, &role, MC_COMPOSE) < 0){
223 cmd_cancelled("Composition");
224 pine_state->next_screen = prev_screen;
225 pine_state->redrawer = redraw;
226 return;
230 * If default role was selected (NULL) we need to make up a role which
231 * won't do anything, but will cause compose_mail to think there's
232 * already a role so that it won't try to confirm the default.
234 if(role)
235 role = combine_inherited_role(role);
236 else{
237 role = (ACTION_S *)fs_get(sizeof(*role));
238 memset((void *)role, 0, sizeof(*role));
239 role->nick = cpystr("Default Role");
242 pine_state->redrawer = NULL;
243 compose_mail(NULL, NULL, role, NULL, NULL);
244 free_action(&role);
245 pine_state->next_screen = prev_screen;
246 pine_state->redrawer = redraw;
250 /*----------------------------------------------------------------------
251 Format envelope for outgoing message and call editor
253 Args: given_to -- An address to send mail to (usually from command line
254 invocation)
255 fcc_arg -- The fcc that goes with this address.
257 If a "To" line is given format that into the envelope and get ready to call
258 the composer
259 If there's a message postponed, offer to continue it, and set it up,
260 otherwise just fill in the outgoing envelope as blank.
262 NOTE: we ignore postponed and interrupted messages in nr mode
263 ----*/
264 void
265 compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
266 PATMT *attach, gf_io_t inc_text_getc)
268 BODY *body = NULL;
269 ENVELOPE *outgoing = NULL;
270 PINEFIELD *custom = NULL;
271 REPLY_S *reply = NULL;
272 REDRAFT_POS_S *redraft_pos = NULL;
273 ACTION_S *role = NULL;
274 MAILSTREAM *stream;
275 char *fcc_to_free,
276 *fcc = NULL,
277 *lcc = NULL,
278 *sig = NULL;
279 int fcc_is_sticky = 0,
280 to_is_sticky = 0,
281 intrptd = 0,
282 postponed = 0,
283 form = 0;
285 dprint((1,
286 "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n"));
288 /*-- Check for INTERRUPTED mail --*/
289 if(!role_arg && !(given_to || attach)){
290 char file_path[MAXPATH+1];
292 /* build filename and see if it exists. build_path creates
293 * an explicit local path name, so all c-client access is thru
294 * local drivers.
296 file_path[0] = '\0';
297 build_path(file_path,
298 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
299 : ps_global->home_dir,
300 INTERRUPTED_MAIL, sizeof(file_path));
302 /* check to see if the folder exists, the user wants to continue
303 * and that we can actually read something in...
305 if(folder_exists(NULL, file_path) & FEX_ISFILE)
306 intrptd = 1;
309 /*-- Check for postponed mail --*/
310 if(!role_arg
311 && !outgoing /* not replying/forwarding */
312 && !(given_to || attach) /* not command line send */
313 && ps_global->VAR_POSTPONED_FOLDER /* folder to look in */
314 && ps_global->VAR_POSTPONED_FOLDER[0])
315 postponed = 1;
317 /*-- Check for form letter folder --*/
318 if(!role_arg
319 && !outgoing /* not replying/forwarding */
320 && !(given_to || attach) /* not command line send */
321 && ps_global->VAR_FORM_FOLDER /* folder to look in */
322 && ps_global->VAR_FORM_FOLDER[0])
323 form = 1;
325 if(!outgoing && !(given_to || attach)
326 && !role_arg && F_ON(F_ALT_COMPOSE_MENU, ps_global)){
327 char prompt[80];
328 char letters[30];
329 char chosen_task;
330 char *new = "New";
331 char *intrpt = "Interrupted";
332 char *postpnd = "Postponed";
333 char *formltr = "FormLetter";
334 char *roles = "setRole";
335 HelpType help = h_composer_browse;
336 ESCKEY_S compose_style[6];
337 unsigned which_help;
338 int ekey_num;
340 ekey_num = 0;
341 compose_style[ekey_num].ch = 'n';
342 compose_style[ekey_num].rval = 'n';
343 compose_style[ekey_num].name = "N";
344 compose_style[ekey_num++].label = new;
346 if(intrptd){
347 compose_style[ekey_num].ch = 'i';
348 compose_style[ekey_num].rval = 'i';
349 compose_style[ekey_num].name = "I";
350 compose_style[ekey_num++].label = intrpt;
353 if(postponed){
354 compose_style[ekey_num].ch = 'p';
355 compose_style[ekey_num].rval = 'p';
356 compose_style[ekey_num].name = "P";
357 compose_style[ekey_num++].label = postpnd;
360 if(form){
361 compose_style[ekey_num].ch = 'f';
362 compose_style[ekey_num].rval = 'f';
363 compose_style[ekey_num].name = "F";
364 compose_style[ekey_num++].label = formltr;
367 compose_style[ekey_num].ch = 'r';
368 compose_style[ekey_num].rval = 'r';
369 compose_style[ekey_num].name = "R";
370 compose_style[ekey_num++].label = roles;
372 compose_style[ekey_num].ch = -1;
374 if(F_ON(F_BLANK_KEYMENU,ps_global)){
375 char *p;
377 p = letters;
378 *p = '\0';
379 for(ekey_num = 0; compose_style[ekey_num].ch != -1; ekey_num++){
380 if(p - letters < sizeof(letters))
381 *p++ = (char) compose_style[ekey_num].ch;
383 if(compose_style[ekey_num + 1].ch != -1 && p - letters < sizeof(letters))
384 *p++ = ',';
387 if(p - letters < sizeof(letters))
388 *p = '\0';
391 which_help = intrptd + 2 * postponed + 4 * form;
392 switch(which_help){
393 case 1:
394 help = h_compose_intrptd;
395 break;
396 case 2:
397 help = h_compose_postponed;
398 break;
399 case 3:
400 help = h_compose_intrptd_postponed;
401 break;
402 case 4:
403 help = h_compose_form;
404 break;
405 case 5:
406 help = h_compose_intrptd_form;
407 break;
408 case 6:
409 help = h_compose_postponed_form;
410 break;
411 case 7:
412 help = h_compose_intrptd_postponed_form;
413 break;
414 default:
415 help = h_compose_default;
416 break;
419 snprintf(prompt, sizeof(prompt),
420 "Choose a compose method from %s : ",
421 F_ON(F_BLANK_KEYMENU,ps_global) ? letters : "the menu below");
422 prompt[sizeof(prompt)-1] = '\0';
424 chosen_task = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
425 compose_style, 'n', 'x', help, RB_NORM);
426 intrptd = postponed = form = 0;
428 switch(chosen_task){
429 case 'i':
430 intrptd = 1;
431 break;
432 case 'p':
433 postponed = 1;
434 break;
435 case 'r':
437 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
438 (*redraw)(void) = ps_global->redrawer;
440 ps_global->redrawer = NULL;
441 ps_global->next_screen = SCREEN_FUN_NULL;
442 if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
443 cmd_cancelled("Composition");
444 ps_global->next_screen = prev_screen;
445 ps_global->redrawer = redraw;
446 return;
449 ps_global->next_screen = prev_screen;
450 ps_global->redrawer = redraw;
451 if(role)
452 role = combine_inherited_role(role);
454 break;
456 case 'f':
457 form = 1;
458 break;
460 case 'x':
461 q_status_message(SM_ORDER, 0, 3,
462 "Composition cancelled");
463 return;
464 break;
466 default:
467 break;
471 if(intrptd && !outgoing){
472 char file_path[MAXPATH+1];
473 int ret = 'n';
475 file_path[0] = '\0';
476 build_path(file_path,
477 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
478 : ps_global->home_dir,
479 INTERRUPTED_MAIL, sizeof(file_path));
480 if(folder_exists(NULL, file_path) & FEX_ISFILE){
481 if((stream = pine_mail_open(NULL, file_path,
482 SP_USEPOOL|SP_TEMPUSE, NULL))
483 && !stream->halfopen){
485 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
486 (ret = redraft_prompt("Interrupted",INTRPT_PMT,'n')) =='y'){
487 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
488 &redraft_pos, &custom, &role, REDRAFT_DEL)){
489 if(stream)
490 pine_mail_close(stream);
492 return;
495 to_is_sticky++;
497 /* redraft() may or may not have closed stream */
498 if(stream)
499 pine_mail_close(stream);
501 postponed = form = 0;
503 else{
504 pine_mail_close(stream);
505 if(ret == 'x'){
506 q_status_message(SM_ORDER, 0, 3,
507 _("Composition cancelled"));
508 return;
512 else{
513 q_status_message1(SM_ORDER | SM_DING, 3, 3,
514 _("Can't open Interrupted mailbox: %s"),
515 file_path);
516 if(stream)
517 pine_mail_close(stream);
522 if(postponed && !outgoing){
523 int ret = 'n', done = 0;
524 int exists;
526 if((exists=postponed_stream(&stream,
527 ps_global->VAR_POSTPONED_FOLDER,
528 "Postponed", 0)) & FEX_ISFILE){
529 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
530 (ret = redraft_prompt("Postponed",PSTPND_PMT,'n')) == 'y'){
531 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
532 &redraft_pos, &custom, &role,
533 REDRAFT_DEL | REDRAFT_PPND))
534 done++;
536 /* stream may or may not be closed in redraft() */
537 if(stream && (stream != ps_global->mail_stream))
538 pine_mail_close(stream);
540 to_is_sticky++;
541 intrptd = form = 0;
543 else{
544 if(stream != ps_global->mail_stream)
545 pine_mail_close(stream);
547 if(ret == 'x'){
548 q_status_message(SM_ORDER, 0, 3,
549 _("Composition cancelled"));
550 done++;
554 else if(F_ON(F_ALT_COMPOSE_MENU, ps_global))
555 done++;
557 if(done)
558 return;
561 if(form && !outgoing){
562 int ret = 'n', done = 0;
563 int exists;
565 if((exists=postponed_stream(&stream,
566 ps_global->VAR_FORM_FOLDER,
567 "Form letter", 1)) & FEX_ISFILE){
568 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
569 (ret = want_to(FORM_PMT,'y','x',NO_HELP,WT_NORM))=='y'){
570 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
571 &redraft_pos, &custom, NULL, REDRAFT_NONE))
572 done++;
574 /* stream may or may not be closed in redraft() */
575 if(stream && (stream != ps_global->mail_stream))
576 pine_mail_close(stream);
578 to_is_sticky++;
579 intrptd = postponed = 0;
581 else{
582 if(stream != ps_global->mail_stream)
583 pine_mail_close(stream);
585 if(ret == 'x'){
586 q_status_message(SM_ORDER, 0, 3,
587 _("Composition cancelled"));
588 done++;
592 else{
593 if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
594 q_status_message(SM_ORDER | SM_DING, 3, 3,
595 _("Form letter folder doesn't exist!"));
596 return;
600 if(done)
601 return;
604 /*-- normal composition --*/
605 if(!outgoing){
606 int impl, template_len = 0;
607 long rflags = ROLE_COMPOSE;
608 PAT_STATE dummy;
610 /*================= Compose new message ===============*/
611 body = mail_newbody();
612 outgoing = mail_newenvelope();
614 if(given_to)
615 rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
617 outgoing->message_id = generate_message_id();
620 * Setup possible role
622 if(role_arg)
623 role = copy_action(role_arg);
625 if(!role){
626 /* Setup possible compose role */
627 if(nonempty_patterns(rflags, &dummy)){
629 * setup default role
630 * Msgno = -1 means there is no msg.
631 * This will match roles which have the Compose Use turned
632 * on, and have no patterns set, and match the Current
633 * Folder Type.
635 role = set_role_from_msg(ps_global, rflags, -1L, NULL);
637 if(confirm_role(rflags, &role))
638 role = combine_inherited_role(role);
639 else{ /* cancel reply */
640 role = NULL;
641 cmd_cancelled("Composition");
642 return;
647 if(role)
648 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
649 role->nick);
652 * The type of storage object allocated below is vitally
653 * important. See SIMPLIFYING ASSUMPTION #37
655 if((body->contents.text.data = (void *) so_get(PicoText,
656 NULL, EDIT_ACCESS)) != NULL){
657 char ch;
659 if(inc_text_getc){
660 while((*inc_text_getc)(&ch))
661 if(!so_writec(ch, (STORE_S *)body->contents.text.data)){
662 break;
666 else{
667 q_status_message(SM_ORDER | SM_DING, 3, 4,
668 _("Problem creating space for message text."));
669 return;
672 if(role && role->template){
673 char *filtered;
675 impl = 1; /* leave cursor in header if not explicit */
676 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
677 if(filtered){
678 if(*filtered){
679 so_puts((STORE_S *)body->contents.text.data, filtered);
680 if(impl == 1)
681 template_len = strlen(filtered);
684 fs_give((void **)&filtered);
687 else
688 impl = 1;
690 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
691 if(impl == 2)
692 redraft_pos->offset += template_len;
694 if(*sig)
695 so_puts((STORE_S *)body->contents.text.data, sig);
697 fs_give((void **)&sig);
700 body->type = TYPETEXT;
702 if(attach)
703 create_message_body(&body, attach, 0);
706 ps_global->prev_screen = compose_screen;
707 if(!(fcc_to_free = fcc) && !(role && role->fcc))
708 fcc = fcc_arg; /* Didn't pick up fcc, use given */
711 * check whether a build_address-produced fcc is different from
712 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
714 if(fcc){
715 char *tmp_fcc = NULL;
717 if(outgoing->to){
718 tmp_fcc = get_fcc_based_on_to(outgoing->to);
719 if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
720 fcc_is_sticky++; /* cause sticky bit to get set */
723 else if((tmp_fcc = get_fcc(NULL)) != NULL &&
724 !strcmp(fcc, tmp_fcc)){
725 /* not sticky */
727 else
728 fcc_is_sticky++;
730 if(tmp_fcc)
731 fs_give((void **)&tmp_fcc);
734 pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc,
735 reply, redraft_pos, lcc, custom,
736 (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0));
738 if(reply){
739 if(reply->mailbox)
740 fs_give((void **) &reply->mailbox);
741 if(reply->origmbox)
742 fs_give((void **) &reply->origmbox);
743 if(reply->prefix)
744 fs_give((void **) &reply->prefix);
745 if(reply->data.uid.msgs)
746 fs_give((void **) &reply->data.uid.msgs);
747 fs_give((void **) &reply);
750 if(fcc_to_free)
751 fs_give((void **)&fcc_to_free);
753 if(lcc)
754 fs_give((void **)&lcc);
756 mail_free_envelope(&outgoing);
757 pine_free_body(&body);
758 free_redraft_pos(&redraft_pos);
759 free_action(&role);
763 /*----------------------------------------------------------------------
764 Args: stream -- This is where we get the postponed messages from
765 We'll expunge and close it here unless it is mail_stream.
767 These are all return values:
768 ================
769 outgoing --
770 body --
771 fcc --
772 lcc --
773 reply --
774 redraft_pos --
775 custom --
776 role --
777 ================
779 flags --
781 ----*/
783 redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body,
784 char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos,
785 PINEFIELD **custom, ACTION_S **role, int flags)
787 MAILSTREAM *stream;
788 long cont_msg = 1L;
789 STORE_S *so;
791 if(!(streamp && *streamp))
792 return(0);
794 stream = *streamp;
797 * If we're manipulating the current folder, don't bother
798 * with index
800 if(!stream->nmsgs){
801 if(REDRAFT_PPND&flags)
802 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!"));
803 else
804 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!"));
806 return(redraft_cleanup(streamp, FALSE, flags));
808 else if(stream == ps_global->mail_stream
809 && ps_global->prev_screen == mail_index_screen){
811 * Since the user's got this folder already opened and they're
812 * on a selected message, pick that one rather than rebuild
813 * another index screen...
815 cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
817 else if(stream->nmsgs > 1L){ /* offer browser ? */
818 int rv;
820 if(REDRAFT_PPND&flags){ /* set to last message postponed */
821 mn_set_cur(sp_msgmap(stream),
822 mn_get_revsort(sp_msgmap(stream))
823 ? 1L : mn_get_total(sp_msgmap(stream)));
825 else{ /* set to top form letter */
826 mn_set_cur(sp_msgmap(stream), 1L);
829 clear_index_cache(stream, 0);
830 while(1){
831 void *ti;
833 ti = stop_threading_temporarily();
834 rv = index_lister(ps_global, NULL, stream->mailbox,
835 stream, sp_msgmap(stream));
836 restore_threading(&ti);
838 cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream)));
839 if(count_flagged(stream, F_DEL)
840 && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){
841 if(REDRAFT_PPND&flags)
842 q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
843 else
844 q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
846 continue;
849 break;
852 clear_index_cache(stream, 0);
854 if(rv){
855 q_status_message(SM_ORDER, 0, 3, _("Composition cancelled"));
856 (void) redraft_cleanup(streamp, FALSE, flags);
858 if(!*streamp && !ps_global->mail_stream){
859 q_status_message2(SM_ORDER, 3, 7,
860 "No more %.200s, returning to \"%.200s\"",
861 (REDRAFT_PPND&flags) ? "postponed messages"
862 : "form letters",
863 ps_global->inbox_name);
864 if(ps_global && ps_global->ttyo){
865 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
866 ps_global->mangled_footer = 1;
869 do_broach_folder(ps_global->inbox_name,
870 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
872 ps_global->next_screen = mail_index_screen;
875 return(0); /* special case */
879 if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL)
880 return(redraft_work(streamp, cont_msg, outgoing, body,
881 fcc, lcc, reply, redraft_pos, custom,
882 role, flags, so));
883 else
884 return(0);
889 redraft_prompt(char *type, char *prompt, int failure)
891 if(background_posting(FALSE)){
892 q_status_message1(SM_ORDER, 0, 3,
893 _("%s folder unavailable while background posting"),
894 type);
895 return(failure);
898 return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM));
902 /* this is for initializing the fixed header elements in pine_send() */
904 prompt::name::help::prwid::maxlen::realaddr::
905 builder::affected_entry::next_affected::selector::key_label::fileedit::
906 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
907 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
909 static struct headerentry he_template[]={
910 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
911 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
912 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
913 {"From : ", "From", h_composer_from, 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 {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
917 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
918 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
919 {"To : ", "To", h_composer_to, 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, 1, KS_TOADDRBOOK},
922 {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL,
923 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
924 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
925 {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL,
926 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
927 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
928 {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL,
929 news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL,
930 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
931 {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL,
932 NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, NULL,
933 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE},
934 {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL,
935 build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete,
936 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
937 {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL,
938 NULL, NULL, NULL, NULL, "To Files", NULL, NULL,
939 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE},
940 {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL,
941 valid_subject, NULL, NULL, NULL, NULL, NULL, NULL,
942 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
943 {"", "References", 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 {"", "Date", 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 {"", "In-Reply-To", 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 {"", "Message-ID", 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 {"", "X-Priority", 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 {"", "User-Agent", 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 {"", "To", 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-Post-Error",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-UID", 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-Reply-Mbox", 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-SMTP-Server", 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-Cursor-Pos", 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 {"", "X-Our-ReplyTo", 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 {"", OUR_HDRS_LIST, 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 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
986 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
987 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
988 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
989 {"", "Sender", NO_HELP, 10, 0, NULL,
990 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
991 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
992 #endif
996 static struct headerentry he_custom_addr_templ={
997 NULL, NULL, h_composer_custom_addr,10, 0, NULL,
998 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
999 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK};
1001 static struct headerentry he_custom_free_templ={
1002 NULL, NULL, h_composer_custom_free,10, 0, NULL,
1003 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1004 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE};
1007 /*----------------------------------------------------------------------
1008 Get addressee for message, then post message
1010 Args: outgoing -- Partially formatted outgoing ENVELOPE
1011 body -- Body of outgoing message
1012 prmpt_who -- Optional prompt for optionally_enter call
1013 prmpt_cnf -- Optional prompt for confirmation call
1014 used_tobufval -- The string that the to was eventually set equal to.
1015 This gets passed back if non-NULL on entry.
1016 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1017 SS_NULLRP - Use null return-path so we'll send an
1018 SMTP MAIL FROM: <>
1020 Result: message "To: " field is provided and message is sent or cancelled.
1022 Fields:
1023 remail -
1024 return_path -
1025 date added here
1026 from added here
1027 sender -
1028 reply_to -
1029 subject passed in, NOT edited but maybe canonized here
1030 to possibly passed in, edited and canonized here
1031 cc -
1032 bcc -
1033 in_reply_to -
1034 message_id -
1036 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1037 with the first part TYPETEXT! All newlines in the text here also end with
1038 CRLF.
1040 Returns 0 on success, -1 on failure.
1041 ----*/
1043 pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */
1044 struct mail_bodystruct **body,
1045 ACTION_S *role,
1046 char *prmpt_who,
1047 char *prmpt_cnf,
1048 char **used_tobufval,
1049 int flagsarg)
1051 char **tobufp, *p;
1052 void *messagebuf;
1053 int done = 0, retval = 0, x;
1054 int lastrc, rc = 0, ku, i, resize_len, result, fcc_result;
1055 int og2s_done = 0;
1056 HelpType help;
1057 static HISTORY_S *history = NULL;
1058 ESCKEY_S ekey[5];
1059 BUILDER_ARG ba_fcc;
1060 METAENV *header;
1062 dprint((1,"\n === simple send called === \n"));
1064 memset(&ba_fcc, 0, sizeof(BUILDER_ARG));
1066 init_hist(&history, HISTSIZE);
1068 header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp);
1070 /*----- Fill in a few general parts of the envelope ----*/
1071 if(!outgoing->date){
1072 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1073 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
1075 rfc822_date(tmp_20k_buf); /* format and copy new date */
1076 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1077 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
1079 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
1082 if(!outgoing->from){
1083 if(role && role->from){
1084 if(ps_global->never_allow_changing_from)
1085 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1086 else
1087 outgoing->from = copyaddrlist(role->from);
1089 else
1090 outgoing->from = generate_from();
1093 if(!(flagsarg & SS_NULLRP))
1094 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1096 ekey[i = 0].ch = ctrl('T');
1097 ekey[i].rval = 2;
1098 ekey[i].name = "^T";
1099 ekey[i++].label = N_("To AddrBk");
1101 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1102 ekey[i].ch = ctrl('I');
1103 ekey[i].rval = 11;
1104 ekey[i].name = "TAB";
1105 ekey[i++].label = N_("Complete");
1108 ekey[i].ch = KEY_UP;
1109 ekey[i].rval = 30;
1110 ekey[i].name = "";
1111 ku = i;
1112 ekey[i++].label = "";
1114 ekey[i].ch = KEY_DOWN;
1115 ekey[i].rval = 31;
1116 ekey[i].name = "";
1117 ekey[i++].label = "";
1119 ekey[i].ch = -1;
1121 /*----------------------------------------------------------------------
1122 Loop editing the "To: " field until everything goes well
1123 ----*/
1124 help = NO_HELP;
1126 while(!done){
1127 int flags;
1129 if(!og2s_done){
1130 og2s_done++;
1131 outgoing2strings(header, *body, &messagebuf, NULL, 1);
1134 lastrc = rc;
1135 if(flagsarg & SS_PROMPTFORTO){
1136 if(!*tobufp)
1137 *tobufp = cpystr("");
1139 resize_len = MAX(MAXPATH, strlen(*tobufp));
1140 fs_resize((void **) tobufp, resize_len+1);
1142 if(items_in_hist(history) > 0){
1143 ekey[ku].name = HISTORY_UP_KEYNAME;
1144 ekey[ku].label = HISTORY_KEYLABEL;
1145 ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
1146 ekey[ku+1].label = HISTORY_KEYLABEL;
1148 else{
1149 ekey[ku].name = "";
1150 ekey[ku].label = "";
1151 ekey[ku+1].name = "";
1152 ekey[ku+1].label = "";
1155 flags = OE_APPEND_CURRENT;
1157 rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
1158 0, resize_len,
1159 prmpt_who
1160 ? prmpt_who
1161 : outgoing->remail == NULL
1162 ? _("FORWARD (as e-mail) to : ")
1163 : _("BOUNCE (redirect) message to : "),
1164 ekey, help, &flags);
1166 else
1167 rc = 0;
1169 switch(rc){
1170 case -1:
1171 q_status_message(SM_ORDER | SM_DING, 3, 4,
1172 "Internal problem encountered");
1173 retval = -1;
1174 done++;
1175 break;
1177 case 30 :
1178 if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){
1179 strncpy(*tobufp, p, resize_len);
1180 (*tobufp)[resize_len-1] = '\0';
1182 else
1183 Writechar(BELL, 0);
1185 break;
1187 case 31 :
1188 if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){
1189 strncpy(*tobufp, p, resize_len);
1190 (*tobufp)[resize_len-1] = '\0';
1192 else
1193 Writechar(BELL, 0);
1195 break;
1197 case 2: /* ^T */
1198 case 0:
1199 {void (*redraw) (void) = ps_global->redrawer;
1200 char *returned_addr = NULL;
1201 int len, l;
1203 if(rc == 2){
1204 int got_something = 0;
1206 push_titlebar_state();
1207 returned_addr = addr_book_bounce();
1210 * Just make it look like user typed this list in.
1212 if(returned_addr){
1213 got_something++;
1214 if((l=resize_len) < (len = strlen(returned_addr)) + 1){
1215 l = len;
1216 fs_resize((void **) tobufp, (size_t) (l+1));
1219 strncpy(*tobufp, returned_addr, l);
1220 (*tobufp)[l] = '\0';
1221 fs_give((void **)&returned_addr);
1224 ClearScreen();
1225 pop_titlebar_state();
1226 redraw_titlebar();
1227 if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
1228 (*ps_global->redrawer)();
1230 if(!got_something)
1231 continue;
1234 if(*tobufp && **tobufp != '\0'){
1235 char *errbuf, *addr;
1236 int tolen;
1238 save_hist(history, *tobufp, 0, NULL);
1240 errbuf = NULL;
1243 * If role has an fcc, use it instead of what build_address
1244 * tells us.
1246 if(role && role->fcc){
1247 if(ba_fcc.tptr)
1248 fs_give((void **) &ba_fcc.tptr);
1250 ba_fcc.tptr = cpystr(role->fcc);
1253 if(build_address(*tobufp, &addr, &errbuf,
1254 (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){
1255 int sendit = 0;
1257 if(errbuf)
1258 fs_give((void **)&errbuf);
1260 if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){
1261 l = tolen;
1262 fs_resize((void **) tobufp, (size_t) (l+1));
1265 strncpy(*tobufp, addr, l);
1266 (*tobufp)[l] = '\0';
1267 if(used_tobufval)
1268 *used_tobufval = cpystr(addr);
1270 /* confirm address */
1271 if(flagsarg & SS_PROMPTFORTO){
1272 char dsn_string[30];
1273 int dsn_label = 0, dsn_show, i;
1274 int verbose_label = 0;
1275 ESCKEY_S opts[13];
1277 strings2outgoing(header, body, NULL, 0);
1279 if((flagsarg & SS_PROMPTFORTO)
1280 && ((x = check_addresses(header)) == CA_BAD
1281 || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE,
1282 ps_global))))
1283 /*--- Addresses didn't check out---*/
1284 continue;
1286 i = 0;
1287 opts[i].ch = 'y';
1288 opts[i].rval = 'y';
1289 opts[i].name = "Y";
1290 opts[i++].label = N_("Yes");
1292 opts[i].ch = 'n';
1293 opts[i].rval = 'n';
1294 opts[i].name = "N";
1295 opts[i++].label = N_("No");
1297 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
1298 if(F_ON(F_VERBOSE_POST, ps_global)){
1299 /* setup keymenu slot to toggle verbose mode */
1300 opts[i].ch = ctrl('W');
1301 opts[i].rval = 12;
1302 opts[i].name = "^W";
1303 verbose_label = i++;
1304 if(F_ON(F_DSN, ps_global)){
1305 opts[i].ch = 0;
1306 opts[i].rval = 0;
1307 opts[i].name = "";
1308 opts[i++].label = "";
1312 /* clear DSN flags */
1313 call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL);
1314 if(F_ON(F_DSN, ps_global)){
1315 /* setup keymenu slots to toggle dsn bits */
1316 opts[i].ch = 'd';
1317 opts[i].rval = 'd';
1318 opts[i].name = "D";
1319 opts[i].label = "DSNOpts";
1320 dsn_label = i++;
1321 opts[i].ch = -2;
1322 opts[i].rval = 's';
1323 opts[i].name = "S";
1324 opts[i++].label = "";
1325 opts[i].ch = -2;
1326 opts[i].rval = 'x';
1327 opts[i].name = "X";
1328 opts[i++].label = "";
1329 opts[i].ch = -2;
1330 opts[i].rval = 'h';
1331 opts[i].name = "H";
1332 opts[i++].label = "";
1335 opts[i].ch = -1;
1337 while(1){
1338 int rv;
1340 dsn_show = (call_mailer_flags & CM_DSN_SHOW);
1341 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
1342 "%s%s%s%s%s%sto \"%s\" ? ",
1343 prmpt_cnf ? prmpt_cnf : "Send message ",
1344 ((call_mailer_flags & CM_VERBOSE)
1345 || (dsn_show))
1346 ? "(" : "",
1347 (call_mailer_flags & CM_VERBOSE)
1348 ? "in verbose mode" : "",
1349 (dsn_show && (call_mailer_flags & CM_VERBOSE))
1350 ? ", " : "",
1351 (dsn_show) ? dsn_string : "",
1352 ((call_mailer_flags & CM_VERBOSE) || dsn_show)
1353 ? ") " : "",
1354 (addr && *addr)
1355 ? addr
1356 : (F_ON(F_FCC_ON_BOUNCE, ps_global)
1357 && ba_fcc.tptr && ba_fcc.tptr[0])
1358 ? ba_fcc.tptr
1359 : "");
1360 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1362 if((strlen(tmp_20k_buf) >
1363 ps_global->ttyo->screen_cols - 2) &&
1364 ps_global->ttyo->screen_cols >= 7)
1365 strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7,
1366 "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7);
1368 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1370 if(verbose_label)
1371 opts[verbose_label].label =
1372 /* TRANSLATORS: several possible key labels follow */
1373 (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
1375 if(F_ON(F_DSN, ps_global)){
1376 if(call_mailer_flags & CM_DSN_SHOW){
1377 opts[dsn_label].label =
1378 (call_mailer_flags & CM_DSN_DELAY)
1379 ? N_("NoDelay") : N_("Delay");
1380 opts[dsn_label+1].ch = 's';
1381 opts[dsn_label+1].label =
1382 (call_mailer_flags & CM_DSN_SUCCESS)
1383 ? N_("NoSuccess") : N_("Success");
1384 opts[dsn_label+2].ch = 'x';
1385 opts[dsn_label+2].label =
1386 (call_mailer_flags & CM_DSN_NEVER)
1387 ? N_("ErrRets") : N_("NoErrRets");
1388 opts[dsn_label+3].ch = 'h';
1389 opts[dsn_label+3].label =
1390 (call_mailer_flags & CM_DSN_FULL)
1391 ? N_("RetHdrs") : N_("RetFull");
1395 rv = radio_buttons(tmp_20k_buf,
1396 -FOOTER_ROWS(ps_global), opts,
1397 'y', 'z', NO_HELP, RB_NORM);
1398 if(rv == 'y'){ /* user ACCEPTS! */
1399 sendit = 1;
1400 break;
1402 else if(rv == 'n'){ /* Declined! */
1403 break;
1405 else if(rv == 'z'){ /* Cancelled! */
1406 break;
1408 else if(rv == 12){ /* flip verbose bit */
1409 if(call_mailer_flags & CM_VERBOSE)
1410 call_mailer_flags &= ~CM_VERBOSE;
1411 else
1412 call_mailer_flags |= CM_VERBOSE;
1414 else if(call_mailer_flags & CM_DSN_SHOW){
1415 if(rv == 's'){ /* flip success bit */
1416 call_mailer_flags ^= CM_DSN_SUCCESS;
1417 /* turn off related bits */
1418 if(call_mailer_flags & CM_DSN_SUCCESS)
1419 call_mailer_flags &= ~(CM_DSN_NEVER);
1421 else if(rv == 'd'){ /* flip delay bit */
1422 call_mailer_flags ^= CM_DSN_DELAY;
1423 /* turn off related bits */
1424 if(call_mailer_flags & CM_DSN_DELAY)
1425 call_mailer_flags &= ~(CM_DSN_NEVER);
1427 else if(rv == 'x'){ /* flip never bit */
1428 call_mailer_flags ^= CM_DSN_NEVER;
1429 /* turn off related bits */
1430 if(call_mailer_flags & CM_DSN_NEVER)
1431 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
1433 else if(rv == 'h'){ /* flip full bit */
1434 call_mailer_flags ^= CM_DSN_FULL;
1437 else if(rv == 'd'){ /* show dsn options */
1438 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
1441 snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"),
1442 (call_mailer_flags & CM_DSN_NEVER)
1443 ? _("Never") : "F",
1444 (call_mailer_flags & CM_DSN_DELAY)
1445 ? "D" : "",
1446 (call_mailer_flags & CM_DSN_SUCCESS)
1447 ? "S" : "",
1448 (call_mailer_flags & CM_DSN_NEVER)
1449 ? ""
1450 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
1451 : "-Hdrs");
1452 dsn_string[sizeof(dsn_string)-1] = '\0';
1456 if(addr)
1457 fs_give((void **)&addr);
1459 if(!(flagsarg & SS_PROMPTFORTO) || sendit){
1460 char *fcc = NULL;
1461 CONTEXT_S *fcc_cntxt = NULL;
1463 if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
1464 if(ba_fcc.tptr)
1465 fcc = cpystr(ba_fcc.tptr);
1467 set_last_fcc(fcc);
1470 * If special name "inbox" then replace it with the
1471 * real inbox path.
1473 if(ps_global->VAR_INBOX_PATH
1474 && strucmp(fcc, ps_global->inbox_name) == 0){
1475 char *replace_fcc;
1477 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
1478 fs_give((void **) &fcc);
1479 fcc = replace_fcc;
1483 /*---- Check out fcc -----*/
1484 if(fcc && *fcc){
1485 (void) commence_fcc(fcc, &fcc_cntxt, FALSE);
1486 if(!lmc.so){
1487 dprint((4,"can't open fcc, cont\n"));
1488 if(!(flagsarg & SS_PROMPTFORTO)){
1489 retval = -1;
1490 fs_give((void **)&fcc);
1491 fcc = NULL;
1492 goto finish;
1494 else
1495 continue;
1497 else
1498 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
1500 else
1501 lmc.so = NULL;
1503 if(!(outgoing->to || outgoing->cc || outgoing->bcc
1504 || lmc.so)){
1505 q_status_message(SM_ORDER, 3, 5, _("No recipients specified!"));
1506 continue;
1509 if(outgoing->to || outgoing->cc || outgoing->bcc){
1510 char **alt_smtp = NULL;
1512 if(role && role->smtp){
1513 if(ps_global->FIX_SMTP_SERVER
1514 && ps_global->FIX_SMTP_SERVER[0])
1515 q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1516 else
1517 alt_smtp = role->smtp;
1520 result = call_mailer(header, *body, alt_smtp,
1521 call_mailer_flags,
1522 call_mailer_file_result,
1523 pipe_callback);
1524 mark_address_failure_for_pico(header);
1526 else
1527 result = 0;
1529 if(result == 1 && !lmc.so)
1530 q_status_message(SM_ORDER, 0, 3, _("Message sent"));
1532 /*----- Was there an fcc involved? -----*/
1533 if(lmc.so){
1534 if(result == 1
1535 || (result == 0
1536 && pine_rfc822_output(header, *body, NULL, NULL))){
1537 char label[50];
1539 strncpy(label, "Fcc", sizeof(label));
1540 label[sizeof(label)-1] = '\0';
1541 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
1542 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
1543 label[sizeof(label)-1] = '\0';
1546 /* Now actually copy to fcc folder and close */
1547 fcc_result =
1548 write_fcc(fcc, fcc_cntxt, lmc.so, NULL,
1549 label,
1550 F_ON(F_MARK_FCC_SEEN, ps_global)
1551 ? "\\SEEN" : NULL);
1553 else if(result == 0){
1554 q_status_message(SM_ORDER,3,5,
1555 _("Fcc Failed!. No message saved."));
1556 retval = -1;
1557 dprint((1, "explicit fcc write failed!\n"));
1560 so_give(&lmc.so);
1563 if(result < 0){
1564 dprint((1, "Bounce failed\n"));
1565 if(!(flagsarg & SS_PROMPTFORTO))
1566 retval = -1;
1567 else
1568 continue;
1570 else if(result == 1){
1571 if(!fcc)
1572 q_status_message(SM_ORDER, 0, 3,
1573 _("Message sent"));
1574 else{
1575 int avail = ps_global->ttyo->screen_cols-2;
1576 int need, fcclen;
1577 char *part1 = "Message sent and ";
1578 char *part2 = fcc_result ? "" : "NOT ";
1579 char *part3 = "copied to ";
1580 fcclen = strlen(fcc);
1582 need = 2 + strlen(part1) + strlen(part2) +
1583 strlen(part3) + fcclen;
1585 if(need > avail && fcclen > 6)
1586 fcclen -= MIN(fcclen-6, need-avail);
1588 q_status_message4(SM_ORDER, 0, 3,
1589 "%s%s%s\"%s\"",
1590 part1, part2, part3,
1591 short_str(fcc,
1592 (char *)tmp_20k_buf,
1593 SIZEOF_20KBUF,
1594 fcclen, FrontDots));
1598 if(fcc)
1599 fs_give((void **)&fcc);
1601 else{
1602 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1603 retval = -1;
1606 else{
1607 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1608 _("Error in address: %s"), errbuf);
1609 if(errbuf)
1610 fs_give((void **)&errbuf);
1612 if(!(flagsarg & SS_PROMPTFORTO))
1613 retval = -1;
1614 else
1615 continue;
1619 else{
1620 q_status_message(SM_ORDER | SM_DING, 3, 5,
1621 _("No addressee! No e-mail sent."));
1622 retval = -1;
1626 done++;
1627 break;
1629 case 1:
1630 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1631 done++;
1632 retval = -1;
1633 break;
1635 case 3:
1636 help = (help == NO_HELP)
1637 ? (outgoing->remail == NULL
1638 ? h_anon_forward
1639 : h_bounce)
1640 : NO_HELP;
1641 break;
1643 case 11:
1644 if(**tobufp){
1645 char *new_nickname = NULL;
1646 int l;
1647 int ambiguity;
1649 ambiguity = abook_nickname_complete(*tobufp, &new_nickname,
1650 (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA);
1651 if(new_nickname){
1652 if(*new_nickname){
1653 if((l=strlen(new_nickname)) > resize_len){
1654 resize_len = l;
1655 fs_resize((void **) tobufp, resize_len+1);
1658 strncpy(*tobufp, new_nickname, l);
1659 (*tobufp)[l] = '\0';
1662 fs_give((void **) &new_nickname);
1665 if(ambiguity != 2)
1666 Writechar(BELL, 0);
1669 break;
1671 case 4: /* can't suspend */
1672 default:
1673 break;
1677 finish:
1678 if(ba_fcc.tptr)
1679 fs_give((void **)&ba_fcc.tptr);
1681 pine_free_env(&header);
1683 return(retval);
1688 * pine_simple_send_header - generate header suitable for simple_sending
1690 METAENV *
1691 pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp)
1693 METAENV *header;
1694 PINEFIELD *pf;
1695 static struct headerentry he_dummy;
1697 header = pine_new_env(outgoing, fccp, tobufpp, NULL);
1699 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1700 for(pf = header->local; pf && pf->name; pf = pf->next)
1701 if(pf->type == Address && !strucmp(pf->name, "to")){
1702 memset((void *) &he_dummy, 0, sizeof(he_dummy));
1703 pf->extdata = (void *) &he_dummy;
1704 HE(pf)->dirty = 1;
1705 break;
1708 return(header);
1713 /*----------------------------------------------------------------------
1714 Prepare data structures for pico, call pico, then post message
1716 Args: outgoing -- Partially formatted outgoing ENVELOPE
1717 body -- Body of outgoing message
1718 editor_title -- Title for anchor line in composer
1719 fcc_arg -- The file carbon copy field
1720 reply -- Struct describing set of msgs being replied-to
1721 lcc_arg --
1722 custom -- custom header list.
1723 sticky_fcc --
1725 Result: message is edited, then postponed, cancelled or sent.
1727 Fields:
1728 remail -
1729 return_path -
1730 date added here
1731 from added here
1732 sender -
1733 reply_to -
1734 subject passed in, edited and cannonized here
1735 to possibly passed in, edited and cannonized here
1736 cc possibly passed in, edited and cannonized here
1737 bcc edited and cannonized here
1738 in_reply_to generated in reply() and passed in
1739 message_id -
1741 Storage for these fields comes from anywhere outside. It is remalloced
1742 here so the composer can realloc them if needed. The copies here are also
1743 freed here.
1745 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1746 with the first part TYPETEXT! All newlines in the text here also end with
1747 CRLF.
1749 There's a further assumption that the text in the TYPETEXT part is
1750 stored in a storage object (see filter.c).
1751 ----*/
1752 void
1753 pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
1754 char *editor_title, ACTION_S *role, char *fcc_arg,
1755 REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg,
1756 PINEFIELD *custom, int flags)
1758 int i, fixed_cnt, total_cnt, index,
1759 editor_result = 0, body_start = 0, use_news_order = 0;
1760 char *p, *addr, *fcc, *fcc_to_free = NULL;
1761 char *start_here_name = NULL;
1762 char *suggested_nntp_server = NULL;
1763 char *title = NULL;
1764 struct headerentry *he, *headents, *he_to, *he_fcc, *he_news = NULL, *he_lcc = NULL,
1765 *he_from = NULL;
1766 PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL,
1767 *pf_smtp_server, *pf_nntp_server,
1768 *pf_fcc = NULL, *pf_err, *pf_uid, *pf_mbox, *pf_curpos,
1769 *pf_ourrep, *pf_ourhdrs, **sending_order;
1770 METAENV header;
1771 ADDRESS *lcc_addr = NULL;
1772 ADDRESS *nobody_addr = NULL;
1773 BODY_PARTICULARS_S *bp;
1774 STORE_S *orig_so = NULL;
1775 PICO pbuf1, *save_previous_pbuf;
1776 CustomType ct;
1777 REDRAFT_POS_S *local_redraft_pos = NULL;
1779 dprint((1,"\n=== send called ===\n"));
1781 save_previous_pbuf = pbf;
1782 pbf = &pbuf1;
1783 standard_picobuf_setup(pbf);
1786 * Cancel any pending initial commands since pico uses a different
1787 * input routine. If we didn't cancel them, they would happen after
1788 * we returned from the editor, which would be confusing.
1790 if(ps_global->in_init_seq){
1791 ps_global->in_init_seq = 0;
1792 ps_global->save_in_init_seq = 0;
1793 clear_cursor_pos();
1794 if(ps_global->initial_cmds){
1795 if(ps_global->free_initial_cmds)
1796 fs_give((void **)&(ps_global->free_initial_cmds));
1798 ps_global->initial_cmds = 0;
1801 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1804 #if defined(DOS) || defined(OS2)
1805 if(!dos_valid_from()){
1806 pbf = save_previous_pbuf;
1807 return;
1810 pbf->upload = NULL;
1811 #else
1812 pbf->upload = (ps_global->VAR_UPLOAD_CMD
1813 && ps_global->VAR_UPLOAD_CMD[0])
1814 ? upload_msg_to_pico : NULL;
1815 #endif
1817 pbf->msgntext = message_format_for_pico;
1818 pbf->mimetype = mime_type_for_pico;
1819 pbf->exittest = send_exit_for_pico;
1820 pbf->user_says_noflow = dont_flow_this_time;
1821 pbf->newthread = new_thread_on_blank_subject;
1822 ps_global->newthread = 0; /* reset this value */
1823 if(F_OFF(F_CANCEL_CONFIRM, ps_global))
1824 pbf->canceltest = cancel_for_pico;
1826 pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] &&
1827 ps_global->VAR_EDITOR[0][0])
1828 ? ps_global->VAR_EDITOR : NULL;
1829 pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0])
1830 ? ps_global->VAR_SPELLER : NULL;
1831 pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
1832 pbf->quote_str = reply && reply->prefix ? reply->prefix : "> ";
1833 /* We actually want to set this only if message we're sending is flowed */
1834 pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
1835 pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
1836 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
1837 && (strcmp(pbf->quote_str, "> ") == 0
1838 || strcmp(pbf->quote_str, ">") == 0));
1839 pbf->edit_offset = 0;
1840 title = cpystr(set_titlebar(editor_title,
1841 ps_global->mail_stream,
1842 ps_global->context_current,
1843 ps_global->cur_folder,ps_global->msgmap,
1844 0, FolderName, 0, 0, NULL));
1845 pbf->pine_anchor = title;
1847 #if defined(DOS) || defined(OS2)
1848 if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){
1849 pbf->oper_dir = ps_global->VAR_FILE_DIR;
1851 #endif
1853 if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE))
1854 pbf->pine_flags |= P_CHKPTNOW;
1856 /* NOTE: initial cursor position set below */
1858 dprint((9, "flags: %x\n", pbf->pine_flags));
1861 * When user runs compose and the current folder is a newsgroup,
1862 * offer to post to the current newsgroup.
1864 if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups))
1865 && IS_NEWS(ps_global->mail_stream)){
1866 char prompt[200], news_group[MAILTMPLEN];
1868 pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group,
1869 sizeof(news_group));
1872 * Replies don't get this far because To or Newsgroups will already
1873 * be filled in. So must be either ordinary compose or forward.
1874 * Forward sets subject, so use that to tell the difference.
1876 if(news_group[0] && !outgoing->subject){
1877 int ch = 'y';
1878 int ret_val;
1879 char *errmsg = NULL;
1880 BUILDER_ARG *fcc_build = NULL;
1882 if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
1883 snprintf(prompt, sizeof(prompt),
1884 _("Post to current newsgroup (%s)"), news_group);
1885 prompt[sizeof(prompt)-1] = '\0';
1886 ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM);
1889 switch(ch){
1890 case 'y':
1891 if(outgoing->newsgroups)
1892 fs_give((void **)&outgoing->newsgroups);
1894 if(!fcc_arg && !(role && role->fcc)){
1895 fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
1896 memset((void *)fcc_build, 0, sizeof(BUILDER_ARG));
1897 fcc_build->tptr = fcc_to_free;
1900 ret_val = news_build(news_group, &outgoing->newsgroups,
1901 &errmsg, fcc_build, NULL);
1903 if(ret_val == -1){
1904 if(outgoing->newsgroups)
1905 fs_give((void **)&outgoing->newsgroups);
1907 outgoing->newsgroups = cpystr(news_group);
1910 if(!fcc_arg && !(role && role->fcc)){
1911 fcc_arg = fcc_to_free = fcc_build->tptr;
1912 fs_give((void **)&fcc_build);
1915 if(errmsg){
1916 if(*errmsg){
1917 q_status_message(SM_ORDER, 3, 3, errmsg);
1918 display_message(NO_OP_COMMAND);
1921 fs_give((void **)&errmsg);
1924 break;
1926 case 'x': /* ^C */
1927 q_status_message(SM_ORDER, 0, 3, _("Message cancelled"));
1928 dprint((4, "=== send: cancelled\n"));
1929 pbf = save_previous_pbuf;
1930 return;
1932 case 'n':
1933 break;
1935 default:
1936 break;
1940 if(F_ON(F_PREDICT_NNTP_SERVER, ps_global)
1941 && outgoing->newsgroups && *outgoing->newsgroups
1942 && IS_NEWS(ps_global->mail_stream)){
1943 NETMBX news_mb;
1945 if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox,
1946 &news_mb))
1947 if(!strucmp(news_mb.service, "nntp")){
1948 if(*ps_global->mail_stream->original_mailbox == '{'){
1949 char *svcp = NULL, *psvcp;
1951 suggested_nntp_server =
1952 cpystr(ps_global->mail_stream->original_mailbox + 1);
1953 if((p = strindex(suggested_nntp_server, '}')) != NULL)
1954 *p = '\0';
1955 for(p = strindex(suggested_nntp_server, '/'); p && *p;
1956 p = strindex(p, '/')){
1957 /* take out /nntp, which gets added in nntp_open */
1958 if(!struncmp(p, "/nntp", 5))
1959 svcp = p + 5;
1960 else if(!struncmp(p, "/service=nntp", 13))
1961 svcp = p + 13;
1962 else if(!struncmp(p, "/service=\"nntp\"", 15))
1963 svcp = p + 15;
1964 else
1965 p++;
1966 if(svcp){
1967 if(*svcp == '\0')
1968 *p = '\0';
1969 else if(*svcp == '/' || *svcp == ':'){
1970 for(psvcp = p; *svcp; svcp++, psvcp++)
1971 *psvcp = *svcp;
1972 *psvcp = '\0';
1974 svcp = NULL;
1978 else
1979 suggested_nntp_server = cpystr(news_mb.orighost);
1984 * If we don't already have custom headers set and the role has custom
1985 * headers, then incorporate those custom headers into "custom".
1987 if(!custom){
1988 PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL;
1990 dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
1992 * If we allow the Combine argument here, we're saying that we want to
1993 * combine the values from the envelope and the role for the fields To,
1994 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
1995 * we'll have a To in the envelope because we're replying. If our role also
1996 * has a To action, then Combine would combine those two and offer both
1997 * to the user. We've decided against doing this. Instead, we always use
1998 * Replace, and the role's header value replaces the value from the
1999 * envelope. It might also make sense in some cases to do the opposite,
2000 * which would be treating the role headers as defaults, just like
2001 * customized-hdrs.
2003 #ifdef WANT_TO_COMBINE_ADDRESSES
2004 if(role && role->cstm)
2005 rolehdrs = parse_custom_hdrs(role->cstm, Combine);
2006 #else
2007 if(role && role->cstm)
2008 rolehdrs = parse_custom_hdrs(role->cstm, Replace);
2009 #endif
2011 if(rolehdrs){
2012 custom = combine_custom_headers(dflthdrs, rolehdrs);
2013 if(dflthdrs){
2014 free_prompts(dflthdrs);
2015 free_customs(dflthdrs);
2018 if(rolehdrs){
2019 free_prompts(rolehdrs);
2020 free_customs(rolehdrs);
2023 else
2024 custom = dflthdrs;
2027 g_rolenick = role ? role->nick : NULL;
2029 /* how many fixed fields are there? */
2030 for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++)
2033 total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1);
2035 /* the fixed part of the PINEFIELDs */
2036 i = fixed_cnt * sizeof(PINEFIELD);
2037 pfields = (PINEFIELD *)fs_get((size_t) i);
2038 memset(pfields, 0, (size_t) i);
2040 /* temporary headerentry array for pico */
2041 i = (total_cnt + 1) * sizeof(struct headerentry);
2042 headents = (struct headerentry *)fs_get((size_t) i);
2043 memset(headents, 0, (size_t) i);
2045 i = total_cnt * sizeof(PINEFIELD *);
2046 sending_order = (PINEFIELD **)fs_get((size_t) i);
2047 memset(sending_order, 0, (size_t) i);
2049 pbf->headents = headents;
2050 header.env = outgoing;
2051 header.local = pfields;
2052 header.sending_order = sending_order;
2054 /* custom part of PINEFIELDs */
2055 header.custom = custom;
2057 he = headents;
2058 pf = pfields;
2061 * For Address types, pf->addr points to an ADDRESS *.
2062 * If that address is in the "outgoing" envelope, it will
2063 * be freed by the caller, otherwise, it should be freed here.
2064 * Pf->textbuf for an Address is used a little to set up a default,
2065 * but then is freed right away below. Pf->scratch is used for a
2066 * pointer to some alloced space for pico to edit in. Addresses in
2067 * the custom area are freed by free_customs().
2069 * For FreeText types, pf->addr is not used. Pf->text points to a
2070 * pointer that points to the text. Pf->textbuf points to a copy of
2071 * the text that must be freed before we leave, otherwise, it is
2072 * probably a pointer into the envelope and that gets freed by the
2073 * caller.
2075 * He->realaddr is the pointer to the text that pico actually edits.
2078 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2079 # define NN 4
2080 #else
2081 # define NN 3
2082 #endif
2084 if(outgoing->newsgroups && *outgoing->newsgroups)
2085 use_news_order++;
2087 /* initialize the fixed header elements of the two temp arrays */
2088 for(i=0; i < fixed_cnt; i++, pf++){
2089 static int news_order[] = {
2090 N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC,
2091 N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY,
2092 N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX,
2093 N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS
2094 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2095 , N_SENDER
2096 #endif
2099 index = i;
2100 /* slightly different editing order if sending to news */
2101 if(use_news_order &&
2102 index >= 0 && index < sizeof(news_order)/sizeof(news_order[0]))
2103 index = news_order[i];
2105 /* copy the templates */
2106 *he = he_template[index];
2108 pf->name = cpystr(pf_template[index].name);
2109 if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
2110 /* slide string over so it is Sender instead of X-X-Sender */
2111 for(p=pf->name; *(p+1); p++)
2112 *p = *(p+4);
2114 pf->type = pf_template[index].type;
2115 pf->canedit = pf_template[index].canedit;
2116 pf->rcptto = pf_template[index].rcptto;
2117 pf->writehdr = pf_template[index].writehdr;
2118 pf->localcopy = pf_template[index].localcopy;
2119 pf->extdata = he;
2120 pf->next = pf + 1;
2122 he->rich_header = view_as_rich(pf->name, he->rich_header);
2123 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2124 he->nickcmpl = NULL;
2126 switch(pf->type){
2127 case FreeText: /* realaddr points to c-client env */
2128 if(index == N_NEWS){
2129 sending_order[1] = pf;
2130 he->realaddr = &outgoing->newsgroups;
2131 he_news = he;
2133 switch(set_default_hdrval(pf, custom)){
2134 case Replace:
2135 if(*he->realaddr)
2136 fs_give((void **)he->realaddr);
2138 *he->realaddr = pf->textbuf;
2139 pf->textbuf = NULL;
2140 he->sticky = 1;
2141 break;
2143 case Combine:
2144 if(*he->realaddr){ /* combine values */
2145 if(pf->textbuf && *pf->textbuf){
2146 char *combined_hdr;
2147 size_t l;
2149 l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
2150 combined_hdr = (char *) fs_get((l+1) * sizeof(char));
2151 strncpy(combined_hdr, *he->realaddr, l);
2152 combined_hdr[l] = '\0';
2153 strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
2154 combined_hdr[l] = '\0';
2155 strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
2156 combined_hdr[l] = '\0';
2158 fs_give((void **)he->realaddr);
2159 *he->realaddr = combined_hdr;
2160 q_status_message(SM_ORDER, 3, 3,
2161 "Adding newsgroup from role");
2162 he->sticky = 1;
2165 else{
2166 *he->realaddr = pf->textbuf;
2167 pf->textbuf = NULL;
2170 break;
2172 case UseAsDef:
2173 /* if no value, use default */
2174 if(!*he->realaddr){
2175 *he->realaddr = pf->textbuf;
2176 pf->textbuf = NULL;
2179 break;
2181 case NoMatch:
2182 break;
2185 /* If there is a newsgroup, we'd better show it */
2186 if(outgoing->newsgroups && *outgoing->newsgroups)
2187 he->rich_header = 0; /* force on by default */
2189 if(pf->textbuf)
2190 fs_give((void **)&pf->textbuf);
2192 pf->text = he->realaddr;
2194 else if(index == N_DATE){
2195 sending_order[2] = pf;
2196 pf->text = (char **) &outgoing->date;
2197 pf->extdata = NULL;
2199 else if(index == N_INREPLY){
2200 sending_order[NN+9] = pf;
2201 pf->text = &outgoing->in_reply_to;
2202 pf->extdata = NULL;
2204 else if(index == N_MSGID){
2205 sending_order[NN+10] = pf;
2206 pf->text = &outgoing->message_id;
2207 pf->extdata = NULL;
2209 else if(index == N_REF){
2210 sending_order[NN+11] = pf;
2211 pf->text = &outgoing->references;
2212 pf->extdata = NULL;
2214 else if(index == N_PRIORITY){
2215 sending_order[NN+12] = pf;
2216 pf->text = &pf->textbuf;
2217 pf->extdata = NULL;
2219 else if(index == N_USERAGENT){
2220 sending_order[NN+13] = pf;
2221 pf->text = &pf->textbuf;
2222 pf->textbuf = generate_user_agent();
2223 pf->extdata = NULL;
2225 else if(index == N_POSTERR){
2226 sending_order[NN+14] = pf;
2227 pf_err = pf;
2228 pf->text = &pf->textbuf;
2229 pf->extdata = NULL;
2231 else if(index == N_RPLUID){
2232 sending_order[NN+15] = pf;
2233 pf_uid = pf;
2234 pf->text = &pf->textbuf;
2235 pf->extdata = NULL;
2237 else if(index == N_RPLMBOX){
2238 sending_order[NN+16] = pf;
2239 pf_mbox = pf;
2240 pf->text = &pf->textbuf;
2241 pf->extdata = NULL;
2243 else if(index == N_SMTP){
2244 sending_order[NN+17] = pf;
2245 pf_smtp_server = pf;
2246 pf->text = &pf->textbuf;
2247 pf->extdata = NULL;
2249 else if(index == N_NNTP){
2250 sending_order[NN+18] = pf;
2251 pf_nntp_server = pf;
2252 pf->text = &pf->textbuf;
2253 pf->extdata = NULL;
2255 else if(index == N_CURPOS){
2256 sending_order[NN+19] = pf;
2257 pf_curpos = pf;
2258 pf->text = &pf->textbuf;
2259 pf->extdata = NULL;
2261 else if(index == N_OURREPLYTO){
2262 sending_order[NN+20] = pf;
2263 pf_ourrep = pf;
2264 pf->text = &pf->textbuf;
2265 pf->extdata = NULL;
2267 else if(index == N_OURHDRS){
2268 sending_order[NN+21] = pf;
2269 pf_ourhdrs = pf;
2270 pf->text = &pf->textbuf;
2271 pf->extdata = NULL;
2273 else if(index == N_AUTHRCVD){
2274 sending_order[0] = pf;
2275 pf_ourhdrs = pf;
2276 pf->text = &pf->textbuf;
2277 pf->extdata = NULL;
2279 else{
2280 q_status_message(SM_ORDER | SM_DING, 3, 7,
2281 "Botched: Unmatched FreeText header in pine_send");
2284 break;
2286 /* can't do a default for this one */
2287 case Attachment:
2288 /* If there is an attachment already, we'd better show them */
2289 if(body && *body && (*body)->type != TYPETEXT)
2290 he->rich_header = 0; /* force on by default */
2292 break;
2294 case Address:
2295 switch(index){
2296 case N_FROM:
2297 sending_order[3] = pf;
2298 pf->addr = &outgoing->from;
2299 if(role && role->from){
2300 if(ps_global->never_allow_changing_from)
2301 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2302 else{
2303 outgoing->from = copyaddrlist(role->from);
2304 he->display_it = 1; /* show it */
2305 he->rich_header = 0;
2309 he_from = he;
2310 break;
2312 case N_TO:
2313 sending_order[NN+2] = pf;
2314 pf->addr = &outgoing->to;
2315 /* If already set, make it act like we typed it in */
2316 if(outgoing->to
2317 && outgoing->to->mailbox
2318 && outgoing->to->mailbox[0]
2319 && flags & PS_STICKY_TO)
2320 he->sticky = 1;
2322 he_to = he;
2323 pf_to = pf;
2324 break;
2326 case N_NOBODY:
2327 sending_order[NN+5] = pf;
2328 pf_nobody = pf;
2329 if(ps_global->VAR_EMPTY_HDR_MSG
2330 && !ps_global->VAR_EMPTY_HDR_MSG[0]){
2331 pf->addr = NULL;
2333 else{
2334 nobody_addr = mail_newaddr();
2335 nobody_addr->next = mail_newaddr();
2336 nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
2337 SIZEOF_20KBUF,
2338 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
2339 ? ps_global->VAR_EMPTY_HDR_MSG
2340 : "undisclosed-recipients"),
2341 ps_global->posting_charmap));
2342 pf->addr = &nobody_addr;
2345 break;
2347 case N_CC:
2348 sending_order[NN+3] = pf;
2349 pf->addr = &outgoing->cc;
2350 break;
2352 case N_BCC:
2353 sending_order[NN+4] = pf;
2354 pf->addr = &outgoing->bcc;
2355 /* if bcc exists, make sure it's exposed so nothing's
2356 * sent by mistake...
2358 if(outgoing->bcc)
2359 he->display_it = 1;
2361 break;
2363 case N_REPLYTO:
2364 sending_order[NN+1] = pf;
2365 pf->addr = &outgoing->reply_to;
2366 if(role && role->replyto){
2367 if(outgoing->reply_to)
2368 mail_free_address(&outgoing->reply_to);
2370 outgoing->reply_to = copyaddrlist(role->replyto);
2371 he->display_it = 1; /* show it */
2372 he->rich_header = 0;
2375 break;
2377 case N_LCC:
2378 sending_order[NN+7] = pf;
2379 pf->addr = &lcc_addr;
2380 he_lcc = he;
2381 if(lcc_arg){
2382 build_address(lcc_arg, &addr, NULL, NULL, NULL);
2383 rfc822_parse_adrlist(&lcc_addr, addr,
2384 ps_global->maildomain);
2385 fs_give((void **)&addr);
2386 he->display_it = 1;
2389 break;
2391 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2392 case N_SENDER:
2393 sending_order[4] = pf;
2394 pf->addr = &outgoing->sender;
2395 break;
2396 #endif
2398 default:
2399 q_status_message1(SM_ORDER,3,7,
2400 "Internal error: Address header %s", comatose(index));
2401 break;
2405 * If this is a reply to news, don't show the regular email
2406 * recipient headers (unless they are non-empty).
2408 if((outgoing->newsgroups && *outgoing->newsgroups)
2409 && (index == N_TO || index == N_CC
2410 || index == N_BCC || index == N_LCC)
2411 && (pf->addr && !*pf->addr)){
2412 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2413 pf->textbuf && *pf->textbuf){
2414 removing_trailing_white_space(pf->textbuf);
2415 (void)removing_double_quotes(pf->textbuf);
2416 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2417 rfc822_parse_adrlist(pf->addr, addr,
2418 ps_global->maildomain);
2419 fs_give((void **)&addr);
2420 if(ct > UseAsDef)
2421 he->sticky = 1;
2423 else
2424 he->rich_header = 1; /* hide */
2428 * If this address doesn't already have a value, then we check
2429 * for a default value assigned by the user.
2431 else if(pf->addr && !*pf->addr){
2432 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2433 (index != N_FROM ||
2434 (!ps_global->never_allow_changing_from &&
2435 F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
2436 pf->textbuf && *pf->textbuf){
2438 removing_trailing_white_space(pf->textbuf);
2439 (void)removing_double_quotes(pf->textbuf);
2442 * Try to set To based on Lcc. Don't attempt Fcc.
2444 if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
2445 BUILDER_ARG *barg = NULL;
2446 char *ppp = NULL;
2448 if(*pf_to->addr)
2449 ppp = addr_list_string(*pf_to->addr, NULL, 1);
2451 if(!ppp)
2452 ppp = cpystr("");
2454 barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
2455 memset(barg, 0, sizeof(*barg));
2456 barg->me = &(he->bldr_private);
2457 barg->aff = &(he_to->bldr_private);
2458 barg->tptr = cpystr(ppp);
2460 build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
2461 he->display_it = 1;
2463 rfc822_parse_adrlist(pf->addr, addr,
2464 ps_global->maildomain);
2465 if(addr)
2466 fs_give((void **) &addr);
2468 if(ct > UseAsDef)
2469 he->sticky = 1;
2471 if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
2472 ADDRESS *a = NULL;
2474 rfc822_parse_adrlist(&a, barg->tptr,
2475 ps_global->maildomain);
2476 if(a){
2477 if(pf_to->addr)
2478 mail_free_address(pf_to->addr);
2480 *pf_to->addr = a;
2484 if(barg){
2485 if(barg->tptr)
2486 fs_give((void **) &barg->tptr);
2488 fs_give((void **) &barg);
2491 if(ppp)
2492 fs_give((void **) &ppp);
2494 else{
2495 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2496 rfc822_parse_adrlist(pf->addr, addr,
2497 ps_global->maildomain);
2498 if(addr)
2499 fs_give((void **) &addr);
2501 if(ct > UseAsDef)
2502 he->sticky = 1;
2506 /* if we still don't have a from */
2507 if(index == N_FROM && !*pf->addr)
2508 *pf->addr = generate_from();
2512 * Addr is already set in the rest of the cases.
2514 else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
2515 ADDRESS *adr = NULL;
2518 * We get to this case of the ifelse if the from or reply-to
2519 * addr was set by a role above.
2522 /* figure out the default value */
2523 (void)set_default_hdrval(pf, custom);
2524 if(pf->textbuf && *pf->textbuf){
2525 removing_trailing_white_space(pf->textbuf);
2526 (void)removing_double_quotes(pf->textbuf);
2527 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2528 rfc822_parse_adrlist(&adr, addr,
2529 ps_global->maildomain);
2530 fs_give((void **)&addr);
2533 /* if value set by role is different from default, show it */
2534 if(adr && !address_is_same(*pf->addr, adr))
2535 he->display_it = 1; /* start this off showing */
2537 /* malformed */
2538 if(!(*pf->addr)->mailbox){
2539 fs_give((void **)pf->addr);
2540 he->display_it = 1;
2543 if(adr)
2544 mail_free_address(&adr);
2546 else if((index == N_TO || index == N_CC || index == N_BCC)
2547 && pf->addr){
2548 ADDRESS *a = NULL, **tail;
2551 * These three are different from the others because we
2552 * might add the addresses to what is already there instead
2553 * of replacing.
2556 switch(set_default_hdrval(pf, custom)){
2557 case Replace:
2558 if(*pf->addr)
2559 mail_free_address(pf->addr);
2561 removing_trailing_white_space(pf->textbuf);
2562 (void)removing_double_quotes(pf->textbuf);
2563 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2564 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2565 fs_give((void **)&addr);
2566 he->sticky = 1;
2567 break;
2569 case Combine:
2570 removing_trailing_white_space(pf->textbuf);
2571 (void)removing_double_quotes(pf->textbuf);
2572 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2573 rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
2574 fs_give((void **)&addr);
2575 he->sticky = 1;
2576 if(a){
2577 for(tail = pf->addr; *tail; tail = &(*tail)->next)
2579 *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
2580 *pf->addr, NULL, a, RCA_ALL);
2581 q_status_message(SM_ORDER, 3, 3,
2582 "Adding addresses from role");
2583 mail_free_address(&a);
2586 break;
2588 case UseAsDef:
2589 case NoMatch:
2590 break;
2593 he->display_it = 1; /* start this off showing */
2595 else if(pf->addr){
2596 switch(set_default_hdrval(pf, custom)){
2597 case Replace:
2598 case Combine:
2599 if(*pf->addr)
2600 mail_free_address(pf->addr);
2602 removing_trailing_white_space(pf->textbuf);
2603 (void)removing_double_quotes(pf->textbuf);
2604 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2605 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2606 fs_give((void **)&addr);
2607 he->sticky = 1;
2608 break;
2610 case UseAsDef:
2611 case NoMatch:
2612 break;
2615 he->display_it = 1;
2618 if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
2619 mail_free_address(pf->addr);
2620 he->display_it = 1; /* start this off showing */
2623 if(pf->textbuf) /* free default value in any case */
2624 fs_give((void **)&pf->textbuf);
2626 /* outgoing2strings will alloc the string pf->scratch below */
2627 he->realaddr = &pf->scratch;
2628 break;
2630 case Fcc:
2631 sending_order[NN+8] = pf;
2632 pf_fcc = pf;
2633 if(role && role->fcc)
2634 fcc = role->fcc;
2635 else
2636 fcc = get_fcc(fcc_arg);
2638 if(fcc_to_free){
2639 fs_give((void **)&fcc_to_free);
2640 fcc_arg = NULL;
2643 if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
2644 he->sticky = 1;
2646 if(role)
2647 role->fcc = NULL;
2649 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
2650 he->display_it = 1; /* start this off showing */
2652 he->realaddr = &fcc;
2653 pf->text = &fcc;
2654 he_fcc = he;
2655 break;
2657 case Subject :
2658 sending_order[NN+6] = pf;
2660 switch(set_default_hdrval(pf, custom)){
2661 case Replace:
2662 case Combine:
2663 pf->scratch = pf->textbuf;
2664 pf->textbuf = NULL;
2665 he->sticky = 1;
2666 if(outgoing->subject)
2667 fs_give((void **)&outgoing->subject);
2669 break;
2671 case UseAsDef:
2672 case NoMatch:
2673 /* if no value, use default */
2674 if(outgoing->subject){
2675 pf->scratch = cpystr(outgoing->subject);
2677 else{
2678 pf->scratch = pf->textbuf;
2679 pf->textbuf = NULL;
2682 break;
2685 he->realaddr = &pf->scratch;
2686 pf->text = &outgoing->subject;
2687 break;
2689 default:
2690 q_status_message1(SM_ORDER,3,7,
2691 "Unknown header type %d in pine_send",
2692 (void *)pf->type);
2693 break;
2697 * We may or may not want to give the user the chance to edit
2698 * the From and Reply-To lines. If they are listed in either
2699 * Default-composer-hdrs or Customized-hdrs, then they can edit
2700 * them, else no.
2701 * If canedit is not set, that means that this header is not in
2702 * the user's customized-hdrs. If rich_header is set, that
2703 * means that this header is not in the user's
2704 * default-composer-hdrs (since From and Reply-To are rich
2705 * by default). So, don't give it an he to edit with in that case.
2707 * For other types, just not setting canedit will cause it to be
2708 * uneditable, regardless of what the user does.
2710 switch(index){
2711 case N_FROM:
2712 /* to allow it, we let this fall through to the reply-to case below */
2713 if(ps_global->never_allow_changing_from ||
2714 (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
2715 !(role && role->from))){
2716 if(pf->canedit || !he->rich_header)
2717 q_status_message(SM_ORDER, 3, 3,
2718 _("Not allowed to change header \"From\""));
2720 memset(he, 0, (size_t)sizeof(*he));
2721 pf->extdata = NULL;
2722 break;
2725 case N_REPLYTO:
2726 if(!pf->canedit && he->rich_header){
2727 memset(he, 0, (size_t)sizeof(*he));
2728 pf->extdata = NULL;
2730 else{
2731 pf->canedit = 1;
2732 he++;
2735 break;
2737 default:
2738 if(!pf->canedit){
2739 memset(he, 0, (size_t)sizeof(*he));
2740 pf->extdata = NULL;
2742 else
2743 he++;
2745 break;
2750 * This is so the builder can tell the composer to fill the affected
2751 * field based on the value in the field on the left.
2753 * Note that this mechanism isn't completely general. Each entry has
2754 * only a single next_affected, so if some other entry points an
2755 * affected entry at an entry with a next_affected, they all inherit
2756 * that next_affected. Since this isn't used much a careful ordering
2757 * of the affected fields should make it a sufficient mechanism.
2759 he_to->affected_entry = he_fcc;
2760 he_news->affected_entry = he_fcc;
2761 he_lcc->affected_entry = he_to;
2762 he_to->next_affected = he_fcc;
2764 (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
2766 i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2768 * Set up headerentries for custom fields.
2769 * NOTE: "i" is assumed to now index first custom field in sending
2770 * order.
2772 for(pf = pf->next; pf && pf->name; pf = pf->next){
2773 char *addr;
2775 if(pf->standard)
2776 continue;
2778 pf->extdata = he;
2779 pf->canedit = 1;
2780 pf->rcptto = 0;
2781 pf->writehdr = 1;
2782 pf->localcopy = 1;
2784 switch(pf->type){
2785 case Address:
2786 if(pf->addr){ /* better be set */
2787 sending_order[i++] = pf;
2788 *he = he_custom_addr_templ;
2789 /* change default text into an ADDRESS */
2790 /* strip quotes around whole default */
2791 removing_trailing_white_space(pf->textbuf);
2792 (void)removing_double_quotes(pf->textbuf);
2793 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2794 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2795 fs_give((void **)&addr);
2796 if(pf->textbuf)
2797 fs_give((void **)&pf->textbuf);
2799 he->realaddr = &pf->scratch;
2800 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2801 he->nickcmpl = NULL;
2804 break;
2806 case FreeText:
2807 sending_order[i++] = pf;
2808 *he = he_custom_free_templ;
2809 he->realaddr = &pf->textbuf;
2810 pf->text = &pf->textbuf;
2811 if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
2812 (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
2813 he->display_it = 1; /* show it */
2815 break;
2817 default:
2818 q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
2819 (void *)pf->type);
2820 break;
2823 he->name = pf->name;
2825 /* use first 8 characters for prompt */
2826 he->prompt = cpystr(" : ");
2827 strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
2829 he->rich_header = view_as_rich(he->name, he->rich_header);
2830 he++;
2834 * Make sure at least *one* field is displayable...
2836 for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
2837 if(HE(pf) && !HE(pf)->rich_header){
2838 index = i;
2839 break;
2843 * None displayable!!! Warn and display defaults.
2845 if(index == -1){
2846 q_status_message(SM_ORDER,0,5,
2847 "No default-composer-hdrs matched, displaying defaults");
2848 for(i = 0, pf = header.local; pf; pf = pf->next, i++)
2849 if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
2850 && HE(pf))
2851 HE(pf)->rich_header = 0;
2855 * Save information about body which set_mime_type_by_grope might change.
2856 * Then, if we get an error sending, we reset these things so that
2857 * grope can do it's thing again after we edit some more.
2859 if ((*body)->type == TYPEMULTIPART)
2860 bp = save_body_particulars(&(*body)->nested.part->body);
2861 else
2862 bp = save_body_particulars(*body);
2865 local_redraft_pos = redraft_pos;
2867 /*----------------------------------------------------------------------
2868 Loop calling the editor until everything goes well
2869 ----*/
2870 while(1){
2871 int saved_user_timeout;
2873 /* Reset body to what it was when we started. */
2874 if ((*body)->type == TYPEMULTIPART)
2875 reset_body_particulars(bp, &(*body)->nested.part->body);
2876 else
2877 reset_body_particulars(bp,*body);
2879 * set initial cursor position based on how many times we've been
2880 * thru the loop...
2882 if(reply && reply->pseudo){
2883 pbf->pine_flags |= reply->data.pico_flags;
2885 else if(body_start){
2886 pbf->pine_flags |= P_BODY;
2887 body_start = 0; /* maybe not next time */
2889 else if(local_redraft_pos){
2890 pbf->edit_offset = local_redraft_pos->offset;
2891 /* set the start_here bit in correct header */
2892 for(pf = header.local; pf && pf->name; pf = pf->next)
2893 if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
2894 && HE(pf)){
2895 HE(pf)->start_here = 1;
2896 break;
2899 /* If didn't find it, we start in body. */
2900 if(!pf || !pf->name)
2901 pbf->pine_flags |= P_BODY;
2903 else if(reply && (!reply->forw && !reply->forwarded)){
2904 pbf->pine_flags |= P_BODY;
2907 /* in case these were turned on in previous pass through loop */
2908 if(pf_nobody){
2909 pf_nobody->writehdr = 0;
2910 pf_nobody->localcopy = 0;
2913 if(pf_fcc)
2914 pf_fcc->localcopy = 0;
2917 * If a sending attempt failed after we passed the message text
2918 * thru a user-defined filter, "orig_so" points to the original
2919 * text. Replace the body's encoded data with the original...
2921 if(orig_so){
2922 STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
2923 ? &(*body)->nested.part->body.contents.text.data
2924 : &(*body)->contents.text.data);
2925 so_give(so);
2926 *so = orig_so;
2927 orig_so = NULL;
2931 * Convert the envelope and body to the string format that
2932 * pico can edit
2934 outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
2936 for(pf = header.local; pf && pf->name; pf = pf->next){
2938 * If this isn't the first time through this loop, we may have
2939 * freed some of the FreeText headers below so that they wouldn't
2940 * show up as empty headers in the finished message. Need to
2941 * alloc them again here so they can be edited.
2943 if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
2944 *HE(pf)->realaddr = cpystr("");
2946 if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
2947 HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
2951 * If From is exposed, probably by a role, then start the cursor
2952 * on the first line which isn't filled in. If it isn't, then we
2953 * don't move the cursor, mostly for back-compat.
2955 if((!reply || reply->forw || reply->forwarded) &&
2956 !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
2957 (he_from->display_it || !he_from->rich_header)){
2958 for(pf = header.local; pf && pf->name; pf = pf->next)
2959 if(HE(pf) &&
2960 (HE(pf)->display_it || !HE(pf)->rich_header) &&
2961 HE(pf)->realaddr &&
2962 (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
2963 HE(pf)->start_here = 1;
2964 break;
2968 #ifdef _WINDOWS
2969 mswin_setwindowmenu (MENU_COMPOSER);
2970 #endif
2972 cancel_busy_cue(-1);
2973 flush_status_messages(1);
2975 /* turn off user input timeout when in composer */
2976 saved_user_timeout = ps_global->hours_to_timeout;
2977 ps_global->hours_to_timeout = 0;
2978 dprint((1, "\n ---- COMPOSER ----\n"));
2979 editor_result = pico(pbf);
2980 dprint((4, "... composer returns (0x%x)\n", editor_result));
2981 ps_global->hours_to_timeout = saved_user_timeout;
2983 #ifdef _WINDOWS
2984 mswin_setwindowmenu (MENU_DEFAULT);
2985 #endif
2986 fix_windsize(ps_global);
2989 * Only reinitialize signals if we didn't receive an interesting
2990 * one while in pico, since pico's return is part of processing that
2991 * signal and it should continue to be ignored.
2993 if(!(editor_result & COMP_GOTHUP))
2994 init_signals(); /* Pico has it's own signal stuff */
2997 * We're going to save in DEADLETTER. Dump attachments first.
2999 if(editor_result & COMP_CANCEL)
3000 free_attachment_list(&pbf->attachments);
3002 /* Turn strings back into structures */
3003 strings2outgoing(&header, body, pbf->attachments, flowing_requested);
3005 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3006 if(outgoing->newsgroups){
3007 sqzspaces(outgoing->newsgroups);
3008 if(!outgoing->newsgroups[0])
3009 fs_give((void **)&(outgoing->newsgroups));
3012 /* Make subject NULL if it is "" (so won't show up in headers) */
3013 if(outgoing->subject && !outgoing->subject[0])
3014 fs_give((void **)&(outgoing->subject));
3016 /* remove custom fields that are empty */
3017 for(pf = header.local; pf && pf->name; pf = pf->next){
3018 if(pf->type == FreeText && pf->textbuf){
3019 if(pf->textbuf[0] == '\0'){
3020 fs_give((void **)&pf->textbuf);
3021 pf->text = NULL;
3026 removing_trailing_white_space(fcc);
3028 /*-------- Stamp it with a current date -------*/
3029 if(outgoing->date) /* update old date */
3030 fs_give((void **)&(outgoing->date));
3032 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3033 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
3035 rfc822_date(tmp_20k_buf); /* format and copy new date */
3036 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3037 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
3039 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
3041 /* Set return_path based on From which is going to be used */
3042 if(outgoing->return_path)
3043 mail_free_address(&outgoing->return_path);
3045 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
3048 * Don't ever believe the sender that is there.
3049 * If From doesn't look quite right, generate our own sender.
3051 if(outgoing->sender)
3052 mail_free_address(&outgoing->sender);
3055 * If the LHS of the address doesn't match, or the RHS
3056 * doesn't match one of localdomain or hostname,
3057 * then add a sender line (really X-X-Sender).
3059 * Don't add a personal_name since the user can change that.
3061 if(F_OFF(F_DISABLE_SENDER, ps_global)
3063 (!outgoing->from
3064 || !outgoing->from->mailbox
3065 || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
3066 || !outgoing->from->host
3067 || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
3068 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
3070 outgoing->sender = mail_newaddr();
3071 outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
3072 outgoing->sender->host = cpystr(ps_global->hostname);
3075 if(ps_global->newthread){
3076 if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
3077 if(outgoing->references) fs_give((void **)&outgoing->references);
3080 /*----- Message is edited, now decide what to do with it ----*/
3081 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
3082 /*=========== Postpone or Interrupted message ============*/
3083 CONTEXT_S *fcc_cntxt = NULL;
3084 char folder[MAXPATH+1];
3085 int fcc_result = 0;
3086 char label[50];
3088 dprint((4, "pine_send:%s handling\n",
3089 (editor_result & COMP_SUSPEND)
3090 ? "SUSPEND"
3091 : (editor_result & COMP_GOTHUP)
3092 ? "HUP"
3093 : (editor_result & COMP_CANCEL)
3094 ? "CANCEL" : "HUH?"));
3095 if((editor_result & COMP_CANCEL)
3096 && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
3097 || ps_global->deadlets == 0)){
3098 q_status_message(SM_ORDER, 0, 3, "Message cancelled");
3099 break;
3103 * The idea here is to use the Fcc: writing facility
3104 * to append to the special postponed message folder...
3106 * NOTE: the strategy now is to write the message and
3107 * all attachments as they exist at composition time.
3108 * In other words, attachments are postponed by value
3109 * and not reference. This may change later, but we'll
3110 * need a local "message/external-body" type that
3111 * outgoing2strings knows how to properly set up for
3112 * the composer. Maybe later...
3115 label[0] = '\0';
3117 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
3118 && (editor_result & COMP_SUSPEND)
3119 && (check_addresses(&header) == CA_BAD)){
3120 /*--- Addresses didn't check out---*/
3121 q_status_message(SM_ORDER, 7, 7,
3122 _("Not allowed to postpone message until addresses are qualified"));
3123 continue;
3127 * Build the local message copy so.
3129 * In the HUP case, we'll write the bezerk delimiter by
3130 * hand and output the message directly into the folder.
3131 * It's not only faster, we don't have to worry about
3132 * c-client reentrance and less hands paw over the data so
3133 * there's less chance of a problem.
3135 * In the Postpone case, just create it if the user wants to
3136 * and create a temporary storage object to write into. */
3137 fake_hup:
3138 lmc.all_written = lmc.text_only = lmc.text_written = 0;
3139 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3140 int newfile = 1;
3141 time_t now = time((time_t *)0);
3143 #if defined(DOS) || defined(OS2)
3145 * we can't assume anything about root or home dirs, so
3146 * just plunk it down in the same place as the pinerc
3148 if(!getenv("HOME")){
3149 char *lc = last_cmpnt(ps_global->pinerc);
3150 folder[0] = '\0';
3151 if(lc != NULL){
3152 strncpy(folder,ps_global->pinerc,
3153 MIN(lc-ps_global->pinerc,sizeof(folder)-1));
3154 folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
3157 strncat(folder, (editor_result & COMP_GOTHUP)
3158 ? INTERRUPTED_MAIL : DEADLETTER,
3159 sizeof(folder)-strlen(folder)-1);
3161 else
3162 #endif
3163 build_path(folder,
3164 ps_global->VAR_OPER_DIR
3165 ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
3166 (editor_result & COMP_GOTHUP)
3167 ? INTERRUPTED_MAIL : DEADLETTER,
3168 sizeof(folder));
3170 if(editor_result & COMP_CANCEL){
3171 char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
3173 if(strlen(folder) + 1 < sizeof(filename))
3174 for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
3175 strncpy(filename, folder, sizeof(filename));
3176 filename[sizeof(filename)-1] = '\0';
3177 strncpy(newfname, filename, sizeof(newfname));
3178 newfname[sizeof(newfname)-1] = '\0';
3180 if(i > 1){
3181 snprintf(nbuf, sizeof(nbuf), "%d", i);
3182 nbuf[sizeof(nbuf)-1] = '\0';
3183 strncat(filename, nbuf,
3184 sizeof(filename)-strlen(filename)-1);
3185 filename[sizeof(filename)-1] = '\0';
3188 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
3189 nbuf[sizeof(nbuf)-1] = '\0';
3190 strncat(newfname, nbuf,
3191 sizeof(newfname)-strlen(newfname)-1);
3192 newfname[sizeof(newfname)-1] = '\0';
3193 (void) rename_file(filename, newfname);
3196 our_unlink(folder);
3198 else
3199 newfile = can_access(folder, ACCESS_EXISTS);
3201 if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
3202 if (outgoing->from){
3203 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
3204 newfile ? "" : "\015\012",
3205 outgoing->from->mailbox,
3206 outgoing->from->host, ctime(&now));
3207 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3208 if(!so_puts(lmc.so, tmp_20k_buf)){
3209 if(editor_result & COMP_CANCEL)
3210 q_status_message2(SM_ORDER | SM_DING, 3, 3,
3211 "Can't write \"%s\": %s",
3212 folder, error_description(errno));
3213 else
3214 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3215 folder ? folder : "?",
3216 error_description(errno)));
3221 else{ /* Must be COMP_SUSPEND */
3222 if(!ps_global->VAR_POSTPONED_FOLDER
3223 || !ps_global->VAR_POSTPONED_FOLDER[0]){
3224 q_status_message(SM_ORDER | SM_DING, 3, 3,
3225 _("No postponed file defined"));
3226 continue;
3230 * Store the cursor position
3232 * First find the header entry with the start_here
3233 * bit set, if any. This means the editor is telling
3234 * us to start on this header field next time.
3236 start_here_name = NULL;
3237 for(pf = header.local; pf && pf->name; pf = pf->next)
3238 if(HE(pf) && HE(pf)->start_here){
3239 start_here_name = pf->name;
3240 break;
3243 /* If there wasn't one, ":" means we start in the body. */
3244 if(!start_here_name || !*start_here_name)
3245 start_here_name = ":";
3247 if(ps_global->VAR_FORM_FOLDER
3248 && ps_global->VAR_FORM_FOLDER[0]
3249 && postpone_prompt() == 'f'){
3250 strncpy(folder, ps_global->VAR_FORM_FOLDER,
3251 sizeof(folder)-1);
3252 folder[sizeof(folder)-1] = '\0';
3253 strncpy(label, "form letter", sizeof(label));
3254 label[sizeof(label)-1] = '\0';
3256 else{
3257 strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
3258 sizeof(folder)-1);
3259 folder[sizeof(folder)-1] = '\0';
3260 strncpy(label, "postponed message", sizeof(label));
3261 label[sizeof(label)-1] = '\0';
3264 lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
3267 if(lmc.so){
3268 size_t sz;
3269 char *lmq = NULL;
3271 /* copy fcc line to postponed or interrupted folder */
3272 if(pf_fcc)
3273 pf_fcc->localcopy = 1;
3275 /* plug error into header for later display to user */
3276 if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
3277 pf_err->writehdr = 1;
3278 pf_err->localcopy = 1;
3279 pf_err->textbuf = lmq;
3283 * if reply, write (UID)folder header field so we can
3284 * later flag the replied-to message \\ANSWERED
3285 * DON'T save MSGNO's.
3287 if(reply && reply->uid){
3288 char uidbuf[MAILTMPLEN], *p;
3289 long i;
3291 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
3292 if(i)
3293 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
3295 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
3298 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3300 pf_uid->writehdr = 1;
3301 pf_uid->localcopy = 1;
3302 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
3303 reply->prefix ? int2string(strlen(reply->prefix))
3304 : (reply->forwarded) ? "": "0 ",
3305 reply->prefix ? " " : "",
3306 reply->prefix ? reply->prefix : "",
3307 i, reply->data.uid.validity,
3308 tmp_20k_buf, reply->mailbox);
3309 uidbuf[sizeof(uidbuf)-1] = '\0';
3310 pf_uid->textbuf = cpystr(uidbuf);
3313 * Logically, this ought to be part of pf_uid, but this
3314 * was added later and so had to be in a separate header
3315 * for backwards compatibility.
3317 pf_mbox->writehdr = 1;
3318 pf_mbox->localcopy = 1;
3319 pf_mbox->textbuf = cpystr(reply->origmbox
3320 ? reply->origmbox
3321 : reply->mailbox);
3324 /* Save cursor position */
3325 if(start_here_name && *start_here_name){
3326 char curposbuf[MAILTMPLEN];
3328 pf_curpos->writehdr = 1;
3329 pf_curpos->localcopy = 1;
3330 snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
3331 pbf->edit_offset);
3332 curposbuf[sizeof(curposbuf)-1] = '\0';
3333 pf_curpos->textbuf = cpystr(curposbuf);
3337 * Work around c-client reply-to bug. C-client will
3338 * return a reply_to in an envelope even if there is
3339 * no reply-to header field. We want to note here whether
3340 * the reply-to is real or not.
3342 if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
3343 pf_ourrep->writehdr = 1;
3344 pf_ourrep->localcopy = 1;
3345 if(outgoing->reply_to)
3346 pf_ourrep->textbuf = cpystr("Full");
3347 else
3348 pf_ourrep->textbuf = cpystr("Empty");
3351 /* Save the role-specific smtp server */
3352 if(role && role->smtp && role->smtp[0]){
3353 char *q, *smtp = NULL;
3354 char **lp;
3355 size_t len = 0;
3358 * Turn the list of smtp servers into a space-
3359 * delimited list in a single string.
3361 for(lp = role->smtp; (q = *lp) != NULL; lp++)
3362 len += (strlen(q) + 1);
3364 if(len){
3365 smtp = (char *) fs_get(len * sizeof(char));
3366 smtp[0] = '\0';
3367 for(lp = role->smtp; (q = *lp) != NULL; lp++){
3368 if(lp != role->smtp)
3369 strncat(smtp, " ", len-strlen(smtp)-1);
3371 strncat(smtp, q, len-strlen(smtp)-1);
3374 smtp[len-1] = '\0';
3377 pf_smtp_server->writehdr = 1;
3378 pf_smtp_server->localcopy = 1;
3379 if(smtp)
3380 pf_smtp_server->textbuf = smtp;
3381 else
3382 pf_smtp_server->textbuf = cpystr("");
3385 /* Save the role-specific nntp server */
3386 if(suggested_nntp_server ||
3387 (role && role->nntp && role->nntp[0])){
3388 char *q, *nntp = NULL;
3389 char **lp;
3390 size_t len = 0;
3392 if(role && role->nntp && role->nntp[0]){
3394 * Turn the list of nntp servers into a space-
3395 * delimited list in a single string.
3397 for(lp = role->nntp; (q = *lp) != NULL; lp++)
3398 len += (strlen(q) + 1);
3400 if(len){
3401 nntp = (char *) fs_get(len * sizeof(char));
3402 nntp[0] = '\0';
3403 for(lp = role->nntp; (q = *lp) != NULL; lp++){
3404 if(lp != role->nntp)
3405 strncat(nntp, " ", len-strlen(nntp)-1);
3407 strncat(nntp, q, len-strlen(nntp)-1);
3410 nntp[len-1] = '\0';
3413 else
3414 nntp = cpystr(suggested_nntp_server);
3416 pf_nntp_server->writehdr = 1;
3417 pf_nntp_server->localcopy = 1;
3418 if(nntp)
3419 pf_nntp_server->textbuf = nntp;
3420 else
3421 pf_nntp_server->textbuf = cpystr("");
3425 * Write the list of custom headers to the
3426 * X-Our-Headers header so that we can recover the
3427 * list in redraft.
3429 sz = 0;
3430 for(pf = header.custom; pf && pf->name; pf = pf->next)
3431 sz += strlen(pf->name) + 1;
3433 if(sz){
3434 char *q;
3436 pf_ourhdrs->writehdr = 1;
3437 pf_ourhdrs->localcopy = 1;
3438 pf_ourhdrs->textbuf = (char *)fs_get(sz);
3439 memset(pf_ourhdrs->textbuf, 0, sz);
3440 q = pf_ourhdrs->textbuf;
3441 for(pf = header.custom; pf && pf->name; pf = pf->next){
3442 if(pf != header.custom)
3443 sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
3445 sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
3448 pf_ourhdrs->textbuf[sz-1] = '\0';;
3452 * We need to make sure any header values that got cleared
3453 * get written to the postponed message (they won't if
3454 * pf->text is NULL). Otherwise, we can't tell previously
3455 * non-existent custom headers or default values from
3456 * custom (or other) headers that got blanked in the
3457 * composer...
3459 for(pf = header.local; pf && pf->name; pf = pf->next)
3460 if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
3461 *(HE(pf)->realaddr) = cpystr("");
3464 * We're saving the message for use later. It may be that the
3465 * characters in the message are not all convertible to the
3466 * user's posting_charmap. We'll save it as UTF-8 instead
3467 * and worry about that the next time they try to send it.
3468 * Use a different save pointer just to be sure we don't
3469 * mess up the other stuff. We should probably make the
3470 * charset an argument.
3472 * We also need to fix the charset of the body part
3473 * the user is editing so that we can read it back
3474 * successfully when we resume the composition.
3476 ps_global->post_utf8 = 1;
3479 PARAMETER *pm;
3480 BODY *bp;
3481 if((*body)->type == TYPEMULTIPART)
3482 bp = &(*body)->nested.part->body;
3483 else
3484 bp = *body;
3486 for(pm = bp->parameter;
3487 pm && strucmp(pm->attribute, "charset") != 0;
3488 pm = pm->next)
3491 if(pm){
3492 if(pm->value)
3493 fs_give((void **) &pm->value);
3495 pm->value = cpystr("UTF-8");
3499 if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
3500 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3501 char *err;
3502 STORE_S *hup_so;
3503 gf_io_t gc, pc;
3504 int we_cancel = 0;
3506 if(editor_result & COMP_CANCEL){
3507 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3508 "Saving to \"%s\"", folder);
3509 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3510 we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
3513 if((hup_so =
3514 so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
3515 gf_set_so_readc(&gc, lmc.so);
3516 gf_set_so_writec(&pc, hup_so);
3517 so_seek(lmc.so, 0L, 0); /* read msg copy and */
3518 so_seek(hup_so, 0L, 2); /* append to folder */
3519 gf_filter_init();
3520 gf_link_filter(gf_nvtnl_local, NULL);
3521 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
3522 dprint((1, "*** PIPE FAILED: %s\n",
3523 err ? err : "?"));
3525 gf_clear_so_readc(lmc.so);
3526 gf_clear_so_writec(hup_so);
3527 so_give(&hup_so);
3529 else
3530 dprint((1, "*** CAN'T CREATE %s: %s\n",
3531 folder ? folder : "?",
3532 error_description(errno)));
3534 if(we_cancel)
3535 cancel_busy_cue(-1);
3537 else
3538 fcc_result = write_fcc(folder, fcc_cntxt,
3539 lmc.so, NULL, label, NULL);
3542 /* discontinue coerced UTF-8 posting */
3543 ps_global->post_utf8 = 0;
3545 so_give(&lmc.so);
3547 else
3548 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3549 error_description(errno)));
3551 if(editor_result & COMP_GOTHUP){
3553 * Special Hack #291: if any hi-byte bits are set in
3554 * editor's result, we put them there.
3556 if(editor_result & 0xff00)
3557 exit(editor_result >> 8);
3559 dprint((1, "Save composition on HUP %sED\n",
3560 fcc_result ? "SUCCEED" : "FAIL"));
3561 hup_signal(); /* Do what we normally do on SIGHUP */
3563 else if((editor_result & COMP_SUSPEND) && fcc_result){
3564 if(ps_global->VAR_FORM_FOLDER
3565 && ps_global->VAR_FORM_FOLDER[0]
3566 && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
3567 q_status_message(SM_ORDER, 0, 3,
3568 _("Composition saved to Form Letter Folder. Select Compose to send."));
3569 else
3570 q_status_message(SM_ORDER, 0, 3,
3571 _("Composition postponed. Select Compose to resume."));
3573 break; /* postpone went OK, get out of here */
3575 else if(editor_result & COMP_CANCEL){
3576 char *lc = NULL;
3578 if(fcc_result && folder)
3579 lc = last_cmpnt(folder);
3581 q_status_message3(SM_ORDER, 0, 3,
3582 _("Message cancelled%s%s%s"),
3583 (lc && *lc) ? " and copied to \"" : "",
3584 (lc && *lc) ? lc : "",
3585 (lc && *lc) ? "\" file" : "");
3586 break;
3588 else{
3589 q_status_message(SM_ORDER, 0, 4,
3590 _("Continuing composition. Message not postponed or sent"));
3591 body_start = 1;
3592 continue; /* postpone failed, jump back in to composer */
3595 else{
3596 /*------ Must be sending mail or posting ! -----*/
3597 int result, valid_addr, continue_with_only_fcc = 0;
3598 CONTEXT_S *fcc_cntxt = NULL;
3600 result = 0;
3601 dprint((4, "=== sending: "));
3603 /* --- If posting, confirm with user ----*/
3604 if(outgoing->newsgroups && *outgoing->newsgroups
3605 && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
3606 && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
3607 q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
3608 dprint((4, "no post, continuing\n"));
3609 continue;
3612 if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
3613 || outgoing->newsgroups)){
3614 if(fcc && fcc[0]){
3615 if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
3616 want_to(_("No recipients, really copy only to Fcc "),
3617 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
3618 continue;
3620 continue_with_only_fcc++;
3622 else{
3623 q_status_message(SM_ORDER, 3, 4,
3624 _("No recipients specified!"));
3625 dprint((4, "no recip, continuing\n"));
3626 continue;
3630 if((valid_addr = check_addresses(&header)) == CA_BAD){
3631 /*--- Addresses didn't check out---*/
3632 dprint((4, "addrs failed, continuing\n"));
3633 continue;
3636 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
3637 && !continue_with_only_fcc
3638 && !(outgoing->to || outgoing->cc || lcc_addr
3639 || outgoing->newsgroups)
3640 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3641 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
3642 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3643 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3644 free_redraft_pos(&local_redraft_pos);
3646 local_redraft_pos
3647 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3648 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3649 local_redraft_pos->hdrname = cpystr(TONAME);
3650 continue;
3653 if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
3654 && check_for_subject(&header) == CF_MISSING){
3655 dprint((4, "No subject, continuing\n"));
3656 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3657 free_redraft_pos(&local_redraft_pos);
3659 local_redraft_pos
3660 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3661 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3662 local_redraft_pos->hdrname = cpystr(SUBJNAME);
3663 continue;
3666 if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
3667 && check_for_fcc(fcc) == CF_MISSING){
3668 dprint((4, "No fcc, continuing\n"));
3669 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3670 free_redraft_pos(&local_redraft_pos);
3672 local_redraft_pos
3673 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3674 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3675 local_redraft_pos->hdrname = cpystr("Fcc");
3676 continue;
3679 set_last_fcc(fcc);
3681 /*---- Check out fcc -----*/
3682 if(fcc && *fcc){
3684 * If special name "inbox" then replace it with the
3685 * real inbox path.
3687 if(ps_global->VAR_INBOX_PATH
3688 && strucmp(fcc, ps_global->inbox_name) == 0){
3689 char *replace_fcc;
3691 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
3692 fs_give((void **)&fcc);
3693 fcc = replace_fcc;
3696 lmc.all_written = lmc.text_written = 0;
3697 /* lmc.text_only set on command line */
3698 if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
3699 /* ---- Open or allocation of fcc failed ----- */
3700 dprint((4,"can't open/allocate fcc, cont'g\n"));
3703 * Find field entry associated with fcc, and start
3704 * composer on it...
3706 for(pf = header.local; pf && pf->name; pf = pf->next)
3707 if(pf->type == Fcc && HE(pf))
3708 HE(pf)->start_here = 1;
3710 continue;
3712 else
3713 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
3715 else
3716 lmc.so = NULL;
3718 /*---- Take care of any requested prefiltering ----*/
3719 if(sending_filter_requested
3720 && !filter_message_text(sending_filter_requested, outgoing,
3721 *body, &orig_so, &header)){
3722 q_status_message1(SM_ORDER, 3, 3,
3723 _("Problem filtering! Nothing sent%s."),
3724 fcc ? " or saved to fcc" : "");
3725 continue;
3728 /*------ Actually post -------*/
3729 if(outgoing->newsgroups){
3730 char **alt_nntp = NULL, *alt_nntp_p[2];
3731 if(((role && role->nntp)
3732 || suggested_nntp_server)){
3733 if(ps_global->FIX_NNTP_SERVER
3734 && ps_global->FIX_NNTP_SERVER[0])
3735 q_status_message(SM_ORDER | SM_DING, 5, 5,
3736 "Using nntp-server that is administratively fixed");
3737 else if(role && role->nntp)
3738 alt_nntp = role->nntp;
3739 else{
3740 alt_nntp_p[0] = suggested_nntp_server;
3741 alt_nntp_p[1] = NULL;
3742 alt_nntp = alt_nntp_p;
3745 if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
3746 dprint((1, "Post failed, continuing\n"));
3747 if(outgoing->message_id)
3748 fs_give((void **) &outgoing->message_id);
3750 outgoing->message_id = generate_message_id();
3752 continue;
3754 else
3755 result |= P_NEWS_WIN;
3759 * BUG: IF we've posted the message *and* an fcc was specified
3760 * then we've already got a neatly formatted message in the
3761 * lmc.so. It'd be nice not to have to re-encode everything
3762 * to insert it into the smtp slot...
3766 * Turn on "undisclosed recipients" header if no To or cc.
3768 if(!(outgoing->to || outgoing->cc)
3769 && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
3770 pf_nobody->writehdr = 1;
3771 pf_nobody->localcopy = 1;
3774 if(priority_requested){
3775 (void) set_priority_header(&header, priority_requested);
3776 fs_give((void **) &priority_requested);
3779 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3781 * If requested, launch backgroud posting...
3783 if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
3784 ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
3785 memset(ps_global->post, 0, sizeof(POST_S));
3786 if(fcc)
3787 ps_global->post->fcc = cpystr(fcc);
3789 if((ps_global->post->pid = fork()) == 0){
3791 * Put us in new process group...
3793 setpgrp(0, ps_global->post->pid);
3795 /* BUG: should fix argv[0] to indicate what we're up to */
3798 * If there are any live streams, pretend we never
3799 * knew them. Problem is two processes writing
3800 * same server process.
3801 * This is not clean but we're just going to exit
3802 * right away anyway. We just want to be sure to leave
3803 * the stuff that the parent is going to use alone.
3804 * The next three lines will disable the re-use of the
3805 * existing streams and cause us to open a new one if
3806 * needed.
3808 ps_global->mail_stream = NULL;
3809 ps_global->s_pool.streams = NULL;
3810 ps_global->s_pool.nstream = 0;
3812 /* quell any display output */
3813 ps_global->in_init_seq = 1;
3815 /*------- Actually mail the message ------*/
3816 if(valid_addr == CA_OK
3817 && (outgoing->to || outgoing->cc
3818 || outgoing->bcc || lcc_addr)){
3819 char **alt_smtp = NULL;
3821 if(role && role->smtp){
3822 if(ps_global->FIX_SMTP_SERVER
3823 && ps_global->FIX_SMTP_SERVER[0])
3824 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3825 else
3826 alt_smtp = role->smtp;
3829 result |= (call_mailer(&header, *body, alt_smtp,
3830 call_mailer_flags,
3831 call_mailer_file_result,
3832 pipe_callback) > 0)
3833 ? P_MAIL_WIN : P_MAIL_LOSE;
3835 if(result & P_MAIL_LOSE)
3836 mark_address_failure_for_pico(&header);
3839 /*----- Was there an fcc involved? -----*/
3840 if(lmc.so){
3841 /*------ Write it if at least something worked ------*/
3842 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3843 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3844 && pine_rfc822_output(&header, *body,
3845 NULL, NULL))){
3846 char label[50];
3848 strncpy(label, "Fcc", sizeof(label));
3849 label[sizeof(label)-1] = '\0';
3850 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3851 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3852 label[sizeof(label)-1] = '\0';
3855 /*-- Now actually copy to fcc folder and close --*/
3856 result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
3857 NULL, label,
3858 F_ON(F_MARK_FCC_SEEN, ps_global)
3859 ? "\\SEEN" : NULL))
3860 ? P_FCC_WIN : P_FCC_LOSE;
3862 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3863 q_status_message(SM_ORDER, 3, 5,
3864 _("Fcc Failed!. No message saved."));
3865 dprint((1,
3866 "explicit fcc write failed!\n"));
3867 result |= P_FCC_LOSE;
3870 so_give(&lmc.so);
3873 if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
3875 * Encode child's result in hi-byte of
3876 * editor's result
3878 editor_result = ((result << 8) | COMP_GOTHUP);
3879 goto fake_hup;
3882 exit(result);
3885 if(ps_global->post->pid > 0){
3886 q_status_message(SM_ORDER, 3, 3,
3887 _("Message handed off for posting"));
3888 break; /* up to our child now */
3890 else{
3891 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3892 "Can't fork for send: %s",
3893 error_description(errno));
3894 if(ps_global->post->fcc)
3895 fs_give((void **) &ps_global->post->fcc);
3897 fs_give((void **) &ps_global->post);
3900 if(lmc.so) /* throw away unused store obj */
3901 so_give(&lmc.so);
3903 if(outgoing->message_id)
3904 fs_give((void **) &outgoing->message_id);
3906 outgoing->message_id = generate_message_id();
3908 continue; /* if we got here, there was a prob */
3910 #endif /* BACKGROUND_POST */
3912 /*------- Actually mail the message ------*/
3913 if(valid_addr == CA_OK
3914 && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
3915 char **alt_smtp = NULL;
3917 if(role && role->smtp){
3918 if(ps_global->FIX_SMTP_SERVER
3919 && ps_global->FIX_SMTP_SERVER[0])
3920 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3921 else
3922 alt_smtp = role->smtp;
3925 result |= (call_mailer(&header, *body, alt_smtp,
3926 call_mailer_flags,
3927 call_mailer_file_result,
3928 pipe_callback) > 0)
3929 ? P_MAIL_WIN : P_MAIL_LOSE;
3931 if(result & P_MAIL_LOSE)
3932 mark_address_failure_for_pico(&header);
3935 /*----- Was there an fcc involved? -----*/
3936 if(lmc.so){
3937 /*------ Write it if at least something worked ------*/
3938 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3939 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3940 && pine_rfc822_output(&header, *body, NULL, NULL))){
3941 char label[50];
3943 strncpy(label, "Fcc", sizeof(label));
3944 label[sizeof(label)-1] = '\0';
3945 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3946 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3947 label[sizeof(label)-1] = '\0';
3950 /*-- Now actually copy to fcc folder and close --*/
3951 result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
3952 F_ON(F_MARK_FCC_SEEN, ps_global)
3953 ? "\\SEEN" : NULL))
3954 ? P_FCC_WIN : P_FCC_LOSE;
3956 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3957 q_status_message(SM_ORDER,3,5,
3958 _("Fcc Failed!. No message saved."));
3959 dprint((1, "explicit fcc write failed!\n"));
3960 result |= P_FCC_LOSE;
3963 so_give(&lmc.so);
3966 /*----- Mail Post FAILED, back to composer -----*/
3967 if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
3968 dprint((1, "Send failed, continuing\n"));
3970 if(result & P_FCC_LOSE){
3972 * Find field entry associated with fcc, and start
3973 * composer on it...
3975 for(pf = header.local; pf && pf->name; pf = pf->next)
3976 if(pf->type == Fcc && HE(pf))
3977 HE(pf)->start_here = 1;
3979 q_status_message(SM_ORDER | SM_DING, 3, 3,
3980 pine_send_status(result, fcc,
3981 tmp_20k_buf, SIZEOF_20KBUF, NULL));
3984 if(outgoing->message_id)
3985 fs_give((void **) &outgoing->message_id);
3987 outgoing->message_id = generate_message_id();
3989 continue;
3993 * If message sent *completely* successfully, there's a
3994 * reply struct AND we're allowed to write back state, do it.
3995 * But also protect against shifted message numbers due
3996 * to new mail arrival. Since the number passed is based
3997 * on the real imap msg no, AND we're sure no expunge has
3998 * been done, just fix up the sorted number...
4000 update_answered_flags(reply);
4002 /*----- Signed, sealed, delivered! ------*/
4003 q_status_message(SM_ORDER, 0, 3,
4004 pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
4006 break; /* All's well, pop out of here */
4010 if(orig_so)
4011 so_give(&orig_so);
4013 if(fcc)
4014 fs_give((void **)&fcc);
4016 free_body_particulars(bp);
4018 free_attachment_list(&pbf->attachments);
4020 standard_picobuf_teardown(pbf);
4022 for(i=0; i < fixed_cnt; i++){
4023 if(pfields[i].textbuf)
4024 fs_give((void **)&pfields[i].textbuf);
4026 fs_give((void **)&pfields[i].name);
4029 if(lcc_addr)
4030 mail_free_address(&lcc_addr);
4032 if(nobody_addr)
4033 mail_free_address(&nobody_addr);
4035 free_prompts(header.custom);
4036 free_customs(header.custom);
4037 fs_give((void **)&pfields);
4038 free_headents(&headents);
4039 fs_give((void **)&sending_order);
4040 if(suggested_nntp_server)
4041 fs_give((void **)&suggested_nntp_server);
4042 if(title)
4043 fs_give((void **)&title);
4045 if(local_redraft_pos && local_redraft_pos != redraft_pos)
4046 free_redraft_pos(&local_redraft_pos);
4048 pbf = save_previous_pbuf;
4049 g_rolenick = NULL;
4051 dprint((4, "=== send returning ===\n"));
4056 * Check for subject in outgoing message.
4058 * Asks user whether to proceed with no subject.
4061 check_for_subject(METAENV *header)
4063 PINEFIELD *pf;
4064 int rv = CF_OK;
4066 for(pf = header->local; pf && pf->name; pf = pf->next)
4067 if(pf->type == Subject){
4068 if(pf->text && *pf->text && **pf->text)
4069 rv = CF_OK;
4070 else{
4071 if(want_to("No Subject, send anyway ",
4072 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
4073 rv = CF_OK;
4074 else
4075 rv = CF_MISSING;
4078 break;
4082 return(rv);
4087 * Check for fcc in outgoing message.
4089 * Asks user whether to proceed with no fcc.
4092 check_for_fcc(char *fcc)
4094 int rv = CF_OK;
4096 if(fcc && *fcc)
4097 rv = CF_OK;
4098 else{
4099 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
4100 rv = CF_OK;
4101 else
4102 rv = CF_MISSING;
4105 return(rv);
4110 * Confirm that the user wants to send to MAILER-DAEMON
4113 confirm_daemon_send(void)
4115 return(want_to("Really send this message to the MAILER-DAEMON",
4116 'n', 'n', NO_HELP, WT_NORM) == 'y');
4120 void
4121 free_prompts(PINEFIELD *head)
4123 PINEFIELD *pf;
4125 for(pf = head; pf && pf->name; pf = pf->next){
4126 if(HE(pf) && HE(pf)->prompt)
4127 fs_give((void **)& HE(pf)->prompt);
4133 postpone_prompt(void)
4135 static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
4136 {'f', 'f', "F", N_("Form Letter Folder")},
4137 {-1, 0, NULL, NULL} };
4139 return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
4140 pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
4145 * call__mailer_file_result - some results from call_mailer might be in a file.
4146 * dislplay that file.
4148 void
4149 call_mailer_file_result(char *filename, int style)
4151 if(filename){
4152 if(style & CM_BR_VERBOSE){
4153 display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
4155 else{
4156 display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
4161 void
4162 mark_address_failure_for_pico(METAENV *header)
4164 PINEFIELD *pf;
4165 ADDRESS *a;
4166 int error_count = 0;
4167 struct headerentry *last_he = NULL;
4169 for(pf = header->local; pf && pf->name; pf = pf->next)
4170 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
4171 for(a = *pf->addr; a != NULL; a = a->next)
4172 if(a->error != NULL
4173 && error_count++ < MAX_ADDR_ERROR
4174 && (HE(pf))){
4175 if(last_he) /* start last reported err */
4176 last_he->start_here = 0;
4178 (last_he = HE(pf))->start_here = 1;
4185 * This is specialized routine. It assumes that the only things that we
4186 * care about restoring are the body type, subtype, encoding and the
4187 * state of the charset parameter. It also assumes that if the charset
4188 * parameter exists when we save it, it won't be removed later.
4190 BODY_PARTICULARS_S *
4191 save_body_particulars(struct mail_bodystruct *body)
4193 BODY_PARTICULARS_S *bp;
4194 PARAMETER *pm;
4196 bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
4198 bp->type = body->type;
4199 bp->encoding = body->encoding;
4200 bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
4201 bp->parameter = body->parameter;
4202 for(pm = bp->parameter;
4203 pm && strucmp(pm->attribute, "charset") != 0;
4204 pm = pm->next)
4205 ;/* searching for possible charset parameter */
4207 if(pm){ /* found one */
4208 bp->had_csp = 1; /* saved body had charset parameter */
4209 bp->charset = pm->value ? cpystr(pm->value) : NULL;
4211 else{
4212 bp->had_csp = 0;
4213 bp->charset = NULL;
4216 return(bp);
4220 void
4221 reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
4223 body->type = bp->type;
4224 body->encoding = bp->encoding;
4225 if(body->subtype)
4226 fs_give((void **)&body->subtype);
4228 body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
4230 if(bp->parameter){
4231 PARAMETER *pm, *pm_prev = NULL;
4233 for(pm = body->parameter;
4234 pm && strucmp(pm->attribute, "charset") != 0;
4235 pm = pm->next)
4236 pm_prev = pm;
4238 if(pm){ /* body has charset parameter */
4239 if(bp->had_csp){ /* reset to what it used to be */
4240 if(pm->value)
4241 fs_give((void **)&pm->value);
4243 pm->value = bp->charset ? cpystr(bp->charset) : NULL;
4245 else{ /* remove charset parameter */
4246 if(pm_prev)
4247 pm_prev->next = pm->next;
4248 else
4249 body->parameter = pm->next;
4251 mail_free_body_parameter(&pm);
4254 else{
4255 if(bp->had_csp){
4257 * This can't happen because grope never removes
4258 * the charset parameter.
4260 q_status_message(SM_ORDER | SM_DING, 5, 5,
4261 "Programmer error: saved charset but no current charset param in pine_send");
4264 else{
4265 ok, still no parameter
4270 else{
4271 if(body->parameter)
4272 mail_free_body_parameter(&body->parameter);
4274 body->parameter = NULL;
4279 void
4280 free_body_particulars(BODY_PARTICULARS_S *bp)
4282 if(bp){
4283 if(bp->subtype)
4284 fs_give((void **)&bp->subtype);
4286 if(bp->charset)
4287 fs_give((void **)&bp->charset);
4289 fs_give((void **)&bp);
4294 /*----------------------------------------------------------------------
4295 Build a status message suitable for framing
4297 Returns: pointer to resulting buffer
4298 ---*/
4299 char *
4300 pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
4302 int avail = ps_global->ttyo->screen_cols - 2;
4303 int fixedneed, need, lenfcc;
4304 char *part1, *part2, *part3, *part4, *part5;
4305 char fbuf[MAILTMPLEN+1];
4307 part1 = (result & P_NEWS_WIN)
4308 ? "posted"
4309 : (result & P_NEWS_LOSE)
4310 ? "NOT POSTED"
4311 : "";
4312 part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
4313 && (result & P_FCC_BITS))
4314 ? ", "
4315 : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
4316 ? " and "
4317 : "";
4318 part3 = (result & P_MAIL_WIN)
4319 ? "sent"
4320 : (result & P_MAIL_LOSE)
4321 ? "NOT SENT"
4322 : "";
4323 part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
4324 ? " and "
4325 : "";
4326 part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
4327 ? "ONLY copied to "
4328 : (result & P_FCC_WIN)
4329 ? "copied to "
4330 : (result & P_FCC_LOSE)
4331 ? "NOT copied to "
4332 : "";
4333 lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
4335 fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
4336 strlen(part4) + strlen(part5);
4337 need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
4339 if(need > avail && fixedneed + 3 >= avail){
4340 /* dots on end of fixed, no fcc */
4341 snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
4342 part1, part2, part3, part4, part5);
4343 short_str(fbuf, buf, buflen, avail, EndDots);
4345 else if(need > avail){
4346 /* include whole fixed part, quotes and dots at end of fcc name */
4347 if(lenfcc > 0)
4348 lenfcc = MAX(1, lenfcc-(need-avail));
4350 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4351 part1, part2, part3, part4, part5,
4352 (result & P_FCC_BITS) ? "\"" : "",
4353 short_str((result & P_FCC_BITS) ? fcc_name : "",
4354 fbuf, sizeof(fbuf), lenfcc, FrontDots),
4355 (result & P_FCC_BITS) ? "\"" : "");
4357 else{
4358 /* whole thing */
4359 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4360 part1, part2, part3, part4, part5,
4361 (result & P_FCC_BITS) ? "\"" : "",
4362 (result & P_FCC_BITS) ? fcc_name : "",
4363 (result & P_FCC_BITS) ? "\"" : "");
4366 if(goodorbad)
4367 *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
4369 return(buf);
4372 /* Callback from Pico to set the conditions for Alpine to start a new thread
4375 void
4376 new_thread_on_blank_subject(void)
4378 ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
4383 /*----------------------------------------------------------------------
4384 Call back for pico to insert the specified message's text
4386 Args: n -- message number to format
4387 f -- function to use to output the formatted message
4390 Returns: returns msg number formatted on success, zero on error.
4391 ----*/
4392 long
4393 message_format_for_pico(long int n, int (*f) (int))
4395 ENVELOPE *e;
4396 BODY *b;
4397 char *old_quote = NULL;
4398 long rv = n;
4400 if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
4401 && (e = pine_mail_fetchstructure(ps_global->mail_stream,
4402 mn_m2raw(ps_global->msgmap, n), &b)))){
4403 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4404 flush_status_messages(0);
4405 return(0L);
4408 /* temporarily assign a new quote string */
4409 old_quote = pbf->quote_str;
4410 pbf->quote_str = reply_quote_str(e);
4412 /* build separator line */
4413 reply_delimiter(e, NULL, f);
4415 /* actually write message text */
4416 if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
4417 FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
4418 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4419 flush_status_messages(0);
4420 rv = 0L;
4423 fs_give((void **)&pbf->quote_str);
4424 pbf->quote_str = old_quote;
4425 return(rv);
4429 /*----------------------------------------------------------------------
4430 Call back for pico to prompt the user for exit confirmation
4432 Args: dflt -- default answer for confirmation prompt
4434 Returns: either NULL if the user accepts exit, or string containing
4435 reason why the user declined.
4436 ----*/
4438 send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
4439 char **result)
4441 int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
4442 int dsn_label = 0, fcc_label = 0, lparen;
4443 int flowing_label = 0, double_rad;
4444 char *rstr = NULL, *p, *lc, *optp;
4445 char dsn_string[30];
4446 void (*redraw)(void) = ps_global->redrawer;
4447 ESCKEY_S opts[18];
4448 struct filters {
4449 char *filter;
4450 int index;
4451 struct filters *prev, *next;
4452 } *filters = NULL, *fp;
4454 sending_filter_requested = NULL;
4455 call_mailer_flags = 0;
4456 background_requested = 0;
4457 flowing_requested = allow_flowed ? 1 : 0;
4458 lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
4459 if(priority_requested)
4460 fs_give((void **) &priority_requested);
4462 if(background_posting(FALSE)){
4463 if(result)
4464 *result = "Can't send while background posting. Use postpone.";
4466 return(1);
4469 if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
4470 if(result)
4471 *result = NULL;
4473 return(0);
4476 ps_global->redrawer = redraw_pico;
4478 if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
4479 (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
4482 * Build list of available filters...
4484 for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
4485 for(p = ps_global->VAR_SEND_FILTER[i];
4486 *p && !isspace((unsigned char)*p); p++)
4489 c = *p;
4490 *p = '\0';
4491 if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
4492 && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
4493 *p = c;
4494 continue;
4497 fp = (struct filters *)fs_get(sizeof(struct filters));
4498 fp->index = i;
4499 if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
4500 fp->filter = cpystr(lc);
4502 else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
4503 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
4504 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4505 fp->filter = cpystr(tmp_20k_buf);
4507 else
4508 fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
4510 *p = c;
4512 if(filters){
4513 fp->next = filters;
4514 fp->prev = filters->prev;
4515 fp->prev->next = filters->prev = fp;
4517 else{
4518 filters = (struct filters *)fs_get(sizeof(struct filters));
4519 filters->index = -1;
4520 filters->filter = NULL;
4521 filters->next = filters->prev = fp;
4522 fp->next = fp->prev = filters;
4526 i = 0;
4527 opts[i].ch = 'y';
4528 opts[i].rval = 'y';
4529 opts[i].name = "Y";
4530 opts[i++].label = N_("Yes");
4532 opts[i].ch = 'n';
4533 opts[i].rval = 'n';
4534 opts[i].name = "N";
4535 opts[i++].label = N_("No");
4537 if(filters){
4538 /* set global_filter_pointer to desired filter or NULL if none */
4539 /* prepare two keymenu slots for selecting filter */
4540 opts[i].ch = ctrl('P');
4541 opts[i].rval = 10;
4542 opts[i].name = "^P";
4543 opts[i++].label = N_("Prev Filter");
4545 opts[i].ch = ctrl('N');
4546 opts[i].rval = 11;
4547 opts[i].name = "^N";
4548 opts[i++].label = N_("Next Filter");
4550 if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
4551 filters = filters->next;
4554 if(F_ON(F_VERBOSE_POST, ps_global)){
4555 /* setup keymenu slot to toggle verbose mode */
4556 opts[i].ch = ctrl('W');
4557 opts[i].rval = 12;
4558 opts[i].name = "^W";
4559 verbose_label = i++;
4562 if(allow_flowed){
4563 /* setup keymenu slot to toggle flowed mode */
4564 opts[i].ch = ctrl('V');
4565 opts[i].rval = 22;
4566 opts[i].name = "^V";
4567 flowing_label = i++;
4568 flowing_requested = 1;
4571 if(F_ON(F_NO_FCC_ATTACH, ps_global)){
4572 /* setup keymenu slot to toggle attacment on fcc */
4573 opts[i].ch = ctrl('F');
4574 opts[i].rval = 21;
4575 opts[i].name = "^F";
4576 fcc_label = i++;
4579 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4580 if(F_ON(F_BACKGROUND_POST, ps_global)){
4581 opts[i].ch = ctrl('R');
4582 opts[i].rval = 15;
4583 opts[i].name = "^R";
4584 bg_label = i++;
4586 #endif
4588 opts[i].ch = 'p';
4589 opts[i].rval = 'p';
4590 opts[i].name = "P";
4591 opts[i++].label = N_("Priority");
4593 #ifdef SMIME
4594 if(F_OFF(F_DONT_DO_SMIME, ps_global)){
4595 opts[i].ch = 'e';
4596 opts[i].rval = 'e';
4597 opts[i].name = "E";
4598 opts[i++].label = "Encrypt";
4600 opts[i].ch = 'g';
4601 opts[i].rval = 'g';
4602 opts[i].name = "G";
4603 opts[i++].label = "Sign";
4605 if(ps_global->smime){
4606 ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
4607 ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
4610 #endif
4612 double_rad = i;
4614 if(F_ON(F_DSN, ps_global)){
4615 /* setup keymenu slots to toggle dsn bits */
4616 opts[i].ch = 'd';
4617 opts[i].rval = 'd';
4618 opts[i].name = "D";
4619 opts[i].label = N_("DSNOpts");
4620 dsn_label = i++;
4621 opts[i].ch = -2;
4622 opts[i].rval = 's';
4623 opts[i].name = "S";
4624 opts[i++].label = "";
4625 opts[i].ch = -2;
4626 opts[i].rval = 'x';
4627 opts[i].name = "X";
4628 opts[i++].label = "";
4629 opts[i].ch = -2;
4630 opts[i].rval = 'h';
4631 opts[i].name = "H";
4632 opts[i++].label = "";
4635 if(filters){
4636 opts[i].ch = KEY_UP;
4637 opts[i].rval = 10;
4638 opts[i].name = "";
4639 opts[i++].label = "";
4641 opts[i].ch = KEY_DOWN;
4642 opts[i].rval = 11;
4643 opts[i].name = "";
4644 opts[i++].label = "";
4647 opts[i].ch = -1;
4649 fix_windsize(ps_global);
4651 while(1){
4652 if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
4653 *p = '\0';
4654 else
4655 p = NULL;
4657 lparen = 0;
4658 strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
4659 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4660 optp = tmp_20k_buf + strlen(tmp_20k_buf);
4662 if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
4663 sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4665 if(allow_flowed && !flowing_requested){
4666 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4667 if(!lparen){
4668 *optp++ = ' ';
4669 *optp++ = '(';
4670 lparen++;
4672 else
4673 *optp++ = ' ';
4676 sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4679 if(filters){
4680 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4681 if(!lparen){
4682 *optp++ = ' ';
4683 *optp++ = '(';
4684 lparen++;
4686 else{
4687 *optp++ = ',';
4688 *optp++ = ' ';
4692 if(filters->filter){
4693 sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4694 sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4695 if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
4696 *optp++ = '\"';
4698 else
4699 sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4702 if((call_mailer_flags & CM_VERBOSE) || background_requested){
4703 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4704 if(!lparen){
4705 *optp++ = ' ';
4706 *optp++ = '(';
4707 lparen++;
4709 else
4710 *optp++ = ' ';
4713 sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4714 if(call_mailer_flags & CM_VERBOSE)
4715 sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4717 if(background_requested)
4718 sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4720 sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4723 if(g_rolenick && !(he && he[N_FROM].dirty)){
4724 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4725 if(!lparen){
4726 *optp++ = ' ';
4727 *optp++ = '(';
4728 lparen++;
4730 else
4731 *optp++ = ' ';
4734 sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4735 sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4736 sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4739 if(call_mailer_flags & CM_DSN_SHOW){
4740 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4741 if(!lparen){
4742 *optp++ = ' ';
4743 *optp++ = '(';
4744 lparen++;
4746 else{
4747 *optp++ = ',';
4748 *optp++ = ' ';
4752 sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4755 #ifdef SMIME
4756 if(ps_global->smime && ps_global->smime->do_encrypt){
4757 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4758 if(!lparen){
4759 *optp++ = ' ';
4760 *optp++ = '(';
4761 lparen++;
4763 else{
4764 *optp++ = ',';
4765 *optp++ = ' ';
4769 sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4772 if(ps_global->smime && ps_global->smime->do_sign){
4773 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4774 if(!lparen){
4775 *optp++ = ' ';
4776 *optp++ = '(';
4777 lparen++;
4779 else{
4780 *optp++ = ',';
4781 *optp++ = ' ';
4785 sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4787 #endif
4789 if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
4790 *optp++ = ')';
4792 sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4793 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4795 if(p)
4796 *p = ' ';
4798 if(flowing_label)
4799 opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
4801 if(verbose_label)
4802 opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
4804 if(bg_label)
4805 opts[bg_label].label = background_requested
4806 ? N_("Foreground") : N_("Background");
4808 if(fcc_label)
4809 opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
4810 : N_("No Fcc Atmts ");
4812 if(F_ON(F_DSN, ps_global)){
4813 if(call_mailer_flags & CM_DSN_SHOW){
4814 opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
4815 ? N_("NoDelay") : N_("Delay");
4816 opts[dsn_label+1].ch = 's';
4817 opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
4818 ? N_("NoSuccess") : N_("Success");
4819 opts[dsn_label+2].ch = 'x';
4820 opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
4821 ? N_("ErrRets") : N_("NoErrRets");
4822 opts[dsn_label+3].ch = 'h';
4823 opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
4824 ? N_("RetHdrs") : N_("RetFull");
4828 if(double_rad +
4829 ((call_mailer_flags & CM_DSN_SHOW)
4830 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
4831 rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4832 'y', 'z',
4833 (F_ON(F_DSN, ps_global) && allow_flowed)
4834 ? h_send_prompt_dsn_flowed :
4835 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4836 allow_flowed ? h_send_prompt_flowed :
4837 h_send_prompt,
4838 RB_NORM);
4839 else
4840 rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4841 'y', 'z',
4842 (double_rad +
4843 ((call_mailer_flags & CM_DSN_SHOW)
4844 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
4845 ? NO_HELP :
4846 (F_ON(F_DSN, ps_global) && allow_flowed)
4847 ? h_send_prompt_dsn_flowed :
4848 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4849 allow_flowed ? h_send_prompt_flowed :
4850 h_send_prompt,
4851 RB_NORM);
4853 if(rv == 'y'){ /* user ACCEPTS! */
4854 break;
4856 else if(rv == 'n'){ /* Declined! */
4857 rstr = _("No Message Sent");
4858 break;
4860 else if(rv == 'z'){ /* Cancelled! */
4861 rstr = _("Send Cancelled");
4862 break;
4864 else if(rv == 10){ /* PREVIOUS filter */
4865 filters = filters->prev;
4867 else if(rv == 11){ /* NEXT filter */
4868 filters = filters->next;
4870 else if(rv == 21){
4871 lmc.text_only = !lmc.text_only;
4873 else if(rv == 12){ /* flip verbose bit */
4874 if(call_mailer_flags & CM_VERBOSE)
4875 call_mailer_flags &= ~CM_VERBOSE;
4876 else
4877 call_mailer_flags |= CM_VERBOSE;
4879 if((call_mailer_flags & CM_VERBOSE) && background_requested)
4880 background_requested = 0;
4882 else if(rv == 22){ /* flip flowing bit */
4883 flowing_requested = !flowing_requested;
4885 else if(rv == 15){
4886 if((background_requested = !background_requested)
4887 && (call_mailer_flags & CM_VERBOSE))
4888 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
4890 else if(call_mailer_flags & CM_DSN_SHOW){
4891 if(rv == 's'){ /* flip success bit */
4892 call_mailer_flags ^= CM_DSN_SUCCESS;
4893 /* turn off related bits */
4894 if(call_mailer_flags & CM_DSN_SUCCESS)
4895 call_mailer_flags &= ~(CM_DSN_NEVER);
4897 else if(rv == 'd'){ /* flip delay bit */
4898 call_mailer_flags ^= CM_DSN_DELAY;
4899 /* turn off related bits */
4900 if(call_mailer_flags & CM_DSN_DELAY)
4901 call_mailer_flags &= ~(CM_DSN_NEVER);
4903 else if(rv == 'x'){ /* flip never bit */
4904 call_mailer_flags ^= CM_DSN_NEVER;
4905 /* turn off related bits */
4906 if(call_mailer_flags & CM_DSN_NEVER)
4907 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
4909 else if(rv == 'h'){ /* flip full bit */
4910 call_mailer_flags ^= CM_DSN_FULL;
4913 else if(rv == 'd'){ /* show dsn options */
4915 * When you turn on DSN, the default is to notify on
4916 * failure, success, or delay; and to return the whole
4917 * body on failure.
4919 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
4921 else if(rv == 'p'){ /* choose X-Priority */
4922 char *prio;
4924 prio = choose_a_priority(priority_requested);
4925 if((ps_global->redrawer = redraw_pico) != NULL){
4926 (*ps_global->redrawer)();
4927 fix_windsize(ps_global);
4930 if(prio){
4931 if(priority_requested)
4932 fs_give((void **) &priority_requested);
4934 if(prio[0])
4935 priority_requested = prio;
4936 else
4937 fs_give((void **) &prio);
4940 #ifdef SMIME
4941 else if(rv=='e'){
4942 if(ps_global->smime)
4943 ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
4945 else if(rv=='g'){
4946 if(ps_global->smime)
4947 ps_global->smime->do_sign = !ps_global->smime->do_sign;
4949 #endif
4951 snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
4952 (call_mailer_flags & CM_DSN_NEVER)
4953 ? "Never" : "F",
4954 (call_mailer_flags & CM_DSN_DELAY)
4955 ? "D" : "",
4956 (call_mailer_flags & CM_DSN_SUCCESS)
4957 ? "S" : "",
4958 (call_mailer_flags & CM_DSN_NEVER)
4959 ? ""
4960 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
4961 : "-Hdrs");
4962 dsn_string[sizeof(dsn_string)-1] = '\0';
4965 /* remember selection */
4966 if(filters && filters->index > -1)
4967 sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
4969 if(filters){
4970 filters->prev->next = NULL; /* tie off list */
4971 while(filters){ /* then free it */
4972 fp = filters->next;
4973 if(filters->filter)
4974 fs_give((void **)&filters->filter);
4976 fs_give((void **)&filters);
4977 filters = fp;
4981 if(old_suspend)
4982 (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
4984 if(result)
4985 *result = rstr;
4987 ps_global->redrawer = redraw;
4989 return((rstr == NULL) ? 0 : 1);
4994 * Allow user to choose a priority for sending.
4996 * Returns an allocated priority on success, NULL otherwise.
4998 char *
4999 choose_a_priority(char *default_val)
5001 char *choice = NULL;
5002 char **priority_list, **lp;
5003 char *starting_val = NULL;
5004 char *none;
5005 int i, cnt;
5006 PRIORITY_S *p;
5008 for(cnt = 0, p = priorities; p && p->desc; p++)
5009 cnt++;
5011 cnt++; /* for NONE entry */
5012 lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
5013 memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
5015 for(i = 0, p = priorities; p && p->desc; p++){
5016 *lp = cpystr(p->desc);
5017 if(default_val && !strcmp(default_val, p->desc))
5018 starting_val = (*lp);
5020 lp++;
5023 none = _("NONE - No X-Priority header included");
5024 *lp = cpystr(none);
5025 if(!starting_val)
5026 starting_val = (*lp);
5028 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5029 TRANSLATORS: Print something1 using something2.
5030 "priorities" is something1 */
5031 choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
5032 _("priorities"), h_select_priority_screen,
5033 _("HELP FOR SELECTING A PRIORITY"),
5034 starting_val);
5036 if(!choice)
5037 q_status_message(SM_ORDER, 1, 4, _("No change"));
5038 else if(!strcmp(choice, none))
5039 choice[0] = '\0';
5041 free_list_array(&priority_list);
5043 return(choice);
5048 dont_flow_this_time(void)
5050 return(flowing_requested ? 0 : 1);
5054 /*----------------------------------------------------------------------
5055 Call back for pico to display mime type of attachment
5057 Args: file -- filename being attached
5059 Returns: returns 1 on success (message queued), zero otherwise (don't know
5060 type so nothing queued).
5061 ----*/
5063 mime_type_for_pico(char *file)
5065 BODY *body;
5066 int rv;
5067 void *file_contents;
5069 body = mail_newbody();
5070 body->type = TYPEOTHER;
5071 body->encoding = ENCOTHER;
5073 /* don't know where the cursor's been, reset it */
5074 clear_cursor_pos();
5075 if(!set_mime_type_by_extension(body, file)){
5076 if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5077 body->contents.text.data = file_contents;
5078 set_mime_type_by_grope(body);
5082 if(body->type != TYPEOTHER){
5083 rv = 1;
5084 q_status_message3(SM_ORDER, 0, 3,
5085 _("File %s attached as type %s/%s"), file,
5086 body_types[body->type],
5087 body->subtype ? body->subtype : rfc822_default_subtype(body->type));
5089 else
5090 rv = 0;
5092 pine_free_body(&body);
5093 return(rv);
5097 /*----------------------------------------------------------------------
5098 Call back for pico to receive an uploaded message
5100 Args: fname -- name for uploaded file (empty if they want us to assign it)
5101 size -- pointer to long to hold the attachment's size
5103 Notes: the attachment is uploaded to a temp file, and
5105 Returns: TRUE on success, FALSE otherwise
5106 ----*/
5108 upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
5110 char cmd[MAXPATH+1], *fnp = NULL;
5111 char *locale_name = NULL;
5112 long l;
5113 PIPE_S *syspipe;
5115 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5116 fname ? fname : "<NO FILE>"));
5118 if(!fname) /* no place for file name */
5119 return(0);
5121 if(!*fname){ /* caller wants temp file */
5122 if((fnp = temp_nam(NULL, "pu")) != NULL){
5123 strncpy(fname, fnp, fnlen);
5124 fname[fnlen-1] = '\0';
5125 our_unlink(fnp);
5126 fs_give((void **)&fnp);
5129 else
5130 locale_name = convert_to_locale(fname);
5132 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
5133 ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
5134 if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
5135 0, pipe_callback, pipe_report_error)) != NULL){
5136 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
5137 if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
5138 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5139 "Error determining size of %s: %s", fname,
5140 fnp = error_description(errno));
5141 dprint((1,
5142 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5143 cmd ? cmd : "?",
5144 fname ? fname : "?",
5145 fnp ? fnp : "?"));
5147 else if(size)
5148 *size = l;
5150 if(locale_name)
5151 fs_give((void **) &locale_name);
5153 return(l >= 0);
5155 else
5156 q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
5158 if(locale_name)
5159 fs_give((void **) &locale_name);
5161 return(0);
5165 char *
5166 cancel_for_pico(void (*redraw_pico)(void))
5168 int rv;
5169 char *rstr = NULL;
5170 char *prompt =
5171 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5172 void (*redraw)(void) = ps_global->redrawer;
5173 static ESCKEY_S opts[] = {
5174 {'c', 'c', "C", N_("Confirm")},
5175 {'n', 'n', "N", N_("No")},
5176 {'y', 'y', "", ""},
5177 {-1, 0, NULL, NULL}
5180 ps_global->redrawer = redraw_pico;
5181 fix_windsize(ps_global);
5183 while(1){
5184 rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
5185 'n', 'x', h_confirm_cancel, RB_NORM);
5186 if(rv == 'c'){ /* user ACCEPTS! */
5187 rstr = "";
5188 break;
5190 else if(rv == 'y'){
5191 q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
5192 display_message('x');
5194 else
5195 break;
5198 ps_global->redrawer = redraw;
5199 return(rstr);
5203 /*----------------------------------------------------------------------
5204 Pass the first text segment of the message thru the "send filter"
5206 Args: body pointer and address for storage object of old data
5208 Returns: returns 1 on success, zero on error.
5209 ----*/
5211 filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
5212 STORE_S **old, METAENV *header)
5214 char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
5215 int key = 0, include_hdrs = 0;
5216 gf_io_t gc, pc;
5217 STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
5218 ? &body->nested.part->body.contents.text.data
5219 : &body->contents.text.data),
5220 *tmp_so = NULL, *tmpf_so,
5221 *save_local_so, *readthis_so, *our_tmpf_so = NULL;
5222 #define DO_HEADERS 1
5224 if(fcmd
5225 && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
5226 &key, &include_hdrs, NULL))){
5227 if(tmpf){
5229 * We need WRITE_TO_LOCALE here because the user is going to
5230 * be operating on tmpf. We need to change it back after they
5231 * are done.
5233 if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
5234 if(key){
5235 so_puts(tmpf_so, filter_session_key());
5236 so_puts(tmpf_so, NEWLINE);
5240 * If the headers are wanted for filtering, we can just
5241 * stick them in the tmpf file that is already there before
5242 * putting the body in.
5244 if(include_hdrs){
5245 save_local_so = lmc.so;
5246 lmc.so = tmpf_so; /* write it to tmpf_so */
5247 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5248 pine_rfc822_header(header, body, NULL, NULL);
5249 lmc.so = save_local_so;
5252 so_seek(*so, 0L, 0);
5253 gf_set_so_readc(&gc, *so);
5254 gf_set_so_writec(&pc, tmpf_so);
5255 gf_filter_init();
5256 errstr = gf_pipe(gc, pc);
5257 gf_clear_so_readc(*so);
5258 gf_clear_so_writec(tmpf_so);
5259 so_give(&tmpf_so);
5261 else
5262 errstr = "Can't create space for filter temporary file.";
5264 else if(include_hdrs){
5266 * Gf_filter wants a single storage object to read from.
5267 * If headers are wanted for filtering we'll have to put them
5268 * and the body into a temp file first and then use that
5269 * as the storage object for gf_filter.
5270 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5271 * takes care of that.
5273 if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
5274 /* put headers in our_tmpf_so */
5275 save_local_so = lmc.so;
5276 lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
5277 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5278 pine_rfc822_header(header, body, NULL, NULL);
5279 lmc.so = save_local_so;
5281 /* put body in our_tmpf_so */
5282 so_seek(*so, 0L, 0);
5283 gf_set_so_readc(&gc, *so);
5284 gf_set_so_writec(&pc, our_tmpf_so);
5285 gf_filter_init();
5286 errstr = gf_pipe(gc, pc);
5287 gf_clear_so_readc(*so);
5288 gf_clear_so_writec(our_tmpf_so);
5290 /* tell gf_filter to read from our_tmpf_so instead of *so */
5291 readthis_so = our_tmpf_so;
5293 else
5294 errstr = "Can't create space for temporary file.";
5296 else
5297 readthis_so = *so;
5299 if(!errstr){
5300 if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
5301 gf_set_so_writec(&pc, tmp_so);
5302 ps_global->mangled_screen = 1;
5303 suspend_busy_cue();
5304 ClearScreen();
5305 fflush(stdout);
5306 if(tmpf){
5307 PIPE_S *fpipe;
5309 if((fpipe = open_system_pipe(cmd, NULL, NULL,
5310 PIPE_NOSHELL | PIPE_RESET,
5311 0, pipe_callback, pipe_report_error)) != NULL){
5312 if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
5314 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5315 if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5316 gf_set_so_readc(&gc, tmpf_so);
5317 gf_filter_init();
5318 errstr = gf_pipe(gc, pc);
5319 gf_clear_so_readc(tmpf_so);
5320 so_give(&tmpf_so);
5322 else
5323 errstr = "Can't open temp file filter wrote.";
5325 else
5326 errstr = "Filter command returned error.";
5328 else
5329 errstr = "Can't exec filter text.";
5331 else
5332 errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
5333 readthis_so, pc, NULL, 0, 0,
5334 pipe_callback);
5336 if(our_tmpf_so)
5337 so_give(&our_tmpf_so);
5339 gf_clear_so_writec(tmp_so);
5341 #ifdef _WINDOWS
5342 if(!errstr){
5344 * Can't really be using stdout, so don't print message that
5345 * gets printed in the else. Ideally the program being called
5346 * will wait after showing the message, we might want to look
5347 * into doing the waiting in console based apps... or not.
5349 #else
5350 if(errstr){
5351 unsigned long ch;
5353 fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
5354 fflush(stdout);
5355 while((ch = read_char(300)) != ctrl('M')
5356 && ch != NO_OP_IDLE)
5357 putchar(BELL);
5359 else{
5360 #endif /* _WINDOWS */
5361 BODY *b = (body->type == TYPEMULTIPART)
5362 ? &body->nested.part->body : body;
5364 *old = *so; /* save old so */
5365 *so = tmp_so; /* return new one */
5366 (*so)->attr = copy_parameters((*old)->attr);
5369 * If the command said it would return new MIME
5370 * mime type data, check it out...
5372 if(mtf){
5373 char buf[MAILTMPLEN], *s;
5374 FILE *fp;
5376 if((fp = our_fopen(mtf, "rb")) != NULL){
5377 if(fgets(buf, sizeof(buf), fp)
5378 && !struncmp(buf, "content-", 8)
5379 && (s = strchr(buf+8, ':'))){
5380 BODY *nb = mail_newbody();
5382 for(*s++ = '\0'; *s == ' '; s++)
5385 rfc822_parse_content_header(nb,
5386 (char *) ucase((unsigned char *) buf+8),s);
5387 if(nb->type == TYPETEXT
5388 && nb->subtype
5389 && (!b->subtype
5390 || strucmp(b->subtype, nb->subtype))){
5391 if(b->subtype)
5392 fs_give((void **) &b->subtype);
5394 b->subtype = nb->subtype;
5395 nb->subtype = NULL;
5397 mail_free_body_parameter(&b->parameter);
5398 b->parameter = nb->parameter;
5399 nb->parameter = NULL;
5400 mail_free_body_parameter(&nb->parameter);
5403 mail_free_body(&nb);
5406 fclose(fp);
5411 * Reevaluate the encoding in case form's changed...
5413 b->encoding = ENCOTHER;
5414 set_mime_type_by_grope(b);
5417 ClearScreen();
5418 resume_busy_cue(0);
5420 else
5421 errstr = "Can't create space for filtered text.";
5424 fs_give((void **)&cmd);
5426 else
5427 return(0);
5429 if(tmpf){
5430 our_unlink(tmpf);
5431 fs_give((void **)&tmpf);
5434 if(mtf){
5435 our_unlink(mtf);
5436 fs_give((void **) &mtf);
5439 if(resultf){
5440 if(name_file_size(resultf) > 0L)
5441 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
5442 our_unlink(resultf);
5443 fs_give((void **)&resultf);
5445 else if(errstr){
5446 if(tmp_so)
5447 so_give(&tmp_so);
5449 q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
5450 errstr);
5451 dprint((1, "Filter FAILED: %s\n",
5452 errstr ? errstr : "?"));
5455 return(errstr == NULL);
5459 /*----------------------------------------------------------------------
5460 Copy the newsgroup name of the given mailbox into the given buffer
5462 Args:
5464 Returns:
5465 ----*/
5466 void
5467 pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
5469 NETMBX mb;
5471 if(*mailbox == '#'){ /* Strip the leading "#news." */
5472 strncpy(group_name, mailbox + 6, len-1);
5473 group_name[len-1] = '\0';
5475 else if(mail_valid_net_parse(mailbox, &mb)){
5476 pine_send_newsgroup_name(mb.mailbox, group_name, len);
5478 else
5479 *group_name = '\0';
5483 /*----------------------------------------------------------------------
5484 Generate and send a message back to the pine development team
5486 Args: none
5488 Returns: none
5489 ----*/
5490 void
5491 phone_home(char *addr)
5493 char tmp[MAX_ADDRESS];
5494 ENVELOPE *outgoing;
5495 BODY *body;
5497 outgoing = mail_newenvelope();
5498 if(!addr || !strindex(addr, '@')){
5499 snprintf(addr = tmp, sizeof(tmp), "alpine%s@%s", PHONE_HOME_VERSION, PHONE_HOME_HOST);
5500 tmp[sizeof(tmp)-1] = '\0';
5503 rfc822_parse_adrlist(&outgoing->to, addr, ps_global->maildomain);
5505 outgoing->message_id = generate_message_id();
5506 outgoing->subject = cpystr("Document Request");
5507 outgoing->from = phone_home_from();
5509 body = mail_newbody();
5510 body->type = TYPETEXT;
5512 if((body->contents.text.data = (void *)so_get(PicoText,NULL,EDIT_ACCESS)) != NULL){
5513 so_puts((STORE_S *)body->contents.text.data, "Document request: ");
5514 so_puts((STORE_S *)body->contents.text.data, "Alpine-");
5515 so_puts((STORE_S *)body->contents.text.data, ALPINE_VERSION);
5516 if(ps_global->first_time_user)
5517 so_puts((STORE_S *)body->contents.text.data, " for New Users");
5519 if(ps_global->VAR_INBOX_PATH && ps_global->VAR_INBOX_PATH[0] == '{')
5520 so_puts((STORE_S *)body->contents.text.data, " and IMAP");
5522 if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
5523 && ps_global->VAR_NNTP_SERVER[0][0])
5524 so_puts((STORE_S *)body->contents.text.data, " and NNTP");
5526 (void) pine_simple_send(outgoing, &body, NULL,NULL,NULL,NULL, SS_NULLRP);
5528 q_status_message(SM_ORDER, 1, 3, "Thanks for being counted!");
5530 else
5531 q_status_message(SM_ORDER | SM_DING, 3, 4,
5532 "Problem creating space for message text.");
5534 mail_free_envelope(&outgoing);
5535 pine_free_body(&body);
5540 /*----------------------------------------------------------------------
5541 Set up fields for passing to pico. Assumes first text part is
5542 intended to be passed along for editing, and is in the form of
5543 of a storage object brought into existence sometime before pico_send().
5544 -----*/
5545 void
5546 outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
5547 PATMT **pico_a, int from_bounce)
5549 PINEFIELD *pf;
5552 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5553 * is guaranteed to be of type PicoText!
5555 if(bod->type == TYPETEXT){
5556 *text = so_text((STORE_S *) bod->contents.text.data);
5558 /* mark storage object as user edited */
5559 if(!from_bounce)
5560 (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
5562 else if(bod->type == TYPEMULTIPART){
5563 PART *part;
5564 PATMT **ppa;
5565 char *type, *name, *p;
5566 int name_l;
5569 * We used to jump out the window if the first part wasn't text,
5570 * but that may not be the case when bouncing a message with
5571 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5572 * contents of the first part to send is still ALWAYS in a
5573 * PicoText storage object, *AND* if that object doesn't contain
5574 * data of type text, then it must contain THE ENCODED NON-TEXT
5575 * DATA of the piece being sent.
5577 * It's up to the programmer to make sure that such a message is
5578 * sent via pine_simple_send and never get to the composer via
5579 * pine_send.
5581 * Make sense?
5583 *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
5585 /* mark storage object as user edited */
5586 if(!from_bounce)
5587 (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
5590 * If we already had a list, blast it now, so we can build a new
5591 * attachment list that reflects what's really there...
5593 if(pico_a){
5594 free_attachment_list(pico_a);
5597 /* Simplifyihg assumption #28e. (see cross reference)
5598 All parts in the body passed in here that are not already
5599 in the attachments list are added to the end of the attachments
5600 list. Attachment items not in the body list will be taken care
5601 of in strings2outgoing, but they are unlikey to occur
5604 for(part = bod->nested.part->next; part != NULL; part = part->next) {
5605 /* Already in list? */
5606 for(ppa = pico_a;
5607 *ppa && strcmp((*ppa)->id, part->body.id);
5608 ppa = &(*ppa)->next)
5611 if(!*ppa){ /* Not in the list! append it... */
5612 *ppa = (PATMT *)fs_get(sizeof(PATMT));
5614 if(part->body.description){
5615 char *p;
5616 size_t len;
5618 len = 4*strlen(part->body.description)+1;
5619 p = (char *)fs_get(len*sizeof(char));
5620 if(rfc1522_decode_to_utf8((unsigned char *)p,
5621 len, part->body.description) == (unsigned char *) p){
5622 (*ppa)->description = p;
5624 else{
5625 fs_give((void **)&p);
5626 (*ppa)->description = cpystr(part->body.description);
5629 else
5630 (*ppa)->description = cpystr("");
5632 type = type_desc(part->body.type, part->body.subtype,
5633 part->body.parameter, NULL, 0);
5636 * If we can find a "name" parm, display that too...
5638 if((name = parameter_val(part->body.parameter, "name")) != NULL){
5639 /* Convert any [ or ]'s the name contained */
5640 for(p = name; *p ; p++)
5641 if(*p == '[')
5642 *p = '(';
5643 else if(*p == ']')
5644 *p = ')';
5646 name_l = p - name;
5648 else
5649 name_l = 0;
5651 (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
5653 snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
5654 name ? ": " : "", name ? name : "");
5655 (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
5657 if(name)
5658 fs_give((void **) &name);
5660 (*ppa)->flags = A_FLIT;
5661 (*ppa)->size = cpystr(byte_string(
5662 send_body_size(&part->body)));
5663 if(!part->body.id)
5664 part->body.id = generate_message_id();
5666 (*ppa)->id = cpystr(part->body.id);
5667 (*ppa)->next = NULL;
5674 /*------------------------------------------------------------------
5675 Malloc strings to pass to composer editor because it expects
5676 such strings so it can realloc them
5677 -----------------------------------------------------------------*/
5679 * turn any address fields into text strings
5682 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5683 * NOT to be RFC1522 decoded. Said differently, they're understood
5684 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5685 * original charset tagging as far into the compose/send pipe as
5686 * we can.
5688 for(pf = header->local; pf && pf->name; pf = pf->next)
5689 if(pf->canedit)
5690 switch(pf->type){
5691 case Address :
5692 if(pf->addr){
5693 char *p, *t, *u;
5694 long l;
5696 pf->scratch = addr_list_string(*pf->addr, NULL, 1);
5699 * Scan for and fix-up patently bogus fields.
5701 * NOTE: collaboration with this code and what's done in
5702 * reply.c:reply_cp_addr to package up the bogus stuff
5703 * is required.
5705 for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
5706 for(t = p; ; t--)
5707 if(*t == '&'){ /* find "leading" token */
5708 int replacelen;
5711 * Rfc822_cat has been changed so that it now quotes
5712 * this sometimes. So we have to look out for quotes
5713 * which confuse the decoder. It was only quoting
5714 * because we were putting \r \n in the input, I think.
5716 if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
5717 t[-1] = p[-1] = ' ';
5719 *t++ = ' '; /* replace token */
5720 *p = '\0'; /* tie off string */
5721 u = rfc822_base64((unsigned char *) t,
5722 (unsigned long) strlen(t),
5723 (unsigned long *) &l);
5724 if(!u)
5725 u = "";
5727 replacelen = strlen(t);
5728 *p = '@'; /* restore 'p' */
5729 rplstr(p, strlen(p), 12, ""); /* clear special token */
5730 rplstr(t, strlen(u)-replacelen+1, replacelen, u);
5731 if(u)
5732 fs_give((void **) &u);
5734 if(HE(pf))
5735 HE(pf)->start_here = 1;
5737 break;
5739 else if(t == pf->scratch)
5740 break;
5742 removing_leading_white_space(pf->scratch);
5743 if(pf->scratch){
5744 size_t l;
5747 * Replace control characters with ^C notation, unless
5748 * some conditions are met (see istrncpy).
5749 * If user doesn't edit, then we switch back to the
5750 * original version. If user does edit, then all bets
5751 * are off.
5753 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5754 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5755 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5756 fs_give((void **)&pf->scratch);
5757 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5760 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5764 break;
5766 case Subject :
5767 if(pf->text){
5768 char *p, *src;
5769 size_t len;
5771 src = pf->scratch ? pf->scratch
5772 : (*pf->text) ? *pf->text : "";
5774 len = 4*strlen(src)+1;
5775 p = (char *)fs_get(len * sizeof(char));
5776 if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
5777 if(pf->scratch)
5778 fs_give((void **)&pf->scratch);
5780 pf->scratch = p;
5782 else{
5783 fs_give((void **)&p);
5784 if(!pf->scratch)
5785 pf->scratch = cpystr(src);
5788 if(pf->scratch){
5789 size_t l;
5792 * Replace control characters with ^C notation, unless
5793 * some conditions are met (see istrncpy).
5794 * If user doesn't edit, then we switch back to the
5795 * original version. If user does edit, then all bets
5796 * are off.
5798 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5799 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5800 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5801 fs_give((void **)&pf->scratch);
5802 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5805 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5809 break;
5811 default :
5812 break;
5817 /*----------------------------------------------------------------------
5818 Restore fields returned from pico to form useful to sending
5819 routines.
5820 -----*/
5821 void
5822 strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
5824 PINEFIELD *pf;
5825 int we_cancel = 0;
5827 we_cancel = busy_cue(NULL, NULL, 1);
5830 * turn any local address strings into address lists
5832 for(pf = header->local; pf && pf->name; pf = pf->next)
5833 if(pf->scratch){
5834 char *the_address = NULL;
5836 switch(pf->type){
5837 case Address :
5838 removing_trailing_white_space(pf->scratch);
5840 if((the_address || *pf->scratch) && pf->addr){
5841 ADDRESS *new_addr = NULL;
5842 static char *fakedomain = "@";
5844 if(!the_address)
5845 the_address = pf->scratch;
5847 rfc822_parse_adrlist(&new_addr, the_address,
5848 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
5849 ? fakedomain : ps_global->maildomain);
5850 mail_free_address(pf->addr); /* free old addrs */
5851 *pf->addr = new_addr; /* assign new addr */
5853 else if(pf->addr)
5854 mail_free_address(pf->addr); /* free old addrs */
5856 break;
5858 case Subject :
5859 if(*pf->text)
5860 fs_give((void **)pf->text);
5862 if(*pf->scratch){
5863 *pf->text = cpystr(pf->scratch);
5866 break;
5868 default :
5869 break;
5872 fs_give((void **)&pf->scratch); /* free now useless text */
5875 create_message_body(bod, attach, flow_it);
5877 if(we_cancel)
5878 cancel_busy_cue(-1);
5882 /*----------------------------------------------------------------------
5884 The head of the body list here is always either TEXT or MULTIPART. It may be
5885 changed from TEXT to MULTIPART if there are attachments to be added
5886 and it is not already multipart.
5887 ----*/
5888 void
5889 create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
5891 PART *p, **pp;
5892 PATMT *pa;
5893 BODY *tmp_body, *text_body = NULL;
5894 void *file_contents;
5895 PARAMETER **parmp;
5896 char *lc;
5898 TIME_STAMP("create_body start.", 1);
5900 * if conditions are met short circuit MIME wrapping
5902 if((*b)->type != TYPEMULTIPART && !attach){
5904 /* only override assigned encoding if it might need upgrading */
5905 if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
5906 (*b)->encoding = ENCOTHER;
5908 create_message_body_text(*b, flow_it);
5910 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
5911 || !((*b)->encoding == ENC8BIT
5912 || (*b)->encoding == ENCBINARY)){
5913 TIME_STAMP("create_body end.", 1);
5914 return;
5916 else /* protect 8bit in multipart */
5917 text_body = *b;
5920 if((*b)->type == TYPETEXT) {
5921 /*-- Current type is text, but there are attachments to add --*/
5922 /*-- Upgrade to a TYPEMULTIPART --*/
5923 tmp_body = (BODY *)mail_newbody();
5924 tmp_body->type = TYPEMULTIPART;
5925 tmp_body->nested.part = mail_newbody_part();
5926 if(text_body){
5928 * Why do we do this?
5929 * The problem is that base64 or quoted-printable encoding is
5930 * sensitive to having random data appended to it's end. If
5931 * we use a single part TEXT message and something in between
5932 * us and the end appends advertising without adjusting for
5933 * the encoding, the message is screwed up. So we wrap the
5934 * text part inside a multipart and then the appended data
5935 * will come after the boundary.
5937 * We wish we could do this on the way out the door in a
5938 * child of post_rfc822_output because at that point we know
5939 * the character set and the encoding being used. For example,
5940 * iso-2022-jp is an encoding that is not sensitive to data
5941 * appended to the end, so it wouldn't need to be wrapped.
5942 * We could conceivably have post_rfc822_body inspect the
5943 * body and change it before doing the output. It would work
5944 * but would be very fragile. We'd be passed a body from
5945 * c-client to output and instead of just doing the output
5946 * we'd change the body and then output it. Not worth it
5947 * since the multipart wrapping is completely correct for
5948 * MIME-aware mailers.
5950 (void) copy_body(&(tmp_body->nested.part->body), *b);
5951 /* move contents which were NOT copied */
5952 tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
5953 (*b)->contents.text.data = NULL;
5955 else{
5956 tmp_body->nested.part->body = **b;
5958 (*b)->subtype = (*b)->id = (*b)->description = NULL;
5959 (*b)->parameter = NULL;
5960 (*b)->contents.text.data = NULL;
5963 pine_free_body(b);
5964 *b = tmp_body;
5967 if(!text_body){
5968 /*-- Now type must be MULTIPART with first part text --*/
5969 (*b)->nested.part->body.encoding = ENCOTHER;
5970 create_message_body_text(&((*b)->nested.part->body), flow_it);
5973 /*------ Go through the parts list remove those to be deleted -----*/
5974 for(pp = &(*b)->nested.part->next; *pp;){
5975 for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
5976 /* already existed? */
5977 if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
5978 char *orig_descp = NULL, *cs = NULL;
5981 * decode original to see if it matches what was decoded
5982 * when we sent it in.
5985 if((*pp)->body.description)
5986 orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
5987 SIZEOF_20KBUF, (*pp)->body.description);
5989 if(!(*pp)->body.description /* update description? */
5990 || (pa->description && strcmp(pa->description, orig_descp))){
5991 if((*pp)->body.description)
5992 fs_give((void **) &(*pp)->body.description);
5994 /* encoding happens as msg text is written */
5995 (*pp)->body.description = cpystr(pa->description);
5998 if(cs)
5999 fs_give((void **) &cs);
6001 break;
6004 if(pa == NULL){
6005 p = *pp; /* prepare to zap *pp */
6006 *pp = p->next; /* pull next one in list up */
6007 p->next = NULL; /* tie off removed node */
6009 pine_free_body_data(&p->body); /* clean up contained data */
6010 mail_free_body_part(&p); /* free up the part */
6012 else
6013 pp = &(*pp)->next;
6016 /*---------- Now add any new attachments ---------*/
6017 for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
6018 for(pa = attach; pa != NULL; pa = pa->next) {
6019 if(pa->id != NULL)
6020 continue; /* Has an ID, it's old */
6023 * the idea is handle ALL attachments as open FILE *'s. Actual
6024 * encoding and such is handled at the time the message
6025 * is shoved into the mail slot or written to disk...
6027 * Also, we never unlink a file, so it's up to whoever opens
6028 * it to deal with tmpfile issues.
6030 if((file_contents = (void *)so_get(FileStar, pa->filename,
6031 READ_ACCESS)) == NULL){
6032 q_status_message2(SM_ORDER | SM_DING, 3, 4,
6033 _("Error \"%s\", couldn't attach file \"%s\""),
6034 error_description(errno), pa->filename);
6035 display_message('x');
6036 continue;
6039 p->next = mail_newbody_part();
6040 p = p->next;
6041 p->body.id = generate_message_id();
6042 p->body.contents.text.data = file_contents;
6045 * Set type to unknown and let set_mime_type_by_* figure it out.
6046 * Always encode attachments we add as BINARY.
6048 p->body.type = TYPEOTHER;
6049 p->body.encoding = ENCBINARY;
6050 p->body.size.bytes = name_file_size(pa->filename);
6051 if(!set_mime_type_by_extension(&p->body, pa->filename)){
6052 set_mime_type_by_grope(&p->body);
6053 set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
6056 so_release((STORE_S *)p->body.contents.text.data);
6058 if(pa->description) /* encoding happens when msg written */
6059 p->body.description = cpystr(pa->description);
6061 /* Add name attribute for backward compatibility */
6062 for(parmp = &p->body.parameter; *parmp; )
6063 if(!struncmp((*parmp)->attribute, "name", 4)
6064 && (!*((*parmp)->attribute + 4)
6065 || *((*parmp)->attribute + 4) == '*')){
6066 PARAMETER *free_me = *parmp;
6067 *parmp = (*parmp)->next;
6068 free_me->next = NULL;
6069 mail_free_body_parameter(&free_me);
6071 else
6072 parmp = &(*parmp)->next;
6074 set_parameter(parmp, "name",
6075 pa->filename
6076 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6077 : NULL);
6079 /* Then set the Content-Disposition ala RFC1806 */
6080 if(!p->body.disposition.type){
6081 p->body.disposition.type = cpystr("attachment");
6082 for(parmp = &p->body.disposition.parameter; *parmp; )
6083 if(!struncmp((*parmp)->attribute, "filename", 4)
6084 && (!*((*parmp)->attribute + 4)
6085 || *((*parmp)->attribute + 4) == '*')){
6086 PARAMETER *free_me = *parmp;
6087 *parmp = (*parmp)->next;
6088 free_me->next = NULL;
6089 mail_free_body_parameter(&free_me);
6091 else
6092 parmp = &(*parmp)->next;
6094 set_parameter(parmp, "filename",
6095 pa->filename
6096 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6097 : NULL);
6100 p->next = NULL;
6101 pa->id = cpystr(p->body.id);
6105 * Now, if this multipart has but one text piece (that is, no
6106 * attachments), then downgrade from a composite type to a discrete
6107 * text/plain message if CTE is not 8bit.
6109 if(!(*b)->nested.part->next
6110 && (*b)->nested.part->body.type == TYPETEXT
6111 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
6112 || !((*b)->nested.part->body.encoding == ENC8BIT
6113 || (*b)->nested.part->body.encoding == ENCBINARY))){
6114 /* Clone the interesting body part */
6115 tmp_body = mail_newbody();
6116 *tmp_body = (*b)->nested.part->body;
6117 /* and rub out what we don't want cleaned up when it's free'd */
6118 mail_initbody(&(*b)->nested.part->body);
6119 mail_free_body(b);
6120 *b = tmp_body;
6124 TIME_STAMP("create_body end.", 1);
6129 * Fill in text BODY part's structure
6132 void
6133 create_message_body_text(struct mail_bodystruct *b, int flow_it)
6135 set_mime_type_by_grope(b);
6136 if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
6137 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
6138 && flow_it)
6139 set_parameter(b ? &b->parameter : NULL, "format", "flowed");
6141 set_body_size(b);
6146 * free_attachment_list - free attachments in given list
6148 void
6149 free_attachment_list(PATMT **alist)
6151 PATMT *leading;
6153 while(alist && *alist){ /* pointer pointing to something */
6154 leading = (*alist)->next;
6155 if((*alist)->description)
6156 fs_give((void **)&(*alist)->description);
6158 if((*alist)->filename){
6159 if((*alist)->flags & A_TMP)
6160 if(our_unlink((*alist)->filename) < 0)
6161 dprint((1, "-- Can't unlink(%s): %s\n",
6162 (*alist)->filename ? (*alist)->filename : "?",
6163 error_description(errno)));
6165 fs_give((void **)&(*alist)->filename);
6168 if((*alist)->size)
6169 fs_give((void **)&(*alist)->size);
6171 if((*alist)->id)
6172 fs_give((void **)&(*alist)->id);
6174 fs_give((void **)alist);
6176 *alist = leading;
6181 void
6182 set_body_size(struct mail_bodystruct *b)
6184 unsigned char c;
6185 int we_cancel = 0;
6187 we_cancel = busy_cue(NULL, NULL, 1);
6188 so_seek((STORE_S *)b->contents.text.data, 0L, 0);
6189 b->size.bytes = 0L;
6190 while(so_readc(&c, (STORE_S *)b->contents.text.data))
6191 b->size.bytes++;
6193 if(we_cancel)
6194 cancel_busy_cue(-1);
6199 * view_as_rich - set the rich_header flag
6201 * name - name of the header field
6202 * deflt - default value to return if user didn't set it
6204 * Note: if the user tries to turn them all off with "", then
6205 * we take that to mean default, since otherwise there is no
6206 * way to get to the headers.
6209 view_as_rich(char *name, int deflt)
6211 char **p;
6212 char *q;
6214 p = ps_global->VAR_COMP_HDRS;
6216 if(p && *p && **p){
6217 for(; (q = *p) != NULL; p++){
6218 if(!struncmp(q, name, strlen(name)))
6219 return 0; /* 0 means we *do* view it by default */
6222 return 1; /* 1 means it starts out hidden */
6224 return(deflt);
6229 * background_posting - return whether or not we're already in the process
6230 * of posting
6233 background_posting(int gripe)
6235 if(ps_global->post){
6236 if(gripe)
6237 q_status_message(SM_ORDER|SM_DING, 3, 3,
6238 _("Can't post while posting!"));
6239 return(1);
6242 return(0);
6246 /*----------------------------------------------------------------------
6247 Validate the given subject relative to any news groups.
6249 Args: none
6251 Returns: always returns 1, but also returns error if
6252 ----*/
6254 valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
6256 struct headerentry *hp;
6258 if(expanded)
6259 *expanded = cpystr(given);
6261 if(error){
6263 * Now look for any header entry we passed to pico that has to do
6264 * with news. If there's no subject, gripe.
6266 for(hp = pbf->headents; hp->prompt; hp++)
6267 if(hp->help == h_composer_news){
6268 if(hp->hd_text->text[0] && !*given)
6269 *error = cpystr(
6270 _("News postings MUST have a subject! Please add one!"));
6272 break;
6276 return(0);
6281 * This is the build_address used by the composer to check for an address
6282 * in the addrbook.
6284 * Args: to -- the passed in line to parse
6285 * full_to -- Address of a pointer to return the full address in.
6286 * This will be allocated here and freed by the caller.
6287 * error -- Address of a pointer to return an error message in.
6288 * This will be allocated here and freed by the caller.
6289 * barg -- Address of a pointer to return the fcc in is in
6290 * fcc->tptr. It will have already been allocated by the
6291 * caller but we may free it and reallocate if we wish.
6292 * Caller will free it.
6294 * Result: 0 is returned if address was OK,
6295 * -1 if address wasn't OK.
6297 * Side effect: Can flush addrbook entry cache entries so they need to be
6298 * re-fetched afterwords.
6301 build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
6303 char *p;
6304 int ret_val, no_repo = 0, *save_nesting_level;
6305 BuildTo bldto;
6306 PrivateTop *pt = NULL;
6307 PrivateAffector *af = NULL;
6308 char *fcc_local = NULL;
6309 jmp_buf save_jmp_buf;
6311 dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
6313 /* check to see if to string is empty to avoid work */
6314 for(p = to; p && *p && isspace((unsigned char)*p); p++)
6315 ;/* do nothing */
6317 if(!p || !*p){
6318 if(full_to)
6319 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
6321 return 0;
6324 if(full_to != NULL)
6325 *full_to = (char *)NULL;
6327 if(error != NULL)
6328 *error = (char *)NULL;
6330 /* No guarantee cursor or status line is how we saved it */
6331 clear_cursor_pos();
6332 mark_status_unknown();
6334 if(ps_global->remote_abook_validity > 0 &&
6335 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6336 *mangled |= BUILDER_SCREEN_MANGLED;
6339 * If we end up jumping back here because somebody else changed one of
6340 * our addrbooks out from underneath us, we may well leak some memory.
6341 * That's probably ok since this will be very rare.
6343 * The reason for the memcpy of the jmp_buf is that we may actually
6344 * be indirectly calling this function from within the address book.
6345 * For example, we may be in the address book screen and then run
6346 * the ComposeTo command which puts us in the composer, then we call
6347 * build_address from there which resets addrbook_changed_unexpectedly.
6348 * Once we leave build_address we need to reset addrbook_changed_un...
6349 * because this position on the stack will no longer be valid.
6350 * Same is true of the other setjmp's in this file which are wrapped
6351 * in memcpy calls.
6353 save_nesting_level = cpyint(ab_nesting_level);
6354 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6355 if(setjmp(addrbook_changed_unexpectedly)){
6356 no_repo = 0;
6357 pt = NULL;
6358 af = NULL;
6359 fcc_local = NULL;
6360 if(error != NULL)
6361 *error = (char *)NULL;
6363 if(full_to && *full_to)
6364 fs_give((void **)full_to);
6366 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6367 dprint((1,
6368 "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
6369 addrbook_reset();
6370 ab_nesting_level = *save_nesting_level;
6373 ab_nesting_level++;
6374 bldto.type = Str;
6375 bldto.arg.str = to;
6376 ret_val = build_address_internal(bldto, full_to, error,
6377 barg ? &fcc_local : NULL,
6378 &no_repo, NULL, save_and_restore,
6379 0, mangled);
6380 ab_nesting_level--;
6381 if(save_nesting_level)
6382 fs_give((void **)&save_nesting_level);
6385 * Have to rfc1522_decode the full_to string before sending it back.
6387 if(full_to && *full_to ){
6388 char *q;
6389 size_t len;
6391 len = 4*strlen(*full_to)+1;
6392 q = (char *)fs_get(len * sizeof(char));
6393 p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
6395 /* p == q means that decoding happened, p is decoded *full_to */
6396 if(p == q){
6397 fs_give((void **)full_to);
6398 *full_to = p;
6400 else
6401 fs_give((void **)&q);
6404 if(fcc_local){
6405 unsigned long csum;
6407 /* Pt will point to headents[Fcc].bldr_private */
6408 pt = NULL;
6409 if(barg && barg->aff)
6410 pt = (PrivateTop *)(*barg->aff);
6413 * If *barg->aff is set, that means fcc was set from a list
6414 * during some previous builder call.
6415 * If the current To line contains the old expansion as a prefix, then
6416 * we should leave things as they are. In order to decide that,
6417 * we look at a hash value computed from the strings.
6419 if(pt && (af=pt->affector) && af->who == BP_To){
6420 int len;
6422 len = strlen(to);
6423 if(len >= af->cksumlen){
6424 int save;
6426 save = to[af->cksumlen];
6427 to[af->cksumlen] = '\0';
6428 csum = line_hash(to);
6429 to[af->cksumlen] = save;
6431 else
6432 csum = af->cksumval + 1; /* something not equal to cksumval */
6435 if(!pt ||
6436 !pt->affector ||
6437 (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
6439 /* replace fcc value */
6440 if(barg->tptr)
6441 fs_give((void **)&barg->tptr);
6443 barg->tptr = fcc_local;
6445 if(barg->aff){
6446 if(!pt){
6447 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6448 pt = (PrivateTop *)(*barg->aff);
6449 memset((void *)pt, 0, sizeof(PrivateTop));
6452 if(no_repo){
6453 if(!pt->affector)
6454 pt->affector =
6455 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6457 af = pt->affector;
6458 af->who = BP_To;
6459 af->cksumlen = strlen(((full_to && *full_to)
6460 ? *full_to : ""));
6461 af->cksumval = line_hash(((full_to && *full_to)
6462 ? *full_to : ""));
6464 else{
6466 * If result is reproducible, we don't keep track here.
6468 if(pt->affector)
6469 fs_give((void **)&pt->affector);
6473 else
6474 fs_give((void **)&fcc_local); /* unused in this case */
6477 /* This is so pico will erase the old message */
6478 if(error != NULL && *error == NULL)
6479 *error = cpystr("");
6481 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6482 flush_status_messages(1);
6483 return(ret_val);
6488 * This is the builder used by the composer for the Lcc line.
6490 * Args: lcc -- the passed in Lcc line to parse
6491 * full_lcc -- Address of a pointer to return the full address in.
6492 * This will be allocated here and freed by the caller.
6493 * error -- Address of a pointer to return an error message in.
6494 * This is not allocated so should not be freed by the caller.
6495 * barg -- This is a pointer to text for affected entries which
6496 * we may be changing. The first one in the list is the
6497 * To entry. We may put the name of the list in empty
6498 * group syntax form there (like List Name: ;).
6499 * The second one in the list is the fcc field.
6500 * The tptr members already point to text allocated in the
6501 * caller. We may free and reallocate here, caller will
6502 * free the result in any case.
6504 * Result: 0 is returned if address was OK,
6505 * -1 if address wasn't OK.
6507 * Side effect: Can flush addrbook entry cache entries so they need to be
6508 * re-fetched afterwords.
6511 build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
6513 int ret_val,
6514 no_repo = 0; /* fcc or lcc not reproducible */
6515 int *save_nesting_level;
6516 BuildTo bldlcc;
6517 PrivateTop *pt = NULL;
6518 PrivateAffector *af = NULL;
6519 char *p,
6520 *fcc_local = NULL,
6521 *to = NULL,
6522 *dummy;
6523 jmp_buf save_jmp_buf;
6525 dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
6527 /* check to see if to string is empty to avoid work */
6528 for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
6529 ;/* do nothing */
6531 if(!p || !*p){
6532 if(full_lcc)
6533 *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
6535 return 0;
6538 if(error != NULL)
6539 *error = (char *)NULL;
6541 if(ps_global->remote_abook_validity > 0 &&
6542 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6543 *mangled |= BUILDER_SCREEN_MANGLED;
6546 * If we end up jumping back here because somebody else changed one of
6547 * our addrbooks out from underneath us, we may well leak some memory.
6548 * That's probably ok since this will be very rare.
6550 save_nesting_level = cpyint(ab_nesting_level);
6551 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6552 if(setjmp(addrbook_changed_unexpectedly)){
6553 no_repo = 0;
6554 pt = NULL;
6555 af = NULL;
6556 fcc_local = NULL;
6557 to = NULL;
6558 if(error != NULL)
6559 *error = (char *)NULL;
6561 if(full_lcc && *full_lcc)
6562 fs_give((void **)full_lcc);
6564 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6565 dprint((1,
6566 "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
6567 addrbook_reset();
6568 ab_nesting_level = *save_nesting_level;
6571 ab_nesting_level++;
6572 bldlcc.type = Str;
6573 bldlcc.arg.str = lcc;
6576 * To is first affected_entry and Fcc is second.
6577 * The conditional stuff for the fcc argument says to only change the
6578 * fcc if the fcc pointer is passed in non-null, and the To pointer
6579 * is also non-null. If they are null, that means they've already been
6580 * entered (are sticky). We don't affect fcc if either fcc or To has
6581 * been typed in.
6583 ret_val = build_address_internal(bldlcc,
6584 full_lcc,
6585 error,
6586 (barg && barg->next && barg->next->tptr && barg->tptr)
6587 ? &fcc_local : NULL,
6588 &no_repo,
6589 (barg && barg->tptr) ? &to : NULL,
6590 save_and_restore, 0, mangled);
6592 ab_nesting_level--;
6593 if(save_nesting_level)
6594 fs_give((void **)&save_nesting_level);
6596 /* full_lcc is what ends up in the Lcc: line */
6597 if(full_lcc && *full_lcc){
6598 size_t len;
6601 * Have to rfc1522_decode the full_lcc string before sending it back.
6603 len = 4*strlen(*full_lcc)+1;
6604 p = (char *)fs_get(len * sizeof(char));
6605 if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
6606 fs_give((void **)full_lcc);
6607 *full_lcc = p;
6609 else
6610 fs_give((void **)&p);
6613 /* to is what ends up in the To: line */
6614 if(to && *to){
6615 unsigned long csum;
6616 size_t len;
6619 * Have to rfc1522_decode the full_to string before sending it back.
6621 len = 4*strlen(to)+1;
6622 p = (char *)fs_get(len * sizeof(char));
6623 dummy = NULL;
6624 if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
6626 * If the caller wants us to try to preserve the charset
6627 * information (they set aff) we copy it into encoded->etext.
6628 * We don't have to worry about pasting together pieces of
6629 * etext like we do in build_address because whenever the
6630 * Lcc line is setting the To line it will be setting the
6631 * whole line, not modifying it.
6632 * Pt will point to headents[To].bldr_private.
6634 if(barg && barg->aff){
6635 pt = (PrivateTop *)(*barg->aff);
6637 if(!pt){
6638 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6639 pt = (PrivateTop *)(*barg->aff);
6640 memset((void *)pt, 0, sizeof(PrivateTop));
6644 fs_give((void **)&to);
6645 to = p;
6647 else
6648 fs_give((void **)&p);
6650 if(dummy)
6651 fs_give((void **)&dummy);
6655 * This part is recording the fact that the To line was set to
6656 * what it is by entering something on the Lcc line. In particular,
6657 * if a list alias was entered here then the fullname of the list
6658 * goes in the To line. We save this affector information so that
6659 * we can tell it shouldn't be modified if we call build_addr_lcc
6660 * again unless we actually modified what's in the Lcc line so that
6661 * it doesn't start with the same thing. The problem we're solving
6662 * is that the contents of the Lcc line no longer look like the
6663 * list they were derived from.
6664 * Pt will point to headents[To].bldr_private.
6666 if(barg && barg->aff)
6667 pt = (PrivateTop *)(*barg->aff);
6669 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6670 int len;
6672 len = strlen(lcc);
6673 if(len >= af->cksumlen){
6674 int save;
6676 save = lcc[af->cksumlen];
6677 lcc[af->cksumlen] = '\0';
6678 csum = line_hash(lcc);
6679 lcc[af->cksumlen] = save;
6681 else
6682 csum = af->cksumval + 1; /* so they aren't equal */
6685 if(!pt ||
6686 !pt->affector ||
6687 pt->affector->who != BP_Lcc ||
6688 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6690 /* replace to value */
6691 if(barg->tptr && barg->tptr[0]){
6692 size_t l;
6693 char *t;
6695 l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
6696 t = (char *)fs_get((l+1) * sizeof(char));
6697 snprintf(t, l+1, "%s%s%s",
6698 barg->tptr,
6699 (to && *to) ? ", " : "",
6700 (to && *to) ? to : "");
6701 fs_give((void **)&barg->tptr);
6702 if(to)
6703 fs_give((void **)&to);
6705 barg->tptr = t;
6707 else{
6708 if(barg->tptr)
6709 fs_give((void **)&barg->tptr);
6711 barg->tptr = to;
6714 if(barg->aff){
6715 if(!pt){
6716 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6717 pt = (PrivateTop *)(*barg->aff);
6718 memset((void *)pt, 0, sizeof(PrivateTop));
6721 if(no_repo){
6722 if(!pt->affector)
6723 pt->affector =
6724 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6726 af = pt->affector;
6727 af->who = BP_Lcc;
6728 af->cksumlen = strlen(((full_lcc && *full_lcc)
6729 ? *full_lcc : ""));
6730 af->cksumval = line_hash(((full_lcc && *full_lcc)
6731 ? *full_lcc : ""));
6733 else{
6735 * If result is reproducible, we don't keep track here.
6737 if(pt->affector)
6738 fs_give((void **)&pt->affector);
6742 else
6743 fs_give((void **)&to); /* unused in this case */
6746 if(fcc_local){
6747 unsigned long csum;
6750 * If *barg->next->aff is set, that means fcc was set from a list
6751 * during some previous builder call. If the current Lcc line
6752 * contains the old expansion as a prefix, then we should leave
6753 * things as they are. In order to decide that we look at a hash
6754 * value computed from the strings.
6755 * Pt will point to headents[Fcc].bldr_private
6757 pt = NULL;
6758 if(barg && barg->next && barg->next->aff)
6759 pt = (PrivateTop *)(*barg->next->aff);
6761 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6762 int len;
6764 len = strlen(lcc);
6765 if(len >= af->cksumlen){
6766 int save;
6768 save = lcc[af->cksumlen];
6769 lcc[af->cksumlen] = '\0';
6770 csum = line_hash(lcc);
6771 lcc[af->cksumlen] = save;
6773 else
6774 csum = af->cksumval + 1; /* something not equal to cksumval */
6777 if(!pt ||
6778 !pt->affector ||
6779 pt->affector->who != BP_Lcc ||
6780 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6782 /* replace fcc value */
6783 if(barg->next->tptr)
6784 fs_give((void **)&barg->next->tptr);
6786 barg->next->tptr = fcc_local;
6788 if(barg->next->aff){
6789 if(!pt){
6790 *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
6791 pt = (PrivateTop *)(*barg->next->aff);
6792 memset((void *)pt, 0, sizeof(PrivateTop));
6795 if(no_repo){
6796 if(!pt->affector)
6797 pt->affector =
6798 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6800 af = pt->affector;
6801 af->who = BP_Lcc;
6802 af->cksumlen = strlen(((full_lcc && *full_lcc)
6803 ? *full_lcc : ""));
6804 af->cksumval = line_hash(((full_lcc && *full_lcc)
6805 ? *full_lcc : ""));
6807 else{
6809 * If result is reproducible, we don't keep track here.
6811 if(pt->affector)
6812 fs_give((void **)&pt->affector);
6816 else
6817 fs_give((void **)&fcc_local); /* unused in this case */
6821 if(error != NULL && *error == NULL)
6822 *error = cpystr("");
6824 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6825 flush_status_messages(0);
6826 return(ret_val);
6830 /*----------------------------------------------------------------------
6831 Verify and canonicalize news groups names.
6832 Called from the message composer
6834 Args: given_group -- List of groups typed by user
6835 expanded_group -- pointer to point to expanded list, which will be
6836 allocated here and freed in caller. If this is
6837 NULL, don't attempt to validate.
6838 error -- pointer to store error message
6839 fcc -- pointer to point to fcc, which will be
6840 allocated here and freed in caller
6842 Returns: 0 if all is OK
6843 -1 if addresses weren't valid
6845 Test the given list of newstroups against those recognized by our nntp
6846 servers. Testing by actually trying to open the list is much cheaper, both
6847 in bandwidth and memory, than yanking the whole list across the wire.
6848 ----*/
6850 news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
6852 int rv;
6853 char *fccptr = NULL;
6855 if(fcc && fcc->tptr)
6856 fccptr = cpystr(fcc->tptr);
6858 clear_cursor_pos();
6860 rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
6862 /* assign any new fcc to the BUILDER_ARG */
6863 if(fccptr){
6864 if(fcc){
6865 /* it changed */
6866 if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
6867 fs_give((void **) &fcc->tptr);
6868 fcc->tptr = fccptr;
6869 fccptr = NULL;
6873 if(fccptr)
6874 fs_give((void **) &fccptr);
6877 /* deal with any busy indicator */
6878 if(news_busy_cue){
6879 news_busy_cue = 0;
6880 cancel_busy_cue(0);
6881 mark_status_dirty();
6882 display_message('x');
6883 if(mangled)
6884 *mangled |= BUILDER_MESSAGE_DISPLAYED;
6888 return(rv);
6892 void
6893 news_build_busy(void)
6895 news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
6899 #if defined(DOS) || defined(OS2)
6901 /*----------------------------------------------------------------------
6902 Verify that the necessary pieces are around to allow for
6903 message sending under DOS
6905 Args: strict -- tells us if a remote stream is required before
6906 sending is permitted.
6908 The idea is to make sure pine knows enough to put together a valid
6909 from line. The things we MUST know are a user-id, user-domain and
6910 smtp server to dump the message off on. Typically these are
6911 provided in pine's configuration file, but if not, the user is
6912 queried here.
6913 ----*/
6915 dos_valid_from()
6917 char prompt[100], answer[80];
6918 int rc, i, flags;
6919 HelpType help;
6922 * query for user name portion of address, use IMAP login
6923 * name as default
6925 if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
6926 NETMBX mb;
6927 int no_prompt_user_id = 0;
6929 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6930 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6931 && *mb.user){
6932 strncpy(answer, mb.user, sizeof(answer)-1);
6933 answer[sizeof(answer)-1] = '\0';
6935 else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
6936 /* no user-id prompting if set */
6937 no_prompt_user_id = 1;
6938 rc = 0;
6939 if(!ps_global->mail_stream)
6940 do_broach_folder(ps_global->inbox_name,
6941 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
6942 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6943 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6944 && *mb.user){
6945 strncpy(answer, mb.user, sizeof(answer)-1);
6946 answer[sizeof(answer)-1] = '\0';
6948 else
6949 answer[0] = '\0';
6951 else
6952 answer[0] = '\0';
6954 if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
6955 /* No prompt, just assume mailbox login is user-id */
6956 no_prompt_user_id = 1;
6957 rc = 0;
6960 snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
6961 prompt[sizeof(prompt)-1] = '\0';
6963 help = NO_HELP;
6964 while(!no_prompt_user_id) {
6965 flags = OE_APPEND_CURRENT;
6966 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
6967 sizeof(answer),prompt,NULL,help,&flags);
6968 if(rc == 2)
6969 continue;
6971 if(rc == 3){
6972 help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
6973 continue;
6976 if(rc != 4)
6977 break;
6980 if(rc == 1 || (rc == 0 && !answer[0])) {
6981 q_status_message(SM_ORDER, 3, 4,
6982 _("Send cancelled (User-id must be provided before sending)"));
6983 return(0);
6986 /* save the name */
6987 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
6988 sizeof(prompt)-50, answer);
6989 prompt[sizeof(prompt)-1] = '\0';
6990 if(ps_global->blank_user_id
6991 && !no_prompt_user_id
6992 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
6993 set_variable(V_USER_ID, answer, 1, 1, Main);
6995 else{
6996 fs_give((void **)&(ps_global->VAR_USER_ID));
6997 ps_global->VAR_USER_ID = cpystr(answer);
7001 /* query for personal name */
7002 if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
7003 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
7004 answer[0] = '\0';
7005 snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
7006 prompt[sizeof(prompt)-1] = '\0';
7008 help = NO_HELP;
7009 while(1) {
7010 flags = OE_APPEND_CURRENT;
7011 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7012 sizeof(answer),prompt,NULL,help,&flags);
7013 if(rc == 2)
7014 continue;
7016 if(rc == 3){
7017 help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
7018 continue;
7021 if(rc != 4)
7022 break;
7025 if(rc == 0 && answer){ /* save the name */
7026 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
7027 sizeof(prompt)-50, answer);
7028 prompt[sizeof(prompt)-1] = '\0';
7029 if(ps_global->blank_personal_name
7030 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7031 set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
7033 else{
7034 fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
7035 ps_global->VAR_PERSONAL_NAME = cpystr(answer);
7041 * query for host/domain portion of address, using IMAP
7042 * host as default
7044 if(ps_global->blank_user_domain
7045 || ps_global->maildomain == ps_global->localdomain
7046 || ps_global->maildomain == ps_global->hostname){
7047 if(ps_global->inbox_name[0] == '{'){
7048 for(i=0;
7049 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7050 answer[i] = ps_global->inbox_name[i+1];
7052 answer[i] = '\0';
7054 else
7055 answer[0] = '\0';
7057 snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
7058 prompt[sizeof(prompt)-1] = '\0';
7060 help = NO_HELP;
7061 while(1) {
7062 flags = OE_APPEND_CURRENT;
7063 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7064 sizeof(answer),prompt,NULL,help,&flags);
7065 if(rc == 2)
7066 continue;
7068 if(rc == 3){
7069 help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
7070 continue;
7073 if(rc != 4)
7074 break;
7077 if(rc == 1 || (rc == 0 && !answer[0])) {
7078 q_status_message(SM_ORDER, 3, 4,
7079 _("Send cancelled (Host/domain name must be provided before sending)"));
7080 return(0);
7083 /* save the name */
7084 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
7085 sizeof(prompt)-50, answer);
7086 prompt[sizeof(prompt)-1] = '\0';
7087 if(!ps_global->userdomain && !ps_global->blank_user_domain
7088 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7089 set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
7090 fs_give((void **)&(ps_global->maildomain)); /* blast old val */
7091 ps_global->userdomain = cpystr(answer);
7092 ps_global->maildomain = ps_global->userdomain;
7094 else{
7095 fs_give((void **)&(ps_global->maildomain));
7096 ps_global->userdomain = cpystr(answer);
7097 ps_global->maildomain = ps_global->userdomain;
7101 /* check for smtp server */
7102 if(!ps_global->VAR_SMTP_SERVER ||
7103 !ps_global->VAR_SMTP_SERVER[0] ||
7104 !ps_global->VAR_SMTP_SERVER[0][0]){
7105 char **list;
7107 if(ps_global->inbox_name[0] == '{'){
7108 for(i=0;
7109 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7110 answer[i] = ps_global->inbox_name[i+1];
7112 answer[i] = '\0';
7114 else
7115 answer[0] = '\0';
7117 snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
7118 prompt[sizeof(prompt)-1] = '\0';
7120 help = NO_HELP;
7121 while(1) {
7122 flags = OE_APPEND_CURRENT;
7123 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7124 sizeof(answer),prompt,NULL,help,&flags);
7125 if(rc == 2)
7126 continue;
7128 if(rc == 3){
7129 help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
7130 continue;
7133 if(rc != 4)
7134 break;
7137 if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
7138 q_status_message(SM_ORDER, 3, 4,
7139 _("Send cancelled (SMTP server must be provided before sending)"));
7140 return(0);
7143 /* save the name */
7144 list = (char **) fs_get(2 * sizeof(char *));
7145 list[0] = cpystr(answer);
7146 list[1] = NULL;
7147 set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
7148 fs_give((void *)&list[0]);
7149 fs_give((void *)list);
7152 return(1);
7155 #endif /* defined(DOS) || defined(OS2) */