1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: send.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2020 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 Functions for composing and sending mail
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
;
75 * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook
77 #define HE(PF) ((struct headerentry *)((PF)->extdata))
83 int redraft(MAILSTREAM
**, ENVELOPE
**, BODY
**, char **, char **, REPLY_S
**,
84 REDRAFT_POS_S
**, PINEFIELD
**, ACTION_S
**, int);
85 int redraft_prompt(char *, char *, int);
86 int check_for_subject(METAENV
*);
87 int check_for_fcc(char *);
88 void free_prompts(PINEFIELD
*);
89 int postpone_prompt(void);
90 METAENV
*pine_simple_send_header(ENVELOPE
*, char **, char ***);
91 void call_mailer_file_result(char *, int);
92 void mark_address_failure_for_pico(METAENV
*);
94 *save_body_particulars(BODY
*);
95 void reset_body_particulars(BODY_PARTICULARS_S
*, BODY
*);
96 void free_body_particulars(BODY_PARTICULARS_S
*);
97 long message_format_for_pico(long, int (*)(int));
98 int send_exit_for_pico(struct headerentry
*, void (*)(void), int, char **);
99 void new_thread_on_blank_subject(void);
100 char *choose_a_priority(char *);
101 int dont_flow_this_time(void);
102 int mime_type_for_pico(char *);
103 char *cancel_for_pico(void (*)(void));
104 int filter_message_text(char *, ENVELOPE
*, BODY
*, STORE_S
**, METAENV
*);
105 void pine_send_newsgroup_name(char *, char*, size_t);
106 void outgoing2strings(METAENV
*, BODY
*, void **, PATMT
**, int);
107 void strings2outgoing(METAENV
*, BODY
**, PATMT
*, int);
108 void create_message_body_text(BODY
*, int);
109 void set_body_size(BODY
*);
110 int view_as_rich(char *, int);
111 int background_posting(int);
112 int valid_subject(char *, char **, char **,BUILDER_ARG
*,int *);
113 int build_addr_lcc(char *, char **, char **, BUILDER_ARG
*, int *);
114 int news_build(char *, char **, char **, BUILDER_ARG
*, int *);
115 void news_build_busy(void);
116 #if defined(DOS) || defined(OS2)
117 int dos_valid_from(void);
118 #endif /* defined(DOS) || defined(OS2) */
122 * Pointer to buffer to hold pointers into pine data that's needed by pico.
127 static char *g_rolenick
= NULL
;
130 static char *sending_filter_requested
;
131 static char background_requested
, flowing_requested
;
132 static unsigned call_mailer_flags
;
133 static char *priority_requested
;
135 /* local global to save busy_cue state */
136 static int news_busy_cue
= 0;
140 * Various useful strings
143 _("Continue INTERRUPTED composition (answering \"n\" won't erase it)")
145 _("Continue postponed composition (answering \"No\" won't erase it)")
147 _("Start composition from Form Letter Folder")
148 #define PSTPN_FORM_PMT \
149 _("Save to Postponed or Form letter folder? ")
151 _("Posted message may go to thousands of readers. Really post")
152 #define INTR_DEL_PMT \
153 _("Deleted messages will be removed from folder after use. Proceed")
157 * Macros to help sort out posting results
159 #define P_MAIL_WIN 0x01
160 #define P_MAIL_LOSE 0x02
161 #define P_MAIL_BITS 0x03
162 #define P_NEWS_WIN 0x04
163 #define P_NEWS_LOSE 0x08
164 #define P_NEWS_BITS 0x0C
165 #define P_FCC_WIN 0x10
166 #define P_FCC_LOSE 0x20
167 #define P_FCC_BITS 0x30
170 #define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE"
174 * For check_for_subject and check_for_fcc
177 #define CF_MISSING 0x2
180 /*----------------------------------------------------------------------
181 Compose screen (not forward or reply). Set up envelope, call composer
183 Args: pine_state -- The usual pine structure
185 Little front end for the compose screen
188 compose_screen(struct pine
*pine_state
)
190 void (*prev_screen
)(struct pine
*) = pine_state
->prev_screen
,
191 (*redraw
)(void) = pine_state
->redrawer
;
193 pine_state
->redrawer
= NULL
;
194 ps_global
->next_screen
= SCREEN_FUN_NULL
;
195 mailcap_free(); /* free resources we won't be using for a while */
196 compose_mail(NULL
, NULL
, NULL
, NULL
, NULL
);
197 pine_state
->next_screen
= prev_screen
;
198 pine_state
->redrawer
= redraw
;
202 /*----------------------------------------------------------------------
203 Alternate compose screen. Set up role and call regular compose.
205 Args: pine_state -- The usual pine structure
208 alt_compose_screen(struct pine
*pine_state
)
210 ACTION_S
*role
= NULL
;
211 void (*prev_screen
)(struct pine
*) = pine_state
->prev_screen
,
212 (*redraw
)(void) = pine_state
->redrawer
;
214 pine_state
->redrawer
= NULL
;
215 ps_global
->next_screen
= SCREEN_FUN_NULL
;
216 mailcap_free(); /* free resources we won't be using for a while */
219 if(role_select_screen(pine_state
, &role
, MC_COMPOSE
) < 0){
220 cmd_cancelled("Composition");
221 pine_state
->next_screen
= prev_screen
;
222 pine_state
->redrawer
= redraw
;
227 * If default role was selected (NULL) we need to make up a role which
228 * won't do anything, but will cause compose_mail to think there's
229 * already a role so that it won't try to confirm the default.
232 role
= combine_inherited_role(role
);
234 role
= (ACTION_S
*)fs_get(sizeof(*role
));
235 memset((void *)role
, 0, sizeof(*role
));
236 role
->nick
= cpystr("Default Role");
239 pine_state
->redrawer
= NULL
;
240 compose_mail(NULL
, NULL
, role
, NULL
, NULL
);
242 pine_state
->next_screen
= prev_screen
;
243 pine_state
->redrawer
= redraw
;
247 /*----------------------------------------------------------------------
248 Format envelope for outgoing message and call editor
250 Args: given_to -- An address to send mail to (usually from command line
252 fcc_arg -- The fcc that goes with this address.
254 If a "To" line is given format that into the envelope and get ready to call
256 If there's a message postponed, offer to continue it, and set it up,
257 otherwise just fill in the outgoing envelope as blank.
259 NOTE: we ignore postponed and interrupted messages in nr mode
262 compose_mail(char *given_to
, char *fcc_arg
, ACTION_S
*role_arg
,
263 PATMT
*attach
, gf_io_t inc_text_getc
)
266 ENVELOPE
*outgoing
= NULL
;
267 PINEFIELD
*custom
= NULL
;
268 REPLY_S
*reply
= NULL
;
269 REDRAFT_POS_S
*redraft_pos
= NULL
;
270 ACTION_S
*role
= NULL
;
276 int fcc_is_sticky
= 0,
283 "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n"));
285 /*-- Check for INTERRUPTED mail --*/
286 if(!role_arg
&& !(given_to
|| attach
)){
287 char file_path
[MAXPATH
+1];
289 /* build filename and see if it exists. build_path creates
290 * an explicit local path name, so all c-client access is thru
294 build_path(file_path
,
295 ps_global
->VAR_OPER_DIR
? ps_global
->VAR_OPER_DIR
296 : ps_global
->home_dir
,
297 INTERRUPTED_MAIL
, sizeof(file_path
));
299 /* check to see if the folder exists, the user wants to continue
300 * and that we can actually read something in...
302 if(folder_exists(NULL
, file_path
) & FEX_ISFILE
)
306 /*-- Check for postponed mail --*/
308 && !outgoing
/* not replying/forwarding */
309 && !(given_to
|| attach
) /* not command line send */
310 && ps_global
->VAR_POSTPONED_FOLDER
/* folder to look in */
311 && ps_global
->VAR_POSTPONED_FOLDER
[0])
314 /*-- Check for form letter folder --*/
316 && !outgoing
/* not replying/forwarding */
317 && !(given_to
|| attach
) /* not command line send */
318 && ps_global
->VAR_FORM_FOLDER
/* folder to look in */
319 && ps_global
->VAR_FORM_FOLDER
[0])
322 if(!outgoing
&& !(given_to
|| attach
)
323 && !role_arg
&& F_ON(F_ALT_COMPOSE_MENU
, ps_global
)){
328 char *intrpt
= "Interrupted";
329 char *postpnd
= "Postponed";
330 char *formltr
= "FormLetter";
331 char *roles
= "setRole";
332 HelpType help
= h_composer_browse
;
333 ESCKEY_S compose_style
[6];
338 compose_style
[ekey_num
].ch
= 'n';
339 compose_style
[ekey_num
].rval
= 'n';
340 compose_style
[ekey_num
].name
= "N";
341 compose_style
[ekey_num
++].label
= new;
344 compose_style
[ekey_num
].ch
= 'i';
345 compose_style
[ekey_num
].rval
= 'i';
346 compose_style
[ekey_num
].name
= "I";
347 compose_style
[ekey_num
++].label
= intrpt
;
351 compose_style
[ekey_num
].ch
= 'p';
352 compose_style
[ekey_num
].rval
= 'p';
353 compose_style
[ekey_num
].name
= "P";
354 compose_style
[ekey_num
++].label
= postpnd
;
358 compose_style
[ekey_num
].ch
= 'f';
359 compose_style
[ekey_num
].rval
= 'f';
360 compose_style
[ekey_num
].name
= "F";
361 compose_style
[ekey_num
++].label
= formltr
;
364 compose_style
[ekey_num
].ch
= 'r';
365 compose_style
[ekey_num
].rval
= 'r';
366 compose_style
[ekey_num
].name
= "R";
367 compose_style
[ekey_num
++].label
= roles
;
369 compose_style
[ekey_num
].ch
= -1;
371 if(F_ON(F_BLANK_KEYMENU
,ps_global
)){
376 for(ekey_num
= 0; compose_style
[ekey_num
].ch
!= -1; ekey_num
++){
377 if(p
- letters
< sizeof(letters
))
378 *p
++ = (char) compose_style
[ekey_num
].ch
;
380 if(compose_style
[ekey_num
+ 1].ch
!= -1 && p
- letters
< sizeof(letters
))
384 if(p
- letters
< sizeof(letters
))
388 which_help
= intrptd
+ 2 * postponed
+ 4 * form
;
391 help
= h_compose_intrptd
;
394 help
= h_compose_postponed
;
397 help
= h_compose_intrptd_postponed
;
400 help
= h_compose_form
;
403 help
= h_compose_intrptd_form
;
406 help
= h_compose_postponed_form
;
409 help
= h_compose_intrptd_postponed_form
;
412 help
= h_compose_default
;
416 snprintf(prompt
, sizeof(prompt
),
417 "Choose a compose method from %s : ",
418 F_ON(F_BLANK_KEYMENU
,ps_global
) ? letters
: "the menu below");
419 prompt
[sizeof(prompt
)-1] = '\0';
421 chosen_task
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
),
422 compose_style
, 'n', 'x', help
, RB_NORM
);
423 intrptd
= postponed
= form
= 0;
434 void (*prev_screen
)(struct pine
*) = ps_global
->prev_screen
,
435 (*redraw
)(void) = ps_global
->redrawer
;
437 ps_global
->redrawer
= NULL
;
438 ps_global
->next_screen
= SCREEN_FUN_NULL
;
439 if(role_select_screen(ps_global
, &role
, MC_COMPOSE
) < 0){
440 cmd_cancelled("Composition");
441 ps_global
->next_screen
= prev_screen
;
442 ps_global
->redrawer
= redraw
;
446 ps_global
->next_screen
= prev_screen
;
447 ps_global
->redrawer
= redraw
;
449 role
= combine_inherited_role(role
);
458 q_status_message(SM_ORDER
, 0, 3,
459 "Composition cancelled");
468 if(intrptd
&& !outgoing
){
469 char file_path
[MAXPATH
+1];
473 build_path(file_path
,
474 ps_global
->VAR_OPER_DIR
? ps_global
->VAR_OPER_DIR
475 : ps_global
->home_dir
,
476 INTERRUPTED_MAIL
, sizeof(file_path
));
477 if(folder_exists(NULL
, file_path
) & FEX_ISFILE
){
478 if((stream
= pine_mail_open(NULL
, file_path
,
479 SP_USEPOOL
|SP_TEMPUSE
, NULL
))
480 && !stream
->halfopen
){
482 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
) ||
483 (ret
= redraft_prompt("Interrupted",INTRPT_PMT
,'n')) =='y'){
484 if(!redraft(&stream
, &outgoing
, &body
, &fcc
, &lcc
, &reply
,
485 &redraft_pos
, &custom
, &role
, REDRAFT_DEL
)){
487 pine_mail_close(stream
);
494 /* redraft() may or may not have closed stream */
496 pine_mail_close(stream
);
498 postponed
= form
= 0;
501 pine_mail_close(stream
);
503 q_status_message(SM_ORDER
, 0, 3,
504 _("Composition cancelled"));
510 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
511 _("Can't open Interrupted mailbox: %s"),
514 pine_mail_close(stream
);
519 if(postponed
&& !outgoing
){
520 int ret
= 'n', done
= 0;
523 if((exists
=postponed_stream(&stream
,
524 ps_global
->VAR_POSTPONED_FOLDER
,
525 "Postponed", 0)) & FEX_ISFILE
){
526 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
) ||
527 (ret
= redraft_prompt("Postponed",PSTPND_PMT
,'n')) == 'y'){
528 if(!redraft(&stream
, &outgoing
, &body
, &fcc
, &lcc
, &reply
,
529 &redraft_pos
, &custom
, &role
,
530 REDRAFT_DEL
| REDRAFT_PPND
))
533 /* stream may or may not be closed in redraft() */
534 if(stream
&& (stream
!= ps_global
->mail_stream
))
535 pine_mail_close(stream
);
541 if(stream
!= ps_global
->mail_stream
)
542 pine_mail_close(stream
);
545 q_status_message(SM_ORDER
, 0, 3,
546 _("Composition cancelled"));
551 else if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
))
558 if(form
&& !outgoing
){
559 int ret
= 'n', done
= 0;
562 if((exists
=postponed_stream(&stream
,
563 ps_global
->VAR_FORM_FOLDER
,
564 "Form letter", 1)) & FEX_ISFILE
){
565 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
) ||
566 (ret
= want_to(FORM_PMT
,'y','x',NO_HELP
,WT_NORM
))=='y'){
567 if(!redraft(&stream
, &outgoing
, &body
, &fcc
, &lcc
, &reply
,
568 &redraft_pos
, &custom
, &role
, REDRAFT_NONE
))
571 /* stream may or may not be closed in redraft() */
572 if(stream
&& (stream
!= ps_global
->mail_stream
))
573 pine_mail_close(stream
);
576 intrptd
= postponed
= 0;
579 if(stream
!= ps_global
->mail_stream
)
580 pine_mail_close(stream
);
583 q_status_message(SM_ORDER
, 0, 3,
584 _("Composition cancelled"));
590 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
)){
591 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
592 _("Form letter folder doesn't exist!"));
601 /*-- normal composition --*/
603 int impl
, template_len
= 0;
604 long rflags
= ROLE_COMPOSE
;
607 /*================= Compose new message ===============*/
608 body
= mail_newbody();
609 outgoing
= mail_newenvelope();
612 rfc822_parse_adrlist(&outgoing
->to
, given_to
, ps_global
->maildomain
);
614 outgoing
->message_id
= generate_message_id();
617 * Setup possible role
620 role
= copy_action(role_arg
);
623 /* Setup possible compose role */
624 if(nonempty_patterns(rflags
, &dummy
)){
627 * Msgno = -1 means there is no msg.
628 * This will match roles which have the Compose Use turned
629 * on, and have no patterns set, and match the Current
632 role
= set_role_from_msg(ps_global
, rflags
, -1L, NULL
);
634 if(confirm_role(rflags
, &role
))
635 role
= combine_inherited_role(role
);
636 else{ /* cancel reply */
638 cmd_cancelled("Composition");
645 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
649 * The type of storage object allocated below is vitally
650 * important. See SIMPLIFYING ASSUMPTION #37
652 if((body
->contents
.text
.data
= (void *) so_get(PicoText
,
653 NULL
, EDIT_ACCESS
)) != NULL
){
657 while((*inc_text_getc
)(&ch
))
658 if(!so_writec(ch
, (STORE_S
*)body
->contents
.text
.data
)){
664 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
665 _("Problem creating space for message text."));
669 if(role
&& role
->template){
672 impl
= 1; /* leave cursor in header if not explicit */
673 filtered
= detoken(role
, NULL
, 0, 0, 0, &redraft_pos
, &impl
);
676 so_puts((STORE_S
*)body
->contents
.text
.data
, filtered
);
678 template_len
= strlen(filtered
);
681 fs_give((void **)&filtered
);
687 if((sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
, &impl
)) != NULL
){
689 redraft_pos
->offset
+= template_len
;
692 so_puts((STORE_S
*)body
->contents
.text
.data
, sig
);
694 fs_give((void **)&sig
);
697 body
->type
= TYPETEXT
;
700 create_message_body(&body
, attach
, 0);
703 ps_global
->prev_screen
= compose_screen
;
704 if(!(fcc_to_free
= fcc
) && !(role
&& role
->fcc
))
705 fcc
= fcc_arg
; /* Didn't pick up fcc, use given */
708 * check whether a build_address-produced fcc is different from
709 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
712 char *tmp_fcc
= NULL
;
715 tmp_fcc
= get_fcc_based_on_to(outgoing
->to
);
716 if(strcmp(fcc
, tmp_fcc
? tmp_fcc
: ""))
717 fcc_is_sticky
++; /* cause sticky bit to get set */
720 else if((tmp_fcc
= get_fcc(NULL
)) != NULL
&&
721 !strcmp(fcc
, tmp_fcc
)){
728 fs_give((void **)&tmp_fcc
);
731 pine_send(outgoing
, &body
, COMPOSE_MAIL_TITLE
, role
, fcc
,
732 reply
, redraft_pos
, lcc
, custom
,
733 (fcc_is_sticky
? PS_STICKY_FCC
: 0) | (to_is_sticky
? PS_STICKY_TO
: 0));
737 fs_give((void **) &reply
->mailbox
);
739 fs_give((void **) &reply
->origmbox
);
741 fs_give((void **) &reply
->prefix
);
742 if(reply
->data
.uid
.msgs
)
743 fs_give((void **) &reply
->data
.uid
.msgs
);
744 fs_give((void **) &reply
);
748 fs_give((void **)&fcc_to_free
);
751 fs_give((void **)&lcc
);
753 mail_free_envelope(&outgoing
);
754 pine_free_body(&body
);
755 free_redraft_pos(&redraft_pos
);
760 /*----------------------------------------------------------------------
761 Args: stream -- This is where we get the postponed messages from
762 We'll expunge and close it here unless it is mail_stream.
764 These are all return values:
780 redraft(MAILSTREAM
**streamp
, ENVELOPE
**outgoing
, struct mail_bodystruct
**body
,
781 char **fcc
, char **lcc
, REPLY_S
**reply
, REDRAFT_POS_S
**redraft_pos
,
782 PINEFIELD
**custom
, ACTION_S
**role
, int flags
)
788 if(!(streamp
&& *streamp
))
794 * If we're manipulating the current folder, don't bother
798 if(REDRAFT_PPND
&flags
)
799 q_status_message(SM_ORDER
| SM_DING
, 3, 5, _("Empty folder! No messages really postponed!"));
801 q_status_message(SM_ORDER
| SM_DING
, 3, 5, _("Empty folder! No messages really interrupted!"));
803 return(redraft_cleanup(streamp
, FALSE
, flags
));
805 else if(stream
== ps_global
->mail_stream
806 && ps_global
->prev_screen
== mail_index_screen
){
808 * Since the user's got this folder already opened and they're
809 * on a selected message, pick that one rather than rebuild
810 * another index screen...
812 cont_msg
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
814 else if(stream
->nmsgs
> 1L){ /* offer browser ? */
817 if(REDRAFT_PPND
&flags
){ /* set to last message postponed */
818 mn_set_cur(sp_msgmap(stream
),
819 mn_get_revsort(sp_msgmap(stream
))
820 ? 1L : mn_get_total(sp_msgmap(stream
)));
822 else{ /* set to top form letter */
823 mn_set_cur(sp_msgmap(stream
), 1L);
826 clear_index_cache(stream
, 0);
830 ti
= stop_threading_temporarily();
831 rv
= index_lister(ps_global
, NULL
, stream
->mailbox
,
832 stream
, sp_msgmap(stream
));
833 restore_threading(&ti
);
835 cont_msg
= mn_m2raw(sp_msgmap(stream
), mn_get_cur(sp_msgmap(stream
)));
836 if(count_flagged(stream
, F_DEL
)
837 && want_to(INTR_DEL_PMT
, 'n', 0, NO_HELP
, WT_NORM
) == 'n'){
838 if(REDRAFT_PPND
&flags
)
839 q_status_message(SM_ORDER
, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
841 q_status_message(SM_ORDER
, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
849 clear_index_cache(stream
, 0);
852 q_status_message(SM_ORDER
, 0, 3, _("Composition cancelled"));
853 (void) redraft_cleanup(streamp
, FALSE
, flags
);
855 if(!*streamp
&& !ps_global
->mail_stream
){
856 q_status_message2(SM_ORDER
, 3, 7,
857 "No more %.200s, returning to \"%.200s\"",
858 (REDRAFT_PPND
&flags
) ? "postponed messages"
860 ps_global
->inbox_name
);
861 if(ps_global
&& ps_global
->ttyo
){
862 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
863 ps_global
->mangled_footer
= 1;
866 do_broach_folder(ps_global
->inbox_name
,
867 ps_global
->context_list
, NULL
, DB_INBOXWOCNTXT
);
869 ps_global
->next_screen
= mail_index_screen
;
872 return(0); /* special case */
876 if((so
= (void *) so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
)
877 return(redraft_work(streamp
, cont_msg
, outgoing
, body
,
878 fcc
, lcc
, reply
, redraft_pos
, custom
,
886 redraft_prompt(char *type
, char *prompt
, int failure
)
888 if(background_posting(FALSE
)){
889 q_status_message1(SM_ORDER
, 0, 3,
890 _("%s folder unavailable while background posting"),
895 return(want_to(prompt
, 'y', 'x', NO_HELP
, WT_NORM
));
899 /* this is for initializing the fixed header elements in pine_send() */
901 prompt::name::help::prwid::maxlen::realaddr::
902 builder::affected_entry::next_affected::selector::key_label::fileedit::
903 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
904 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
906 static struct headerentry he_template
[]={
907 {"", "X-Auth-Received", NO_HELP
, 10, 0, NULL
,
908 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
909 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
910 {"From : ", "From", h_composer_from
, 10, 0, NULL
,
911 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
912 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
},
913 {"Reply-To: ", "Reply To", h_composer_reply_to
, 10, 0, NULL
,
914 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
915 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
},
916 {"To : ", "To", h_composer_to
, 10, 0, NULL
,
917 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
918 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK
},
919 {"Cc : ", "Cc", h_composer_cc
, 10, 0, NULL
,
920 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
921 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
},
922 {"Bcc : ", "Bcc", h_composer_bcc
, 10, 0, NULL
,
923 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
924 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
},
925 {"Newsgrps: ", "Newsgroups", h_composer_news
, 10, 0, NULL
,
926 news_build
, NULL
, NULL
, news_group_selector
, "To NwsGrps", NULL
, NULL
,
927 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
928 {"Fcc : ", "Fcc", h_composer_fcc
, 10, 0, NULL
,
929 NULL
, NULL
, NULL
, folders_for_fcc
, "To Fldrs", NULL
, fcc_tab_complete
,
930 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE
},
931 {"Lcc : ", "Lcc", h_composer_lcc
, 10, 0, NULL
,
932 build_addr_lcc
, NULL
, NULL
, addr_book_compose_lcc
,"To AddrBk", NULL
, abook_nickname_complete
,
933 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
934 {"Attchmnt: ", "Attchmnt", h_composer_attachment
, 10, 0, NULL
,
935 NULL
, NULL
, NULL
, NULL
, "To Files", NULL
, NULL
,
936 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE
},
937 {"Subject : ", "Subject", h_composer_subject
, 10, 0, NULL
,
938 valid_subject
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
939 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
940 {"", "References", NO_HELP
, 10, 0, NULL
,
941 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
942 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
943 {"", "Date", NO_HELP
, 10, 0, NULL
,
944 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
945 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
946 {"", "In-Reply-To", NO_HELP
, 10, 0, NULL
,
947 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
948 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
949 {"", "Message-ID", NO_HELP
, 10, 0, NULL
,
950 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
951 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
952 {"", "X-Priority", NO_HELP
, 10, 0, NULL
,
953 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
954 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
955 {"", "User-Agent", NO_HELP
, 10, 0, NULL
,
956 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
957 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
958 {"", "To", NO_HELP
, 10, 0, NULL
,
959 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
960 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
961 {"", "X-Post-Error",NO_HELP
, 10, 0, NULL
,
962 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
963 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
964 {"", "X-Reply-UID", NO_HELP
, 10, 0, NULL
,
965 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
966 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
967 {"", "X-Reply-Mbox", NO_HELP
, 10, 0, NULL
,
968 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
969 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
970 {"", "X-SMTP-Server", NO_HELP
, 10, 0, NULL
,
971 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
972 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
973 {"", "X-Cursor-Pos", NO_HELP
, 10, 0, NULL
,
974 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
975 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
976 {"", "X-Our-ReplyTo", NO_HELP
, 10, 0, NULL
,
977 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
978 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
979 {"", OUR_HDRS_LIST
, NO_HELP
, 10, 0, NULL
,
980 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
981 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
982 {"", "X-Auth-Received", NO_HELP
, 10, 0, NULL
,
983 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
984 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
985 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
986 {"", "Sender", NO_HELP
, 10, 0, NULL
,
987 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
988 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
993 static struct headerentry he_custom_addr_templ
={
994 NULL
, NULL
, h_composer_custom_addr
,10, 0, NULL
,
995 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
996 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
};
998 static struct headerentry he_custom_free_templ
={
999 NULL
, NULL
, h_composer_custom_free
,10, 0, NULL
,
1000 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1001 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE
};
1004 /*----------------------------------------------------------------------
1005 Get addressee for message, then post message
1007 Args: outgoing -- Partially formatted outgoing ENVELOPE
1008 body -- Body of outgoing message
1009 prmpt_who -- Optional prompt for optionally_enter call
1010 prmpt_cnf -- Optional prompt for confirmation call
1011 used_tobufval -- The string that the to was eventually set equal to.
1012 This gets passed back if non-NULL on entry.
1013 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1014 SS_NULLRP - Use null return-path so we'll send an
1017 Result: message "To: " field is provided and message is sent or cancelled.
1026 subject passed in, NOT edited but maybe canonized here
1027 to possibly passed in, edited and canonized here
1033 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1034 with the first part TYPETEXT! All newlines in the text here also end with
1037 Returns 0 on success, -1 on failure.
1040 pine_simple_send(ENVELOPE
*outgoing
, /* envelope for outgoing message */
1041 struct mail_bodystruct
**body
,
1045 char **used_tobufval
,
1048 char **tobufp
, *p
, tmp
[MAILTMPLEN
];
1050 int done
= 0, retval
= 0, x
;
1051 int lastrc
, rc
= 0, ku
, i
, resize_len
, result
, fcc_result
;
1054 static HISTORY_S
*history
= NULL
;
1058 ACTION_S
*role
= rolep
? *rolep
: NULL
;
1061 dprint((1,"\n === simple send called === \n"));
1063 memset(&ba_fcc
, 0, sizeof(BUILDER_ARG
));
1065 init_hist(&history
, HISTSIZE
);
1067 header
= pine_simple_send_header(outgoing
, &ba_fcc
.tptr
, &tobufp
);
1069 /*----- Fill in a few general parts of the envelope ----*/
1070 if(!outgoing
->date
){
1071 if(F_ON(F_QUELL_TIMEZONE
, ps_global
))
1072 mail_parameters(NULL
, SET_DISABLE822TZTEXT
, (void *) TRUE
);
1074 rfc822_date(tmp_20k_buf
); /* format and copy new date */
1075 if(F_ON(F_QUELL_TIMEZONE
, ps_global
))
1076 mail_parameters(NULL
, SET_DISABLE822TZTEXT
, (void *) FALSE
);
1078 outgoing
->date
= (unsigned char *) cpystr(tmp_20k_buf
);
1081 if(!outgoing
->from
){
1082 if(role
&& role
->from
){
1083 if(ps_global
->never_allow_changing_from
)
1084 q_status_message(SM_ORDER
, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1086 outgoing
->from
= copyaddrlist(role
->from
);
1089 outgoing
->from
= generate_from();
1092 if(!(flagsarg
& SS_NULLRP
))
1093 outgoing
->return_path
= rfc822_cpy_adr(outgoing
->from
);
1095 ekey
[i
= 0].ch
= ctrl('T');
1097 ekey
[i
].name
= "^T";
1098 ekey
[i
++].label
= N_("To AddrBk");
1100 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
1101 ekey
[i
].ch
= ctrl('I');
1103 ekey
[i
].name
= "TAB";
1104 ekey
[i
++].label
= N_("Complete");
1107 if(nonempty_patterns(ROLE_DO_ROLES
, &pstate
) && first_pattern(&pstate
)){
1108 ekey
[i
].ch
= ctrl('R');
1110 ekey
[i
].name
= "^R";
1111 ekey
[i
++].label
= "Set Role";
1114 ekey
[i
].ch
= KEY_UP
;
1118 ekey
[i
++].label
= "";
1120 ekey
[i
].ch
= KEY_DOWN
;
1123 ekey
[i
++].label
= "";
1127 if(outgoing
->remail
== NULL
)
1128 strcpy(tmp
, _("FORWARD (as e-mail) to : "));
1130 /*----------------------------------------------------------------------
1131 Loop editing the "To: " field until everything goes well
1138 if(outgoing
->remail
){
1140 snprintf(tmp
, sizeof(tmp
), _("BOUNCE (redirect) message using role \"%s\" to : "), role
->nick
);
1142 strncpy(tmp
, _("BOUNCE (redirect) message to : "), sizeof(tmp
));
1143 tmp
[sizeof(tmp
)-1] = '\0';
1148 outgoing2strings(header
, *body
, &messagebuf
, NULL
, 1);
1152 if(flagsarg
& SS_PROMPTFORTO
){
1154 *tobufp
= cpystr("");
1156 resize_len
= MAX(MAXPATH
, strlen(*tobufp
));
1157 fs_resize((void **) tobufp
, resize_len
+1);
1159 if(items_in_hist(history
) > 0){
1160 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
1161 ekey
[ku
].label
= HISTORY_KEYLABEL
;
1162 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
1163 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
1167 ekey
[ku
].label
= "";
1168 ekey
[ku
+1].name
= "";
1169 ekey
[ku
+1].label
= "";
1172 flags
= OE_APPEND_CURRENT
;
1174 rc
= optionally_enter(*tobufp
, -FOOTER_ROWS(ps_global
),
1179 ekey
, help
, &flags
);
1186 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1187 "Internal problem encountered");
1192 case 15 : /* set a role */
1193 {void (*prev_screen
)(struct pine
*) = NULL
, (*redraw
)(void) = NULL
;
1195 redraw
= ps_global
->redrawer
;
1196 ps_global
->redrawer
= NULL
;
1197 prev_screen
= ps_global
->prev_screen
;
1199 ps_global
->next_screen
= SCREEN_FUN_NULL
;
1201 if(role_select_screen(ps_global
, &role
,
1202 outgoing
->remail
? MC_BOUNCE
: MC_FORWARD
) < 0)
1203 cmd_cancelled(_("Set Role"));
1206 role
= combine_inherited_role(role
);
1208 role
= (ACTION_S
*) fs_get(sizeof(*role
));
1209 memset((void *) role
, 0, sizeof(*role
));
1210 role
->nick
= cpystr("Default Role");
1217 ps_global
->next_screen
= prev_screen
;
1218 ps_global
->redrawer
= redraw
;
1219 ps_global
->mangled_screen
= 1;
1221 if(role
&& role
->from
&& !ps_global
->never_allow_changing_from
){
1222 mail_free_address (&outgoing
->from
);
1223 outgoing
->from
= copyaddrlist(role
->from
);
1225 if(rolep
) *rolep
= role
;
1230 if((p
= get_prev_hist(history
, *tobufp
, 0, NULL
)) != NULL
){
1231 strncpy(*tobufp
, p
, resize_len
);
1232 (*tobufp
)[resize_len
-1] = '\0';
1240 if((p
= get_next_hist(history
, *tobufp
, 0, NULL
)) != NULL
){
1241 strncpy(*tobufp
, p
, resize_len
);
1242 (*tobufp
)[resize_len
-1] = '\0';
1251 {void (*redraw
) (void) = ps_global
->redrawer
;
1252 char *returned_addr
= NULL
;
1256 int got_something
= 0;
1258 push_titlebar_state();
1259 returned_addr
= addr_book_bounce();
1262 * Just make it look like user typed this list in.
1266 if((l
=resize_len
) < (len
= strlen(returned_addr
)) + 1){
1268 fs_resize((void **) tobufp
, (size_t) (l
+1));
1271 strncpy(*tobufp
, returned_addr
, l
);
1272 (*tobufp
)[l
] = '\0';
1273 fs_give((void **)&returned_addr
);
1277 pop_titlebar_state();
1279 if((ps_global
->redrawer
= redraw
) != NULL
) /* reset old value, and test */
1280 (*ps_global
->redrawer
)();
1286 if(*tobufp
&& **tobufp
!= '\0'){
1287 char *errbuf
, *addr
;
1290 save_hist(history
, *tobufp
, 0, NULL
);
1295 * If role has an fcc, use it instead of what build_address
1298 if(role
&& role
->fcc
){
1300 fs_give((void **) &ba_fcc
.tptr
);
1302 ba_fcc
.tptr
= cpystr(role
->fcc
);
1305 if(build_address(*tobufp
, &addr
, &errbuf
,
1306 (role
&& role
->fcc
) ? NULL
: &ba_fcc
, NULL
) >= 0){
1310 fs_give((void **)&errbuf
);
1312 if((l
=strlen(*tobufp
)) < (tolen
= strlen(addr
)) + 1){
1314 fs_resize((void **) tobufp
, (size_t) (l
+1));
1317 strncpy(*tobufp
, addr
, l
);
1318 (*tobufp
)[l
] = '\0';
1320 *used_tobufval
= cpystr(addr
);
1322 /* confirm address */
1323 if(flagsarg
& SS_PROMPTFORTO
){
1324 char dsn_string
[30];
1325 int dsn_label
= 0, dsn_show
, i
;
1326 int verbose_label
= 0;
1329 strings2outgoing(header
, body
, NULL
, 0);
1331 if((flagsarg
& SS_PROMPTFORTO
)
1332 && ((x
= check_addresses(header
)) == CA_BAD
1333 || (x
== CA_EMPTY
&& F_OFF(F_FCC_ON_BOUNCE
,
1335 /*--- Addresses didn't check out---*/
1342 opts
[i
++].label
= N_("Yes");
1347 opts
[i
++].label
= N_("No");
1349 call_mailer_flags
&= ~CM_VERBOSE
; /* clear verbose */
1350 if(F_ON(F_VERBOSE_POST
, ps_global
)){
1351 /* setup keymenu slot to toggle verbose mode */
1352 opts
[i
].ch
= ctrl('W');
1354 opts
[i
].name
= "^W";
1355 verbose_label
= i
++;
1356 if(F_ON(F_DSN
, ps_global
)){
1360 opts
[i
++].label
= "";
1364 /* clear DSN flags */
1365 call_mailer_flags
&= ~(CM_DSN_NEVER
| CM_DSN_DELAY
| CM_DSN_SUCCESS
| CM_DSN_FULL
);
1366 if(F_ON(F_DSN
, ps_global
)){
1367 /* setup keymenu slots to toggle dsn bits */
1371 opts
[i
].label
= "DSNOpts";
1376 opts
[i
++].label
= "";
1380 opts
[i
++].label
= "";
1384 opts
[i
++].label
= "";
1392 dsn_show
= (call_mailer_flags
& CM_DSN_SHOW
);
1393 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
1394 "%s%s%s%s%s%sto \"%s\" ? ",
1395 prmpt_cnf
? prmpt_cnf
: "Send message ",
1396 ((call_mailer_flags
& CM_VERBOSE
)
1399 (call_mailer_flags
& CM_VERBOSE
)
1400 ? "in verbose mode" : "",
1401 (dsn_show
&& (call_mailer_flags
& CM_VERBOSE
))
1403 (dsn_show
) ? dsn_string
: "",
1404 ((call_mailer_flags
& CM_VERBOSE
) || dsn_show
)
1408 : (F_ON(F_FCC_ON_BOUNCE
, ps_global
)
1409 && ba_fcc
.tptr
&& ba_fcc
.tptr
[0])
1412 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1414 if((strlen(tmp_20k_buf
) >
1415 ps_global
->ttyo
->screen_cols
- 2) &&
1416 ps_global
->ttyo
->screen_cols
>= 7)
1417 strncpy(tmp_20k_buf
+ps_global
->ttyo
->screen_cols
-7,
1418 "...? ", SIZEOF_20KBUF
-ps_global
->ttyo
->screen_cols
-7);
1420 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1423 opts
[verbose_label
].label
=
1424 /* TRANSLATORS: several possible key labels follow */
1425 (call_mailer_flags
& CM_VERBOSE
) ? N_("Normal") : N_("Verbose");
1427 if(F_ON(F_DSN
, ps_global
)){
1428 if(call_mailer_flags
& CM_DSN_SHOW
){
1429 opts
[dsn_label
].label
=
1430 (call_mailer_flags
& CM_DSN_DELAY
)
1431 ? N_("NoDelay") : N_("Delay");
1432 opts
[dsn_label
+1].ch
= 's';
1433 opts
[dsn_label
+1].label
=
1434 (call_mailer_flags
& CM_DSN_SUCCESS
)
1435 ? N_("NoSuccess") : N_("Success");
1436 opts
[dsn_label
+2].ch
= 'x';
1437 opts
[dsn_label
+2].label
=
1438 (call_mailer_flags
& CM_DSN_NEVER
)
1439 ? N_("ErrRets") : N_("NoErrRets");
1440 opts
[dsn_label
+3].ch
= 'h';
1441 opts
[dsn_label
+3].label
=
1442 (call_mailer_flags
& CM_DSN_FULL
)
1443 ? N_("RetHdrs") : N_("RetFull");
1447 rv
= radio_buttons(tmp_20k_buf
,
1448 -FOOTER_ROWS(ps_global
), opts
,
1449 'y', 'z', NO_HELP
, RB_NORM
);
1450 if(rv
== 'y'){ /* user ACCEPTS! */
1454 else if(rv
== 'n'){ /* Declined! */
1457 else if(rv
== 'z'){ /* Cancelled! */
1460 else if(rv
== 12){ /* flip verbose bit */
1461 if(call_mailer_flags
& CM_VERBOSE
)
1462 call_mailer_flags
&= ~CM_VERBOSE
;
1464 call_mailer_flags
|= CM_VERBOSE
;
1466 else if(call_mailer_flags
& CM_DSN_SHOW
){
1467 if(rv
== 's'){ /* flip success bit */
1468 call_mailer_flags
^= CM_DSN_SUCCESS
;
1469 /* turn off related bits */
1470 if(call_mailer_flags
& CM_DSN_SUCCESS
)
1471 call_mailer_flags
&= ~(CM_DSN_NEVER
);
1473 else if(rv
== 'd'){ /* flip delay bit */
1474 call_mailer_flags
^= CM_DSN_DELAY
;
1475 /* turn off related bits */
1476 if(call_mailer_flags
& CM_DSN_DELAY
)
1477 call_mailer_flags
&= ~(CM_DSN_NEVER
);
1479 else if(rv
== 'x'){ /* flip never bit */
1480 call_mailer_flags
^= CM_DSN_NEVER
;
1481 /* turn off related bits */
1482 if(call_mailer_flags
& CM_DSN_NEVER
)
1483 call_mailer_flags
&= ~(CM_DSN_SUCCESS
| CM_DSN_DELAY
);
1485 else if(rv
== 'h'){ /* flip full bit */
1486 call_mailer_flags
^= CM_DSN_FULL
;
1489 else if(rv
== 'd'){ /* show dsn options */
1490 call_mailer_flags
|= (CM_DSN_SHOW
| CM_DSN_SUCCESS
| CM_DSN_DELAY
| CM_DSN_FULL
);
1493 snprintf(dsn_string
, sizeof(dsn_string
), _("DSN requested[%s%s%s%s]"),
1494 (call_mailer_flags
& CM_DSN_NEVER
)
1496 (call_mailer_flags
& CM_DSN_DELAY
)
1498 (call_mailer_flags
& CM_DSN_SUCCESS
)
1500 (call_mailer_flags
& CM_DSN_NEVER
)
1502 : (call_mailer_flags
& CM_DSN_FULL
) ? "-Full"
1504 dsn_string
[sizeof(dsn_string
)-1] = '\0';
1509 fs_give((void **)&addr
);
1511 if(!(flagsarg
& SS_PROMPTFORTO
) || sendit
){
1513 CONTEXT_S
*fcc_cntxt
= NULL
;
1515 if(F_ON(F_FCC_ON_BOUNCE
, ps_global
)){
1517 fcc
= cpystr(ba_fcc
.tptr
);
1522 * If special name "inbox" then replace it with the
1525 if(ps_global
->VAR_INBOX_PATH
1526 && strucmp(fcc
, ps_global
->inbox_name
) == 0){
1529 replace_fcc
= cpystr(ps_global
->VAR_INBOX_PATH
);
1530 fs_give((void **) &fcc
);
1535 /*---- Check out fcc -----*/
1537 (void) commence_fcc(fcc
, &fcc_cntxt
, FALSE
);
1539 dprint((4,"can't open fcc, cont\n"));
1540 if(!(flagsarg
& SS_PROMPTFORTO
)){
1542 fs_give((void **)&fcc
);
1550 so_truncate(lmc
.so
, fcc_size_guess(*body
) + 2048);
1555 if(!(outgoing
->to
|| outgoing
->cc
|| outgoing
->bcc
1557 q_status_message(SM_ORDER
, 3, 5, _("No recipients specified!"));
1561 if(outgoing
->to
|| outgoing
->cc
|| outgoing
->bcc
){
1562 char **alt_smtp
= NULL
;
1564 if(role
&& role
->smtp
){
1565 if(ps_global
->FIX_SMTP_SERVER
1566 && ps_global
->FIX_SMTP_SERVER
[0])
1567 q_status_message(SM_ORDER
| SM_DING
, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1569 alt_smtp
= role
->smtp
;
1572 result
= call_mailer(header
, *body
, alt_smtp
,
1574 call_mailer_file_result
,
1576 mark_address_failure_for_pico(header
);
1581 if(result
== 1 && !lmc
.so
)
1582 q_status_message(SM_ORDER
, 0, 3, _("Message sent"));
1584 /*----- Was there an fcc involved? -----*/
1588 && pine_rfc822_output(header
, *body
, NULL
, NULL
))){
1591 strncpy(label
, "Fcc", sizeof(label
));
1592 label
[sizeof(label
)-1] = '\0';
1593 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
)){
1594 snprintf(label
+ 3, sizeof(label
)-3, " to %s", fcc
);
1595 label
[sizeof(label
)-1] = '\0';
1598 /* Now actually copy to fcc folder and close */
1600 write_fcc(fcc
, fcc_cntxt
, lmc
.so
, NULL
,
1602 F_ON(F_MARK_FCC_SEEN
, ps_global
)
1605 else if(result
== 0){
1606 q_status_message(SM_ORDER
,3,5,
1607 _("Fcc Failed!. No message saved."));
1609 dprint((1, "explicit fcc write failed!\n"));
1616 dprint((1, "Bounce failed\n"));
1617 if(!(flagsarg
& SS_PROMPTFORTO
))
1622 else if(result
== 1){
1624 q_status_message(SM_ORDER
, 0, 3,
1627 int avail
= ps_global
->ttyo
->screen_cols
-2;
1629 char *part1
= "Message sent and ";
1630 char *part2
= fcc_result
? "" : "NOT ";
1631 char *part3
= "copied to ";
1632 fcclen
= strlen(fcc
);
1634 need
= 2 + strlen(part1
) + strlen(part2
) +
1635 strlen(part3
) + fcclen
;
1637 if(need
> avail
&& fcclen
> 6)
1638 fcclen
-= MIN(fcclen
-6, need
-avail
);
1640 q_status_message4(SM_ORDER
, 0, 3,
1642 part1
, part2
, part3
,
1644 (char *)tmp_20k_buf
,
1646 fcclen
, FrontDots
));
1651 fs_give((void **)&fcc
);
1654 q_status_message(SM_ORDER
, 0, 3, _("Send cancelled"));
1659 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1660 _("Error in address: %s"), errbuf
);
1662 fs_give((void **)&errbuf
);
1664 if(!(flagsarg
& SS_PROMPTFORTO
))
1672 q_status_message(SM_ORDER
| SM_DING
, 3, 5,
1673 _("No addressee! No e-mail sent."));
1682 q_status_message(SM_ORDER
, 0, 3, _("Send cancelled"));
1688 help
= (help
== NO_HELP
)
1689 ? (outgoing
->remail
== NULL
1697 char *new_nickname
= NULL
;
1701 ambiguity
= abook_nickname_complete(*tobufp
, &new_nickname
,
1702 (lastrc
==rc
&& !(flags
& OE_USER_MODIFIED
)), ANC_AFTERCOMMA
);
1705 if((l
=strlen(new_nickname
)) > resize_len
){
1707 fs_resize((void **) tobufp
, resize_len
+1);
1710 strncpy(*tobufp
, new_nickname
, l
);
1711 (*tobufp
)[l
] = '\0';
1714 fs_give((void **) &new_nickname
);
1723 case 4: /* can't suspend */
1731 fs_give((void **)&ba_fcc
.tptr
);
1733 pine_free_env(&header
);
1740 * pine_simple_send_header - generate header suitable for simple_sending
1743 pine_simple_send_header(ENVELOPE
*outgoing
, char **fccp
, char ***tobufpp
)
1747 static struct headerentry he_dummy
;
1749 header
= pine_new_env(outgoing
, fccp
, tobufpp
, NULL
);
1751 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1752 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
1753 if(pf
->type
== Address
&& !strucmp(pf
->name
, "to")){
1754 memset((void *) &he_dummy
, 0, sizeof(he_dummy
));
1755 pf
->extdata
= (void *) &he_dummy
;
1765 /*----------------------------------------------------------------------
1766 Prepare data structures for pico, call pico, then post message
1768 Args: outgoing -- Partially formatted outgoing ENVELOPE
1769 body -- Body of outgoing message
1770 editor_title -- Title for anchor line in composer
1771 fcc_arg -- The file carbon copy field
1772 reply -- Struct describing set of msgs being replied-to
1774 custom -- custom header list.
1777 Result: message is edited, then postponed, cancelled or sent.
1786 subject passed in, edited and cannonized here
1787 to possibly passed in, edited and cannonized here
1788 cc possibly passed in, edited and cannonized here
1789 bcc edited and cannonized here
1790 in_reply_to generated in reply() and passed in
1793 Storage for these fields comes from anywhere outside. It is remalloced
1794 here so the composer can realloc them if needed. The copies here are also
1797 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1798 with the first part TYPETEXT! All newlines in the text here also end with
1801 There's a further assumption that the text in the TYPETEXT part is
1802 stored in a storage object (see filter.c).
1805 pine_send(ENVELOPE
*outgoing
, struct mail_bodystruct
**body
,
1806 char *editor_title
, ACTION_S
*role
, char *fcc_arg
,
1807 REPLY_S
*reply
, REDRAFT_POS_S
*redraft_pos
, char *lcc_arg
,
1808 PINEFIELD
*custom
, int flags
)
1810 int i
, fixed_cnt
, total_cnt
, index
,
1811 editor_result
= 0, body_start
= 0, use_news_order
= 0;
1812 char *p
, *addr
, *fcc
, *fcc_to_free
= NULL
;
1813 char *start_here_name
= NULL
;
1814 char *suggested_nntp_server
= NULL
;
1816 struct headerentry
*he
, *headents
, *he_to
, *he_fcc
, *he_news
= NULL
, *he_lcc
= NULL
,
1818 PINEFIELD
*pfields
, *pf
, *pf_nobody
= NULL
, *pf_to
= NULL
,
1819 *pf_smtp_server
, *pf_nntp_server
,
1820 *pf_fcc
= NULL
, *pf_err
, *pf_uid
, *pf_mbox
, *pf_curpos
,
1821 *pf_ourrep
, *pf_ourhdrs
, **sending_order
;
1823 ADDRESS
*lcc_addr
= NULL
;
1824 ADDRESS
*nobody_addr
= NULL
;
1825 BODY_PARTICULARS_S
*bp
;
1826 STORE_S
*orig_so
= NULL
;
1827 PICO pbuf1
, *save_previous_pbuf
;
1829 REDRAFT_POS_S
*local_redraft_pos
= NULL
;
1831 dprint((1,"\n=== send called ===\n"));
1833 save_previous_pbuf
= pbf
;
1835 standard_picobuf_setup(pbf
);
1838 * Cancel any pending initial commands since pico uses a different
1839 * input routine. If we didn't cancel them, they would happen after
1840 * we returned from the editor, which would be confusing.
1842 if(ps_global
->in_init_seq
){
1843 ps_global
->in_init_seq
= 0;
1844 ps_global
->save_in_init_seq
= 0;
1846 if(ps_global
->initial_cmds
){
1847 if(ps_global
->free_initial_cmds
)
1848 fs_give((void **)&(ps_global
->free_initial_cmds
));
1850 ps_global
->initial_cmds
= 0;
1853 F_SET(F_USE_FK
,ps_global
,ps_global
->orig_use_fkeys
);
1856 #if defined(DOS) || defined(OS2)
1857 if(!dos_valid_from()){
1858 pbf
= save_previous_pbuf
;
1864 pbf
->upload
= (ps_global
->VAR_UPLOAD_CMD
1865 && ps_global
->VAR_UPLOAD_CMD
[0])
1866 ? upload_msg_to_pico
: NULL
;
1869 pbf
->msgntext
= message_format_for_pico
;
1870 pbf
->mimetype
= mime_type_for_pico
;
1871 pbf
->exittest
= send_exit_for_pico
;
1872 pbf
->user_says_noflow
= dont_flow_this_time
;
1873 pbf
->newthread
= new_thread_on_blank_subject
;
1874 ps_global
->newthread
= 0; /* reset this value */
1875 if(F_OFF(F_CANCEL_CONFIRM
, ps_global
))
1876 pbf
->canceltest
= cancel_for_pico
;
1878 pbf
->dict
= (ps_global
->VAR_DICTIONARY
1879 && ps_global
->VAR_DICTIONARY
[0]
1880 && ps_global
->VAR_DICTIONARY
[0][0])
1881 ? ps_global
->VAR_DICTIONARY
: NULL
;
1882 pbf
->chosen_dict
= -1; /* not chosen yet */
1883 #endif /* _WINDOWS */
1884 pbf
->alt_ed
= (ps_global
->VAR_EDITOR
&& ps_global
->VAR_EDITOR
[0] &&
1885 ps_global
->VAR_EDITOR
[0][0])
1886 ? ps_global
->VAR_EDITOR
: NULL
;
1887 pbf
->alt_spell
= (ps_global
->VAR_SPELLER
&& ps_global
->VAR_SPELLER
[0])
1888 ? ps_global
->VAR_SPELLER
: NULL
;
1889 pbf
->always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
1890 pbf
->quote_str
= reply
&& reply
->prefix
? reply
->prefix
: "> ";
1891 /* We actually want to set this only if message we're sending is flowed */
1892 pbf
->strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
1893 pbf
->allow_flowed_text
= (F_OFF(F_QUELL_FLOWED_TEXT
, ps_global
)
1894 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps_global
)
1895 && (strcmp(pbf
->quote_str
, "> ") == 0
1896 || strcmp(pbf
->quote_str
, ">") == 0));
1897 pbf
->edit_offset
= 0;
1898 title
= cpystr(set_titlebar(editor_title
,
1899 ps_global
->mail_stream
,
1900 ps_global
->context_current
,
1901 ps_global
->cur_folder
,ps_global
->msgmap
,
1902 0, FolderName
, 0, 0, NULL
));
1903 pbf
->pine_anchor
= title
;
1905 #if defined(DOS) || defined(OS2)
1906 if(!pbf
->oper_dir
&& ps_global
->VAR_FILE_DIR
){
1907 pbf
->oper_dir
= ps_global
->VAR_FILE_DIR
;
1911 if(redraft_pos
&& editor_title
&& !strcmp(editor_title
, COMPOSE_MAIL_TITLE
))
1912 pbf
->pine_flags
|= P_CHKPTNOW
;
1914 /* NOTE: initial cursor position set below */
1916 dprint((9, "flags: %x\n", pbf
->pine_flags
));
1919 * When user runs compose and the current folder is a newsgroup,
1920 * offer to post to the current newsgroup.
1922 if(!(outgoing
->to
|| (outgoing
->newsgroups
&& *outgoing
->newsgroups
))
1923 && IS_NEWS(ps_global
->mail_stream
)){
1924 char prompt
[200], news_group
[MAILTMPLEN
];
1926 pine_send_newsgroup_name(ps_global
->mail_stream
->mailbox
, news_group
,
1927 sizeof(news_group
));
1930 * Replies don't get this far because To or Newsgroups will already
1931 * be filled in. So must be either ordinary compose or forward.
1932 * Forward sets subject, so use that to tell the difference.
1934 if(news_group
[0] && !outgoing
->subject
){
1937 char *errmsg
= NULL
;
1938 BUILDER_ARG
*fcc_build
= NULL
;
1940 if(F_OFF(F_COMPOSE_TO_NEWSGRP
,ps_global
)){
1941 snprintf(prompt
, sizeof(prompt
),
1942 _("Post to current newsgroup (%s)"), news_group
);
1943 prompt
[sizeof(prompt
)-1] = '\0';
1944 ch
= want_to(prompt
, 'y', 'x', NO_HELP
, WT_NORM
);
1949 if(outgoing
->newsgroups
)
1950 fs_give((void **)&outgoing
->newsgroups
);
1952 if(!fcc_arg
&& !(role
&& role
->fcc
)){
1953 fcc_build
= (BUILDER_ARG
*)fs_get(sizeof(BUILDER_ARG
));
1954 memset((void *)fcc_build
, 0, sizeof(BUILDER_ARG
));
1955 fcc_build
->tptr
= fcc_to_free
;
1958 ret_val
= news_build(news_group
, &outgoing
->newsgroups
,
1959 &errmsg
, fcc_build
, NULL
);
1962 if(outgoing
->newsgroups
)
1963 fs_give((void **)&outgoing
->newsgroups
);
1965 outgoing
->newsgroups
= cpystr(news_group
);
1968 if(!fcc_arg
&& !(role
&& role
->fcc
)){
1969 fcc_arg
= fcc_to_free
= fcc_build
->tptr
;
1970 fs_give((void **)&fcc_build
);
1975 q_status_message(SM_ORDER
, 3, 3, errmsg
);
1976 display_message(NO_OP_COMMAND
);
1979 fs_give((void **)&errmsg
);
1985 q_status_message(SM_ORDER
, 0, 3, _("Message cancelled"));
1986 dprint((4, "=== send: cancelled\n"));
1987 pbf
= save_previous_pbuf
;
1998 if(F_ON(F_PREDICT_NNTP_SERVER
, ps_global
)
1999 && outgoing
->newsgroups
&& *outgoing
->newsgroups
2000 && IS_NEWS(ps_global
->mail_stream
)){
2003 if(mail_valid_net_parse(ps_global
->mail_stream
->original_mailbox
,
2005 if(!strucmp(news_mb
.service
, "nntp")){
2006 if(*ps_global
->mail_stream
->original_mailbox
== '{'){
2007 char *svcp
= NULL
, *psvcp
;
2009 suggested_nntp_server
=
2010 cpystr(ps_global
->mail_stream
->original_mailbox
+ 1);
2011 if((p
= strindex(suggested_nntp_server
, '}')) != NULL
)
2013 for(p
= strindex(suggested_nntp_server
, '/'); p
&& *p
;
2014 p
= strindex(p
, '/')){
2015 /* take out /nntp, which gets added in nntp_open */
2016 if(!struncmp(p
, "/nntp", 5))
2018 else if(!struncmp(p
, "/service=nntp", 13))
2020 else if(!struncmp(p
, "/service=\"nntp\"", 15))
2027 else if(*svcp
== '/' || *svcp
== ':'){
2028 for(psvcp
= p
; *svcp
; svcp
++, psvcp
++)
2037 suggested_nntp_server
= cpystr(news_mb
.orighost
);
2042 * If we don't already have custom headers set and the role has custom
2043 * headers, then incorporate those custom headers into "custom".
2046 PINEFIELD
*dflthdrs
= NULL
, *rolehdrs
= NULL
;
2048 dflthdrs
= parse_custom_hdrs(ps_global
->VAR_CUSTOM_HDRS
, UseAsDef
);
2050 * If we allow the Combine argument here, we're saying that we want to
2051 * combine the values from the envelope and the role for the fields To,
2052 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
2053 * we'll have a To in the envelope because we're replying. If our role also
2054 * has a To action, then Combine would combine those two and offer both
2055 * to the user. We've decided against doing this. Instead, we always use
2056 * Replace, and the role's header value replaces the value from the
2057 * envelope. It might also make sense in some cases to do the opposite,
2058 * which would be treating the role headers as defaults, just like
2061 #ifdef WANT_TO_COMBINE_ADDRESSES
2062 if(role
&& role
->cstm
)
2063 rolehdrs
= parse_custom_hdrs(role
->cstm
, Combine
);
2065 if(role
&& role
->cstm
)
2066 rolehdrs
= parse_custom_hdrs(role
->cstm
, Replace
);
2070 custom
= combine_custom_headers(dflthdrs
, rolehdrs
);
2072 free_prompts(dflthdrs
);
2073 free_customs(dflthdrs
);
2077 free_prompts(rolehdrs
);
2078 free_customs(rolehdrs
);
2085 g_rolenick
= role
? role
->nick
: NULL
;
2087 /* how many fixed fields are there? */
2088 for(fixed_cnt
= 0; pf_template
&& pf_template
[fixed_cnt
].name
; fixed_cnt
++)
2091 total_cnt
= fixed_cnt
+ count_custom_hdrs_pf(custom
,1);
2093 /* the fixed part of the PINEFIELDs */
2094 i
= fixed_cnt
* sizeof(PINEFIELD
);
2095 pfields
= (PINEFIELD
*)fs_get((size_t) i
);
2096 memset(pfields
, 0, (size_t) i
);
2098 /* temporary headerentry array for pico */
2099 i
= (total_cnt
+ 1) * sizeof(struct headerentry
);
2100 headents
= (struct headerentry
*)fs_get((size_t) i
);
2101 memset(headents
, 0, (size_t) i
);
2103 i
= total_cnt
* sizeof(PINEFIELD
*);
2104 sending_order
= (PINEFIELD
**)fs_get((size_t) i
);
2105 memset(sending_order
, 0, (size_t) i
);
2107 pbf
->headents
= headents
;
2108 header
.env
= outgoing
;
2109 header
.local
= pfields
;
2110 header
.sending_order
= sending_order
;
2112 /* custom part of PINEFIELDs */
2113 header
.custom
= custom
;
2119 * For Address types, pf->addr points to an ADDRESS *.
2120 * If that address is in the "outgoing" envelope, it will
2121 * be freed by the caller, otherwise, it should be freed here.
2122 * Pf->textbuf for an Address is used a little to set up a default,
2123 * but then is freed right away below. Pf->scratch is used for a
2124 * pointer to some alloced space for pico to edit in. Addresses in
2125 * the custom area are freed by free_customs().
2127 * For FreeText types, pf->addr is not used. Pf->text points to a
2128 * pointer that points to the text. Pf->textbuf points to a copy of
2129 * the text that must be freed before we leave, otherwise, it is
2130 * probably a pointer into the envelope and that gets freed by the
2133 * He->realaddr is the pointer to the text that pico actually edits.
2136 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2142 if(outgoing
->newsgroups
&& *outgoing
->newsgroups
)
2145 /* initialize the fixed header elements of the two temp arrays */
2146 for(i
=0; i
< fixed_cnt
; i
++, pf
++){
2147 static int news_order
[] = {
2148 N_AUTHRCVD
,N_FROM
, N_REPLYTO
, N_NEWS
, N_TO
, N_CC
, N_BCC
,
2149 N_FCC
, N_LCC
, N_ATTCH
, N_SUBJ
, N_REF
, N_DATE
, N_INREPLY
,
2150 N_MSGID
, N_PRIORITY
, N_USERAGENT
, N_NOBODY
, N_POSTERR
, N_RPLUID
, N_RPLMBOX
,
2151 N_SMTP
, N_NNTP
, N_CURPOS
, N_OURREPLYTO
, N_OURHDRS
2152 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2158 /* slightly different editing order if sending to news */
2159 if(use_news_order
&&
2160 index
>= 0 && index
< sizeof(news_order
)/sizeof(news_order
[0]))
2161 index
= news_order
[i
];
2163 /* copy the templates */
2164 *he
= he_template
[index
];
2166 pf
->name
= cpystr(pf_template
[index
].name
);
2167 if(index
== N_SENDER
&& F_ON(F_USE_SENDER_NOT_X
, ps_global
)){
2168 /* slide string over so it is Sender instead of X-X-Sender */
2169 for(p
= pf
->name
+4; *p
!= '\0'; p
++)
2173 pf
->type
= pf_template
[index
].type
;
2174 pf
->canedit
= pf_template
[index
].canedit
;
2175 pf
->rcptto
= pf_template
[index
].rcptto
;
2176 pf
->writehdr
= pf_template
[index
].writehdr
;
2177 pf
->localcopy
= pf_template
[index
].localcopy
;
2181 he
->rich_header
= view_as_rich(pf
->name
, he
->rich_header
);
2182 if(F_OFF(F_ENABLE_TAB_COMPLETE
,ps_global
))
2183 he
->nickcmpl
= NULL
;
2186 case FreeText
: /* realaddr points to c-client env */
2187 if(index
== N_NEWS
){
2188 sending_order
[1] = pf
;
2189 he
->realaddr
= &outgoing
->newsgroups
;
2192 switch(set_default_hdrval(pf
, custom
)){
2195 fs_give((void **)he
->realaddr
);
2197 *he
->realaddr
= pf
->textbuf
;
2203 if(*he
->realaddr
){ /* combine values */
2204 if(pf
->textbuf
&& *pf
->textbuf
){
2208 l
= strlen(*he
->realaddr
) + strlen(pf
->textbuf
) + 1;
2209 combined_hdr
= (char *) fs_get((l
+1) * sizeof(char));
2210 strncpy(combined_hdr
, *he
->realaddr
, l
);
2211 combined_hdr
[l
] = '\0';
2212 strncat(combined_hdr
, ",", l
+1-1-strlen(combined_hdr
));
2213 combined_hdr
[l
] = '\0';
2214 strncat(combined_hdr
, pf
->textbuf
, l
+1-1-strlen(combined_hdr
));
2215 combined_hdr
[l
] = '\0';
2217 fs_give((void **)he
->realaddr
);
2218 *he
->realaddr
= combined_hdr
;
2219 q_status_message(SM_ORDER
, 3, 3,
2220 "Adding newsgroup from role");
2225 *he
->realaddr
= pf
->textbuf
;
2232 /* if no value, use default */
2234 *he
->realaddr
= pf
->textbuf
;
2244 /* If there is a newsgroup, we'd better show it */
2245 if(outgoing
->newsgroups
&& *outgoing
->newsgroups
)
2246 he
->rich_header
= 0; /* force on by default */
2249 fs_give((void **)&pf
->textbuf
);
2251 pf
->text
= he
->realaddr
;
2253 else if(index
== N_DATE
){
2254 sending_order
[2] = pf
;
2255 pf
->text
= (char **) &outgoing
->date
;
2258 else if(index
== N_INREPLY
){
2259 sending_order
[NN
+9] = pf
;
2260 pf
->text
= &outgoing
->in_reply_to
;
2263 else if(index
== N_MSGID
){
2264 sending_order
[NN
+10] = pf
;
2265 pf
->text
= &outgoing
->message_id
;
2268 else if(index
== N_REF
){
2269 sending_order
[NN
+11] = pf
;
2270 pf
->text
= &outgoing
->references
;
2273 else if(index
== N_PRIORITY
){
2274 sending_order
[NN
+12] = pf
;
2275 pf
->text
= &pf
->textbuf
;
2278 else if(index
== N_USERAGENT
){
2279 sending_order
[NN
+13] = pf
;
2280 pf
->text
= &pf
->textbuf
;
2281 pf
->textbuf
= generate_user_agent();
2284 else if(index
== N_POSTERR
){
2285 sending_order
[NN
+14] = pf
;
2287 pf
->text
= &pf
->textbuf
;
2290 else if(index
== N_RPLUID
){
2291 sending_order
[NN
+15] = pf
;
2293 pf
->text
= &pf
->textbuf
;
2296 else if(index
== N_RPLMBOX
){
2297 sending_order
[NN
+16] = pf
;
2299 pf
->text
= &pf
->textbuf
;
2302 else if(index
== N_SMTP
){
2303 sending_order
[NN
+17] = pf
;
2304 pf_smtp_server
= pf
;
2305 pf
->text
= &pf
->textbuf
;
2308 else if(index
== N_NNTP
){
2309 sending_order
[NN
+18] = pf
;
2310 pf_nntp_server
= pf
;
2311 pf
->text
= &pf
->textbuf
;
2314 else if(index
== N_CURPOS
){
2315 sending_order
[NN
+19] = pf
;
2317 pf
->text
= &pf
->textbuf
;
2320 else if(index
== N_OURREPLYTO
){
2321 sending_order
[NN
+20] = pf
;
2323 pf
->text
= &pf
->textbuf
;
2326 else if(index
== N_OURHDRS
){
2327 sending_order
[NN
+21] = pf
;
2329 pf
->text
= &pf
->textbuf
;
2332 else if(index
== N_AUTHRCVD
){
2333 sending_order
[0] = pf
;
2335 pf
->text
= &pf
->textbuf
;
2339 q_status_message(SM_ORDER
| SM_DING
, 3, 7,
2340 "Botched: Unmatched FreeText header in pine_send");
2345 /* can't do a default for this one */
2347 /* If there is an attachment already, we'd better show them */
2348 if(body
&& *body
&& (*body
)->type
!= TYPETEXT
)
2349 he
->rich_header
= 0; /* force on by default */
2356 sending_order
[3] = pf
;
2357 pf
->addr
= &outgoing
->from
;
2358 if(role
&& role
->from
){
2359 if(ps_global
->never_allow_changing_from
)
2360 q_status_message(SM_ORDER
, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2362 outgoing
->from
= copyaddrlist(role
->from
);
2363 he
->display_it
= 1; /* show it */
2364 he
->rich_header
= 0;
2372 sending_order
[NN
+2] = pf
;
2373 pf
->addr
= &outgoing
->to
;
2374 /* If already set, make it act like we typed it in */
2376 && outgoing
->to
->mailbox
2377 && outgoing
->to
->mailbox
[0]
2378 && flags
& PS_STICKY_TO
)
2386 sending_order
[NN
+5] = pf
;
2388 if(ps_global
->VAR_EMPTY_HDR_MSG
2389 && !ps_global
->VAR_EMPTY_HDR_MSG
[0]){
2393 nobody_addr
= mail_newaddr();
2394 nobody_addr
->next
= mail_newaddr();
2395 nobody_addr
->mailbox
= cpystr(rfc1522_encode(tmp_20k_buf
,
2397 (unsigned char *)(ps_global
->VAR_EMPTY_HDR_MSG
2398 ? ps_global
->VAR_EMPTY_HDR_MSG
2399 : "undisclosed-recipients"),
2400 ps_global
->posting_charmap
));
2401 pf
->addr
= &nobody_addr
;
2407 sending_order
[NN
+3] = pf
;
2408 pf
->addr
= &outgoing
->cc
;
2412 sending_order
[NN
+4] = pf
;
2413 pf
->addr
= &outgoing
->bcc
;
2414 /* if bcc exists, make sure it's exposed so nothing's
2415 * sent by mistake...
2423 sending_order
[NN
+1] = pf
;
2424 pf
->addr
= &outgoing
->reply_to
;
2425 if(role
&& role
->replyto
){
2426 if(outgoing
->reply_to
)
2427 mail_free_address(&outgoing
->reply_to
);
2429 outgoing
->reply_to
= copyaddrlist(role
->replyto
);
2430 he
->display_it
= 1; /* show it */
2431 he
->rich_header
= 0;
2437 sending_order
[NN
+7] = pf
;
2438 pf
->addr
= &lcc_addr
;
2441 build_address(lcc_arg
, &addr
, NULL
, NULL
, NULL
);
2442 rfc822_parse_adrlist(&lcc_addr
, addr
,
2443 ps_global
->maildomain
);
2444 fs_give((void **)&addr
);
2450 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2452 sending_order
[4] = pf
;
2453 pf
->addr
= &outgoing
->sender
;
2458 q_status_message1(SM_ORDER
,3,7,
2459 "Internal error: Address header %s", comatose(index
));
2464 * If this is a reply to news, don't show the regular email
2465 * recipient headers (unless they are non-empty).
2467 if((outgoing
->newsgroups
&& *outgoing
->newsgroups
)
2468 && (index
== N_TO
|| index
== N_CC
2469 || index
== N_BCC
|| index
== N_LCC
)
2470 && (pf
->addr
&& !*pf
->addr
)){
2471 if((ct
=set_default_hdrval(pf
, custom
)) >= UseAsDef
&&
2472 pf
->textbuf
&& *pf
->textbuf
){
2473 removing_trailing_white_space(pf
->textbuf
);
2474 (void)removing_double_quotes(pf
->textbuf
);
2475 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2476 rfc822_parse_adrlist(pf
->addr
, addr
,
2477 ps_global
->maildomain
);
2478 fs_give((void **)&addr
);
2483 he
->rich_header
= 1; /* hide */
2487 * If this address doesn't already have a value, then we check
2488 * for a default value assigned by the user.
2490 else if(pf
->addr
&& !*pf
->addr
){
2491 if((ct
=set_default_hdrval(pf
, custom
)) >= UseAsDef
&&
2493 (!ps_global
->never_allow_changing_from
&&
2494 F_ON(F_ALLOW_CHANGING_FROM
, ps_global
))) &&
2495 pf
->textbuf
&& *pf
->textbuf
){
2497 removing_trailing_white_space(pf
->textbuf
);
2498 (void)removing_double_quotes(pf
->textbuf
);
2501 * Try to set To based on Lcc. Don't attempt Fcc.
2503 if(index
== N_LCC
&& !he_to
->sticky
&& pf_to
&& pf_to
->addr
){
2504 BUILDER_ARG
*barg
= NULL
;
2508 ppp
= addr_list_string(*pf_to
->addr
, NULL
, 1);
2513 barg
= (BUILDER_ARG
*) fs_get(sizeof(*barg
));
2514 memset(barg
, 0, sizeof(*barg
));
2515 barg
->me
= &(he
->bldr_private
);
2516 barg
->aff
= &(he_to
->bldr_private
);
2517 barg
->tptr
= cpystr(ppp
);
2519 build_addr_lcc(pf
->textbuf
, &addr
, NULL
, barg
, NULL
);
2522 rfc822_parse_adrlist(pf
->addr
, addr
,
2523 ps_global
->maildomain
);
2525 fs_give((void **) &addr
);
2530 if(barg
&& barg
->tptr
&& strcmp(ppp
, barg
->tptr
)){
2533 rfc822_parse_adrlist(&a
, barg
->tptr
,
2534 ps_global
->maildomain
);
2537 mail_free_address(pf_to
->addr
);
2545 fs_give((void **) &barg
->tptr
);
2547 fs_give((void **) &barg
);
2551 fs_give((void **) &ppp
);
2554 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2555 rfc822_parse_adrlist(pf
->addr
, addr
,
2556 ps_global
->maildomain
);
2558 fs_give((void **) &addr
);
2565 /* if we still don't have a from */
2566 if(index
== N_FROM
&& !*pf
->addr
)
2567 *pf
->addr
= generate_from();
2571 * Addr is already set in the rest of the cases.
2573 else if((index
== N_FROM
|| index
== N_REPLYTO
) && pf
->addr
){
2574 ADDRESS
*adr
= NULL
;
2577 * We get to this case of the ifelse if the from or reply-to
2578 * addr was set by a role above.
2581 /* figure out the default value */
2582 (void)set_default_hdrval(pf
, custom
);
2583 if(pf
->textbuf
&& *pf
->textbuf
){
2584 removing_trailing_white_space(pf
->textbuf
);
2585 (void)removing_double_quotes(pf
->textbuf
);
2586 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2587 rfc822_parse_adrlist(&adr
, addr
,
2588 ps_global
->maildomain
);
2589 fs_give((void **)&addr
);
2592 /* if value set by role is different from default, show it */
2593 if(adr
&& !address_is_same(*pf
->addr
, adr
))
2594 he
->display_it
= 1; /* start this off showing */
2597 if(!(*pf
->addr
)->mailbox
){
2598 fs_give((void **)pf
->addr
);
2603 mail_free_address(&adr
);
2605 else if((index
== N_TO
|| index
== N_CC
|| index
== N_BCC
)
2607 ADDRESS
*a
= NULL
, **tail
;
2610 * These three are different from the others because we
2611 * might add the addresses to what is already there instead
2615 switch(set_default_hdrval(pf
, custom
)){
2618 mail_free_address(pf
->addr
);
2620 removing_trailing_white_space(pf
->textbuf
);
2621 (void)removing_double_quotes(pf
->textbuf
);
2622 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2623 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
2624 fs_give((void **)&addr
);
2629 removing_trailing_white_space(pf
->textbuf
);
2630 (void)removing_double_quotes(pf
->textbuf
);
2631 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2632 rfc822_parse_adrlist(&a
, addr
, ps_global
->maildomain
);
2633 fs_give((void **)&addr
);
2636 for(tail
= pf
->addr
; *tail
; tail
= &(*tail
)->next
)
2638 *tail
= reply_cp_addr(ps_global
, 0, NULL
, NULL
,
2639 *pf
->addr
, NULL
, a
, RCA_ALL
);
2640 q_status_message(SM_ORDER
, 3, 3,
2641 "Adding addresses from role");
2642 mail_free_address(&a
);
2652 he
->display_it
= 1; /* start this off showing */
2655 switch(set_default_hdrval(pf
, custom
)){
2659 mail_free_address(pf
->addr
);
2661 removing_trailing_white_space(pf
->textbuf
);
2662 (void)removing_double_quotes(pf
->textbuf
);
2663 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2664 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
2665 fs_give((void **)&addr
);
2677 if(pf
->addr
&& *pf
->addr
&& !(*pf
->addr
)->mailbox
){
2678 mail_free_address(pf
->addr
);
2679 he
->display_it
= 1; /* start this off showing */
2682 if(pf
->textbuf
) /* free default value in any case */
2683 fs_give((void **)&pf
->textbuf
);
2685 /* outgoing2strings will alloc the string pf->scratch below */
2686 he
->realaddr
= &pf
->scratch
;
2690 sending_order
[NN
+8] = pf
;
2692 if(role
&& role
->fcc
)
2695 fcc
= get_fcc(fcc_arg
);
2698 fs_give((void **)&fcc_to_free
);
2702 if(((flags
& PS_STICKY_FCC
) && fcc
[0]) || (role
&& role
->fcc
))
2708 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
) != 0)
2709 he
->display_it
= 1; /* start this off showing */
2711 he
->realaddr
= &fcc
;
2717 sending_order
[NN
+6] = pf
;
2719 switch(set_default_hdrval(pf
, custom
)){
2722 pf
->scratch
= pf
->textbuf
;
2725 if(outgoing
->subject
)
2726 fs_give((void **)&outgoing
->subject
);
2732 /* if no value, use default */
2733 if(outgoing
->subject
){
2734 pf
->scratch
= cpystr(outgoing
->subject
);
2737 pf
->scratch
= pf
->textbuf
;
2744 he
->realaddr
= &pf
->scratch
;
2745 pf
->text
= &outgoing
->subject
;
2749 q_status_message1(SM_ORDER
,3,7,
2750 "Unknown header type %d in pine_send",
2756 * We may or may not want to give the user the chance to edit
2757 * the From and Reply-To lines. If they are listed in either
2758 * Default-composer-hdrs or Customized-hdrs, then they can edit
2760 * If canedit is not set, that means that this header is not in
2761 * the user's customized-hdrs. If rich_header is set, that
2762 * means that this header is not in the user's
2763 * default-composer-hdrs (since From and Reply-To are rich
2764 * by default). So, don't give it an he to edit with in that case.
2766 * For other types, just not setting canedit will cause it to be
2767 * uneditable, regardless of what the user does.
2771 /* to allow it, we let this fall through to the reply-to case below */
2772 if(ps_global
->never_allow_changing_from
||
2773 (F_OFF(F_ALLOW_CHANGING_FROM
, ps_global
) &&
2774 !(role
&& role
->from
))){
2775 if(pf
->canedit
|| !he
->rich_header
)
2776 q_status_message(SM_ORDER
, 3, 3,
2777 _("Not allowed to change header \"From\""));
2779 memset(he
, 0, (size_t)sizeof(*he
));
2785 if(!pf
->canedit
&& he
->rich_header
){
2786 memset(he
, 0, (size_t)sizeof(*he
));
2798 memset(he
, 0, (size_t)sizeof(*he
));
2809 * This is so the builder can tell the composer to fill the affected
2810 * field based on the value in the field on the left.
2812 * Note that this mechanism isn't completely general. Each entry has
2813 * only a single next_affected, so if some other entry points an
2814 * affected entry at an entry with a next_affected, they all inherit
2815 * that next_affected. Since this isn't used much a careful ordering
2816 * of the affected fields should make it a sufficient mechanism.
2818 he_to
->affected_entry
= he_fcc
;
2819 he_news
->affected_entry
= he_fcc
;
2820 he_lcc
->affected_entry
= he_to
;
2821 he_to
->next_affected
= he_fcc
;
2823 (--pf
)->next
= (total_cnt
!= fixed_cnt
) ? header
.custom
: NULL
;
2825 i
--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2827 * Set up headerentries for custom fields.
2828 * NOTE: "i" is assumed to now index first custom field in sending
2831 for(pf
= pf
->next
; pf
&& pf
->name
; pf
= pf
->next
){
2845 if(pf
->addr
){ /* better be set */
2846 sending_order
[i
++] = pf
;
2847 *he
= he_custom_addr_templ
;
2848 /* change default text into an ADDRESS */
2849 /* strip quotes around whole default */
2850 removing_trailing_white_space(pf
->textbuf
);
2851 (void)removing_double_quotes(pf
->textbuf
);
2852 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2853 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
2854 fs_give((void **)&addr
);
2856 fs_give((void **)&pf
->textbuf
);
2858 he
->realaddr
= &pf
->scratch
;
2859 if(F_OFF(F_ENABLE_TAB_COMPLETE
,ps_global
))
2860 he
->nickcmpl
= NULL
;
2866 sending_order
[i
++] = pf
;
2867 *he
= he_custom_free_templ
;
2868 he
->realaddr
= &pf
->textbuf
;
2869 pf
->text
= &pf
->textbuf
;
2870 if(((!pf
->val
|| !pf
->val
[0]) && pf
->textbuf
&& pf
->textbuf
[0]) ||
2871 (pf
->val
&& (!pf
->textbuf
|| strcmp(pf
->textbuf
, pf
->val
))))
2872 he
->display_it
= 1; /* show it */
2877 q_status_message1(SM_ORDER
,0,7,"Unknown custom header type %d",
2882 he
->name
= pf
->name
;
2884 /* use first 8 characters for prompt */
2885 he
->prompt
= cpystr(" : ");
2886 strncpy(he
->prompt
, he
->name
, MIN(strlen(he
->name
), he
->prwid
- 2));
2888 he
->rich_header
= view_as_rich(he
->name
, he
->rich_header
);
2893 * Make sure at least *one* field is displayable...
2895 for(index
= -1, i
=0, pf
=header
.local
; pf
&& pf
->name
; pf
=pf
->next
, i
++)
2896 if(HE(pf
) && !HE(pf
)->rich_header
){
2902 * None displayable!!! Warn and display defaults.
2905 q_status_message(SM_ORDER
,0,5,
2906 "No default-composer-hdrs matched, displaying defaults");
2907 for(i
= 0, pf
= header
.local
; pf
; pf
= pf
->next
, i
++)
2908 if((i
== N_TO
|| i
== N_CC
|| i
== N_SUBJ
|| i
== N_ATTCH
)
2910 HE(pf
)->rich_header
= 0;
2914 * Save information about body which set_mime_type_by_grope might change.
2915 * Then, if we get an error sending, we reset these things so that
2916 * grope can do it's thing again after we edit some more.
2918 if ((*body
)->type
== TYPEMULTIPART
)
2919 bp
= save_body_particulars(&(*body
)->nested
.part
->body
);
2921 bp
= save_body_particulars(*body
);
2924 local_redraft_pos
= redraft_pos
;
2926 /*----------------------------------------------------------------------
2927 Loop calling the editor until everything goes well
2930 int saved_user_timeout
;
2932 /* Reset body to what it was when we started. */
2933 if ((*body
)->type
== TYPEMULTIPART
)
2934 reset_body_particulars(bp
, &(*body
)->nested
.part
->body
);
2936 reset_body_particulars(bp
,*body
);
2938 * set initial cursor position based on how many times we've been
2941 if(reply
&& reply
->pseudo
){
2942 pbf
->pine_flags
|= reply
->data
.pico_flags
;
2944 else if(body_start
){
2945 pbf
->pine_flags
|= P_BODY
;
2946 body_start
= 0; /* maybe not next time */
2948 else if(local_redraft_pos
){
2949 pbf
->edit_offset
= local_redraft_pos
->offset
;
2950 /* set the start_here bit in correct header */
2951 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
2952 if(strcmp(pf
->name
, local_redraft_pos
->hdrname
) == 0
2954 HE(pf
)->start_here
= 1;
2958 /* If didn't find it, we start in body. */
2959 if(!pf
|| !pf
->name
)
2960 pbf
->pine_flags
|= P_BODY
;
2962 else if(reply
&& (!reply
->forw
&& !reply
->forwarded
)){
2963 pbf
->pine_flags
|= P_BODY
;
2966 /* in case these were turned on in previous pass through loop */
2968 pf_nobody
->writehdr
= 0;
2969 pf_nobody
->localcopy
= 0;
2973 pf_fcc
->localcopy
= 0;
2976 * If a sending attempt failed after we passed the message text
2977 * thru a user-defined filter, "orig_so" points to the original
2978 * text. Replace the body's encoded data with the original...
2981 STORE_S
**so
= (STORE_S
**)(((*body
)->type
== TYPEMULTIPART
)
2982 ? &(*body
)->nested
.part
->body
.contents
.text
.data
2983 : &(*body
)->contents
.text
.data
);
2990 * Convert the envelope and body to the string format that
2993 outgoing2strings(&header
, *body
, &pbf
->msgtext
, &pbf
->attachments
, 0);
2995 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
){
2997 * If this isn't the first time through this loop, we may have
2998 * freed some of the FreeText headers below so that they wouldn't
2999 * show up as empty headers in the finished message. Need to
3000 * alloc them again here so they can be edited.
3002 if(pf
->type
== FreeText
&& HE(pf
) && !*HE(pf
)->realaddr
)
3003 *HE(pf
)->realaddr
= cpystr("");
3005 if(pf
->type
!= Attachment
&& HE(pf
) && *HE(pf
)->realaddr
)
3006 HE(pf
)->maxlen
= strlen(*HE(pf
)->realaddr
);
3010 * If From is exposed, probably by a role, then start the cursor
3011 * on the first line which isn't filled in. If it isn't, then we
3012 * don't move the cursor, mostly for back-compat.
3014 if((!reply
|| reply
->forw
|| reply
->forwarded
) &&
3015 !local_redraft_pos
&& !(pbf
->pine_flags
& P_BODY
) && he_from
&&
3016 (he_from
->display_it
|| !he_from
->rich_header
)){
3017 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
3019 (HE(pf
)->display_it
|| !HE(pf
)->rich_header
) &&
3021 (!*HE(pf
)->realaddr
|| !**HE(pf
)->realaddr
)){
3022 HE(pf
)->start_here
= 1;
3028 mswin_setwindowmenu (MENU_COMPOSER
);
3031 cancel_busy_cue(-1);
3032 flush_status_messages(1);
3034 /* turn off user input timeout when in composer */
3035 saved_user_timeout
= ps_global
->hours_to_timeout
;
3036 ps_global
->hours_to_timeout
= 0;
3037 dprint((1, "\n ---- COMPOSER ----\n"));
3038 editor_result
= pico(pbf
);
3039 dprint((4, "... composer returns (0x%x)\n", editor_result
));
3040 ps_global
->hours_to_timeout
= saved_user_timeout
;
3043 mswin_setwindowmenu (MENU_DEFAULT
);
3045 fix_windsize(ps_global
);
3048 * Only reinitialize signals if we didn't receive an interesting
3049 * one while in pico, since pico's return is part of processing that
3050 * signal and it should continue to be ignored.
3052 if(!(editor_result
& COMP_GOTHUP
))
3053 init_signals(); /* Pico has it's own signal stuff */
3056 * We're going to save in DEADLETTER. Dump attachments first.
3058 if(editor_result
& COMP_CANCEL
)
3059 free_attachment_list(&pbf
->attachments
);
3061 /* Turn strings back into structures */
3062 strings2outgoing(&header
, body
, pbf
->attachments
, flowing_requested
);
3064 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3065 if(outgoing
->newsgroups
){
3066 sqzspaces(outgoing
->newsgroups
);
3067 if(!outgoing
->newsgroups
[0])
3068 fs_give((void **)&(outgoing
->newsgroups
));
3071 /* Make subject NULL if it is "" (so won't show up in headers) */
3072 if(outgoing
->subject
&& !outgoing
->subject
[0])
3073 fs_give((void **)&(outgoing
->subject
));
3075 /* remove custom fields that are empty */
3076 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
){
3077 if(pf
->type
== FreeText
&& pf
->textbuf
){
3078 if(pf
->textbuf
[0] == '\0'){
3079 fs_give((void **)&pf
->textbuf
);
3085 removing_trailing_white_space(fcc
);
3087 /*-------- Stamp it with a current date -------*/
3088 if(outgoing
->date
) /* update old date */
3089 fs_give((void **)&(outgoing
->date
));
3091 if(F_ON(F_QUELL_TIMEZONE
, ps_global
))
3092 mail_parameters(NULL
, SET_DISABLE822TZTEXT
, (void *) TRUE
);
3094 rfc822_date(tmp_20k_buf
); /* format and copy new date */
3095 if(F_ON(F_QUELL_TIMEZONE
, ps_global
))
3096 mail_parameters(NULL
, SET_DISABLE822TZTEXT
, (void *) FALSE
);
3098 outgoing
->date
= (unsigned char *) cpystr(tmp_20k_buf
);
3100 /* Set return_path based on From which is going to be used */
3101 if(outgoing
->return_path
)
3102 mail_free_address(&outgoing
->return_path
);
3104 outgoing
->return_path
= rfc822_cpy_adr(outgoing
->from
);
3107 * Don't ever believe the sender that is there.
3108 * If From doesn't look quite right, generate our own sender.
3110 if(outgoing
->sender
)
3111 mail_free_address(&outgoing
->sender
);
3114 * If the LHS of the address doesn't match, or the RHS
3115 * doesn't match one of localdomain or hostname,
3116 * then add a sender line (really X-X-Sender).
3118 * Don't add a personal_name since the user can change that.
3120 if(F_OFF(F_DISABLE_SENDER
, ps_global
)
3123 || !outgoing
->from
->mailbox
3124 || strucmp(outgoing
->from
->mailbox
, ps_global
->VAR_USER_ID
) != 0
3125 || !outgoing
->from
->host
3126 || !(strucmp(outgoing
->from
->host
, ps_global
->localdomain
) == 0
3127 || strucmp(outgoing
->from
->host
, ps_global
->hostname
) == 0))){
3129 outgoing
->sender
= mail_newaddr();
3130 outgoing
->sender
->mailbox
= cpystr(ps_global
->VAR_USER_ID
);
3131 outgoing
->sender
->host
= cpystr(ps_global
->hostname
);
3134 if(ps_global
->newthread
){
3135 if(outgoing
->in_reply_to
) fs_give((void **)&outgoing
->in_reply_to
);
3136 if(outgoing
->references
) fs_give((void **)&outgoing
->references
);
3139 /*----- Message is edited, now decide what to do with it ----*/
3140 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
3141 /*=========== Postpone or Interrupted message ============*/
3142 CONTEXT_S
*fcc_cntxt
= NULL
;
3143 char folder
[MAXPATH
+1];
3147 dprint((4, "pine_send:%s handling\n",
3148 (editor_result
& COMP_SUSPEND
)
3150 : (editor_result
& COMP_GOTHUP
)
3152 : (editor_result
& COMP_CANCEL
)
3153 ? "CANCEL" : "HUH?"));
3154 if((editor_result
& COMP_CANCEL
)
3155 && (F_ON(F_QUELL_DEAD_LETTER
, ps_global
)
3156 || ps_global
->deadlets
== 0)){
3157 q_status_message(SM_ORDER
, 0, 3, "Message cancelled");
3162 * The idea here is to use the Fcc: writing facility
3163 * to append to the special postponed message folder...
3165 * NOTE: the strategy now is to write the message and
3166 * all attachments as they exist at composition time.
3167 * In other words, attachments are postponed by value
3168 * and not reference. This may change later, but we'll
3169 * need a local "message/external-body" type that
3170 * outgoing2strings knows how to properly set up for
3171 * the composer. Maybe later...
3176 if(F_ON(F_COMPOSE_REJECTS_UNQUAL
, ps_global
)
3177 && (editor_result
& COMP_SUSPEND
)
3178 && (check_addresses(&header
) == CA_BAD
)){
3179 /*--- Addresses didn't check out---*/
3180 q_status_message(SM_ORDER
, 7, 7,
3181 _("Not allowed to postpone message until addresses are qualified"));
3186 * Build the local message copy so.
3188 * In the HUP case, we'll write the bezerk delimiter by
3189 * hand and output the message directly into the folder.
3190 * It's not only faster, we don't have to worry about
3191 * c-client reentrance and less hands paw over the data so
3192 * there's less chance of a problem.
3194 * In the Postpone case, just create it if the user wants to
3195 * and create a temporary storage object to write into. */
3197 lmc
.all_written
= lmc
.text_only
= lmc
.text_written
= 0;
3198 if(editor_result
& (COMP_GOTHUP
| COMP_CANCEL
)){
3200 time_t now
= time((time_t *)0);
3202 #if defined(DOS) || defined(OS2)
3204 * we can't assume anything about root or home dirs, so
3205 * just plunk it down in the same place as the pinerc
3207 if(!getenv("HOME")){
3208 char *lc
= last_cmpnt(ps_global
->pinerc
);
3211 strncpy(folder
,ps_global
->pinerc
,
3212 MIN(lc
-ps_global
->pinerc
,sizeof(folder
)-1));
3213 folder
[MIN(lc
-ps_global
->pinerc
,sizeof(folder
)-1)]='\0';
3216 strncat(folder
, (editor_result
& COMP_GOTHUP
)
3217 ? INTERRUPTED_MAIL
: DEADLETTER
,
3218 sizeof(folder
)-strlen(folder
)-1);
3223 ps_global
->VAR_OPER_DIR
3224 ? ps_global
->VAR_OPER_DIR
: ps_global
->home_dir
,
3225 (editor_result
& COMP_GOTHUP
)
3226 ? INTERRUPTED_MAIL
: DEADLETTER
,
3229 if(editor_result
& COMP_CANCEL
){
3230 char filename
[MAXPATH
+1], newfname
[MAXPATH
+1], nbuf
[5];
3232 if(strlen(folder
) + 1 < sizeof(filename
))
3233 for(i
= ps_global
->deadlets
- 1; i
> 0 && i
< 9; i
--){
3234 strncpy(filename
, folder
, sizeof(filename
));
3235 filename
[sizeof(filename
)-1] = '\0';
3236 strncpy(newfname
, filename
, sizeof(newfname
));
3237 newfname
[sizeof(newfname
)-1] = '\0';
3240 snprintf(nbuf
, sizeof(nbuf
), "%d", i
);
3241 nbuf
[sizeof(nbuf
)-1] = '\0';
3242 strncat(filename
, nbuf
,
3243 sizeof(filename
)-strlen(filename
)-1);
3244 filename
[sizeof(filename
)-1] = '\0';
3247 snprintf(nbuf
, sizeof(nbuf
), "%d", i
+1);
3248 nbuf
[sizeof(nbuf
)-1] = '\0';
3249 strncat(newfname
, nbuf
,
3250 sizeof(newfname
)-strlen(newfname
)-1);
3251 newfname
[sizeof(newfname
)-1] = '\0';
3252 (void) rename_file(filename
, newfname
);
3258 newfile
= can_access(folder
, ACCESS_EXISTS
);
3260 if((lmc
.so
= so_get(FCC_SOURCE
, NULL
, WRITE_ACCESS
)) != NULL
){
3261 if (outgoing
->from
){
3262 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%sFrom %s@%s %.24s\015\012",
3263 newfile
? "" : "\015\012",
3264 outgoing
->from
->mailbox
,
3265 outgoing
->from
->host
, ctime(&now
));
3266 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
3267 if(!so_puts(lmc
.so
, tmp_20k_buf
)){
3268 if(editor_result
& COMP_CANCEL
)
3269 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
3270 "Can't write \"%s\": %s",
3271 folder
, error_description(errno
));
3273 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3274 folder
? folder
: "?",
3275 error_description(errno
)));
3280 else{ /* Must be COMP_SUSPEND */
3281 if(!ps_global
->VAR_POSTPONED_FOLDER
3282 || !ps_global
->VAR_POSTPONED_FOLDER
[0]){
3283 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3284 _("No postponed file defined"));
3289 * Store the cursor position
3291 * First find the header entry with the start_here
3292 * bit set, if any. This means the editor is telling
3293 * us to start on this header field next time.
3295 start_here_name
= NULL
;
3296 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
3297 if(HE(pf
) && HE(pf
)->start_here
){
3298 start_here_name
= pf
->name
;
3302 /* If there wasn't one, ":" means we start in the body. */
3303 if(!start_here_name
|| !*start_here_name
)
3304 start_here_name
= ":";
3306 if(ps_global
->VAR_FORM_FOLDER
3307 && ps_global
->VAR_FORM_FOLDER
[0]
3308 && postpone_prompt() == 'f'){
3309 strncpy(folder
, ps_global
->VAR_FORM_FOLDER
,
3311 folder
[sizeof(folder
)-1] = '\0';
3312 strncpy(label
, "form letter", sizeof(label
));
3313 label
[sizeof(label
)-1] = '\0';
3316 strncpy(folder
, ps_global
->VAR_POSTPONED_FOLDER
,
3318 folder
[sizeof(folder
)-1] = '\0';
3319 strncpy(label
, "postponed message", sizeof(label
));
3320 label
[sizeof(label
)-1] = '\0';
3323 lmc
.so
= open_fcc(folder
,&fcc_cntxt
, 1, NULL
, NULL
);
3330 /* copy fcc line to postponed or interrupted folder */
3332 pf_fcc
->localcopy
= 1;
3334 /* plug error into header for later display to user */
3335 if((editor_result
& ~0xff) && (lmq
= last_message_queued()) != NULL
){
3336 pf_err
->writehdr
= 1;
3337 pf_err
->localcopy
= 1;
3338 pf_err
->textbuf
= lmq
;
3342 * if reply, write (UID)folder header field so we can
3343 * later flag the replied-to message \\ANSWERED
3344 * DON'T save MSGNO's.
3346 if(reply
&& reply
->uid
){
3347 char uidbuf
[MAILTMPLEN
], *p
;
3350 for(i
= 0L, p
= tmp_20k_buf
; reply
->data
.uid
.msgs
[i
]; i
++){
3352 sstrncpy(&p
, ",", SIZEOF_20KBUF
-(p
-tmp_20k_buf
));
3354 sstrncpy(&p
,ulong2string(reply
->data
.uid
.msgs
[i
]),SIZEOF_20KBUF
-(p
-tmp_20k_buf
));
3357 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
3359 pf_uid
->writehdr
= 1;
3360 pf_uid
->localcopy
= 1;
3361 snprintf(uidbuf
, sizeof(uidbuf
), "(%s%s%s)(%ld %lu %s)%s",
3362 reply
->prefix
? int2string(strlen(reply
->prefix
))
3363 : (reply
->forwarded
) ? "": "0 ",
3364 reply
->prefix
? " " : "",
3365 reply
->prefix
? reply
->prefix
: "",
3366 i
, reply
->data
.uid
.validity
,
3367 tmp_20k_buf
, reply
->mailbox
);
3368 uidbuf
[sizeof(uidbuf
)-1] = '\0';
3369 pf_uid
->textbuf
= cpystr(uidbuf
);
3372 * Logically, this ought to be part of pf_uid, but this
3373 * was added later and so had to be in a separate header
3374 * for backwards compatibility.
3376 pf_mbox
->writehdr
= 1;
3377 pf_mbox
->localcopy
= 1;
3378 pf_mbox
->textbuf
= cpystr(reply
->origmbox
3383 /* Save cursor position */
3384 if(start_here_name
&& *start_here_name
){
3385 char curposbuf
[MAILTMPLEN
];
3387 pf_curpos
->writehdr
= 1;
3388 pf_curpos
->localcopy
= 1;
3389 snprintf(curposbuf
, sizeof(curposbuf
), "%s %ld", start_here_name
,
3391 curposbuf
[sizeof(curposbuf
)-1] = '\0';
3392 pf_curpos
->textbuf
= cpystr(curposbuf
);
3396 * Work around c-client reply-to bug. C-client will
3397 * return a reply_to in an envelope even if there is
3398 * no reply-to header field. We want to note here whether
3399 * the reply-to is real or not.
3401 if(outgoing
->reply_to
|| hdr_is_in_list("reply-to", custom
)){
3402 pf_ourrep
->writehdr
= 1;
3403 pf_ourrep
->localcopy
= 1;
3404 if(outgoing
->reply_to
)
3405 pf_ourrep
->textbuf
= cpystr("Full");
3407 pf_ourrep
->textbuf
= cpystr("Empty");
3410 /* Save the role-specific smtp server */
3411 if(role
&& role
->smtp
&& role
->smtp
[0]){
3412 char *q
, *smtp
= NULL
;
3417 * Turn the list of smtp servers into a space-
3418 * delimited list in a single string.
3420 for(lp
= role
->smtp
; (q
= *lp
) != NULL
; lp
++)
3421 len
+= (strlen(q
) + 1);
3424 smtp
= (char *) fs_get(len
* sizeof(char));
3426 for(lp
= role
->smtp
; (q
= *lp
) != NULL
; lp
++){
3427 if(lp
!= role
->smtp
)
3428 strncat(smtp
, " ", len
-strlen(smtp
)-1);
3430 strncat(smtp
, q
, len
-strlen(smtp
)-1);
3436 pf_smtp_server
->writehdr
= 1;
3437 pf_smtp_server
->localcopy
= 1;
3439 pf_smtp_server
->textbuf
= smtp
;
3441 pf_smtp_server
->textbuf
= cpystr("");
3444 /* Save the role-specific nntp server */
3445 if(suggested_nntp_server
||
3446 (role
&& role
->nntp
&& role
->nntp
[0])){
3447 char *q
, *nntp
= NULL
;
3451 if(role
&& role
->nntp
&& role
->nntp
[0]){
3453 * Turn the list of nntp servers into a space-
3454 * delimited list in a single string.
3456 for(lp
= role
->nntp
; (q
= *lp
) != NULL
; lp
++)
3457 len
+= (strlen(q
) + 1);
3460 nntp
= (char *) fs_get(len
* sizeof(char));
3462 for(lp
= role
->nntp
; (q
= *lp
) != NULL
; lp
++){
3463 if(lp
!= role
->nntp
)
3464 strncat(nntp
, " ", len
-strlen(nntp
)-1);
3466 strncat(nntp
, q
, len
-strlen(nntp
)-1);
3473 nntp
= cpystr(suggested_nntp_server
);
3475 pf_nntp_server
->writehdr
= 1;
3476 pf_nntp_server
->localcopy
= 1;
3478 pf_nntp_server
->textbuf
= nntp
;
3480 pf_nntp_server
->textbuf
= cpystr("");
3484 * Write the list of custom headers to the
3485 * X-Our-Headers header so that we can recover the
3489 for(pf
= header
.custom
; pf
&& pf
->name
; pf
= pf
->next
)
3490 sz
+= strlen(pf
->name
) + 1;
3495 pf_ourhdrs
->writehdr
= 1;
3496 pf_ourhdrs
->localcopy
= 1;
3497 pf_ourhdrs
->textbuf
= (char *)fs_get(sz
);
3498 memset(pf_ourhdrs
->textbuf
, 0, sz
);
3499 q
= pf_ourhdrs
->textbuf
;
3500 for(pf
= header
.custom
; pf
&& pf
->name
; pf
= pf
->next
){
3501 if(pf
!= header
.custom
)
3502 sstrncpy(&q
, ",", sz
-(q
-pf_ourhdrs
->textbuf
));
3504 sstrncpy(&q
, pf
->name
, sz
-(q
-pf_ourhdrs
->textbuf
));
3507 pf_ourhdrs
->textbuf
[sz
-1] = '\0';;
3511 * We need to make sure any header values that got cleared
3512 * get written to the postponed message (they won't if
3513 * pf->text is NULL). Otherwise, we can't tell previously
3514 * non-existent custom headers or default values from
3515 * custom (or other) headers that got blanked in the
3518 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
3519 if(pf
->type
== FreeText
&& HE(pf
) && !*(HE(pf
)->realaddr
))
3520 *(HE(pf
)->realaddr
) = cpystr("");
3523 * We're saving the message for use later. It may be that the
3524 * characters in the message are not all convertible to the
3525 * user's posting_charmap. We'll save it as UTF-8 instead
3526 * and worry about that the next time they try to send it.
3527 * Use a different save pointer just to be sure we don't
3528 * mess up the other stuff. We should probably make the
3529 * charset an argument.
3531 * We also need to fix the charset of the body part
3532 * the user is editing so that we can read it back
3533 * successfully when we resume the composition.
3535 ps_global
->post_utf8
= 1;
3540 if((*body
)->type
== TYPEMULTIPART
)
3541 bp
= &(*body
)->nested
.part
->body
;
3545 for(pm
= bp
->parameter
;
3546 pm
&& strucmp(pm
->attribute
, "charset") != 0;
3552 fs_give((void **) &pm
->value
);
3554 pm
->value
= cpystr("UTF-8");
3558 if(pine_rfc822_output(&header
,*body
,NULL
,NULL
) >= 0L){
3559 if(editor_result
& (COMP_GOTHUP
| COMP_CANCEL
)){
3565 if(editor_result
& COMP_CANCEL
){
3566 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
3567 "Saving to \"%s\"", folder
);
3568 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
3569 we_cancel
= busy_cue((char *)tmp_20k_buf
, NULL
, 1);
3573 so_get(FileStar
, folder
, WRITE_ACCESS
|OWNER_ONLY
)) != NULL
){
3574 gf_set_so_readc(&gc
, lmc
.so
);
3575 gf_set_so_writec(&pc
, hup_so
);
3576 so_seek(lmc
.so
, 0L, 0); /* read msg copy and */
3577 so_seek(hup_so
, 0L, 2); /* append to folder */
3579 gf_link_filter(gf_nvtnl_local
, NULL
);
3580 if(!(fcc_result
= !(err
= gf_pipe(gc
, pc
))))
3581 dprint((1, "*** PIPE FAILED: %s\n",
3584 gf_clear_so_readc(lmc
.so
);
3585 gf_clear_so_writec(hup_so
);
3589 dprint((1, "*** CAN'T CREATE %s: %s\n",
3590 folder
? folder
: "?",
3591 error_description(errno
)));
3594 cancel_busy_cue(-1);
3597 fcc_result
= write_fcc(folder
, fcc_cntxt
,
3598 lmc
.so
, NULL
, label
, NULL
);
3601 /* discontinue coerced UTF-8 posting */
3602 ps_global
->post_utf8
= 0;
3607 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3608 error_description(errno
)));
3610 if(editor_result
& COMP_GOTHUP
){
3612 * Special Hack #291: if any hi-byte bits are set in
3613 * editor's result, we put them there.
3615 if(editor_result
& 0xff00)
3616 exit(editor_result
>> 8);
3618 dprint((1, "Save composition on HUP %sED\n",
3619 fcc_result
? "SUCCEED" : "FAIL"));
3620 hup_signal(); /* Do what we normally do on SIGHUP */
3622 else if((editor_result
& COMP_SUSPEND
) && fcc_result
){
3623 if(ps_global
->VAR_FORM_FOLDER
3624 && ps_global
->VAR_FORM_FOLDER
[0]
3625 && !strcmp(folder
, ps_global
->VAR_FORM_FOLDER
))
3626 q_status_message(SM_ORDER
, 0, 3,
3627 _("Composition saved to Form Letter Folder. Select Compose to send."));
3629 q_status_message(SM_ORDER
, 0, 3,
3630 _("Composition postponed. Select Compose to resume."));
3632 break; /* postpone went OK, get out of here */
3634 else if(editor_result
& COMP_CANCEL
){
3637 if(fcc_result
&& folder
)
3638 lc
= last_cmpnt(folder
);
3640 q_status_message3(SM_ORDER
, 0, 3,
3641 _("Message cancelled%s%s%s"),
3642 (lc
&& *lc
) ? " and copied to \"" : "",
3643 (lc
&& *lc
) ? lc
: "",
3644 (lc
&& *lc
) ? "\" file" : "");
3648 q_status_message(SM_ORDER
, 0, 4,
3649 _("Continuing composition. Message not postponed or sent"));
3651 continue; /* postpone failed, jump back in to composer */
3655 /*------ Must be sending mail or posting ! -----*/
3656 int result
, valid_addr
, continue_with_only_fcc
= 0;
3657 CONTEXT_S
*fcc_cntxt
= NULL
;
3660 dprint((4, "=== sending: "));
3662 /* --- If posting, confirm with user ----*/
3663 if(outgoing
->newsgroups
&& *outgoing
->newsgroups
3664 && F_OFF(F_QUELL_EXTRA_POST_PROMPT
, ps_global
)
3665 && want_to(POST_PMT
, 'n', 'n', NO_HELP
, WT_NORM
) == 'n'){
3666 q_status_message(SM_ORDER
, 0, 3, _("Message not posted"));
3667 dprint((4, "no post, continuing\n"));
3671 if(!(outgoing
->to
|| outgoing
->cc
|| outgoing
->bcc
|| lcc_addr
3672 || outgoing
->newsgroups
)){
3674 if(F_OFF(F_AUTO_FCC_ONLY
, ps_global
) &&
3675 want_to(_("No recipients, really copy only to Fcc "),
3676 'n', 'n', h_send_fcc_only
, WT_NORM
) != 'y')
3679 continue_with_only_fcc
++;
3682 q_status_message(SM_ORDER
, 3, 4,
3683 _("No recipients specified!"));
3684 dprint((4, "no recip, continuing\n"));
3689 if((valid_addr
= check_addresses(&header
)) == CA_BAD
){
3690 /*--- Addresses didn't check out---*/
3691 dprint((4, "addrs failed, continuing\n"));
3695 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC
, ps_global
)
3696 && !continue_with_only_fcc
3697 && !(outgoing
->to
|| outgoing
->cc
|| lcc_addr
3698 || outgoing
->newsgroups
)
3699 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3700 'n', 'n', h_send_check_to_cc
, WT_NORM
) != 'y')){
3701 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3702 if(local_redraft_pos
&& local_redraft_pos
!= redraft_pos
)
3703 free_redraft_pos(&local_redraft_pos
);
3706 = (REDRAFT_POS_S
*) fs_get(sizeof(*local_redraft_pos
));
3707 memset((void *) local_redraft_pos
,0,sizeof(*local_redraft_pos
));
3708 local_redraft_pos
->hdrname
= cpystr(TONAME
);
3712 if(F_ON(F_WARN_ABOUT_NO_SUBJECT
, ps_global
)
3713 && check_for_subject(&header
) == CF_MISSING
){
3714 dprint((4, "No subject, continuing\n"));
3715 if(local_redraft_pos
&& local_redraft_pos
!= redraft_pos
)
3716 free_redraft_pos(&local_redraft_pos
);
3719 = (REDRAFT_POS_S
*) fs_get(sizeof(*local_redraft_pos
));
3720 memset((void *) local_redraft_pos
,0,sizeof(*local_redraft_pos
));
3721 local_redraft_pos
->hdrname
= cpystr(SUBJNAME
);
3725 if(F_ON(F_WARN_ABOUT_NO_FCC
, ps_global
)
3726 && check_for_fcc(fcc
) == CF_MISSING
){
3727 dprint((4, "No fcc, continuing\n"));
3728 if(local_redraft_pos
&& local_redraft_pos
!= redraft_pos
)
3729 free_redraft_pos(&local_redraft_pos
);
3732 = (REDRAFT_POS_S
*) fs_get(sizeof(*local_redraft_pos
));
3733 memset((void *) local_redraft_pos
,0,sizeof(*local_redraft_pos
));
3734 local_redraft_pos
->hdrname
= cpystr("Fcc");
3740 /*---- Check out fcc -----*/
3743 * If special name "inbox" then replace it with the
3746 if(ps_global
->VAR_INBOX_PATH
3747 && strucmp(fcc
, ps_global
->inbox_name
) == 0){
3750 replace_fcc
= cpystr(ps_global
->VAR_INBOX_PATH
);
3751 fs_give((void **)&fcc
);
3755 lmc
.all_written
= lmc
.text_written
= 0;
3756 /* lmc.text_only set on command line */
3757 if(!(lmc
.so
= open_fcc(fcc
, &fcc_cntxt
, 0, NULL
, NULL
))){
3758 /* ---- Open or allocation of fcc failed ----- */
3759 dprint((4,"can't open/allocate fcc, cont'g\n"));
3762 * Find field entry associated with fcc, and start
3765 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
3766 if(pf
->type
== Fcc
&& HE(pf
))
3767 HE(pf
)->start_here
= 1;
3772 so_truncate(lmc
.so
, fcc_size_guess(*body
) + 2048);
3777 /*---- Take care of any requested prefiltering ----*/
3778 if(sending_filter_requested
3779 && !filter_message_text(sending_filter_requested
, outgoing
,
3780 *body
, &orig_so
, &header
)){
3781 q_status_message1(SM_ORDER
, 3, 3,
3782 _("Problem filtering! Nothing sent%s."),
3783 fcc
? " or saved to fcc" : "");
3787 /*------ Actually post -------*/
3788 if(outgoing
->newsgroups
){
3789 char **alt_nntp
= NULL
, *alt_nntp_p
[2];
3790 if(((role
&& role
->nntp
)
3791 || suggested_nntp_server
)){
3792 if(ps_global
->FIX_NNTP_SERVER
3793 && ps_global
->FIX_NNTP_SERVER
[0])
3794 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
3795 "Using nntp-server that is administratively fixed");
3796 else if(role
&& role
->nntp
)
3797 alt_nntp
= role
->nntp
;
3799 alt_nntp_p
[0] = suggested_nntp_server
;
3800 alt_nntp_p
[1] = NULL
;
3801 alt_nntp
= alt_nntp_p
;
3804 if(news_poster(&header
, *body
, alt_nntp
, pipe_callback
) < 0){
3805 dprint((1, "Post failed, continuing\n"));
3806 if(outgoing
->message_id
)
3807 fs_give((void **) &outgoing
->message_id
);
3809 outgoing
->message_id
= generate_message_id();
3814 result
|= P_NEWS_WIN
;
3818 * BUG: IF we've posted the message *and* an fcc was specified
3819 * then we've already got a neatly formatted message in the
3820 * lmc.so. It'd be nice not to have to re-encode everything
3821 * to insert it into the smtp slot...
3825 * Turn on "undisclosed recipients" header if no To or cc.
3827 if(!(outgoing
->to
|| outgoing
->cc
)
3828 && (outgoing
->bcc
|| lcc_addr
) && pf_nobody
&& pf_nobody
->addr
){
3829 pf_nobody
->writehdr
= 1;
3830 pf_nobody
->localcopy
= 1;
3833 if(priority_requested
){
3834 (void) set_priority_header(&header
, priority_requested
);
3835 fs_give((void **) &priority_requested
);
3838 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3840 * If requested, launch background posting...
3842 if(background_requested
&& !(call_mailer_flags
& CM_VERBOSE
)){
3843 ps_global
->post
= (POST_S
*)fs_get(sizeof(POST_S
));
3844 memset(ps_global
->post
, 0, sizeof(POST_S
));
3846 ps_global
->post
->fcc
= cpystr(fcc
);
3848 if((ps_global
->post
->pid
= fork()) == 0){
3850 * Put us in new process group...
3852 setpgrp(0, ps_global
->post
->pid
);
3854 /* BUG: should fix argv[0] to indicate what we're up to */
3857 * If there are any live streams, pretend we never
3858 * knew them. Problem is two processes writing
3859 * same server process.
3860 * This is not clean but we're just going to exit
3861 * right away anyway. We just want to be sure to leave
3862 * the stuff that the parent is going to use alone.
3863 * The next three lines will disable the re-use of the
3864 * existing streams and cause us to open a new one if
3867 ps_global
->mail_stream
= NULL
;
3868 ps_global
->s_pool
.streams
= NULL
;
3869 ps_global
->s_pool
.nstream
= 0;
3871 /* quell any display output */
3872 ps_global
->in_init_seq
= 1;
3874 /*------- Actually mail the message ------*/
3875 if(valid_addr
== CA_OK
3876 && (outgoing
->to
|| outgoing
->cc
3877 || outgoing
->bcc
|| lcc_addr
)){
3878 char **alt_smtp
= NULL
;
3880 if(role
&& role
->smtp
){
3881 if(ps_global
->FIX_SMTP_SERVER
3882 && ps_global
->FIX_SMTP_SERVER
[0])
3883 q_status_message(SM_ORDER
| SM_DING
, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3885 alt_smtp
= role
->smtp
;
3888 result
|= (call_mailer(&header
, *body
, alt_smtp
,
3890 call_mailer_file_result
,
3892 ? P_MAIL_WIN
: P_MAIL_LOSE
;
3894 if(result
& P_MAIL_LOSE
)
3895 mark_address_failure_for_pico(&header
);
3898 /*----- Was there an fcc involved? -----*/
3900 /*------ Write it if at least something worked ------*/
3901 if((result
& (P_MAIL_WIN
| P_NEWS_WIN
))
3902 || (!(result
& (P_MAIL_BITS
| P_NEWS_BITS
))
3903 && pine_rfc822_output(&header
, *body
,
3907 strncpy(label
, "Fcc", sizeof(label
));
3908 label
[sizeof(label
)-1] = '\0';
3909 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
)){
3910 snprintf(label
+ 3, sizeof(label
)-3, " to %s", fcc
);
3911 label
[sizeof(label
)-1] = '\0';
3914 /*-- Now actually copy to fcc folder and close --*/
3915 result
|= (write_fcc(fcc
, fcc_cntxt
, lmc
.so
,
3917 F_ON(F_MARK_FCC_SEEN
, ps_global
)
3919 ? P_FCC_WIN
: P_FCC_LOSE
;
3921 else if(!(result
& (P_MAIL_BITS
| P_NEWS_BITS
))){
3922 q_status_message(SM_ORDER
, 3, 5,
3923 _("Fcc Failed!. No message saved."));
3925 "explicit fcc write failed!\n"));
3926 result
|= P_FCC_LOSE
;
3932 if(result
& (P_MAIL_LOSE
| P_NEWS_LOSE
| P_FCC_LOSE
)){
3934 * Encode child's result in hi-byte of
3937 editor_result
= ((result
<< 8) | COMP_GOTHUP
);
3944 if(ps_global
->post
->pid
> 0){
3945 q_status_message(SM_ORDER
, 3, 3,
3946 _("Message handed off for posting"));
3947 break; /* up to our child now */
3950 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
3951 "Can't fork for send: %s",
3952 error_description(errno
));
3953 if(ps_global
->post
->fcc
)
3954 fs_give((void **) &ps_global
->post
->fcc
);
3956 fs_give((void **) &ps_global
->post
);
3959 if(lmc
.so
) /* throw away unused store obj */
3962 if(outgoing
->message_id
)
3963 fs_give((void **) &outgoing
->message_id
);
3965 outgoing
->message_id
= generate_message_id();
3967 continue; /* if we got here, there was a prob */
3969 #endif /* BACKGROUND_POST */
3971 /*------- Actually mail the message ------*/
3972 if(valid_addr
== CA_OK
3973 && (outgoing
->to
|| outgoing
->cc
|| outgoing
->bcc
|| lcc_addr
)){
3974 char **alt_smtp
= NULL
;
3976 if(role
&& role
->smtp
){
3977 if(ps_global
->FIX_SMTP_SERVER
3978 && ps_global
->FIX_SMTP_SERVER
[0])
3979 q_status_message(SM_ORDER
| SM_DING
, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3981 alt_smtp
= role
->smtp
;
3984 result
|= (call_mailer(&header
, *body
, alt_smtp
,
3986 call_mailer_file_result
,
3988 ? P_MAIL_WIN
: P_MAIL_LOSE
;
3990 if(result
& P_MAIL_LOSE
)
3991 mark_address_failure_for_pico(&header
);
3994 /*----- Was there an fcc involved? -----*/
3996 /*------ Write it if at least something worked ------*/
3997 if((result
& (P_MAIL_WIN
| P_NEWS_WIN
))
3998 || (!(result
& (P_MAIL_BITS
| P_NEWS_BITS
))
3999 && pine_rfc822_output(&header
, *body
, NULL
, NULL
))){
4002 strncpy(label
, "Fcc", sizeof(label
));
4003 label
[sizeof(label
)-1] = '\0';
4004 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
)){
4005 snprintf(label
+ 3, sizeof(label
)-3, " to %s", fcc
);
4006 label
[sizeof(label
)-1] = '\0';
4009 /*-- Now actually copy to fcc folder and close --*/
4010 result
|= (write_fcc(fcc
, fcc_cntxt
, lmc
.so
, NULL
, label
,
4011 F_ON(F_MARK_FCC_SEEN
, ps_global
)
4013 ? P_FCC_WIN
: P_FCC_LOSE
;
4015 else if(!(result
& (P_MAIL_BITS
| P_NEWS_BITS
))){
4016 q_status_message(SM_ORDER
,3,5,
4017 _("Fcc Failed!. No message saved."));
4018 dprint((1, "explicit fcc write failed!\n"));
4019 result
|= P_FCC_LOSE
;
4025 /*----- Mail Post FAILED, back to composer -----*/
4026 if(result
& (P_MAIL_LOSE
| P_FCC_LOSE
)){
4027 dprint((1, "Send failed, continuing\n"));
4029 if(result
& P_FCC_LOSE
){
4031 * Find field entry associated with fcc, and start
4034 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
4035 if(pf
->type
== Fcc
&& HE(pf
))
4036 HE(pf
)->start_here
= 1;
4038 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
4039 pine_send_status(result
, fcc
,
4040 tmp_20k_buf
, SIZEOF_20KBUF
, NULL
));
4043 if(outgoing
->message_id
)
4044 fs_give((void **) &outgoing
->message_id
);
4046 outgoing
->message_id
= generate_message_id();
4052 * If message sent *completely* successfully, there's a
4053 * reply struct AND we're allowed to write back state, do it.
4054 * But also protect against shifted message numbers due
4055 * to new mail arrival. Since the number passed is based
4056 * on the real imap msg no, AND we're sure no expunge has
4057 * been done, just fix up the sorted number...
4059 update_answered_flags(reply
);
4061 /*----- Signed, sealed, delivered! ------*/
4062 q_status_message(SM_ORDER
, 0, 3,
4063 pine_send_status(result
, fcc
, tmp_20k_buf
, SIZEOF_20KBUF
, NULL
));
4065 break; /* All's well, pop out of here */
4073 fs_give((void **)&fcc
);
4075 free_body_particulars(bp
);
4077 free_attachment_list(&pbf
->attachments
);
4079 standard_picobuf_teardown(pbf
);
4081 for(i
=0; i
< fixed_cnt
; i
++){
4082 if(pfields
[i
].textbuf
)
4083 fs_give((void **)&pfields
[i
].textbuf
);
4085 fs_give((void **)&pfields
[i
].name
);
4089 mail_free_address(&lcc_addr
);
4092 mail_free_address(&nobody_addr
);
4094 free_prompts(header
.custom
);
4095 free_customs(header
.custom
);
4096 fs_give((void **)&pfields
);
4097 free_headents(&headents
);
4098 fs_give((void **)&sending_order
);
4099 if(suggested_nntp_server
)
4100 fs_give((void **)&suggested_nntp_server
);
4102 fs_give((void **)&title
);
4104 if(local_redraft_pos
&& local_redraft_pos
!= redraft_pos
)
4105 free_redraft_pos(&local_redraft_pos
);
4107 pbf
= save_previous_pbuf
;
4110 dprint((4, "=== send returning ===\n"));
4115 * Check for subject in outgoing message.
4117 * Asks user whether to proceed with no subject.
4120 check_for_subject(METAENV
*header
)
4125 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
4126 if(pf
->type
== Subject
){
4127 if(pf
->text
&& *pf
->text
&& **pf
->text
)
4130 if(want_to("No Subject, send anyway ",
4131 'n', 'n', h_send_check_subj
, WT_NORM
) == 'y')
4146 * Check for fcc in outgoing message.
4148 * Asks user whether to proceed with no fcc.
4151 check_for_fcc(char *fcc
)
4158 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc
, WT_NORM
) == 'y')
4169 * Confirm that the user wants to send to MAILER-DAEMON
4172 confirm_daemon_send(void)
4174 return(want_to("Really send this message to the MAILER-DAEMON",
4175 'n', 'n', NO_HELP
, WT_NORM
) == 'y');
4180 free_prompts(PINEFIELD
*head
)
4184 for(pf
= head
; pf
&& pf
->name
; pf
= pf
->next
){
4185 if(HE(pf
) && HE(pf
)->prompt
)
4186 fs_give((void **)& HE(pf
)->prompt
);
4192 postpone_prompt(void)
4194 static ESCKEY_S pstpn_form_opt
[] = { {'p', 'p', "P", N_("Postponed Folder")},
4195 {'f', 'f', "F", N_("Form Letter Folder")},
4196 {-1, 0, NULL
, NULL
} };
4198 return(radio_buttons(PSTPN_FORM_PMT
, -FOOTER_ROWS(ps_global
),
4199 pstpn_form_opt
, 'p', 0, NO_HELP
, RB_FLUSH_IN
));
4204 * call__mailer_file_result - some results from call_mailer might be in a file.
4205 * dislplay that file.
4208 call_mailer_file_result(char *filename
, int style
)
4211 if(style
& CM_BR_VERBOSE
){
4212 display_output_file(filename
, "Verbose SMTP Interaction", NULL
, DOF_BRIEF
);
4215 display_output_file(filename
, "POSTING ERRORS", "Posting Error", DOF_EMPTY
);
4221 mark_address_failure_for_pico(METAENV
*header
)
4225 int error_count
= 0;
4226 struct headerentry
*last_he
= NULL
;
4228 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
4229 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
)
4230 for(a
= *pf
->addr
; a
!= NULL
; a
= a
->next
)
4232 && error_count
++ < MAX_ADDR_ERROR
4234 if(last_he
) /* start last reported err */
4235 last_he
->start_here
= 0;
4237 (last_he
= HE(pf
))->start_here
= 1;
4244 * This is specialized routine. It assumes that the only things that we
4245 * care about restoring are the body type, subtype, encoding and the
4246 * state of the charset parameter. It also assumes that if the charset
4247 * parameter exists when we save it, it won't be removed later.
4249 BODY_PARTICULARS_S
*
4250 save_body_particulars(struct mail_bodystruct
*body
)
4252 BODY_PARTICULARS_S
*bp
;
4255 bp
= (BODY_PARTICULARS_S
*)fs_get(sizeof(BODY_PARTICULARS_S
));
4257 bp
->type
= body
->type
;
4258 bp
->encoding
= body
->encoding
;
4259 bp
->subtype
= body
->subtype
? cpystr(body
->subtype
) : NULL
;
4260 bp
->parameter
= body
->parameter
;
4261 for(pm
= bp
->parameter
;
4262 pm
&& strucmp(pm
->attribute
, "charset") != 0;
4264 ;/* searching for possible charset parameter */
4266 if(pm
){ /* found one */
4267 bp
->had_csp
= 1; /* saved body had charset parameter */
4268 bp
->charset
= pm
->value
? cpystr(pm
->value
) : NULL
;
4280 reset_body_particulars(BODY_PARTICULARS_S
*bp
, struct mail_bodystruct
*body
)
4282 body
->type
= bp
->type
;
4283 body
->encoding
= bp
->encoding
;
4285 fs_give((void **)&body
->subtype
);
4287 body
->subtype
= bp
->subtype
? cpystr(bp
->subtype
) : NULL
;
4290 PARAMETER
*pm
, *pm_prev
= NULL
;
4292 for(pm
= body
->parameter
;
4293 pm
&& strucmp(pm
->attribute
, "charset") != 0;
4297 if(pm
){ /* body has charset parameter */
4298 if(bp
->had_csp
){ /* reset to what it used to be */
4300 fs_give((void **)&pm
->value
);
4302 pm
->value
= bp
->charset
? cpystr(bp
->charset
) : NULL
;
4304 else{ /* remove charset parameter */
4306 pm_prev
->next
= pm
->next
;
4308 body
->parameter
= pm
->next
;
4310 mail_free_body_parameter(&pm
);
4316 * This can't happen because grope never removes
4317 * the charset parameter.
4319 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
4320 "Programmer error: saved charset but no current charset param in pine_send");
4324 ok, still no parameter
4331 mail_free_body_parameter(&body
->parameter
);
4333 body
->parameter
= NULL
;
4339 free_body_particulars(BODY_PARTICULARS_S
*bp
)
4343 fs_give((void **)&bp
->subtype
);
4346 fs_give((void **)&bp
->charset
);
4348 fs_give((void **)&bp
);
4353 /*----------------------------------------------------------------------
4354 Build a status message suitable for framing
4356 Returns: pointer to resulting buffer
4359 pine_send_status(int result
, char *fcc_name
, char *buf
, size_t buflen
, int *goodorbad
)
4361 int avail
= ps_global
->ttyo
->screen_cols
- 2;
4362 int fixedneed
, need
, lenfcc
;
4363 char *part1
, *part2
, *part3
, *part4
, *part5
;
4364 char fbuf
[MAILTMPLEN
+1];
4366 part1
= (result
& P_NEWS_WIN
)
4368 : (result
& P_NEWS_LOSE
)
4371 part2
= ((result
& P_NEWS_BITS
) && (result
& P_MAIL_BITS
)
4372 && (result
& P_FCC_BITS
))
4374 : ((result
& P_NEWS_BITS
) && (result
& (P_MAIL_BITS
| P_FCC_BITS
)))
4377 part3
= (result
& P_MAIL_WIN
)
4379 : (result
& P_MAIL_LOSE
)
4382 part4
= ((result
& P_MAIL_BITS
) && (result
& P_FCC_BITS
))
4385 part5
= ((result
& P_FCC_WIN
) && !(result
& (P_MAIL_WIN
| P_NEWS_WIN
)))
4387 : (result
& P_FCC_WIN
)
4389 : (result
& P_FCC_LOSE
)
4392 lenfcc
= MIN(sizeof(fbuf
)-1, (result
& P_FCC_BITS
) ? strlen(fcc_name
) : 0);
4394 fixedneed
= 9 + strlen(part1
) + strlen(part2
) + strlen(part3
) +
4395 strlen(part4
) + strlen(part5
);
4396 need
= fixedneed
+ ((result
& P_FCC_BITS
) ? 2 : 0) + lenfcc
;
4398 if(need
> avail
&& fixedneed
+ 3 >= avail
){
4399 /* dots on end of fixed, no fcc */
4400 snprintf(fbuf
, sizeof(fbuf
), "Message %s%s%s%s%s ",
4401 part1
, part2
, part3
, part4
, part5
);
4402 short_str(fbuf
, buf
, buflen
, avail
, EndDots
);
4404 else if(need
> avail
){
4405 /* include whole fixed part, quotes and dots at end of fcc name */
4407 lenfcc
= MAX(1, lenfcc
-(need
-avail
));
4409 snprintf(buf
, buflen
, "Message %s%s%s%s%s%s%s%s.",
4410 part1
, part2
, part3
, part4
, part5
,
4411 (result
& P_FCC_BITS
) ? "\"" : "",
4412 short_str((result
& P_FCC_BITS
) ? fcc_name
: "",
4413 fbuf
, sizeof(fbuf
), lenfcc
, FrontDots
),
4414 (result
& P_FCC_BITS
) ? "\"" : "");
4418 snprintf(buf
, buflen
, "Message %s%s%s%s%s%s%s%s.",
4419 part1
, part2
, part3
, part4
, part5
,
4420 (result
& P_FCC_BITS
) ? "\"" : "",
4421 (result
& P_FCC_BITS
) ? fcc_name
: "",
4422 (result
& P_FCC_BITS
) ? "\"" : "");
4426 *goodorbad
= (result
& (P_MAIL_LOSE
| P_NEWS_LOSE
| P_FCC_LOSE
)) == 0;
4431 /* Callback from Pico to set the conditions for Alpine to start a new thread
4435 new_thread_on_blank_subject(void)
4437 ps_global
->newthread
= F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT
, ps_global
);
4442 /*----------------------------------------------------------------------
4443 Call back for pico to insert the specified message's text
4445 Args: n -- message number to format
4446 f -- function to use to output the formatted message
4449 Returns: returns msg number formatted on success, zero on error.
4452 message_format_for_pico(long int n
, int (*f
) (int))
4456 char *old_quote
= NULL
;
4459 if(!(n
> 0L && n
<= mn_get_total(ps_global
->msgmap
)
4460 && (e
= pine_mail_fetchstructure(ps_global
->mail_stream
,
4461 mn_m2raw(ps_global
->msgmap
, n
), &b
)))){
4462 q_status_message(SM_ORDER
|SM_DING
,3,3,"Error inserting Message");
4463 flush_status_messages(0);
4467 /* temporarily assign a new quote string */
4468 old_quote
= pbf
->quote_str
;
4469 pbf
->quote_str
= reply_quote_str(e
);
4471 /* build separator line */
4472 reply_delimiter(e
, NULL
, f
);
4474 /* actually write message text */
4475 if(!format_message(mn_m2raw(ps_global
->msgmap
, n
), e
, b
, NULL
,
4476 FM_NEW_MESS
| FM_DISPLAY
| FM_NOCOLOR
| FM_NOINDENT
, f
)){
4477 q_status_message(SM_ORDER
|SM_DING
,3,3,"Error inserting Message");
4478 flush_status_messages(0);
4482 fs_give((void **)&pbf
->quote_str
);
4483 pbf
->quote_str
= old_quote
;
4488 /*----------------------------------------------------------------------
4489 Call back for pico to prompt the user for exit confirmation
4491 Args: dflt -- default answer for confirmation prompt
4493 Returns: either NULL if the user accepts exit, or string containing
4494 reason why the user declined.
4497 send_exit_for_pico(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
4500 int i
, rv
, c
, verbose_label
= 0, bg_label
= 0, old_suspend
;
4501 int dsn_label
= 0, fcc_label
= 0, lparen
;
4502 int flowing_label
= 0, double_rad
;
4503 char *rstr
= NULL
, *p
, *lc
, *optp
;
4504 char dsn_string
[30];
4505 void (*redraw
)(void) = ps_global
->redrawer
;
4510 struct filters
*prev
, *next
;
4511 } *filters
= NULL
, *fp
;
4513 sending_filter_requested
= NULL
;
4514 call_mailer_flags
= 0;
4515 background_requested
= 0;
4516 flowing_requested
= allow_flowed
? 1 : 0;
4517 lmc
.text_only
= F_ON(F_NO_FCC_ATTACH
, ps_global
) != 0;
4518 if(priority_requested
)
4519 fs_give((void **) &priority_requested
);
4521 if(background_posting(FALSE
)){
4523 *result
= "Can't send while background posting. Use postpone.";
4528 if(F_ON(F_SEND_WO_CONFIRM
, ps_global
)){
4535 ps_global
->redrawer
= redraw_pico
;
4537 if((old_suspend
= F_ON(F_CAN_SUSPEND
, ps_global
)) != 0)
4538 (void) F_SET(F_CAN_SUSPEND
, ps_global
, 0);
4541 * Build list of available filters...
4543 for(i
=0; ps_global
->VAR_SEND_FILTER
&& ps_global
->VAR_SEND_FILTER
[i
]; i
++){
4544 for(p
= ps_global
->VAR_SEND_FILTER
[i
];
4545 *p
&& !isspace((unsigned char)*p
); p
++)
4550 if(!(is_absolute_path(ps_global
->VAR_SEND_FILTER
[i
])
4551 && can_access(ps_global
->VAR_SEND_FILTER
[i
],EXECUTE_ACCESS
) ==0)){
4556 fp
= (struct filters
*)fs_get(sizeof(struct filters
));
4558 if((lc
= last_cmpnt(ps_global
->VAR_SEND_FILTER
[i
])) != NULL
){
4559 fp
->filter
= cpystr(lc
);
4561 else if((p
- ps_global
->VAR_SEND_FILTER
[i
]) > 20){
4562 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "...%s", p
- 17);
4563 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
4564 fp
->filter
= cpystr(tmp_20k_buf
);
4567 fp
->filter
= cpystr(ps_global
->VAR_SEND_FILTER
[i
]);
4573 fp
->prev
= filters
->prev
;
4574 fp
->prev
->next
= filters
->prev
= fp
;
4577 filters
= (struct filters
*)fs_get(sizeof(struct filters
));
4578 filters
->index
= -1;
4579 filters
->filter
= NULL
;
4580 filters
->next
= filters
->prev
= fp
;
4581 fp
->next
= fp
->prev
= filters
;
4589 opts
[i
++].label
= N_("Yes");
4594 opts
[i
++].label
= N_("No");
4597 /* set global_filter_pointer to desired filter or NULL if none */
4598 /* prepare two keymenu slots for selecting filter */
4599 opts
[i
].ch
= ctrl('P');
4601 opts
[i
].name
= "^P";
4602 opts
[i
++].label
= N_("Prev Filter");
4604 opts
[i
].ch
= ctrl('N');
4606 opts
[i
].name
= "^N";
4607 opts
[i
++].label
= N_("Next Filter");
4609 if(F_ON(F_FIRST_SEND_FILTER_DFLT
, ps_global
))
4610 filters
= filters
->next
;
4613 if(F_ON(F_VERBOSE_POST
, ps_global
)){
4614 /* setup keymenu slot to toggle verbose mode */
4615 opts
[i
].ch
= ctrl('W');
4617 opts
[i
].name
= "^W";
4618 verbose_label
= i
++;
4622 /* setup keymenu slot to toggle flowed mode */
4623 opts
[i
].ch
= ctrl('V');
4625 opts
[i
].name
= "^V";
4626 flowing_label
= i
++;
4627 flowing_requested
= 1;
4630 if(F_ON(F_NO_FCC_ATTACH
, ps_global
)){
4631 /* setup keymenu slot to toggle attacment on fcc */
4632 opts
[i
].ch
= ctrl('F');
4634 opts
[i
].name
= "^F";
4638 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4639 if(F_ON(F_BACKGROUND_POST
, ps_global
)){
4640 opts
[i
].ch
= ctrl('R');
4642 opts
[i
].name
= "^R";
4650 opts
[i
++].label
= N_("Priority");
4653 if(F_OFF(F_DONT_DO_SMIME
, ps_global
)){
4657 opts
[i
++].label
= "Encrypt";
4662 opts
[i
++].label
= "Sign";
4664 if(ps_global
->smime
){
4665 ps_global
->smime
->do_encrypt
= F_ON(F_ENCRYPT_DEFAULT_ON
, ps_global
);
4666 ps_global
->smime
->do_sign
= F_ON(F_SIGN_DEFAULT_ON
, ps_global
);
4673 if(F_ON(F_DSN
, ps_global
)){
4674 /* setup keymenu slots to toggle dsn bits */
4678 opts
[i
].label
= N_("DSNOpts");
4683 opts
[i
++].label
= "";
4687 opts
[i
++].label
= "";
4691 opts
[i
++].label
= "";
4695 opts
[i
].ch
= KEY_UP
;
4698 opts
[i
++].label
= "";
4700 opts
[i
].ch
= KEY_DOWN
;
4703 opts
[i
++].label
= "";
4708 fix_windsize(ps_global
);
4711 if(filters
&& filters
->filter
&& (p
= strindex(filters
->filter
, ' ')))
4717 strncpy(tmp_20k_buf
, "Send message", SIZEOF_20KBUF
);
4718 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
4719 optp
= tmp_20k_buf
+ strlen(tmp_20k_buf
);
4721 if(F_ON(F_NO_FCC_ATTACH
, ps_global
) && !lmc
.text_only
)
4722 sstrncpy(&optp
, " and Fcc Atmts", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4724 if(allow_flowed
&& !flowing_requested
){
4725 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4735 sstrncpy(&optp
, "not flowed", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4739 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4751 if(filters
->filter
){
4752 sstrncpy(&optp
, "filtered thru \"", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4753 sstrncpy(&optp
, filters
->filter
, SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4754 if((optp
-tmp_20k_buf
) < SIZEOF_20KBUF
)
4758 sstrncpy(&optp
, "unfiltered", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4761 if((call_mailer_flags
& CM_VERBOSE
) || background_requested
){
4762 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4772 sstrncpy(&optp
, "in ", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4773 if(call_mailer_flags
& CM_VERBOSE
)
4774 sstrncpy(&optp
, "verbose ", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4776 if(background_requested
)
4777 sstrncpy(&optp
, "background ", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4779 sstrncpy(&optp
, "mode", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4782 if(g_rolenick
&& !(he
&& he
[N_FROM
].dirty
)){
4783 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4793 sstrncpy(&optp
, "as \"", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4794 sstrncpy(&optp
, g_rolenick
, SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4795 sstrncpy(&optp
, "\"", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4798 if(call_mailer_flags
& CM_DSN_SHOW
){
4799 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4811 sstrncpy(&optp
, dsn_string
, SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4815 if(ps_global
->smime
&& ps_global
->smime
->do_encrypt
){
4816 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4828 sstrncpy(&optp
, "Encrypted", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4831 if(ps_global
->smime
&& ps_global
->smime
->do_sign
){
4832 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4844 sstrncpy(&optp
, "Signed", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4848 if(lparen
&& (optp
-tmp_20k_buf
) < SIZEOF_20KBUF
)
4851 sstrncpy(&optp
, "? ", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4852 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
4858 opts
[flowing_label
].label
= flowing_requested
? N_("NoFlow") : N_("Flow");
4861 opts
[verbose_label
].label
= (call_mailer_flags
& CM_VERBOSE
) ? N_("Normal") : N_("Verbose");
4864 opts
[bg_label
].label
= background_requested
4865 ? N_("Foreground") : N_("Background");
4868 opts
[fcc_label
].label
= lmc
.text_only
? N_("Fcc Attchmnts")
4869 : N_("No Fcc Atmts ");
4871 if(F_ON(F_DSN
, ps_global
)){
4872 if(call_mailer_flags
& CM_DSN_SHOW
){
4873 opts
[dsn_label
].label
= (call_mailer_flags
& CM_DSN_DELAY
)
4874 ? N_("NoDelay") : N_("Delay");
4875 opts
[dsn_label
+1].ch
= 's';
4876 opts
[dsn_label
+1].label
= (call_mailer_flags
& CM_DSN_SUCCESS
)
4877 ? N_("NoSuccess") : N_("Success");
4878 opts
[dsn_label
+2].ch
= 'x';
4879 opts
[dsn_label
+2].label
= (call_mailer_flags
& CM_DSN_NEVER
)
4880 ? N_("ErrRets") : N_("NoErrRets");
4881 opts
[dsn_label
+3].ch
= 'h';
4882 opts
[dsn_label
+3].label
= (call_mailer_flags
& CM_DSN_FULL
)
4883 ? N_("RetHdrs") : N_("RetFull");
4888 ((call_mailer_flags
& CM_DSN_SHOW
)
4889 ? 4 : F_ON(F_DSN
, ps_global
) ? 1 : 0) > 11)
4890 rv
= double_radio_buttons(tmp_20k_buf
, -FOOTER_ROWS(ps_global
), opts
,
4892 (F_ON(F_DSN
, ps_global
) && allow_flowed
)
4893 ? h_send_prompt_dsn_flowed
:
4894 F_ON(F_DSN
, ps_global
) ? h_send_prompt_dsn
:
4895 allow_flowed
? h_send_prompt_flowed
:
4899 rv
= radio_buttons(tmp_20k_buf
, -FOOTER_ROWS(ps_global
), opts
,
4902 ((call_mailer_flags
& CM_DSN_SHOW
)
4903 ? 4 : F_ON(F_DSN
, ps_global
) ? 1 : 0) == 11)
4905 (F_ON(F_DSN
, ps_global
) && allow_flowed
)
4906 ? h_send_prompt_dsn_flowed
:
4907 F_ON(F_DSN
, ps_global
) ? h_send_prompt_dsn
:
4908 allow_flowed
? h_send_prompt_flowed
:
4912 if(rv
== 'y'){ /* user ACCEPTS! */
4915 else if(rv
== 'n'){ /* Declined! */
4916 rstr
= _("No Message Sent");
4919 else if(rv
== 'z'){ /* Cancelled! */
4920 rstr
= _("Send Cancelled");
4923 else if(rv
== 10){ /* PREVIOUS filter */
4924 filters
= filters
->prev
;
4926 else if(rv
== 11){ /* NEXT filter */
4927 filters
= filters
->next
;
4930 lmc
.text_only
= !lmc
.text_only
;
4932 else if(rv
== 12){ /* flip verbose bit */
4933 if(call_mailer_flags
& CM_VERBOSE
)
4934 call_mailer_flags
&= ~CM_VERBOSE
;
4936 call_mailer_flags
|= CM_VERBOSE
;
4938 if((call_mailer_flags
& CM_VERBOSE
) && background_requested
)
4939 background_requested
= 0;
4941 else if(rv
== 22){ /* flip flowing bit */
4942 flowing_requested
= !flowing_requested
;
4945 if((background_requested
= !background_requested
)
4946 && (call_mailer_flags
& CM_VERBOSE
))
4947 call_mailer_flags
&= ~CM_VERBOSE
; /* clear verbose */
4949 else if(call_mailer_flags
& CM_DSN_SHOW
){
4950 if(rv
== 's'){ /* flip success bit */
4951 call_mailer_flags
^= CM_DSN_SUCCESS
;
4952 /* turn off related bits */
4953 if(call_mailer_flags
& CM_DSN_SUCCESS
)
4954 call_mailer_flags
&= ~(CM_DSN_NEVER
);
4956 else if(rv
== 'd'){ /* flip delay bit */
4957 call_mailer_flags
^= CM_DSN_DELAY
;
4958 /* turn off related bits */
4959 if(call_mailer_flags
& CM_DSN_DELAY
)
4960 call_mailer_flags
&= ~(CM_DSN_NEVER
);
4962 else if(rv
== 'x'){ /* flip never bit */
4963 call_mailer_flags
^= CM_DSN_NEVER
;
4964 /* turn off related bits */
4965 if(call_mailer_flags
& CM_DSN_NEVER
)
4966 call_mailer_flags
&= ~(CM_DSN_SUCCESS
| CM_DSN_DELAY
);
4968 else if(rv
== 'h'){ /* flip full bit */
4969 call_mailer_flags
^= CM_DSN_FULL
;
4972 else if(rv
== 'd'){ /* show dsn options */
4974 * When you turn on DSN, the default is to notify on
4975 * failure, success, or delay; and to return the whole
4978 call_mailer_flags
|= (CM_DSN_SHOW
| CM_DSN_SUCCESS
| CM_DSN_DELAY
| CM_DSN_FULL
);
4980 else if(rv
== 'p'){ /* choose X-Priority */
4983 prio
= choose_a_priority(priority_requested
);
4984 if((ps_global
->redrawer
= redraw_pico
) != NULL
){
4985 (*ps_global
->redrawer
)();
4986 fix_windsize(ps_global
);
4990 if(priority_requested
)
4991 fs_give((void **) &priority_requested
);
4994 priority_requested
= prio
;
4996 fs_give((void **) &prio
);
5001 if(ps_global
->smime
)
5002 ps_global
->smime
->do_encrypt
= !ps_global
->smime
->do_encrypt
;
5005 if(ps_global
->smime
)
5006 ps_global
->smime
->do_sign
= !ps_global
->smime
->do_sign
;
5010 snprintf(dsn_string
, sizeof(dsn_string
), "DSN requested[%s%s%s%s]",
5011 (call_mailer_flags
& CM_DSN_NEVER
)
5013 (call_mailer_flags
& CM_DSN_DELAY
)
5015 (call_mailer_flags
& CM_DSN_SUCCESS
)
5017 (call_mailer_flags
& CM_DSN_NEVER
)
5019 : (call_mailer_flags
& CM_DSN_FULL
) ? "-Full"
5021 dsn_string
[sizeof(dsn_string
)-1] = '\0';
5024 /* remember selection */
5025 if(filters
&& filters
->index
> -1)
5026 sending_filter_requested
= ps_global
->VAR_SEND_FILTER
[filters
->index
];
5029 filters
->prev
->next
= NULL
; /* tie off list */
5030 while(filters
){ /* then free it */
5033 fs_give((void **)&filters
->filter
);
5035 fs_give((void **)&filters
);
5041 (void) F_SET(F_CAN_SUSPEND
, ps_global
, 1);
5046 ps_global
->redrawer
= redraw
;
5048 return((rstr
== NULL
) ? 0 : 1);
5053 * Allow user to choose a priority for sending.
5055 * Returns an allocated priority on success, NULL otherwise.
5058 choose_a_priority(char *default_val
)
5060 char *choice
= NULL
;
5061 char **priority_list
, **lp
;
5062 char *starting_val
= NULL
;
5067 for(cnt
= 0, p
= priorities
; p
&& p
->desc
; p
++)
5070 cnt
++; /* for NONE entry */
5071 lp
= priority_list
= (char **) fs_get((cnt
+ 1) * sizeof(*priority_list
));
5072 memset(priority_list
, 0, (cnt
+1) * sizeof(*priority_list
));
5074 for(i
= 0, p
= priorities
; p
&& p
->desc
; p
++){
5075 *lp
= cpystr(p
->desc
);
5076 if(default_val
&& !strcmp(default_val
, p
->desc
))
5077 starting_val
= (*lp
);
5082 none
= _("NONE - No X-Priority header included");
5085 starting_val
= (*lp
);
5087 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5088 TRANSLATORS: Print something1 using something2.
5089 "priorities" is something1 */
5090 choice
= choose_item_from_list(priority_list
, NULL
, _("SELECT A PRIORITY"),
5091 _("priorities"), h_select_priority_screen
,
5092 _("HELP FOR SELECTING A PRIORITY"),
5096 q_status_message(SM_ORDER
, 1, 4, _("No change"));
5097 else if(!strcmp(choice
, none
))
5100 free_list_array(&priority_list
);
5107 dont_flow_this_time(void)
5109 return(flowing_requested
? 0 : 1);
5113 /*----------------------------------------------------------------------
5114 Call back for pico to display mime type of attachment
5116 Args: file -- filename being attached
5118 Returns: returns 1 on success (message queued), zero otherwise (don't know
5119 type so nothing queued).
5122 mime_type_for_pico(char *file
)
5126 void *file_contents
;
5128 body
= mail_newbody();
5129 body
->type
= TYPEOTHER
;
5130 body
->encoding
= ENCOTHER
;
5132 /* don't know where the cursor's been, reset it */
5134 if(!set_mime_type_by_extension(body
, file
)){
5135 if((file_contents
=(void *)so_get(FileStar
,file
,READ_ACCESS
|READ_FROM_LOCALE
)) != NULL
){
5136 body
->contents
.text
.data
= file_contents
;
5137 set_mime_type_by_grope(body
);
5141 if(body
->type
!= TYPEOTHER
){
5143 q_status_message3(SM_ORDER
, 0, 3,
5144 _("File %s attached as type %s/%s"), file
,
5145 body_types
[body
->type
],
5146 body
->subtype
? body
->subtype
: rfc822_default_subtype(body
->type
));
5151 pine_free_body(&body
);
5156 /*----------------------------------------------------------------------
5157 Call back for pico to receive an uploaded message
5159 Args: fname -- name for uploaded file (empty if they want us to assign it)
5160 size -- pointer to long to hold the attachment's size
5162 Notes: the attachment is uploaded to a temp file, and
5164 Returns: TRUE on success, FALSE otherwise
5167 upload_msg_to_pico(char *fname
, size_t fnlen
, long int *size
)
5169 char cmd
[MAXPATH
+1], *fnp
= NULL
;
5170 char *locale_name
= NULL
;
5174 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5175 fname
? fname
: "<NO FILE>"));
5177 if(!fname
) /* no place for file name */
5180 if(!*fname
){ /* caller wants temp file */
5181 if((fnp
= temp_nam(NULL
, "pu")) != NULL
){
5182 strncpy(fname
, fnp
, fnlen
);
5183 fname
[fnlen
-1] = '\0';
5185 fs_give((void **)&fnp
);
5189 locale_name
= convert_to_locale(fname
);
5191 build_updown_cmd(cmd
, sizeof(cmd
), ps_global
->VAR_UPLOAD_CMD_PREFIX
,
5192 ps_global
->VAR_UPLOAD_CMD
, locale_name
? locale_name
: fname
);
5193 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
, PIPE_USER
| PIPE_RESET
,
5194 0, pipe_callback
, pipe_report_error
)) != NULL
){
5195 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
5196 if((l
= name_file_size(locale_name
? locale_name
: fname
)) < 0L){
5197 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
5198 "Error determining size of %s: %s", fname
,
5199 fnp
= error_description(errno
));
5201 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5203 fname
? fname
: "?",
5210 fs_give((void **) &locale_name
);
5215 q_status_message(SM_ORDER
| SM_DING
, 3, 4, _("Error opening pipe"));
5218 fs_give((void **) &locale_name
);
5225 cancel_for_pico(void (*redraw_pico
)(void))
5230 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5231 void (*redraw
)(void) = ps_global
->redrawer
;
5232 static ESCKEY_S opts
[] = {
5233 {'c', 'c', "C", N_("Confirm")},
5234 {'n', 'n', "N", N_("No")},
5239 ps_global
->redrawer
= redraw_pico
;
5240 fix_windsize(ps_global
);
5243 rv
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), opts
,
5244 'n', 'x', h_confirm_cancel
, RB_NORM
);
5245 if(rv
== 'c'){ /* user ACCEPTS! */
5250 q_status_message(SM_INFO
, 1, 3, _(" Type \"C\" to cancel message "));
5251 display_message('x');
5257 ps_global
->redrawer
= redraw
;
5262 /*----------------------------------------------------------------------
5263 Pass the first text segment of the message thru the "send filter"
5265 Args: body pointer and address for storage object of old data
5267 Returns: returns 1 on success, zero on error.
5270 filter_message_text(char *fcmd
, ENVELOPE
*outgoing
, struct mail_bodystruct
*body
,
5271 STORE_S
**old
, METAENV
*header
)
5273 char *cmd
, *tmpf
= NULL
, *resultf
= NULL
, *errstr
= NULL
, *mtf
= NULL
;
5274 int key
= 0, include_hdrs
= 0;
5276 STORE_S
**so
= (STORE_S
**)((body
->type
== TYPEMULTIPART
)
5277 ? &body
->nested
.part
->body
.contents
.text
.data
5278 : &body
->contents
.text
.data
),
5279 *tmp_so
= NULL
, *tmpf_so
,
5280 *save_local_so
, *readthis_so
, *our_tmpf_so
= NULL
;
5281 #define DO_HEADERS 1
5284 && (cmd
=expand_filter_tokens(fcmd
, outgoing
, &tmpf
, &resultf
, &mtf
,
5285 &key
, &include_hdrs
, NULL
))){
5288 * We need WRITE_TO_LOCALE here because the user is going to
5289 * be operating on tmpf. We need to change it back after they
5292 if((tmpf_so
= so_get(FileStar
, tmpf
, EDIT_ACCESS
|OWNER_ONLY
|WRITE_TO_LOCALE
)) != NULL
){
5294 so_puts(tmpf_so
, filter_session_key());
5295 so_puts(tmpf_so
, NEWLINE
);
5299 * If the headers are wanted for filtering, we can just
5300 * stick them in the tmpf file that is already there before
5301 * putting the body in.
5304 save_local_so
= lmc
.so
;
5305 lmc
.so
= tmpf_so
; /* write it to tmpf_so */
5306 lmc
.all_written
= lmc
.text_written
= lmc
.text_only
= 0;
5307 pine_rfc822_header(header
, body
, NULL
, NULL
);
5308 lmc
.so
= save_local_so
;
5311 so_seek(*so
, 0L, 0);
5312 gf_set_so_readc(&gc
, *so
);
5313 gf_set_so_writec(&pc
, tmpf_so
);
5315 errstr
= gf_pipe(gc
, pc
);
5316 gf_clear_so_readc(*so
);
5317 gf_clear_so_writec(tmpf_so
);
5321 errstr
= "Can't create space for filter temporary file.";
5323 else if(include_hdrs
){
5325 * Gf_filter wants a single storage object to read from.
5326 * If headers are wanted for filtering we'll have to put them
5327 * and the body into a temp file first and then use that
5328 * as the storage object for gf_filter.
5329 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5330 * takes care of that.
5332 if((our_tmpf_so
= so_get(TmpFileStar
, NULL
, EDIT_ACCESS
|OWNER_ONLY
)) != NULL
){
5333 /* put headers in our_tmpf_so */
5334 save_local_so
= lmc
.so
;
5335 lmc
.so
= our_tmpf_so
; /* write it to our_tmpf_so */
5336 lmc
.all_written
= lmc
.text_written
= lmc
.text_only
= 0;
5337 pine_rfc822_header(header
, body
, NULL
, NULL
);
5338 lmc
.so
= save_local_so
;
5340 /* put body in our_tmpf_so */
5341 so_seek(*so
, 0L, 0);
5342 gf_set_so_readc(&gc
, *so
);
5343 gf_set_so_writec(&pc
, our_tmpf_so
);
5345 errstr
= gf_pipe(gc
, pc
);
5346 gf_clear_so_readc(*so
);
5347 gf_clear_so_writec(our_tmpf_so
);
5349 /* tell gf_filter to read from our_tmpf_so instead of *so */
5350 readthis_so
= our_tmpf_so
;
5353 errstr
= "Can't create space for temporary file.";
5359 if((tmp_so
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
5360 gf_set_so_writec(&pc
, tmp_so
);
5361 ps_global
->mangled_screen
= 1;
5368 if((fpipe
= open_system_pipe(cmd
, NULL
, NULL
,
5369 PIPE_NOSHELL
| PIPE_RESET
,
5370 0, pipe_callback
, pipe_report_error
)) != NULL
){
5371 if(close_system_pipe(&fpipe
, NULL
, pipe_callback
) == 0){
5373 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5374 if((tmpf_so
= so_get(FileStar
, tmpf
, READ_ACCESS
|READ_FROM_LOCALE
)) != NULL
){
5375 gf_set_so_readc(&gc
, tmpf_so
);
5377 errstr
= gf_pipe(gc
, pc
);
5378 gf_clear_so_readc(tmpf_so
);
5382 errstr
= "Can't open temp file filter wrote.";
5385 errstr
= "Filter command returned error.";
5388 errstr
= "Can't exec filter text.";
5391 errstr
= gf_filter(cmd
, key
? filter_session_key() : NULL
,
5392 readthis_so
, pc
, NULL
, 0, 0,
5396 so_give(&our_tmpf_so
);
5398 gf_clear_so_writec(tmp_so
);
5403 * Can't really be using stdout, so don't print message that
5404 * gets printed in the else. Ideally the program being called
5405 * will wait after showing the message, we might want to look
5406 * into doing the waiting in console based apps... or not.
5412 fprintf(stdout
, "\r\n%s Hit return to continue.", errstr
);
5414 while((ch
= read_char(300)) != ctrl('M')
5415 && ch
!= NO_OP_IDLE
)
5419 #endif /* _WINDOWS */
5420 BODY
*b
= (body
->type
== TYPEMULTIPART
)
5421 ? &body
->nested
.part
->body
: body
;
5423 *old
= *so
; /* save old so */
5424 *so
= tmp_so
; /* return new one */
5425 (*so
)->attr
= copy_parameters((*old
)->attr
);
5428 * If the command said it would return new MIME
5429 * mime type data, check it out...
5432 char buf
[MAILTMPLEN
], *s
;
5435 if((fp
= our_fopen(mtf
, "rb")) != NULL
){
5436 if(fgets(buf
, sizeof(buf
), fp
)
5437 && !struncmp(buf
, "content-", 8)
5438 && (s
= strchr(buf
+8, ':'))){
5439 BODY
*nb
= mail_newbody();
5441 for(*s
++ = '\0'; *s
== ' '; s
++)
5444 rfc822_parse_content_header(nb
,
5445 (char *) ucase((unsigned char *) buf
+8),s
);
5446 if(nb
->type
== TYPETEXT
5449 || strucmp(b
->subtype
, nb
->subtype
))){
5451 fs_give((void **) &b
->subtype
);
5453 b
->subtype
= nb
->subtype
;
5456 mail_free_body_parameter(&b
->parameter
);
5457 b
->parameter
= nb
->parameter
;
5458 nb
->parameter
= NULL
;
5459 mail_free_body_parameter(&nb
->parameter
);
5462 mail_free_body(&nb
);
5470 * Reevaluate the encoding in case form's changed...
5472 b
->encoding
= ENCOTHER
;
5473 set_mime_type_by_grope(b
);
5480 errstr
= "Can't create space for filtered text.";
5483 fs_give((void **)&cmd
);
5490 fs_give((void **)&tmpf
);
5495 fs_give((void **) &mtf
);
5499 if(name_file_size(resultf
) > 0L)
5500 display_output_file(resultf
, "Filter", NULL
, DOF_BRIEF
);
5501 our_unlink(resultf
);
5502 fs_give((void **)&resultf
);
5508 q_status_message1(SM_ORDER
| SM_DING
, 3, 6, _("Problem filtering: %s"),
5510 dprint((1, "Filter FAILED: %s\n",
5511 errstr
? errstr
: "?"));
5514 return(errstr
== NULL
);
5518 /*----------------------------------------------------------------------
5519 Copy the newsgroup name of the given mailbox into the given buffer
5526 pine_send_newsgroup_name(char *mailbox
, char *group_name
, size_t len
)
5530 if(*mailbox
== '#'){ /* Strip the leading "#news." */
5531 strncpy(group_name
, mailbox
+ 6, len
-1);
5532 group_name
[len
-1] = '\0';
5534 else if(mail_valid_net_parse(mailbox
, &mb
)){
5535 pine_send_newsgroup_name(mb
.mailbox
, group_name
, len
);
5542 /*----------------------------------------------------------------------
5543 Set up fields for passing to pico. Assumes first text part is
5544 intended to be passed along for editing, and is in the form of
5545 of a storage object brought into existence sometime before pico_send().
5548 outgoing2strings(METAENV
*header
, struct mail_bodystruct
*bod
, void **text
,
5549 PATMT
**pico_a
, int from_bounce
)
5554 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5555 * is guaranteed to be of type PicoText!
5557 if(bod
->type
== TYPETEXT
){
5558 *text
= so_text((STORE_S
*) bod
->contents
.text
.data
);
5560 /* mark storage object as user edited */
5562 (void) so_attr((STORE_S
*) bod
->contents
.text
.data
, "edited", "1");
5564 else if(bod
->type
== TYPEMULTIPART
){
5567 char *type
, *name
, *p
;
5571 * We used to jump out the window if the first part wasn't text,
5572 * but that may not be the case when bouncing a message with
5573 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5574 * contents of the first part to send is still ALWAYS in a
5575 * PicoText storage object, *AND* if that object doesn't contain
5576 * data of type text, then it must contain THE ENCODED NON-TEXT
5577 * DATA of the piece being sent.
5579 * It's up to the programmer to make sure that such a message is
5580 * sent via pine_simple_send and never get to the composer via
5585 *text
= so_text((STORE_S
*) bod
->nested
.part
->body
.contents
.text
.data
);
5587 /* mark storage object as user edited */
5589 (void) so_attr((STORE_S
*) bod
->nested
.part
->body
.contents
.text
.data
, "edited", "1");
5592 * If we already had a list, blast it now, so we can build a new
5593 * attachment list that reflects what's really there...
5596 free_attachment_list(pico_a
);
5599 /* Simplifyihg assumption #28e. (see cross reference)
5600 All parts in the body passed in here that are not already
5601 in the attachments list are added to the end of the attachments
5602 list. Attachment items not in the body list will be taken care
5603 of in strings2outgoing, but they are unlikely to occur
5606 for(part
= bod
->nested
.part
->next
; part
!= NULL
; part
= part
->next
) {
5607 /* Already in list? */
5609 *ppa
&& strcmp((*ppa
)->id
, part
->body
.id
);
5610 ppa
= &(*ppa
)->next
)
5613 if(!*ppa
){ /* Not in the list! append it... */
5614 *ppa
= (PATMT
*)fs_get(sizeof(PATMT
));
5616 if(part
->body
.description
){
5620 len
= 4*strlen(part
->body
.description
)+1;
5621 p
= (char *)fs_get(len
*sizeof(char));
5622 if(rfc1522_decode_to_utf8((unsigned char *)p
,
5623 len
, part
->body
.description
) == (unsigned char *) p
){
5624 (*ppa
)->description
= p
;
5627 fs_give((void **)&p
);
5628 (*ppa
)->description
= cpystr(part
->body
.description
);
5632 (*ppa
)->description
= cpystr("");
5634 type
= type_desc(part
->body
.type
, part
->body
.subtype
,
5635 part
->body
.parameter
, NULL
, 0);
5638 * If we can find a "name" parm, display that too...
5640 if((name
= parameter_val(part
->body
.parameter
, "name")) != NULL
){
5641 /* Convert any [ or ]'s the name contained */
5642 for(p
= name
; *p
; p
++)
5653 (*ppa
)->filename
= fs_get(strlen(type
) + name_l
+ 5);
5655 snprintf((*ppa
)->filename
, strlen(type
) + name_l
+ 5, "[%s%s%s]", type
,
5656 name
? ": " : "", name
? name
: "");
5657 (*ppa
)->filename
[strlen(type
) + name_l
+ 5 - 1] = '\0';
5660 fs_give((void **) &name
);
5662 (*ppa
)->flags
= A_FLIT
;
5663 (*ppa
)->size
= cpystr(byte_string(
5664 send_body_size(&part
->body
)));
5666 part
->body
.id
= generate_message_id();
5668 (*ppa
)->id
= cpystr(part
->body
.id
);
5669 (*ppa
)->next
= NULL
;
5676 /*------------------------------------------------------------------
5677 Malloc strings to pass to composer editor because it expects
5678 such strings so it can realloc them
5679 -----------------------------------------------------------------*/
5681 * turn any address fields into text strings
5684 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5685 * NOT to be RFC1522 decoded. Said differently, they're understood
5686 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5687 * original charset tagging as far into the compose/send pipe as
5690 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
5698 pf
->scratch
= addr_list_string(*pf
->addr
, NULL
, 1);
5701 * Scan for and fix-up patently bogus fields.
5703 * NOTE: collaboration with this code and what's done in
5704 * reply.c:reply_cp_addr to package up the bogus stuff
5707 for(p
= pf
->scratch
; (p
= strstr(p
, "@" RAWFIELD
)); )
5709 if(*t
== '&'){ /* find "leading" token */
5713 * Rfc822_cat has been changed so that it now quotes
5714 * this sometimes. So we have to look out for quotes
5715 * which confuse the decoder. It was only quoting
5716 * because we were putting \r \n in the input, I think.
5718 if(t
> pf
->scratch
&& t
[-1] == '\"' && p
[-1] == '\"')
5719 t
[-1] = p
[-1] = ' ';
5721 *t
++ = ' '; /* replace token */
5722 *p
= '\0'; /* tie off string */
5723 u
= rfc822_base64((unsigned char *) t
,
5724 (unsigned long) strlen(t
),
5725 (unsigned long *) &l
);
5729 replacelen
= strlen(t
);
5730 *p
= '@'; /* restore 'p' */
5731 rplstr(p
, strlen(p
), 12, ""); /* clear special token */
5732 rplstr(t
, strlen(u
)-replacelen
+1, replacelen
, u
);
5734 fs_give((void **) &u
);
5737 HE(pf
)->start_here
= 1;
5741 else if(t
== pf
->scratch
)
5744 removing_leading_white_space(pf
->scratch
);
5749 * Replace control characters with ^C notation, unless
5750 * some conditions are met (see istrncpy).
5751 * If user doesn't edit, then we switch back to the
5752 * original version. If user does edit, then all bets
5755 iutf8ncpy((char *)tmp_20k_buf
, pf
->scratch
, SIZEOF_20KBUF
-1);
5756 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
5757 if((l
=strlen((char *)tmp_20k_buf
)) > strlen(pf
->scratch
)){
5758 fs_give((void **)&pf
->scratch
);
5759 pf
->scratch
= (char *)fs_get((l
+1) * sizeof(char));
5762 strncpy(pf
->scratch
, (char *)tmp_20k_buf
, l
+1);
5773 src
= pf
->scratch
? pf
->scratch
5774 : (*pf
->text
) ? *pf
->text
: "";
5776 len
= 4*strlen(src
)+1;
5777 p
= (char *)fs_get(len
* sizeof(char));
5778 if(rfc1522_decode_to_utf8((unsigned char *)p
, len
, src
) == (unsigned char *) p
){
5780 fs_give((void **)&pf
->scratch
);
5785 fs_give((void **)&p
);
5787 pf
->scratch
= cpystr(src
);
5794 * Replace control characters with ^C notation, unless
5795 * some conditions are met (see istrncpy).
5796 * If user doesn't edit, then we switch back to the
5797 * original version. If user does edit, then all bets
5800 iutf8ncpy((char *)tmp_20k_buf
, pf
->scratch
, SIZEOF_20KBUF
-1);
5801 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
5802 if((l
=strlen((char *)tmp_20k_buf
)) > strlen(pf
->scratch
)){
5803 fs_give((void **)&pf
->scratch
);
5804 pf
->scratch
= (char *)fs_get((l
+1) * sizeof(char));
5807 strncpy(pf
->scratch
, (char *)tmp_20k_buf
, l
+1);
5819 /*----------------------------------------------------------------------
5820 Restore fields returned from pico to form useful to sending
5824 strings2outgoing(METAENV
*header
, struct mail_bodystruct
**bod
, PATMT
*attach
, int flow_it
)
5829 we_cancel
= busy_cue(NULL
, NULL
, 1);
5832 * turn any local address strings into address lists
5834 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
5836 char *the_address
= NULL
;
5840 removing_trailing_white_space(pf
->scratch
);
5842 if((the_address
|| *pf
->scratch
) && pf
->addr
){
5843 ADDRESS
*new_addr
= NULL
;
5844 static char *fakedomain
= "@";
5847 the_address
= pf
->scratch
;
5849 rfc822_parse_adrlist(&new_addr
, the_address
,
5850 (F_ON(F_COMPOSE_REJECTS_UNQUAL
, ps_global
))
5851 ? fakedomain
: ps_global
->maildomain
);
5852 mail_free_address(pf
->addr
); /* free old addrs */
5853 *pf
->addr
= new_addr
; /* assign new addr */
5856 mail_free_address(pf
->addr
); /* free old addrs */
5862 fs_give((void **)pf
->text
);
5865 *pf
->text
= cpystr(pf
->scratch
);
5874 fs_give((void **)&pf
->scratch
); /* free now useless text */
5877 create_message_body(bod
, attach
, flow_it
);
5880 cancel_busy_cue(-1);
5884 /*----------------------------------------------------------------------
5886 The head of the body list here is always either TEXT or MULTIPART. It may be
5887 changed from TEXT to MULTIPART if there are attachments to be added
5888 and it is not already multipart.
5891 create_message_body(struct mail_bodystruct
**b
, PATMT
*attach
, int flow_it
)
5895 BODY
*tmp_body
, *text_body
= NULL
;
5896 void *file_contents
;
5900 TIME_STAMP("create_body start.", 1);
5902 * if conditions are met short circuit MIME wrapping
5904 if((*b
)->type
!= TYPEMULTIPART
&& !attach
){
5906 /* only override assigned encoding if it might need upgrading */
5907 if((*b
)->type
== TYPETEXT
&& (*b
)->encoding
== ENC7BIT
)
5908 (*b
)->encoding
= ENCOTHER
;
5910 create_message_body_text(*b
, flow_it
);
5912 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE
, ps_global
)
5913 || !((*b
)->encoding
== ENC8BIT
5914 || (*b
)->encoding
== ENCBINARY
)){
5915 TIME_STAMP("create_body end.", 1);
5918 else /* protect 8bit in multipart */
5922 if((*b
)->type
== TYPETEXT
) {
5923 /*-- Current type is text, but there are attachments to add --*/
5924 /*-- Upgrade to a TYPEMULTIPART --*/
5925 tmp_body
= (BODY
*)mail_newbody();
5926 tmp_body
->type
= TYPEMULTIPART
;
5927 tmp_body
->nested
.part
= mail_newbody_part();
5930 * Why do we do this?
5931 * The problem is that base64 or quoted-printable encoding is
5932 * sensitive to having random data appended to it's end. If
5933 * we use a single part TEXT message and something in between
5934 * us and the end appends advertising without adjusting for
5935 * the encoding, the message is screwed up. So we wrap the
5936 * text part inside a multipart and then the appended data
5937 * will come after the boundary.
5939 * We wish we could do this on the way out the door in a
5940 * child of post_rfc822_output because at that point we know
5941 * the character set and the encoding being used. For example,
5942 * iso-2022-jp is an encoding that is not sensitive to data
5943 * appended to the end, so it wouldn't need to be wrapped.
5944 * We could conceivably have post_rfc822_body inspect the
5945 * body and change it before doing the output. It would work
5946 * but would be very fragile. We'd be passed a body from
5947 * c-client to output and instead of just doing the output
5948 * we'd change the body and then output it. Not worth it
5949 * since the multipart wrapping is completely correct for
5950 * MIME-aware mailers.
5952 (void) copy_body(&(tmp_body
->nested
.part
->body
), *b
);
5953 /* move contents which were NOT copied */
5954 tmp_body
->nested
.part
->body
.contents
.text
.data
= (*b
)->contents
.text
.data
;
5955 (*b
)->contents
.text
.data
= NULL
;
5958 tmp_body
->nested
.part
->body
= **b
;
5960 (*b
)->subtype
= (*b
)->id
= (*b
)->description
= NULL
;
5961 (*b
)->parameter
= NULL
;
5962 (*b
)->contents
.text
.data
= NULL
;
5970 /*-- Now type must be MULTIPART with first part text --*/
5971 (*b
)->nested
.part
->body
.encoding
= ENCOTHER
;
5972 create_message_body_text(&((*b
)->nested
.part
->body
), flow_it
);
5975 /*------ Go through the parts list remove those to be deleted -----*/
5976 for(pp
= &(*b
)->nested
.part
->next
; *pp
;){
5977 for(pa
= attach
; pa
&& (*pp
)->body
.id
; pa
= pa
->next
)
5978 /* already existed? */
5979 if(pa
->id
&& strcmp(pa
->id
, (*pp
)->body
.id
) == 0){
5980 char *orig_descp
= NULL
, *cs
= NULL
;
5983 * decode original to see if it matches what was decoded
5984 * when we sent it in.
5987 if((*pp
)->body
.description
)
5988 orig_descp
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
5989 SIZEOF_20KBUF
, (*pp
)->body
.description
);
5991 if(!(*pp
)->body
.description
/* update description? */
5992 || (pa
->description
&& strcmp(pa
->description
, orig_descp
))){
5993 if((*pp
)->body
.description
)
5994 fs_give((void **) &(*pp
)->body
.description
);
5996 /* encoding happens as msg text is written */
5997 (*pp
)->body
.description
= cpystr(pa
->description
);
6001 fs_give((void **) &cs
);
6007 p
= *pp
; /* prepare to zap *pp */
6008 *pp
= p
->next
; /* pull next one in list up */
6009 p
->next
= NULL
; /* tie off removed node */
6011 pine_free_body_data(&p
->body
); /* clean up contained data */
6012 mail_free_body_part(&p
); /* free up the part */
6018 /*---------- Now add any new attachments ---------*/
6019 for(p
= (*b
)->nested
.part
; p
->next
!= NULL
; p
= p
->next
);
6020 for(pa
= attach
; pa
!= NULL
; pa
= pa
->next
) {
6022 continue; /* Has an ID, it's old */
6025 * the idea is handle ALL attachments as open FILE *'s. Actual
6026 * encoding and such is handled at the time the message
6027 * is shoved into the mail slot or written to disk...
6029 * Also, we never unlink a file, so it's up to whoever opens
6030 * it to deal with tmpfile issues.
6032 if((file_contents
= (void *)so_get(FileStar
, pa
->filename
,
6033 READ_ACCESS
)) == NULL
){
6034 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
6035 _("Error \"%s\", couldn't attach file \"%s\""),
6036 error_description(errno
), pa
->filename
);
6037 display_message('x');
6041 p
->next
= mail_newbody_part();
6043 p
->body
.id
= generate_message_id();
6044 p
->body
.contents
.text
.data
= file_contents
;
6047 * Set type to unknown and let set_mime_type_by_* figure it out.
6048 * Always encode attachments we add as BINARY.
6050 p
->body
.type
= TYPEOTHER
;
6051 p
->body
.encoding
= ENCBINARY
;
6052 p
->body
.size
.bytes
= name_file_size(pa
->filename
);
6053 if(!set_mime_type_by_extension(&p
->body
, pa
->filename
)){
6054 set_mime_type_by_grope(&p
->body
);
6055 set_charset_possibly_to_ascii(&p
->body
, ps_global
->keyboard_charmap
);
6058 so_release((STORE_S
*)p
->body
.contents
.text
.data
);
6060 if(pa
->description
) /* encoding happens when msg written */
6061 p
->body
.description
= cpystr(pa
->description
);
6063 /* Add name attribute for backward compatibility */
6064 for(parmp
= &p
->body
.parameter
; *parmp
; )
6065 if(!struncmp((*parmp
)->attribute
, "name", 4)
6066 && (!*((*parmp
)->attribute
+ 4)
6067 || *((*parmp
)->attribute
+ 4) == '*')){
6068 PARAMETER
*free_me
= *parmp
;
6069 *parmp
= (*parmp
)->next
;
6070 free_me
->next
= NULL
;
6071 mail_free_body_parameter(&free_me
);
6074 parmp
= &(*parmp
)->next
;
6076 set_parameter(parmp
, "name",
6078 ? ((lc
= last_cmpnt(pa
->filename
)) ? lc
: pa
->filename
)
6081 /* Then set the Content-Disposition ala RFC1806 */
6082 if(!p
->body
.disposition
.type
){
6083 p
->body
.disposition
.type
= cpystr("attachment");
6084 for(parmp
= &p
->body
.disposition
.parameter
; *parmp
; )
6085 if(!struncmp((*parmp
)->attribute
, "filename", 4)
6086 && (!*((*parmp
)->attribute
+ 4)
6087 || *((*parmp
)->attribute
+ 4) == '*')){
6088 PARAMETER
*free_me
= *parmp
;
6089 *parmp
= (*parmp
)->next
;
6090 free_me
->next
= NULL
;
6091 mail_free_body_parameter(&free_me
);
6094 parmp
= &(*parmp
)->next
;
6096 set_parameter(parmp
, "filename",
6098 ? ((lc
= last_cmpnt(pa
->filename
)) ? lc
: pa
->filename
)
6103 pa
->id
= cpystr(p
->body
.id
);
6107 * Now, if this multipart has but one text piece (that is, no
6108 * attachments), then downgrade from a composite type to a discrete
6109 * text/plain message if CTE is not 8bit.
6111 if(!(*b
)->nested
.part
->next
6112 && (*b
)->nested
.part
->body
.type
== TYPETEXT
6113 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE
, ps_global
)
6114 || !((*b
)->nested
.part
->body
.encoding
== ENC8BIT
6115 || (*b
)->nested
.part
->body
.encoding
== ENCBINARY
))){
6116 /* Clone the interesting body part */
6117 tmp_body
= mail_newbody();
6118 *tmp_body
= (*b
)->nested
.part
->body
;
6119 /* and rub out what we don't want cleaned up when it's free'd */
6120 mail_initbody(&(*b
)->nested
.part
->body
);
6126 TIME_STAMP("create_body end.", 1);
6131 * Fill in text BODY part's structure
6135 create_message_body_text(struct mail_bodystruct
*b
, int flow_it
)
6137 set_mime_type_by_grope(b
);
6139 remove_parameter(&b
->parameter
, "format"); /* we will set it up below */
6140 remove_parameter(&b
->parameter
, "delsp"); /* we never set this up */
6142 if(F_OFF(F_QUELL_FLOWED_TEXT
, ps_global
)
6143 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps_global
)
6145 set_parameter(b
? &b
->parameter
: NULL
, "format", "flowed");
6152 * free_attachment_list - free attachments in given list
6155 free_attachment_list(PATMT
**alist
)
6159 while(alist
&& *alist
){ /* pointer pointing to something */
6160 leading
= (*alist
)->next
;
6161 if((*alist
)->description
)
6162 fs_give((void **)&(*alist
)->description
);
6164 if((*alist
)->filename
){
6165 if((*alist
)->flags
& A_TMP
)
6166 if(our_unlink((*alist
)->filename
) < 0)
6167 dprint((1, "-- Can't unlink(%s): %s\n",
6168 (*alist
)->filename
? (*alist
)->filename
: "?",
6169 error_description(errno
)));
6171 fs_give((void **)&(*alist
)->filename
);
6175 fs_give((void **)&(*alist
)->size
);
6178 fs_give((void **)&(*alist
)->id
);
6180 fs_give((void **)alist
);
6188 set_body_size(struct mail_bodystruct
*b
)
6193 we_cancel
= busy_cue(NULL
, NULL
, 1);
6194 so_seek((STORE_S
*)b
->contents
.text
.data
, 0L, 0);
6196 while(so_readc(&c
, (STORE_S
*)b
->contents
.text
.data
))
6200 cancel_busy_cue(-1);
6205 * view_as_rich - set the rich_header flag
6207 * name - name of the header field
6208 * deflt - default value to return if user didn't set it
6210 * Note: if the user tries to turn them all off with "", then
6211 * we take that to mean default, since otherwise there is no
6212 * way to get to the headers.
6215 view_as_rich(char *name
, int deflt
)
6220 p
= ps_global
->VAR_COMP_HDRS
;
6223 for(; (q
= *p
) != NULL
; p
++){
6224 if(!struncmp(q
, name
, strlen(name
)))
6225 return 0; /* 0 means we *do* view it by default */
6228 return 1; /* 1 means it starts out hidden */
6235 * background_posting - return whether or not we're already in the process
6239 background_posting(int gripe
)
6241 if(ps_global
->post
){
6243 q_status_message(SM_ORDER
|SM_DING
, 3, 3,
6244 _("Can't post while posting!"));
6252 /*----------------------------------------------------------------------
6253 Validate the given subject relative to any news groups.
6257 Returns: always returns 1, but also returns error if
6260 valid_subject(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
6262 struct headerentry
*hp
;
6265 *expanded
= cpystr(given
);
6269 * Now look for any header entry we passed to pico that has to do
6270 * with news. If there's no subject, gripe.
6272 for(hp
= pbf
->headents
; hp
->prompt
; hp
++)
6273 if(hp
->help
== h_composer_news
){
6274 if(hp
->hd_text
->text
[0] && !*given
)
6276 _("News postings MUST have a subject! Please add one!"));
6287 * This is the build_address used by the composer to check for an address
6290 * Args: to -- the passed in line to parse
6291 * full_to -- Address of a pointer to return the full address in.
6292 * This will be allocated here and freed by the caller.
6293 * error -- Address of a pointer to return an error message in.
6294 * This will be allocated here and freed by the caller.
6295 * barg -- Address of a pointer to return the fcc in is in
6296 * fcc->tptr. It will have already been allocated by the
6297 * caller but we may free it and reallocate if we wish.
6298 * Caller will free it.
6300 * Result: 0 is returned if address was OK,
6301 * -1 if address wasn't OK.
6303 * Side effect: Can flush addrbook entry cache entries so they need to be
6304 * re-fetched afterwords.
6307 build_address(char *to
, char **full_to
, char **error
, BUILDER_ARG
*barg
, int *mangled
)
6310 int ret_val
, no_repo
= 0, *save_nesting_level
;
6312 PrivateTop
*pt
= NULL
;
6313 PrivateAffector
*af
= NULL
;
6314 char *fcc_local
= NULL
;
6315 jmp_buf save_jmp_buf
;
6317 dprint((5, "- build_address - (%s)\n", to
? to
: "nul"));
6319 /* check to see if to string is empty to avoid work */
6320 for(p
= to
; p
&& *p
&& isspace((unsigned char)*p
); p
++)
6325 *full_to
= cpystr(to
? to
: ""); /* because pico does a strcmp() */
6331 *full_to
= (char *)NULL
;
6334 *error
= (char *)NULL
;
6336 /* No guarantee cursor or status line is how we saved it */
6338 mark_status_unknown();
6340 if(ps_global
->remote_abook_validity
> 0 &&
6341 adrbk_check_and_fix_all(ab_nesting_level
== 0, 0, 0) && mangled
)
6342 *mangled
|= BUILDER_SCREEN_MANGLED
;
6345 * If we end up jumping back here because somebody else changed one of
6346 * our addrbooks out from underneath us, we may well leak some memory.
6347 * That's probably ok since this will be very rare.
6349 * The reason for the memcpy of the jmp_buf is that we may actually
6350 * be indirectly calling this function from within the address book.
6351 * For example, we may be in the address book screen and then run
6352 * the ComposeTo command which puts us in the composer, then we call
6353 * build_address from there which resets addrbook_changed_unexpectedly.
6354 * Once we leave build_address we need to reset addrbook_changed_un...
6355 * because this position on the stack will no longer be valid.
6356 * Same is true of the other setjmp's in this file which are wrapped
6359 save_nesting_level
= cpyint(ab_nesting_level
);
6360 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
6361 if(setjmp(addrbook_changed_unexpectedly
)){
6367 *error
= (char *)NULL
;
6369 if(full_to
&& *full_to
)
6370 fs_give((void **)full_to
);
6372 q_status_message(SM_ORDER
, 3, 5, "Resetting address book...");
6374 "RESETTING address book... build_address(%s)!\n", to
? to
: "?"));
6376 ab_nesting_level
= *save_nesting_level
;
6382 ret_val
= build_address_internal(bldto
, full_to
, error
,
6383 barg
? &fcc_local
: NULL
,
6384 &no_repo
, NULL
, save_and_restore
,
6387 if(save_nesting_level
)
6388 fs_give((void **)&save_nesting_level
);
6391 * Have to rfc1522_decode the full_to string before sending it back.
6393 if(full_to
&& *full_to
){
6397 len
= 4*strlen(*full_to
)+1;
6398 q
= (char *)fs_get(len
* sizeof(char));
6399 p
= (char *)rfc1522_decode_to_utf8((unsigned char *)q
, len
, *full_to
);
6401 /* p == q means that decoding happened, p is decoded *full_to */
6403 fs_give((void **)full_to
);
6407 fs_give((void **)&q
);
6413 /* Pt will point to headents[Fcc].bldr_private */
6415 if(barg
&& barg
->aff
)
6416 pt
= (PrivateTop
*)(*barg
->aff
);
6419 * If *barg->aff is set, that means fcc was set from a list
6420 * during some previous builder call.
6421 * If the current To line contains the old expansion as a prefix, then
6422 * we should leave things as they are. In order to decide that,
6423 * we look at a hash value computed from the strings.
6425 if(pt
&& (af
=pt
->affector
) && af
->who
== BP_To
){
6429 if(len
>= af
->cksumlen
){
6432 save
= to
[af
->cksumlen
];
6433 to
[af
->cksumlen
] = '\0';
6434 csum
= line_hash(to
);
6435 to
[af
->cksumlen
] = save
;
6438 csum
= af
->cksumval
+ 1; /* something not equal to cksumval */
6443 (pt
->affector
->who
== BP_To
&& csum
!= pt
->affector
->cksumval
)){
6445 /* replace fcc value */
6447 fs_give((void **)&barg
->tptr
);
6449 barg
->tptr
= fcc_local
;
6453 *barg
->aff
= (void *)fs_get(sizeof(PrivateTop
));
6454 pt
= (PrivateTop
*)(*barg
->aff
);
6455 memset((void *)pt
, 0, sizeof(PrivateTop
));
6461 (PrivateAffector
*)fs_get(sizeof(PrivateAffector
));
6465 af
->cksumlen
= strlen(((full_to
&& *full_to
)
6467 af
->cksumval
= line_hash(((full_to
&& *full_to
)
6472 * If result is reproducible, we don't keep track here.
6475 fs_give((void **)&pt
->affector
);
6480 fs_give((void **)&fcc_local
); /* unused in this case */
6483 /* This is so pico will erase the old message */
6484 if(error
!= NULL
&& *error
== NULL
)
6485 *error
= cpystr("");
6487 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
6488 flush_status_messages(1);
6494 * This is the builder used by the composer for the Lcc line.
6496 * Args: lcc -- the passed in Lcc line to parse
6497 * full_lcc -- Address of a pointer to return the full address in.
6498 * This will be allocated here and freed by the caller.
6499 * error -- Address of a pointer to return an error message in.
6500 * This is not allocated so should not be freed by the caller.
6501 * barg -- This is a pointer to text for affected entries which
6502 * we may be changing. The first one in the list is the
6503 * To entry. We may put the name of the list in empty
6504 * group syntax form there (like List Name: ;).
6505 * The second one in the list is the fcc field.
6506 * The tptr members already point to text allocated in the
6507 * caller. We may free and reallocate here, caller will
6508 * free the result in any case.
6510 * Result: 0 is returned if address was OK,
6511 * -1 if address wasn't OK.
6513 * Side effect: Can flush addrbook entry cache entries so they need to be
6514 * re-fetched afterwords.
6517 build_addr_lcc(char *lcc
, char **full_lcc
, char **error
, BUILDER_ARG
*barg
, int *mangled
)
6520 no_repo
= 0; /* fcc or lcc not reproducible */
6521 int *save_nesting_level
;
6523 PrivateTop
*pt
= NULL
;
6524 PrivateAffector
*af
= NULL
;
6529 jmp_buf save_jmp_buf
;
6531 dprint((5, "- build_addr_lcc - (%s)\n", lcc
? lcc
: "nul"));
6533 /* check to see if to string is empty to avoid work */
6534 for(p
= lcc
; p
&& *p
&& isspace((unsigned char)*p
); p
++)
6539 *full_lcc
= cpystr(lcc
? lcc
: ""); /* because pico does a strcmp() */
6545 *error
= (char *)NULL
;
6547 if(ps_global
->remote_abook_validity
> 0 &&
6548 adrbk_check_and_fix_all(ab_nesting_level
== 0, 0, 0) && mangled
)
6549 *mangled
|= BUILDER_SCREEN_MANGLED
;
6552 * If we end up jumping back here because somebody else changed one of
6553 * our addrbooks out from underneath us, we may well leak some memory.
6554 * That's probably ok since this will be very rare.
6556 save_nesting_level
= cpyint(ab_nesting_level
);
6557 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
6558 if(setjmp(addrbook_changed_unexpectedly
)){
6565 *error
= (char *)NULL
;
6567 if(full_lcc
&& *full_lcc
)
6568 fs_give((void **)full_lcc
);
6570 q_status_message(SM_ORDER
, 3, 5, "Resetting address book...");
6572 "RESETTING address book... build_address(%s)!\n", lcc
? lcc
: "?"));
6574 ab_nesting_level
= *save_nesting_level
;
6579 bldlcc
.arg
.str
= lcc
;
6582 * To is first affected_entry and Fcc is second.
6583 * The conditional stuff for the fcc argument says to only change the
6584 * fcc if the fcc pointer is passed in non-null, and the To pointer
6585 * is also non-null. If they are null, that means they've already been
6586 * entered (are sticky). We don't affect fcc if either fcc or To has
6589 ret_val
= build_address_internal(bldlcc
,
6592 (barg
&& barg
->next
&& barg
->next
->tptr
&& barg
->tptr
)
6593 ? &fcc_local
: NULL
,
6595 (barg
&& barg
->tptr
) ? &to
: NULL
,
6596 save_and_restore
, 0, mangled
);
6599 if(save_nesting_level
)
6600 fs_give((void **)&save_nesting_level
);
6602 /* full_lcc is what ends up in the Lcc: line */
6603 if(full_lcc
&& *full_lcc
){
6607 * Have to rfc1522_decode the full_lcc string before sending it back.
6609 len
= 4*strlen(*full_lcc
)+1;
6610 p
= (char *)fs_get(len
* sizeof(char));
6611 if(rfc1522_decode_to_utf8((unsigned char *)p
, len
, *full_lcc
) == (unsigned char *)p
){
6612 fs_give((void **)full_lcc
);
6616 fs_give((void **)&p
);
6619 /* to is what ends up in the To: line */
6625 * Have to rfc1522_decode the full_to string before sending it back.
6627 len
= 4*strlen(to
)+1;
6628 p
= (char *)fs_get(len
* sizeof(char));
6630 if(rfc1522_decode_to_utf8((unsigned char *)p
, len
, to
) == (unsigned char *)p
){
6632 * If the caller wants us to try to preserve the charset
6633 * information (they set aff) we copy it into encoded->etext.
6634 * We don't have to worry about pasting together pieces of
6635 * etext like we do in build_address because whenever the
6636 * Lcc line is setting the To line it will be setting the
6637 * whole line, not modifying it.
6638 * Pt will point to headents[To].bldr_private.
6640 if(barg
&& barg
->aff
){
6641 pt
= (PrivateTop
*)(*barg
->aff
);
6644 *barg
->aff
= (void *)fs_get(sizeof(PrivateTop
));
6645 pt
= (PrivateTop
*)(*barg
->aff
);
6646 memset((void *)pt
, 0, sizeof(PrivateTop
));
6650 fs_give((void **)&to
);
6654 fs_give((void **)&p
);
6657 fs_give((void **)&dummy
);
6661 * This part is recording the fact that the To line was set to
6662 * what it is by entering something on the Lcc line. In particular,
6663 * if a list alias was entered here then the fullname of the list
6664 * goes in the To line. We save this affector information so that
6665 * we can tell it shouldn't be modified if we call build_addr_lcc
6666 * again unless we actually modified what's in the Lcc line so that
6667 * it doesn't start with the same thing. The problem we're solving
6668 * is that the contents of the Lcc line no longer look like the
6669 * list they were derived from.
6670 * Pt will point to headents[To].bldr_private.
6672 if(barg
&& barg
->aff
)
6673 pt
= (PrivateTop
*)(*barg
->aff
);
6675 if(pt
&& (af
=pt
->affector
) && af
->who
== BP_Lcc
){
6679 if(len
>= af
->cksumlen
){
6682 save
= lcc
[af
->cksumlen
];
6683 lcc
[af
->cksumlen
] = '\0';
6684 csum
= line_hash(lcc
);
6685 lcc
[af
->cksumlen
] = save
;
6688 csum
= af
->cksumval
+ 1; /* so they aren't equal */
6693 pt
->affector
->who
!= BP_Lcc
||
6694 (pt
->affector
->who
== BP_Lcc
&& csum
!= pt
->affector
->cksumval
)){
6696 /* replace to value */
6697 if(barg
->tptr
&& barg
->tptr
[0]){
6701 l
= strlen(barg
->tptr
) + strlen(to
? to
: "") + 2;
6702 t
= (char *)fs_get((l
+1) * sizeof(char));
6703 snprintf(t
, l
+1, "%s%s%s",
6705 (to
&& *to
) ? ", " : "",
6706 (to
&& *to
) ? to
: "");
6707 fs_give((void **)&barg
->tptr
);
6709 fs_give((void **)&to
);
6715 fs_give((void **)&barg
->tptr
);
6722 *barg
->aff
= (void *)fs_get(sizeof(PrivateTop
));
6723 pt
= (PrivateTop
*)(*barg
->aff
);
6724 memset((void *)pt
, 0, sizeof(PrivateTop
));
6730 (PrivateAffector
*)fs_get(sizeof(PrivateAffector
));
6734 af
->cksumlen
= strlen(((full_lcc
&& *full_lcc
)
6736 af
->cksumval
= line_hash(((full_lcc
&& *full_lcc
)
6741 * If result is reproducible, we don't keep track here.
6744 fs_give((void **)&pt
->affector
);
6749 fs_give((void **)&to
); /* unused in this case */
6756 * If *barg->next->aff is set, that means fcc was set from a list
6757 * during some previous builder call. If the current Lcc line
6758 * contains the old expansion as a prefix, then we should leave
6759 * things as they are. In order to decide that we look at a hash
6760 * value computed from the strings.
6761 * Pt will point to headents[Fcc].bldr_private
6764 if(barg
&& barg
->next
&& barg
->next
->aff
)
6765 pt
= (PrivateTop
*)(*barg
->next
->aff
);
6767 if(pt
&& (af
=pt
->affector
) && af
->who
== BP_Lcc
){
6771 if(len
>= af
->cksumlen
){
6774 save
= lcc
[af
->cksumlen
];
6775 lcc
[af
->cksumlen
] = '\0';
6776 csum
= line_hash(lcc
);
6777 lcc
[af
->cksumlen
] = save
;
6780 csum
= af
->cksumval
+ 1; /* something not equal to cksumval */
6785 pt
->affector
->who
!= BP_Lcc
||
6786 (pt
->affector
->who
== BP_Lcc
&& csum
!= pt
->affector
->cksumval
)){
6788 /* replace fcc value */
6789 if(barg
->next
->tptr
)
6790 fs_give((void **)&barg
->next
->tptr
);
6792 barg
->next
->tptr
= fcc_local
;
6794 if(barg
->next
->aff
){
6796 *barg
->next
->aff
= (void *)fs_get(sizeof(PrivateTop
));
6797 pt
= (PrivateTop
*)(*barg
->next
->aff
);
6798 memset((void *)pt
, 0, sizeof(PrivateTop
));
6804 (PrivateAffector
*)fs_get(sizeof(PrivateAffector
));
6808 af
->cksumlen
= strlen(((full_lcc
&& *full_lcc
)
6810 af
->cksumval
= line_hash(((full_lcc
&& *full_lcc
)
6815 * If result is reproducible, we don't keep track here.
6818 fs_give((void **)&pt
->affector
);
6823 fs_give((void **)&fcc_local
); /* unused in this case */
6827 if(error
!= NULL
&& *error
== NULL
)
6828 *error
= cpystr("");
6830 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
6831 flush_status_messages(0);
6836 /*----------------------------------------------------------------------
6837 Verify and canonicalize news groups names.
6838 Called from the message composer
6840 Args: given_group -- List of groups typed by user
6841 expanded_group -- pointer to point to expanded list, which will be
6842 allocated here and freed in caller. If this is
6843 NULL, don't attempt to validate.
6844 error -- pointer to store error message
6845 fcc -- pointer to point to fcc, which will be
6846 allocated here and freed in caller
6848 Returns: 0 if all is OK
6849 -1 if addresses weren't valid
6851 Test the given list of newstroups against those recognized by our nntp
6852 servers. Testing by actually trying to open the list is much cheaper, both
6853 in bandwidth and memory, than yanking the whole list across the wire.
6856 news_build(char *given_group
, char **expanded_group
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
6859 char *fccptr
= NULL
;
6861 if(fcc
&& fcc
->tptr
)
6862 fccptr
= cpystr(fcc
->tptr
);
6866 rv
= news_grouper(given_group
, expanded_group
, error
, &fccptr
, news_build_busy
);
6868 /* assign any new fcc to the BUILDER_ARG */
6872 if(fcc
->tptr
&& strcmp(fcc
->tptr
, fccptr
)){
6873 fs_give((void **) &fcc
->tptr
);
6880 fs_give((void **) &fccptr
);
6883 /* deal with any busy indicator */
6887 mark_status_dirty();
6888 display_message('x');
6890 *mangled
|= BUILDER_MESSAGE_DISPLAYED
;
6899 news_build_busy(void)
6901 news_busy_cue
= busy_cue("Validating newsgroup(s)", NULL
, 0);
6905 #if defined(DOS) || defined(OS2)
6907 /*----------------------------------------------------------------------
6908 Verify that the necessary pieces are around to allow for
6909 message sending under DOS
6911 Args: strict -- tells us if a remote stream is required before
6912 sending is permitted.
6914 The idea is to make sure pine knows enough to put together a valid
6915 from line. The things we MUST know are a user-id, user-domain and
6916 smtp server to dump the message off on. Typically these are
6917 provided in pine's configuration file, but if not, the user is
6923 char prompt
[100], answer
[80];
6928 * query for user name portion of address, use IMAP login
6931 if(!ps_global
->VAR_USER_ID
|| ps_global
->VAR_USER_ID
[0] == '\0'){
6933 int no_prompt_user_id
= 0;
6935 if(ps_global
->mail_stream
&& ps_global
->mail_stream
->mailbox
6936 && mail_valid_net_parse(ps_global
->mail_stream
->mailbox
, &mb
)
6938 strncpy(answer
, mb
.user
, sizeof(answer
)-1);
6939 answer
[sizeof(answer
)-1] = '\0';
6941 else if(F_ON(F_QUELL_USER_ID_PROMPT
, ps_global
)){
6942 /* no user-id prompting if set */
6943 no_prompt_user_id
= 1;
6945 if(!ps_global
->mail_stream
)
6946 do_broach_folder(ps_global
->inbox_name
,
6947 ps_global
->context_list
, NULL
, DB_INBOXWOCNTXT
);
6948 if(ps_global
->mail_stream
&& ps_global
->mail_stream
->mailbox
6949 && mail_valid_net_parse(ps_global
->mail_stream
->mailbox
, &mb
)
6951 strncpy(answer
, mb
.user
, sizeof(answer
)-1);
6952 answer
[sizeof(answer
)-1] = '\0';
6960 if(F_ON(F_QUELL_USER_ID_PROMPT
, ps_global
) && answer
[0]){
6961 /* No prompt, just assume mailbox login is user-id */
6962 no_prompt_user_id
= 1;
6966 snprintf(prompt
,sizeof(prompt
),_("User-id for From address : "));
6967 prompt
[sizeof(prompt
)-1] = '\0';
6970 while(!no_prompt_user_id
) {
6971 flags
= OE_APPEND_CURRENT
;
6972 rc
= optionally_enter(answer
,-FOOTER_ROWS(ps_global
),0,
6973 sizeof(answer
),prompt
,NULL
,help
,&flags
);
6978 help
= (help
== NO_HELP
) ? h_sticky_user_id
: NO_HELP
;
6986 if(rc
== 1 || (rc
== 0 && !answer
[0])) {
6987 q_status_message(SM_ORDER
, 3, 4,
6988 _("Send cancelled (User-id must be provided before sending)"));
6993 snprintf(prompt
, sizeof(prompt
), _("Preserve %.*s as \"user-id\" in PINERC"),
6994 sizeof(prompt
)-50, answer
);
6995 prompt
[sizeof(prompt
)-1] = '\0';
6996 if(ps_global
->blank_user_id
6997 && !no_prompt_user_id
6998 && want_to(prompt
, 'y', 'n', NO_HELP
, WT_NORM
) == 'y'){
6999 set_variable(V_USER_ID
, answer
, 1, 1, Main
);
7002 fs_give((void **)&(ps_global
->VAR_USER_ID
));
7003 ps_global
->VAR_USER_ID
= cpystr(answer
);
7007 /* query for personal name */
7008 if(!ps_global
->VAR_PERSONAL_NAME
|| ps_global
->VAR_PERSONAL_NAME
[0]=='\0'
7009 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT
, ps_global
)){
7011 snprintf(prompt
, sizeof(prompt
), _("Personal name for From address : "));
7012 prompt
[sizeof(prompt
)-1] = '\0';
7016 flags
= OE_APPEND_CURRENT
;
7017 rc
= optionally_enter(answer
,-FOOTER_ROWS(ps_global
),0,
7018 sizeof(answer
),prompt
,NULL
,help
,&flags
);
7023 help
= (help
== NO_HELP
) ? h_sticky_personal_name
: NO_HELP
;
7031 if(rc
== 0 && answer
){ /* save the name */
7032 snprintf(prompt
, sizeof(prompt
), _("Preserve %.*s as \"personal-name\" in PINERC"),
7033 sizeof(prompt
)-50, answer
);
7034 prompt
[sizeof(prompt
)-1] = '\0';
7035 if(ps_global
->blank_personal_name
7036 && want_to(prompt
, 'y', 'n', NO_HELP
, WT_NORM
) == 'y'){
7037 set_variable(V_PERSONAL_NAME
, answer
, 1, 1, Main
);
7040 fs_give((void **)&(ps_global
->VAR_PERSONAL_NAME
));
7041 ps_global
->VAR_PERSONAL_NAME
= cpystr(answer
);
7047 * query for host/domain portion of address, using IMAP
7050 if(ps_global
->blank_user_domain
7051 || ps_global
->maildomain
== ps_global
->localdomain
7052 || ps_global
->maildomain
== ps_global
->hostname
){
7053 if(ps_global
->inbox_name
[0] == '{'){
7055 i
< sizeof(answer
)-1 && ps_global
->inbox_name
[i
+1] != '}'; i
++)
7056 answer
[i
] = ps_global
->inbox_name
[i
+1];
7063 snprintf(prompt
,sizeof(prompt
),_("Host/domain for From address : "));
7064 prompt
[sizeof(prompt
)-1] = '\0';
7068 flags
= OE_APPEND_CURRENT
;
7069 rc
= optionally_enter(answer
,-FOOTER_ROWS(ps_global
),0,
7070 sizeof(answer
),prompt
,NULL
,help
,&flags
);
7075 help
= (help
== NO_HELP
) ? h_sticky_domain
: NO_HELP
;
7083 if(rc
== 1 || (rc
== 0 && !answer
[0])) {
7084 q_status_message(SM_ORDER
, 3, 4,
7085 _("Send cancelled (Host/domain name must be provided before sending)"));
7090 snprintf(prompt
, sizeof(prompt
), _("Preserve %.*s as \"user-domain\" in PINERC"),
7091 sizeof(prompt
)-50, answer
);
7092 prompt
[sizeof(prompt
)-1] = '\0';
7093 if(!ps_global
->userdomain
&& !ps_global
->blank_user_domain
7094 && want_to(prompt
, 'y', 'n', NO_HELP
, WT_NORM
) == 'y'){
7095 set_variable(V_USER_DOMAIN
, answer
, 1, 1, Main
);
7096 fs_give((void **)&(ps_global
->maildomain
)); /* blast old val */
7097 ps_global
->userdomain
= cpystr(answer
);
7098 ps_global
->maildomain
= ps_global
->userdomain
;
7101 fs_give((void **)&(ps_global
->maildomain
));
7102 ps_global
->userdomain
= cpystr(answer
);
7103 ps_global
->maildomain
= ps_global
->userdomain
;
7107 /* check for smtp server */
7108 if(!ps_global
->VAR_SMTP_SERVER
||
7109 !ps_global
->VAR_SMTP_SERVER
[0] ||
7110 !ps_global
->VAR_SMTP_SERVER
[0][0]){
7113 if(ps_global
->inbox_name
[0] == '{'){
7115 i
< sizeof(answer
)-1 && ps_global
->inbox_name
[i
+1] != '}'; i
++)
7116 answer
[i
] = ps_global
->inbox_name
[i
+1];
7123 snprintf(prompt
,sizeof(prompt
),_("SMTP server to forward message : "));
7124 prompt
[sizeof(prompt
)-1] = '\0';
7128 flags
= OE_APPEND_CURRENT
;
7129 rc
= optionally_enter(answer
,-FOOTER_ROWS(ps_global
),0,
7130 sizeof(answer
),prompt
,NULL
,help
,&flags
);
7135 help
= (help
== NO_HELP
) ? h_sticky_smtp
: NO_HELP
;
7143 if(rc
== 1 || (rc
== 0 && answer
[0] == '\0')) {
7144 q_status_message(SM_ORDER
, 3, 4,
7145 _("Send cancelled (SMTP server must be provided before sending)"));
7150 list
= (char **) fs_get(2 * sizeof(char *));
7151 list
[0] = cpystr(answer
);
7153 set_variable_list(V_SMTP_SERVER
, list
, TRUE
, Main
);
7154 fs_give((void *)&list
[0]);
7155 fs_give((void *)list
);
7161 #endif /* defined(DOS) || defined(OS2) */