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 2006-2008 University of Washington
8 * Copyright 2013-2015 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 Functions for composing and sending mail
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
;
74 #define PHONE_HOME_VERSION "-count"
75 #define PHONE_HOME_HOST "patches.freeiz.com"
78 * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook
80 #define HE(PF) ((struct headerentry *)((PF)->extdata))
86 int redraft(MAILSTREAM
**, ENVELOPE
**, BODY
**, char **, char **, REPLY_S
**,
87 REDRAFT_POS_S
**, PINEFIELD
**, ACTION_S
**, int);
88 int redraft_prompt(char *, char *, int);
89 int check_for_subject(METAENV
*);
90 int check_for_fcc(char *);
91 void free_prompts(PINEFIELD
*);
92 int postpone_prompt(void);
93 METAENV
*pine_simple_send_header(ENVELOPE
*, char **, char ***);
94 void call_mailer_file_result(char *, int);
95 void mark_address_failure_for_pico(METAENV
*);
97 *save_body_particulars(BODY
*);
98 void reset_body_particulars(BODY_PARTICULARS_S
*, BODY
*);
99 void free_body_particulars(BODY_PARTICULARS_S
*);
100 long message_format_for_pico(long, int (*)(int));
101 int send_exit_for_pico(struct headerentry
*, void (*)(void), int, char **);
102 void new_thread_on_blank_subject(void);
103 char *choose_a_priority(char *);
104 int dont_flow_this_time(void);
105 int mime_type_for_pico(char *);
106 char *cancel_for_pico(void (*)(void));
107 int filter_message_text(char *, ENVELOPE
*, BODY
*, STORE_S
**, METAENV
*);
108 void pine_send_newsgroup_name(char *, char*, size_t);
109 void outgoing2strings(METAENV
*, BODY
*, void **, PATMT
**, int);
110 void strings2outgoing(METAENV
*, BODY
**, PATMT
*, int);
111 void create_message_body_text(BODY
*, int);
112 void set_body_size(BODY
*);
113 int view_as_rich(char *, int);
114 int background_posting(int);
115 int valid_subject(char *, char **, char **,BUILDER_ARG
*,int *);
116 int build_addr_lcc(char *, char **, char **, BUILDER_ARG
*, int *);
117 int news_build(char *, char **, char **, BUILDER_ARG
*, int *);
118 void news_build_busy(void);
119 #if defined(DOS) || defined(OS2)
120 int dos_valid_from(void);
121 #endif /* defined(DOS) || defined(OS2) */
125 * Pointer to buffer to hold pointers into pine data that's needed by pico.
130 static char *g_rolenick
= NULL
;
133 static char *sending_filter_requested
;
134 static char background_requested
, flowing_requested
;
135 static unsigned call_mailer_flags
;
136 static char *priority_requested
;
138 /* local global to save busy_cue state */
139 static int news_busy_cue
= 0;
143 * Various useful strings
146 _("Continue INTERRUPTED composition (answering \"n\" won't erase it)")
148 _("Continue postponed composition (answering \"No\" won't erase it)")
150 _("Start composition from Form Letter Folder")
151 #define PSTPN_FORM_PMT \
152 _("Save to Postponed or Form letter folder? ")
154 _("Posted message may go to thousands of readers. Really post")
155 #define INTR_DEL_PMT \
156 _("Deleted messages will be removed from folder after use. Proceed")
160 * Macros to help sort out posting results
162 #define P_MAIL_WIN 0x01
163 #define P_MAIL_LOSE 0x02
164 #define P_MAIL_BITS 0x03
165 #define P_NEWS_WIN 0x04
166 #define P_NEWS_LOSE 0x08
167 #define P_NEWS_BITS 0x0C
168 #define P_FCC_WIN 0x10
169 #define P_FCC_LOSE 0x20
170 #define P_FCC_BITS 0x30
173 #define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE"
177 * For check_for_subject and check_for_fcc
180 #define CF_MISSING 0x2
183 /*----------------------------------------------------------------------
184 Compose screen (not forward or reply). Set up envelope, call composer
186 Args: pine_state -- The usual pine structure
188 Little front end for the compose screen
191 compose_screen(struct pine
*pine_state
)
193 void (*prev_screen
)(struct pine
*) = pine_state
->prev_screen
,
194 (*redraw
)(void) = pine_state
->redrawer
;
196 pine_state
->redrawer
= NULL
;
197 ps_global
->next_screen
= SCREEN_FUN_NULL
;
198 mailcap_free(); /* free resources we won't be using for a while */
199 compose_mail(NULL
, NULL
, NULL
, NULL
, NULL
);
200 pine_state
->next_screen
= prev_screen
;
201 pine_state
->redrawer
= redraw
;
205 /*----------------------------------------------------------------------
206 Alternate compose screen. Set up role and call regular compose.
208 Args: pine_state -- The usual pine structure
211 alt_compose_screen(struct pine
*pine_state
)
213 ACTION_S
*role
= NULL
;
214 void (*prev_screen
)(struct pine
*) = pine_state
->prev_screen
,
215 (*redraw
)(void) = pine_state
->redrawer
;
217 pine_state
->redrawer
= NULL
;
218 ps_global
->next_screen
= SCREEN_FUN_NULL
;
219 mailcap_free(); /* free resources we won't be using for a while */
222 if(role_select_screen(pine_state
, &role
, MC_COMPOSE
) < 0){
223 cmd_cancelled("Composition");
224 pine_state
->next_screen
= prev_screen
;
225 pine_state
->redrawer
= redraw
;
230 * If default role was selected (NULL) we need to make up a role which
231 * won't do anything, but will cause compose_mail to think there's
232 * already a role so that it won't try to confirm the default.
235 role
= combine_inherited_role(role
);
237 role
= (ACTION_S
*)fs_get(sizeof(*role
));
238 memset((void *)role
, 0, sizeof(*role
));
239 role
->nick
= cpystr("Default Role");
242 pine_state
->redrawer
= NULL
;
243 compose_mail(NULL
, NULL
, role
, NULL
, NULL
);
245 pine_state
->next_screen
= prev_screen
;
246 pine_state
->redrawer
= redraw
;
250 /*----------------------------------------------------------------------
251 Format envelope for outgoing message and call editor
253 Args: given_to -- An address to send mail to (usually from command line
255 fcc_arg -- The fcc that goes with this address.
257 If a "To" line is given format that into the envelope and get ready to call
259 If there's a message postponed, offer to continue it, and set it up,
260 otherwise just fill in the outgoing envelope as blank.
262 NOTE: we ignore postponed and interrupted messages in nr mode
265 compose_mail(char *given_to
, char *fcc_arg
, ACTION_S
*role_arg
,
266 PATMT
*attach
, gf_io_t inc_text_getc
)
269 ENVELOPE
*outgoing
= NULL
;
270 PINEFIELD
*custom
= NULL
;
271 REPLY_S
*reply
= NULL
;
272 REDRAFT_POS_S
*redraft_pos
= NULL
;
273 ACTION_S
*role
= NULL
;
279 int fcc_is_sticky
= 0,
286 "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n"));
288 /*-- Check for INTERRUPTED mail --*/
289 if(!role_arg
&& !(given_to
|| attach
)){
290 char file_path
[MAXPATH
+1];
292 /* build filename and see if it exists. build_path creates
293 * an explicit local path name, so all c-client access is thru
297 build_path(file_path
,
298 ps_global
->VAR_OPER_DIR
? ps_global
->VAR_OPER_DIR
299 : ps_global
->home_dir
,
300 INTERRUPTED_MAIL
, sizeof(file_path
));
302 /* check to see if the folder exists, the user wants to continue
303 * and that we can actually read something in...
305 if(folder_exists(NULL
, file_path
) & FEX_ISFILE
)
309 /*-- Check for postponed mail --*/
311 && !outgoing
/* not replying/forwarding */
312 && !(given_to
|| attach
) /* not command line send */
313 && ps_global
->VAR_POSTPONED_FOLDER
/* folder to look in */
314 && ps_global
->VAR_POSTPONED_FOLDER
[0])
317 /*-- Check for form letter folder --*/
319 && !outgoing
/* not replying/forwarding */
320 && !(given_to
|| attach
) /* not command line send */
321 && ps_global
->VAR_FORM_FOLDER
/* folder to look in */
322 && ps_global
->VAR_FORM_FOLDER
[0])
325 if(!outgoing
&& !(given_to
|| attach
)
326 && !role_arg
&& F_ON(F_ALT_COMPOSE_MENU
, ps_global
)){
331 char *intrpt
= "Interrupted";
332 char *postpnd
= "Postponed";
333 char *formltr
= "FormLetter";
334 char *roles
= "setRole";
335 HelpType help
= h_composer_browse
;
336 ESCKEY_S compose_style
[6];
341 compose_style
[ekey_num
].ch
= 'n';
342 compose_style
[ekey_num
].rval
= 'n';
343 compose_style
[ekey_num
].name
= "N";
344 compose_style
[ekey_num
++].label
= new;
347 compose_style
[ekey_num
].ch
= 'i';
348 compose_style
[ekey_num
].rval
= 'i';
349 compose_style
[ekey_num
].name
= "I";
350 compose_style
[ekey_num
++].label
= intrpt
;
354 compose_style
[ekey_num
].ch
= 'p';
355 compose_style
[ekey_num
].rval
= 'p';
356 compose_style
[ekey_num
].name
= "P";
357 compose_style
[ekey_num
++].label
= postpnd
;
361 compose_style
[ekey_num
].ch
= 'f';
362 compose_style
[ekey_num
].rval
= 'f';
363 compose_style
[ekey_num
].name
= "F";
364 compose_style
[ekey_num
++].label
= formltr
;
367 compose_style
[ekey_num
].ch
= 'r';
368 compose_style
[ekey_num
].rval
= 'r';
369 compose_style
[ekey_num
].name
= "R";
370 compose_style
[ekey_num
++].label
= roles
;
372 compose_style
[ekey_num
].ch
= -1;
374 if(F_ON(F_BLANK_KEYMENU
,ps_global
)){
379 for(ekey_num
= 0; compose_style
[ekey_num
].ch
!= -1; ekey_num
++){
380 if(p
- letters
< sizeof(letters
))
381 *p
++ = (char) compose_style
[ekey_num
].ch
;
383 if(compose_style
[ekey_num
+ 1].ch
!= -1 && p
- letters
< sizeof(letters
))
387 if(p
- letters
< sizeof(letters
))
391 which_help
= intrptd
+ 2 * postponed
+ 4 * form
;
394 help
= h_compose_intrptd
;
397 help
= h_compose_postponed
;
400 help
= h_compose_intrptd_postponed
;
403 help
= h_compose_form
;
406 help
= h_compose_intrptd_form
;
409 help
= h_compose_postponed_form
;
412 help
= h_compose_intrptd_postponed_form
;
415 help
= h_compose_default
;
419 snprintf(prompt
, sizeof(prompt
),
420 "Choose a compose method from %s : ",
421 F_ON(F_BLANK_KEYMENU
,ps_global
) ? letters
: "the menu below");
422 prompt
[sizeof(prompt
)-1] = '\0';
424 chosen_task
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
),
425 compose_style
, 'n', 'x', help
, RB_NORM
);
426 intrptd
= postponed
= form
= 0;
437 void (*prev_screen
)(struct pine
*) = ps_global
->prev_screen
,
438 (*redraw
)(void) = ps_global
->redrawer
;
440 ps_global
->redrawer
= NULL
;
441 ps_global
->next_screen
= SCREEN_FUN_NULL
;
442 if(role_select_screen(ps_global
, &role
, MC_COMPOSE
) < 0){
443 cmd_cancelled("Composition");
444 ps_global
->next_screen
= prev_screen
;
445 ps_global
->redrawer
= redraw
;
449 ps_global
->next_screen
= prev_screen
;
450 ps_global
->redrawer
= redraw
;
452 role
= combine_inherited_role(role
);
461 q_status_message(SM_ORDER
, 0, 3,
462 "Composition cancelled");
471 if(intrptd
&& !outgoing
){
472 char file_path
[MAXPATH
+1];
476 build_path(file_path
,
477 ps_global
->VAR_OPER_DIR
? ps_global
->VAR_OPER_DIR
478 : ps_global
->home_dir
,
479 INTERRUPTED_MAIL
, sizeof(file_path
));
480 if(folder_exists(NULL
, file_path
) & FEX_ISFILE
){
481 if((stream
= pine_mail_open(NULL
, file_path
,
482 SP_USEPOOL
|SP_TEMPUSE
, NULL
))
483 && !stream
->halfopen
){
485 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
) ||
486 (ret
= redraft_prompt("Interrupted",INTRPT_PMT
,'n')) =='y'){
487 if(!redraft(&stream
, &outgoing
, &body
, &fcc
, &lcc
, &reply
,
488 &redraft_pos
, &custom
, &role
, REDRAFT_DEL
)){
490 pine_mail_close(stream
);
497 /* redraft() may or may not have closed stream */
499 pine_mail_close(stream
);
501 postponed
= form
= 0;
504 pine_mail_close(stream
);
506 q_status_message(SM_ORDER
, 0, 3,
507 _("Composition cancelled"));
513 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
514 _("Can't open Interrupted mailbox: %s"),
517 pine_mail_close(stream
);
522 if(postponed
&& !outgoing
){
523 int ret
= 'n', done
= 0;
526 if((exists
=postponed_stream(&stream
,
527 ps_global
->VAR_POSTPONED_FOLDER
,
528 "Postponed", 0)) & FEX_ISFILE
){
529 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
) ||
530 (ret
= redraft_prompt("Postponed",PSTPND_PMT
,'n')) == 'y'){
531 if(!redraft(&stream
, &outgoing
, &body
, &fcc
, &lcc
, &reply
,
532 &redraft_pos
, &custom
, &role
,
533 REDRAFT_DEL
| REDRAFT_PPND
))
536 /* stream may or may not be closed in redraft() */
537 if(stream
&& (stream
!= ps_global
->mail_stream
))
538 pine_mail_close(stream
);
544 if(stream
!= ps_global
->mail_stream
)
545 pine_mail_close(stream
);
548 q_status_message(SM_ORDER
, 0, 3,
549 _("Composition cancelled"));
554 else if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
))
561 if(form
&& !outgoing
){
562 int ret
= 'n', done
= 0;
565 if((exists
=postponed_stream(&stream
,
566 ps_global
->VAR_FORM_FOLDER
,
567 "Form letter", 1)) & FEX_ISFILE
){
568 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
) ||
569 (ret
= want_to(FORM_PMT
,'y','x',NO_HELP
,WT_NORM
))=='y'){
570 if(!redraft(&stream
, &outgoing
, &body
, &fcc
, &lcc
, &reply
,
571 &redraft_pos
, &custom
, NULL
, REDRAFT_NONE
))
574 /* stream may or may not be closed in redraft() */
575 if(stream
&& (stream
!= ps_global
->mail_stream
))
576 pine_mail_close(stream
);
579 intrptd
= postponed
= 0;
582 if(stream
!= ps_global
->mail_stream
)
583 pine_mail_close(stream
);
586 q_status_message(SM_ORDER
, 0, 3,
587 _("Composition cancelled"));
593 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
)){
594 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
595 _("Form letter folder doesn't exist!"));
604 /*-- normal composition --*/
606 int impl
, template_len
= 0;
607 long rflags
= ROLE_COMPOSE
;
610 /*================= Compose new message ===============*/
611 body
= mail_newbody();
612 outgoing
= mail_newenvelope();
615 rfc822_parse_adrlist(&outgoing
->to
, given_to
, ps_global
->maildomain
);
617 outgoing
->message_id
= generate_message_id();
620 * Setup possible role
623 role
= copy_action(role_arg
);
626 /* Setup possible compose role */
627 if(nonempty_patterns(rflags
, &dummy
)){
630 * Msgno = -1 means there is no msg.
631 * This will match roles which have the Compose Use turned
632 * on, and have no patterns set, and match the Current
635 role
= set_role_from_msg(ps_global
, rflags
, -1L, NULL
);
637 if(confirm_role(rflags
, &role
))
638 role
= combine_inherited_role(role
);
639 else{ /* cancel reply */
641 cmd_cancelled("Composition");
648 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
652 * The type of storage object allocated below is vitally
653 * important. See SIMPLIFYING ASSUMPTION #37
655 if((body
->contents
.text
.data
= (void *) so_get(PicoText
,
656 NULL
, EDIT_ACCESS
)) != NULL
){
660 while((*inc_text_getc
)(&ch
))
661 if(!so_writec(ch
, (STORE_S
*)body
->contents
.text
.data
)){
667 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
668 _("Problem creating space for message text."));
672 if(role
&& role
->template){
675 impl
= 1; /* leave cursor in header if not explicit */
676 filtered
= detoken(role
, NULL
, 0, 0, 0, &redraft_pos
, &impl
);
679 so_puts((STORE_S
*)body
->contents
.text
.data
, filtered
);
681 template_len
= strlen(filtered
);
684 fs_give((void **)&filtered
);
690 if((sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
, &impl
)) != NULL
){
692 redraft_pos
->offset
+= template_len
;
695 so_puts((STORE_S
*)body
->contents
.text
.data
, sig
);
697 fs_give((void **)&sig
);
700 body
->type
= TYPETEXT
;
703 create_message_body(&body
, attach
, 0);
706 ps_global
->prev_screen
= compose_screen
;
707 if(!(fcc_to_free
= fcc
) && !(role
&& role
->fcc
))
708 fcc
= fcc_arg
; /* Didn't pick up fcc, use given */
711 * check whether a build_address-produced fcc is different from
712 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
715 char *tmp_fcc
= NULL
;
718 tmp_fcc
= get_fcc_based_on_to(outgoing
->to
);
719 if(strcmp(fcc
, tmp_fcc
? tmp_fcc
: ""))
720 fcc_is_sticky
++; /* cause sticky bit to get set */
723 else if((tmp_fcc
= get_fcc(NULL
)) != NULL
&&
724 !strcmp(fcc
, tmp_fcc
)){
731 fs_give((void **)&tmp_fcc
);
734 pine_send(outgoing
, &body
, COMPOSE_MAIL_TITLE
, role
, fcc
,
735 reply
, redraft_pos
, lcc
, custom
,
736 (fcc_is_sticky
? PS_STICKY_FCC
: 0) | (to_is_sticky
? PS_STICKY_TO
: 0));
740 fs_give((void **) &reply
->mailbox
);
742 fs_give((void **) &reply
->origmbox
);
744 fs_give((void **) &reply
->prefix
);
745 if(reply
->data
.uid
.msgs
)
746 fs_give((void **) &reply
->data
.uid
.msgs
);
747 fs_give((void **) &reply
);
751 fs_give((void **)&fcc_to_free
);
754 fs_give((void **)&lcc
);
756 mail_free_envelope(&outgoing
);
757 pine_free_body(&body
);
758 free_redraft_pos(&redraft_pos
);
763 /*----------------------------------------------------------------------
764 Args: stream -- This is where we get the postponed messages from
765 We'll expunge and close it here unless it is mail_stream.
767 These are all return values:
783 redraft(MAILSTREAM
**streamp
, ENVELOPE
**outgoing
, struct mail_bodystruct
**body
,
784 char **fcc
, char **lcc
, REPLY_S
**reply
, REDRAFT_POS_S
**redraft_pos
,
785 PINEFIELD
**custom
, ACTION_S
**role
, int flags
)
791 if(!(streamp
&& *streamp
))
797 * If we're manipulating the current folder, don't bother
801 if(REDRAFT_PPND
&flags
)
802 q_status_message(SM_ORDER
| SM_DING
, 3, 5, _("Empty folder! No messages really postponed!"));
804 q_status_message(SM_ORDER
| SM_DING
, 3, 5, _("Empty folder! No messages really interrupted!"));
806 return(redraft_cleanup(streamp
, FALSE
, flags
));
808 else if(stream
== ps_global
->mail_stream
809 && ps_global
->prev_screen
== mail_index_screen
){
811 * Since the user's got this folder already opened and they're
812 * on a selected message, pick that one rather than rebuild
813 * another index screen...
815 cont_msg
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
817 else if(stream
->nmsgs
> 1L){ /* offer browser ? */
820 if(REDRAFT_PPND
&flags
){ /* set to last message postponed */
821 mn_set_cur(sp_msgmap(stream
),
822 mn_get_revsort(sp_msgmap(stream
))
823 ? 1L : mn_get_total(sp_msgmap(stream
)));
825 else{ /* set to top form letter */
826 mn_set_cur(sp_msgmap(stream
), 1L);
829 clear_index_cache(stream
, 0);
833 ti
= stop_threading_temporarily();
834 rv
= index_lister(ps_global
, NULL
, stream
->mailbox
,
835 stream
, sp_msgmap(stream
));
836 restore_threading(&ti
);
838 cont_msg
= mn_m2raw(sp_msgmap(stream
), mn_get_cur(sp_msgmap(stream
)));
839 if(count_flagged(stream
, F_DEL
)
840 && want_to(INTR_DEL_PMT
, 'n', 0, NO_HELP
, WT_NORM
) == 'n'){
841 if(REDRAFT_PPND
&flags
)
842 q_status_message(SM_ORDER
, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
844 q_status_message(SM_ORDER
, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
852 clear_index_cache(stream
, 0);
855 q_status_message(SM_ORDER
, 0, 3, _("Composition cancelled"));
856 (void) redraft_cleanup(streamp
, FALSE
, flags
);
858 if(!*streamp
&& !ps_global
->mail_stream
){
859 q_status_message2(SM_ORDER
, 3, 7,
860 "No more %.200s, returning to \"%.200s\"",
861 (REDRAFT_PPND
&flags
) ? "postponed messages"
863 ps_global
->inbox_name
);
864 if(ps_global
&& ps_global
->ttyo
){
865 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
866 ps_global
->mangled_footer
= 1;
869 do_broach_folder(ps_global
->inbox_name
,
870 ps_global
->context_list
, NULL
, DB_INBOXWOCNTXT
);
872 ps_global
->next_screen
= mail_index_screen
;
875 return(0); /* special case */
879 if((so
= (void *) so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
)
880 return(redraft_work(streamp
, cont_msg
, outgoing
, body
,
881 fcc
, lcc
, reply
, redraft_pos
, custom
,
889 redraft_prompt(char *type
, char *prompt
, int failure
)
891 if(background_posting(FALSE
)){
892 q_status_message1(SM_ORDER
, 0, 3,
893 _("%s folder unavailable while background posting"),
898 return(want_to(prompt
, 'y', 'x', NO_HELP
, WT_NORM
));
902 /* this is for initializing the fixed header elements in pine_send() */
904 prompt::name::help::prwid::maxlen::realaddr::
905 builder::affected_entry::next_affected::selector::key_label::fileedit::
906 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
907 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
909 static struct headerentry he_template
[]={
910 {"", "X-Auth-Received", NO_HELP
, 10, 0, NULL
,
911 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
912 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
913 {"From : ", "From", h_composer_from
, 10, 0, NULL
,
914 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
915 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
},
916 {"Reply-To: ", "Reply To", h_composer_reply_to
, 10, 0, NULL
,
917 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
918 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
},
919 {"To : ", "To", h_composer_to
, 10, 0, NULL
,
920 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
921 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK
},
922 {"Cc : ", "Cc", h_composer_cc
, 10, 0, NULL
,
923 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
924 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
},
925 {"Bcc : ", "Bcc", h_composer_bcc
, 10, 0, NULL
,
926 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
927 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
},
928 {"Newsgrps: ", "Newsgroups", h_composer_news
, 10, 0, NULL
,
929 news_build
, NULL
, NULL
, news_group_selector
, "To NwsGrps", NULL
, NULL
,
930 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
931 {"Fcc : ", "Fcc", h_composer_fcc
, 10, 0, NULL
,
932 NULL
, NULL
, NULL
, folders_for_fcc
, "To Fldrs", NULL
, NULL
,
933 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE
},
934 {"Lcc : ", "Lcc", h_composer_lcc
, 10, 0, NULL
,
935 build_addr_lcc
, NULL
, NULL
, addr_book_compose_lcc
,"To AddrBk", NULL
, abook_nickname_complete
,
936 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
937 {"Attchmnt: ", "Attchmnt", h_composer_attachment
, 10, 0, NULL
,
938 NULL
, NULL
, NULL
, NULL
, "To Files", NULL
, NULL
,
939 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE
},
940 {"Subject : ", "Subject", h_composer_subject
, 10, 0, NULL
,
941 valid_subject
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
942 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
943 {"", "References", NO_HELP
, 10, 0, NULL
,
944 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
945 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
946 {"", "Date", NO_HELP
, 10, 0, NULL
,
947 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
948 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
949 {"", "In-Reply-To", NO_HELP
, 10, 0, NULL
,
950 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
951 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
952 {"", "Message-ID", NO_HELP
, 10, 0, NULL
,
953 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
954 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
955 {"", "X-Priority", NO_HELP
, 10, 0, NULL
,
956 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
957 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
958 {"", "User-Agent", NO_HELP
, 10, 0, NULL
,
959 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
960 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
961 {"", "To", NO_HELP
, 10, 0, NULL
,
962 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
963 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
964 {"", "X-Post-Error",NO_HELP
, 10, 0, NULL
,
965 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
966 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
967 {"", "X-Reply-UID", NO_HELP
, 10, 0, NULL
,
968 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
969 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
970 {"", "X-Reply-Mbox", NO_HELP
, 10, 0, NULL
,
971 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
972 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
973 {"", "X-SMTP-Server", NO_HELP
, 10, 0, NULL
,
974 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
975 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
976 {"", "X-Cursor-Pos", NO_HELP
, 10, 0, NULL
,
977 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
978 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
979 {"", "X-Our-ReplyTo", NO_HELP
, 10, 0, NULL
,
980 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
981 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
982 {"", OUR_HDRS_LIST
, NO_HELP
, 10, 0, NULL
,
983 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
984 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
985 {"", "X-Auth-Received", NO_HELP
, 10, 0, NULL
,
986 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
987 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
},
988 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
989 {"", "Sender", NO_HELP
, 10, 0, NULL
,
990 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
991 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE
}
996 static struct headerentry he_custom_addr_templ
={
997 NULL
, NULL
, h_composer_custom_addr
,10, 0, NULL
,
998 build_address
, NULL
, NULL
, addr_book_compose
, "To AddrBk", NULL
, abook_nickname_complete
,
999 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK
};
1001 static struct headerentry he_custom_free_templ
={
1002 NULL
, NULL
, h_composer_custom_free
,10, 0, NULL
,
1003 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
1004 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE
};
1007 /*----------------------------------------------------------------------
1008 Get addressee for message, then post message
1010 Args: outgoing -- Partially formatted outgoing ENVELOPE
1011 body -- Body of outgoing message
1012 prmpt_who -- Optional prompt for optionally_enter call
1013 prmpt_cnf -- Optional prompt for confirmation call
1014 used_tobufval -- The string that the to was eventually set equal to.
1015 This gets passed back if non-NULL on entry.
1016 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1017 SS_NULLRP - Use null return-path so we'll send an
1020 Result: message "To: " field is provided and message is sent or cancelled.
1029 subject passed in, NOT edited but maybe canonized here
1030 to possibly passed in, edited and canonized here
1036 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1037 with the first part TYPETEXT! All newlines in the text here also end with
1040 Returns 0 on success, -1 on failure.
1043 pine_simple_send(ENVELOPE
*outgoing
, /* envelope for outgoing message */
1044 struct mail_bodystruct
**body
,
1048 char **used_tobufval
,
1053 int done
= 0, retval
= 0, x
;
1054 int lastrc
, rc
= 0, ku
, i
, resize_len
, result
, fcc_result
;
1057 static HISTORY_S
*history
= NULL
;
1062 dprint((1,"\n === simple send called === \n"));
1064 memset(&ba_fcc
, 0, sizeof(BUILDER_ARG
));
1066 init_hist(&history
, HISTSIZE
);
1068 header
= pine_simple_send_header(outgoing
, &ba_fcc
.tptr
, &tobufp
);
1070 /*----- Fill in a few general parts of the envelope ----*/
1071 if(!outgoing
->date
){
1072 if(F_ON(F_QUELL_TIMEZONE
, ps_global
))
1073 mail_parameters(NULL
, SET_DISABLE822TZTEXT
, (void *) TRUE
);
1075 rfc822_date(tmp_20k_buf
); /* format and copy new date */
1076 if(F_ON(F_QUELL_TIMEZONE
, ps_global
))
1077 mail_parameters(NULL
, SET_DISABLE822TZTEXT
, (void *) FALSE
);
1079 outgoing
->date
= (unsigned char *) cpystr(tmp_20k_buf
);
1082 if(!outgoing
->from
){
1083 if(role
&& role
->from
){
1084 if(ps_global
->never_allow_changing_from
)
1085 q_status_message(SM_ORDER
, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1087 outgoing
->from
= copyaddrlist(role
->from
);
1090 outgoing
->from
= generate_from();
1093 if(!(flagsarg
& SS_NULLRP
))
1094 outgoing
->return_path
= rfc822_cpy_adr(outgoing
->from
);
1096 ekey
[i
= 0].ch
= ctrl('T');
1098 ekey
[i
].name
= "^T";
1099 ekey
[i
++].label
= N_("To AddrBk");
1101 if(F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
)){
1102 ekey
[i
].ch
= ctrl('I');
1104 ekey
[i
].name
= "TAB";
1105 ekey
[i
++].label
= N_("Complete");
1108 ekey
[i
].ch
= KEY_UP
;
1112 ekey
[i
++].label
= "";
1114 ekey
[i
].ch
= KEY_DOWN
;
1117 ekey
[i
++].label
= "";
1121 /*----------------------------------------------------------------------
1122 Loop editing the "To: " field until everything goes well
1131 outgoing2strings(header
, *body
, &messagebuf
, NULL
, 1);
1135 if(flagsarg
& SS_PROMPTFORTO
){
1137 *tobufp
= cpystr("");
1139 resize_len
= MAX(MAXPATH
, strlen(*tobufp
));
1140 fs_resize((void **) tobufp
, resize_len
+1);
1142 if(items_in_hist(history
) > 0){
1143 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
1144 ekey
[ku
].label
= HISTORY_KEYLABEL
;
1145 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
1146 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
1150 ekey
[ku
].label
= "";
1151 ekey
[ku
+1].name
= "";
1152 ekey
[ku
+1].label
= "";
1155 flags
= OE_APPEND_CURRENT
;
1157 rc
= optionally_enter(*tobufp
, -FOOTER_ROWS(ps_global
),
1161 : outgoing
->remail
== NULL
1162 ? _("FORWARD (as e-mail) to : ")
1163 : _("BOUNCE (redirect) message to : "),
1164 ekey
, help
, &flags
);
1171 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1172 "Internal problem encountered");
1178 if((p
= get_prev_hist(history
, *tobufp
, 0, NULL
)) != NULL
){
1179 strncpy(*tobufp
, p
, resize_len
);
1180 (*tobufp
)[resize_len
-1] = '\0';
1188 if((p
= get_next_hist(history
, *tobufp
, 0, NULL
)) != NULL
){
1189 strncpy(*tobufp
, p
, resize_len
);
1190 (*tobufp
)[resize_len
-1] = '\0';
1199 {void (*redraw
) (void) = ps_global
->redrawer
;
1200 char *returned_addr
= NULL
;
1204 int got_something
= 0;
1206 push_titlebar_state();
1207 returned_addr
= addr_book_bounce();
1210 * Just make it look like user typed this list in.
1214 if((l
=resize_len
) < (len
= strlen(returned_addr
)) + 1){
1216 fs_resize((void **) tobufp
, (size_t) (l
+1));
1219 strncpy(*tobufp
, returned_addr
, l
);
1220 (*tobufp
)[l
] = '\0';
1221 fs_give((void **)&returned_addr
);
1225 pop_titlebar_state();
1227 if((ps_global
->redrawer
= redraw
) != NULL
) /* reset old value, and test */
1228 (*ps_global
->redrawer
)();
1234 if(*tobufp
&& **tobufp
!= '\0'){
1235 char *errbuf
, *addr
;
1238 save_hist(history
, *tobufp
, 0, NULL
);
1243 * If role has an fcc, use it instead of what build_address
1246 if(role
&& role
->fcc
){
1248 fs_give((void **) &ba_fcc
.tptr
);
1250 ba_fcc
.tptr
= cpystr(role
->fcc
);
1253 if(build_address(*tobufp
, &addr
, &errbuf
,
1254 (role
&& role
->fcc
) ? NULL
: &ba_fcc
, NULL
) >= 0){
1258 fs_give((void **)&errbuf
);
1260 if((l
=strlen(*tobufp
)) < (tolen
= strlen(addr
)) + 1){
1262 fs_resize((void **) tobufp
, (size_t) (l
+1));
1265 strncpy(*tobufp
, addr
, l
);
1266 (*tobufp
)[l
] = '\0';
1268 *used_tobufval
= cpystr(addr
);
1270 /* confirm address */
1271 if(flagsarg
& SS_PROMPTFORTO
){
1272 char dsn_string
[30];
1273 int dsn_label
= 0, dsn_show
, i
;
1274 int verbose_label
= 0;
1277 strings2outgoing(header
, body
, NULL
, 0);
1279 if((flagsarg
& SS_PROMPTFORTO
)
1280 && ((x
= check_addresses(header
)) == CA_BAD
1281 || (x
== CA_EMPTY
&& F_OFF(F_FCC_ON_BOUNCE
,
1283 /*--- Addresses didn't check out---*/
1290 opts
[i
++].label
= N_("Yes");
1295 opts
[i
++].label
= N_("No");
1297 call_mailer_flags
&= ~CM_VERBOSE
; /* clear verbose */
1298 if(F_ON(F_VERBOSE_POST
, ps_global
)){
1299 /* setup keymenu slot to toggle verbose mode */
1300 opts
[i
].ch
= ctrl('W');
1302 opts
[i
].name
= "^W";
1303 verbose_label
= i
++;
1304 if(F_ON(F_DSN
, ps_global
)){
1308 opts
[i
++].label
= "";
1312 /* clear DSN flags */
1313 call_mailer_flags
&= ~(CM_DSN_NEVER
| CM_DSN_DELAY
| CM_DSN_SUCCESS
| CM_DSN_FULL
);
1314 if(F_ON(F_DSN
, ps_global
)){
1315 /* setup keymenu slots to toggle dsn bits */
1319 opts
[i
].label
= "DSNOpts";
1324 opts
[i
++].label
= "";
1328 opts
[i
++].label
= "";
1332 opts
[i
++].label
= "";
1340 dsn_show
= (call_mailer_flags
& CM_DSN_SHOW
);
1341 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
1342 "%s%s%s%s%s%sto \"%s\" ? ",
1343 prmpt_cnf
? prmpt_cnf
: "Send message ",
1344 ((call_mailer_flags
& CM_VERBOSE
)
1347 (call_mailer_flags
& CM_VERBOSE
)
1348 ? "in verbose mode" : "",
1349 (dsn_show
&& (call_mailer_flags
& CM_VERBOSE
))
1351 (dsn_show
) ? dsn_string
: "",
1352 ((call_mailer_flags
& CM_VERBOSE
) || dsn_show
)
1356 : (F_ON(F_FCC_ON_BOUNCE
, ps_global
)
1357 && ba_fcc
.tptr
&& ba_fcc
.tptr
[0])
1360 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1362 if((strlen(tmp_20k_buf
) >
1363 ps_global
->ttyo
->screen_cols
- 2) &&
1364 ps_global
->ttyo
->screen_cols
>= 7)
1365 strncpy(tmp_20k_buf
+ps_global
->ttyo
->screen_cols
-7,
1366 "...? ", SIZEOF_20KBUF
-ps_global
->ttyo
->screen_cols
-7);
1368 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1371 opts
[verbose_label
].label
=
1372 /* TRANSLATORS: several possible key labels follow */
1373 (call_mailer_flags
& CM_VERBOSE
) ? N_("Normal") : N_("Verbose");
1375 if(F_ON(F_DSN
, ps_global
)){
1376 if(call_mailer_flags
& CM_DSN_SHOW
){
1377 opts
[dsn_label
].label
=
1378 (call_mailer_flags
& CM_DSN_DELAY
)
1379 ? N_("NoDelay") : N_("Delay");
1380 opts
[dsn_label
+1].ch
= 's';
1381 opts
[dsn_label
+1].label
=
1382 (call_mailer_flags
& CM_DSN_SUCCESS
)
1383 ? N_("NoSuccess") : N_("Success");
1384 opts
[dsn_label
+2].ch
= 'x';
1385 opts
[dsn_label
+2].label
=
1386 (call_mailer_flags
& CM_DSN_NEVER
)
1387 ? N_("ErrRets") : N_("NoErrRets");
1388 opts
[dsn_label
+3].ch
= 'h';
1389 opts
[dsn_label
+3].label
=
1390 (call_mailer_flags
& CM_DSN_FULL
)
1391 ? N_("RetHdrs") : N_("RetFull");
1395 rv
= radio_buttons(tmp_20k_buf
,
1396 -FOOTER_ROWS(ps_global
), opts
,
1397 'y', 'z', NO_HELP
, RB_NORM
);
1398 if(rv
== 'y'){ /* user ACCEPTS! */
1402 else if(rv
== 'n'){ /* Declined! */
1405 else if(rv
== 'z'){ /* Cancelled! */
1408 else if(rv
== 12){ /* flip verbose bit */
1409 if(call_mailer_flags
& CM_VERBOSE
)
1410 call_mailer_flags
&= ~CM_VERBOSE
;
1412 call_mailer_flags
|= CM_VERBOSE
;
1414 else if(call_mailer_flags
& CM_DSN_SHOW
){
1415 if(rv
== 's'){ /* flip success bit */
1416 call_mailer_flags
^= CM_DSN_SUCCESS
;
1417 /* turn off related bits */
1418 if(call_mailer_flags
& CM_DSN_SUCCESS
)
1419 call_mailer_flags
&= ~(CM_DSN_NEVER
);
1421 else if(rv
== 'd'){ /* flip delay bit */
1422 call_mailer_flags
^= CM_DSN_DELAY
;
1423 /* turn off related bits */
1424 if(call_mailer_flags
& CM_DSN_DELAY
)
1425 call_mailer_flags
&= ~(CM_DSN_NEVER
);
1427 else if(rv
== 'x'){ /* flip never bit */
1428 call_mailer_flags
^= CM_DSN_NEVER
;
1429 /* turn off related bits */
1430 if(call_mailer_flags
& CM_DSN_NEVER
)
1431 call_mailer_flags
&= ~(CM_DSN_SUCCESS
| CM_DSN_DELAY
);
1433 else if(rv
== 'h'){ /* flip full bit */
1434 call_mailer_flags
^= CM_DSN_FULL
;
1437 else if(rv
== 'd'){ /* show dsn options */
1438 call_mailer_flags
|= (CM_DSN_SHOW
| CM_DSN_SUCCESS
| CM_DSN_DELAY
| CM_DSN_FULL
);
1441 snprintf(dsn_string
, sizeof(dsn_string
), _("DSN requested[%s%s%s%s]"),
1442 (call_mailer_flags
& CM_DSN_NEVER
)
1444 (call_mailer_flags
& CM_DSN_DELAY
)
1446 (call_mailer_flags
& CM_DSN_SUCCESS
)
1448 (call_mailer_flags
& CM_DSN_NEVER
)
1450 : (call_mailer_flags
& CM_DSN_FULL
) ? "-Full"
1452 dsn_string
[sizeof(dsn_string
)-1] = '\0';
1457 fs_give((void **)&addr
);
1459 if(!(flagsarg
& SS_PROMPTFORTO
) || sendit
){
1461 CONTEXT_S
*fcc_cntxt
= NULL
;
1463 if(F_ON(F_FCC_ON_BOUNCE
, ps_global
)){
1465 fcc
= cpystr(ba_fcc
.tptr
);
1470 * If special name "inbox" then replace it with the
1473 if(ps_global
->VAR_INBOX_PATH
1474 && strucmp(fcc
, ps_global
->inbox_name
) == 0){
1477 replace_fcc
= cpystr(ps_global
->VAR_INBOX_PATH
);
1478 fs_give((void **) &fcc
);
1483 /*---- Check out fcc -----*/
1485 (void) commence_fcc(fcc
, &fcc_cntxt
, FALSE
);
1487 dprint((4,"can't open fcc, cont\n"));
1488 if(!(flagsarg
& SS_PROMPTFORTO
)){
1490 fs_give((void **)&fcc
);
1498 so_truncate(lmc
.so
, fcc_size_guess(*body
) + 2048);
1503 if(!(outgoing
->to
|| outgoing
->cc
|| outgoing
->bcc
1505 q_status_message(SM_ORDER
, 3, 5, _("No recipients specified!"));
1509 if(outgoing
->to
|| outgoing
->cc
|| outgoing
->bcc
){
1510 char **alt_smtp
= NULL
;
1512 if(role
&& role
->smtp
){
1513 if(ps_global
->FIX_SMTP_SERVER
1514 && ps_global
->FIX_SMTP_SERVER
[0])
1515 q_status_message(SM_ORDER
| SM_DING
, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1517 alt_smtp
= role
->smtp
;
1520 result
= call_mailer(header
, *body
, alt_smtp
,
1522 call_mailer_file_result
,
1524 mark_address_failure_for_pico(header
);
1529 if(result
== 1 && !lmc
.so
)
1530 q_status_message(SM_ORDER
, 0, 3, _("Message sent"));
1532 /*----- Was there an fcc involved? -----*/
1536 && pine_rfc822_output(header
, *body
, NULL
, NULL
))){
1539 strncpy(label
, "Fcc", sizeof(label
));
1540 label
[sizeof(label
)-1] = '\0';
1541 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
)){
1542 snprintf(label
+ 3, sizeof(label
)-3, " to %s", fcc
);
1543 label
[sizeof(label
)-1] = '\0';
1546 /* Now actually copy to fcc folder and close */
1548 write_fcc(fcc
, fcc_cntxt
, lmc
.so
, NULL
,
1550 F_ON(F_MARK_FCC_SEEN
, ps_global
)
1553 else if(result
== 0){
1554 q_status_message(SM_ORDER
,3,5,
1555 _("Fcc Failed!. No message saved."));
1557 dprint((1, "explicit fcc write failed!\n"));
1564 dprint((1, "Bounce failed\n"));
1565 if(!(flagsarg
& SS_PROMPTFORTO
))
1570 else if(result
== 1){
1572 q_status_message(SM_ORDER
, 0, 3,
1575 int avail
= ps_global
->ttyo
->screen_cols
-2;
1577 char *part1
= "Message sent and ";
1578 char *part2
= fcc_result
? "" : "NOT ";
1579 char *part3
= "copied to ";
1580 fcclen
= strlen(fcc
);
1582 need
= 2 + strlen(part1
) + strlen(part2
) +
1583 strlen(part3
) + fcclen
;
1585 if(need
> avail
&& fcclen
> 6)
1586 fcclen
-= MIN(fcclen
-6, need
-avail
);
1588 q_status_message4(SM_ORDER
, 0, 3,
1590 part1
, part2
, part3
,
1592 (char *)tmp_20k_buf
,
1594 fcclen
, FrontDots
));
1599 fs_give((void **)&fcc
);
1602 q_status_message(SM_ORDER
, 0, 3, _("Send cancelled"));
1607 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1608 _("Error in address: %s"), errbuf
);
1610 fs_give((void **)&errbuf
);
1612 if(!(flagsarg
& SS_PROMPTFORTO
))
1620 q_status_message(SM_ORDER
| SM_DING
, 3, 5,
1621 _("No addressee! No e-mail sent."));
1630 q_status_message(SM_ORDER
, 0, 3, _("Send cancelled"));
1636 help
= (help
== NO_HELP
)
1637 ? (outgoing
->remail
== NULL
1645 char *new_nickname
= NULL
;
1649 ambiguity
= abook_nickname_complete(*tobufp
, &new_nickname
,
1650 (lastrc
==rc
&& !(flags
& OE_USER_MODIFIED
)), ANC_AFTERCOMMA
);
1653 if((l
=strlen(new_nickname
)) > resize_len
){
1655 fs_resize((void **) tobufp
, resize_len
+1);
1658 strncpy(*tobufp
, new_nickname
, l
);
1659 (*tobufp
)[l
] = '\0';
1662 fs_give((void **) &new_nickname
);
1671 case 4: /* can't suspend */
1679 fs_give((void **)&ba_fcc
.tptr
);
1681 pine_free_env(&header
);
1688 * pine_simple_send_header - generate header suitable for simple_sending
1691 pine_simple_send_header(ENVELOPE
*outgoing
, char **fccp
, char ***tobufpp
)
1695 static struct headerentry he_dummy
;
1697 header
= pine_new_env(outgoing
, fccp
, tobufpp
, NULL
);
1699 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1700 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
1701 if(pf
->type
== Address
&& !strucmp(pf
->name
, "to")){
1702 memset((void *) &he_dummy
, 0, sizeof(he_dummy
));
1703 pf
->extdata
= (void *) &he_dummy
;
1713 /*----------------------------------------------------------------------
1714 Prepare data structures for pico, call pico, then post message
1716 Args: outgoing -- Partially formatted outgoing ENVELOPE
1717 body -- Body of outgoing message
1718 editor_title -- Title for anchor line in composer
1719 fcc_arg -- The file carbon copy field
1720 reply -- Struct describing set of msgs being replied-to
1722 custom -- custom header list.
1725 Result: message is edited, then postponed, cancelled or sent.
1734 subject passed in, edited and cannonized here
1735 to possibly passed in, edited and cannonized here
1736 cc possibly passed in, edited and cannonized here
1737 bcc edited and cannonized here
1738 in_reply_to generated in reply() and passed in
1741 Storage for these fields comes from anywhere outside. It is remalloced
1742 here so the composer can realloc them if needed. The copies here are also
1745 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1746 with the first part TYPETEXT! All newlines in the text here also end with
1749 There's a further assumption that the text in the TYPETEXT part is
1750 stored in a storage object (see filter.c).
1753 pine_send(ENVELOPE
*outgoing
, struct mail_bodystruct
**body
,
1754 char *editor_title
, ACTION_S
*role
, char *fcc_arg
,
1755 REPLY_S
*reply
, REDRAFT_POS_S
*redraft_pos
, char *lcc_arg
,
1756 PINEFIELD
*custom
, int flags
)
1758 int i
, fixed_cnt
, total_cnt
, index
,
1759 editor_result
= 0, body_start
= 0, use_news_order
= 0;
1760 char *p
, *addr
, *fcc
, *fcc_to_free
= NULL
;
1761 char *start_here_name
= NULL
;
1762 char *suggested_nntp_server
= NULL
;
1764 struct headerentry
*he
, *headents
, *he_to
, *he_fcc
, *he_news
= NULL
, *he_lcc
= NULL
,
1766 PINEFIELD
*pfields
, *pf
, *pf_nobody
= NULL
, *pf_to
= NULL
,
1767 *pf_smtp_server
, *pf_nntp_server
,
1768 *pf_fcc
= NULL
, *pf_err
, *pf_uid
, *pf_mbox
, *pf_curpos
,
1769 *pf_ourrep
, *pf_ourhdrs
, **sending_order
;
1771 ADDRESS
*lcc_addr
= NULL
;
1772 ADDRESS
*nobody_addr
= NULL
;
1773 BODY_PARTICULARS_S
*bp
;
1774 STORE_S
*orig_so
= NULL
;
1775 PICO pbuf1
, *save_previous_pbuf
;
1777 REDRAFT_POS_S
*local_redraft_pos
= NULL
;
1779 dprint((1,"\n=== send called ===\n"));
1781 save_previous_pbuf
= pbf
;
1783 standard_picobuf_setup(pbf
);
1786 * Cancel any pending initial commands since pico uses a different
1787 * input routine. If we didn't cancel them, they would happen after
1788 * we returned from the editor, which would be confusing.
1790 if(ps_global
->in_init_seq
){
1791 ps_global
->in_init_seq
= 0;
1792 ps_global
->save_in_init_seq
= 0;
1794 if(ps_global
->initial_cmds
){
1795 if(ps_global
->free_initial_cmds
)
1796 fs_give((void **)&(ps_global
->free_initial_cmds
));
1798 ps_global
->initial_cmds
= 0;
1801 F_SET(F_USE_FK
,ps_global
,ps_global
->orig_use_fkeys
);
1804 #if defined(DOS) || defined(OS2)
1805 if(!dos_valid_from()){
1806 pbf
= save_previous_pbuf
;
1812 pbf
->upload
= (ps_global
->VAR_UPLOAD_CMD
1813 && ps_global
->VAR_UPLOAD_CMD
[0])
1814 ? upload_msg_to_pico
: NULL
;
1817 pbf
->msgntext
= message_format_for_pico
;
1818 pbf
->mimetype
= mime_type_for_pico
;
1819 pbf
->exittest
= send_exit_for_pico
;
1820 pbf
->user_says_noflow
= dont_flow_this_time
;
1821 pbf
->newthread
= new_thread_on_blank_subject
;
1822 ps_global
->newthread
= 0; /* reset this value */
1823 if(F_OFF(F_CANCEL_CONFIRM
, ps_global
))
1824 pbf
->canceltest
= cancel_for_pico
;
1826 pbf
->alt_ed
= (ps_global
->VAR_EDITOR
&& ps_global
->VAR_EDITOR
[0] &&
1827 ps_global
->VAR_EDITOR
[0][0])
1828 ? ps_global
->VAR_EDITOR
: NULL
;
1829 pbf
->alt_spell
= (ps_global
->VAR_SPELLER
&& ps_global
->VAR_SPELLER
[0])
1830 ? ps_global
->VAR_SPELLER
: NULL
;
1831 pbf
->always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
1832 pbf
->quote_str
= reply
&& reply
->prefix
? reply
->prefix
: "> ";
1833 /* We actually want to set this only if message we're sending is flowed */
1834 pbf
->strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
1835 pbf
->allow_flowed_text
= (F_OFF(F_QUELL_FLOWED_TEXT
, ps_global
)
1836 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps_global
)
1837 && (strcmp(pbf
->quote_str
, "> ") == 0
1838 || strcmp(pbf
->quote_str
, ">") == 0));
1839 pbf
->edit_offset
= 0;
1840 title
= cpystr(set_titlebar(editor_title
,
1841 ps_global
->mail_stream
,
1842 ps_global
->context_current
,
1843 ps_global
->cur_folder
,ps_global
->msgmap
,
1844 0, FolderName
, 0, 0, NULL
));
1845 pbf
->pine_anchor
= title
;
1847 #if defined(DOS) || defined(OS2)
1848 if(!pbf
->oper_dir
&& ps_global
->VAR_FILE_DIR
){
1849 pbf
->oper_dir
= ps_global
->VAR_FILE_DIR
;
1853 if(redraft_pos
&& editor_title
&& !strcmp(editor_title
, COMPOSE_MAIL_TITLE
))
1854 pbf
->pine_flags
|= P_CHKPTNOW
;
1856 /* NOTE: initial cursor position set below */
1858 dprint((9, "flags: %x\n", pbf
->pine_flags
));
1861 * When user runs compose and the current folder is a newsgroup,
1862 * offer to post to the current newsgroup.
1864 if(!(outgoing
->to
|| (outgoing
->newsgroups
&& *outgoing
->newsgroups
))
1865 && IS_NEWS(ps_global
->mail_stream
)){
1866 char prompt
[200], news_group
[MAILTMPLEN
];
1868 pine_send_newsgroup_name(ps_global
->mail_stream
->mailbox
, news_group
,
1869 sizeof(news_group
));
1872 * Replies don't get this far because To or Newsgroups will already
1873 * be filled in. So must be either ordinary compose or forward.
1874 * Forward sets subject, so use that to tell the difference.
1876 if(news_group
[0] && !outgoing
->subject
){
1879 char *errmsg
= NULL
;
1880 BUILDER_ARG
*fcc_build
= NULL
;
1882 if(F_OFF(F_COMPOSE_TO_NEWSGRP
,ps_global
)){
1883 snprintf(prompt
, sizeof(prompt
),
1884 _("Post to current newsgroup (%s)"), news_group
);
1885 prompt
[sizeof(prompt
)-1] = '\0';
1886 ch
= want_to(prompt
, 'y', 'x', NO_HELP
, WT_NORM
);
1891 if(outgoing
->newsgroups
)
1892 fs_give((void **)&outgoing
->newsgroups
);
1894 if(!fcc_arg
&& !(role
&& role
->fcc
)){
1895 fcc_build
= (BUILDER_ARG
*)fs_get(sizeof(BUILDER_ARG
));
1896 memset((void *)fcc_build
, 0, sizeof(BUILDER_ARG
));
1897 fcc_build
->tptr
= fcc_to_free
;
1900 ret_val
= news_build(news_group
, &outgoing
->newsgroups
,
1901 &errmsg
, fcc_build
, NULL
);
1904 if(outgoing
->newsgroups
)
1905 fs_give((void **)&outgoing
->newsgroups
);
1907 outgoing
->newsgroups
= cpystr(news_group
);
1910 if(!fcc_arg
&& !(role
&& role
->fcc
)){
1911 fcc_arg
= fcc_to_free
= fcc_build
->tptr
;
1912 fs_give((void **)&fcc_build
);
1917 q_status_message(SM_ORDER
, 3, 3, errmsg
);
1918 display_message(NO_OP_COMMAND
);
1921 fs_give((void **)&errmsg
);
1927 q_status_message(SM_ORDER
, 0, 3, _("Message cancelled"));
1928 dprint((4, "=== send: cancelled\n"));
1929 pbf
= save_previous_pbuf
;
1940 if(F_ON(F_PREDICT_NNTP_SERVER
, ps_global
)
1941 && outgoing
->newsgroups
&& *outgoing
->newsgroups
1942 && IS_NEWS(ps_global
->mail_stream
)){
1945 if(mail_valid_net_parse(ps_global
->mail_stream
->original_mailbox
,
1947 if(!strucmp(news_mb
.service
, "nntp")){
1948 if(*ps_global
->mail_stream
->original_mailbox
== '{'){
1949 char *svcp
= NULL
, *psvcp
;
1951 suggested_nntp_server
=
1952 cpystr(ps_global
->mail_stream
->original_mailbox
+ 1);
1953 if((p
= strindex(suggested_nntp_server
, '}')) != NULL
)
1955 for(p
= strindex(suggested_nntp_server
, '/'); p
&& *p
;
1956 p
= strindex(p
, '/')){
1957 /* take out /nntp, which gets added in nntp_open */
1958 if(!struncmp(p
, "/nntp", 5))
1960 else if(!struncmp(p
, "/service=nntp", 13))
1962 else if(!struncmp(p
, "/service=\"nntp\"", 15))
1969 else if(*svcp
== '/' || *svcp
== ':'){
1970 for(psvcp
= p
; *svcp
; svcp
++, psvcp
++)
1979 suggested_nntp_server
= cpystr(news_mb
.orighost
);
1984 * If we don't already have custom headers set and the role has custom
1985 * headers, then incorporate those custom headers into "custom".
1988 PINEFIELD
*dflthdrs
= NULL
, *rolehdrs
= NULL
;
1990 dflthdrs
= parse_custom_hdrs(ps_global
->VAR_CUSTOM_HDRS
, UseAsDef
);
1992 * If we allow the Combine argument here, we're saying that we want to
1993 * combine the values from the envelope and the role for the fields To,
1994 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
1995 * we'll have a To in the envelope because we're replying. If our role also
1996 * has a To action, then Combine would combine those two and offer both
1997 * to the user. We've decided against doing this. Instead, we always use
1998 * Replace, and the role's header value replaces the value from the
1999 * envelope. It might also make sense in some cases to do the opposite,
2000 * which would be treating the role headers as defaults, just like
2003 #ifdef WANT_TO_COMBINE_ADDRESSES
2004 if(role
&& role
->cstm
)
2005 rolehdrs
= parse_custom_hdrs(role
->cstm
, Combine
);
2007 if(role
&& role
->cstm
)
2008 rolehdrs
= parse_custom_hdrs(role
->cstm
, Replace
);
2012 custom
= combine_custom_headers(dflthdrs
, rolehdrs
);
2014 free_prompts(dflthdrs
);
2015 free_customs(dflthdrs
);
2019 free_prompts(rolehdrs
);
2020 free_customs(rolehdrs
);
2027 g_rolenick
= role
? role
->nick
: NULL
;
2029 /* how many fixed fields are there? */
2030 for(fixed_cnt
= 0; pf_template
&& pf_template
[fixed_cnt
].name
; fixed_cnt
++)
2033 total_cnt
= fixed_cnt
+ count_custom_hdrs_pf(custom
,1);
2035 /* the fixed part of the PINEFIELDs */
2036 i
= fixed_cnt
* sizeof(PINEFIELD
);
2037 pfields
= (PINEFIELD
*)fs_get((size_t) i
);
2038 memset(pfields
, 0, (size_t) i
);
2040 /* temporary headerentry array for pico */
2041 i
= (total_cnt
+ 1) * sizeof(struct headerentry
);
2042 headents
= (struct headerentry
*)fs_get((size_t) i
);
2043 memset(headents
, 0, (size_t) i
);
2045 i
= total_cnt
* sizeof(PINEFIELD
*);
2046 sending_order
= (PINEFIELD
**)fs_get((size_t) i
);
2047 memset(sending_order
, 0, (size_t) i
);
2049 pbf
->headents
= headents
;
2050 header
.env
= outgoing
;
2051 header
.local
= pfields
;
2052 header
.sending_order
= sending_order
;
2054 /* custom part of PINEFIELDs */
2055 header
.custom
= custom
;
2061 * For Address types, pf->addr points to an ADDRESS *.
2062 * If that address is in the "outgoing" envelope, it will
2063 * be freed by the caller, otherwise, it should be freed here.
2064 * Pf->textbuf for an Address is used a little to set up a default,
2065 * but then is freed right away below. Pf->scratch is used for a
2066 * pointer to some alloced space for pico to edit in. Addresses in
2067 * the custom area are freed by free_customs().
2069 * For FreeText types, pf->addr is not used. Pf->text points to a
2070 * pointer that points to the text. Pf->textbuf points to a copy of
2071 * the text that must be freed before we leave, otherwise, it is
2072 * probably a pointer into the envelope and that gets freed by the
2075 * He->realaddr is the pointer to the text that pico actually edits.
2078 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2084 if(outgoing
->newsgroups
&& *outgoing
->newsgroups
)
2087 /* initialize the fixed header elements of the two temp arrays */
2088 for(i
=0; i
< fixed_cnt
; i
++, pf
++){
2089 static int news_order
[] = {
2090 N_AUTHRCVD
,N_FROM
, N_REPLYTO
, N_NEWS
, N_TO
, N_CC
, N_BCC
,
2091 N_FCC
, N_LCC
, N_ATTCH
, N_SUBJ
, N_REF
, N_DATE
, N_INREPLY
,
2092 N_MSGID
, N_PRIORITY
, N_USERAGENT
, N_NOBODY
, N_POSTERR
, N_RPLUID
, N_RPLMBOX
,
2093 N_SMTP
, N_NNTP
, N_CURPOS
, N_OURREPLYTO
, N_OURHDRS
2094 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2100 /* slightly different editing order if sending to news */
2101 if(use_news_order
&&
2102 index
>= 0 && index
< sizeof(news_order
)/sizeof(news_order
[0]))
2103 index
= news_order
[i
];
2105 /* copy the templates */
2106 *he
= he_template
[index
];
2108 pf
->name
= cpystr(pf_template
[index
].name
);
2109 if(index
== N_SENDER
&& F_ON(F_USE_SENDER_NOT_X
, ps_global
))
2110 /* slide string over so it is Sender instead of X-X-Sender */
2111 for(p
=pf
->name
; *(p
+1); p
++)
2114 pf
->type
= pf_template
[index
].type
;
2115 pf
->canedit
= pf_template
[index
].canedit
;
2116 pf
->rcptto
= pf_template
[index
].rcptto
;
2117 pf
->writehdr
= pf_template
[index
].writehdr
;
2118 pf
->localcopy
= pf_template
[index
].localcopy
;
2122 he
->rich_header
= view_as_rich(pf
->name
, he
->rich_header
);
2123 if(F_OFF(F_ENABLE_TAB_COMPLETE
,ps_global
))
2124 he
->nickcmpl
= NULL
;
2127 case FreeText
: /* realaddr points to c-client env */
2128 if(index
== N_NEWS
){
2129 sending_order
[1] = pf
;
2130 he
->realaddr
= &outgoing
->newsgroups
;
2133 switch(set_default_hdrval(pf
, custom
)){
2136 fs_give((void **)he
->realaddr
);
2138 *he
->realaddr
= pf
->textbuf
;
2144 if(*he
->realaddr
){ /* combine values */
2145 if(pf
->textbuf
&& *pf
->textbuf
){
2149 l
= strlen(*he
->realaddr
) + strlen(pf
->textbuf
) + 1;
2150 combined_hdr
= (char *) fs_get((l
+1) * sizeof(char));
2151 strncpy(combined_hdr
, *he
->realaddr
, l
);
2152 combined_hdr
[l
] = '\0';
2153 strncat(combined_hdr
, ",", l
+1-1-strlen(combined_hdr
));
2154 combined_hdr
[l
] = '\0';
2155 strncat(combined_hdr
, pf
->textbuf
, l
+1-1-strlen(combined_hdr
));
2156 combined_hdr
[l
] = '\0';
2158 fs_give((void **)he
->realaddr
);
2159 *he
->realaddr
= combined_hdr
;
2160 q_status_message(SM_ORDER
, 3, 3,
2161 "Adding newsgroup from role");
2166 *he
->realaddr
= pf
->textbuf
;
2173 /* if no value, use default */
2175 *he
->realaddr
= pf
->textbuf
;
2185 /* If there is a newsgroup, we'd better show it */
2186 if(outgoing
->newsgroups
&& *outgoing
->newsgroups
)
2187 he
->rich_header
= 0; /* force on by default */
2190 fs_give((void **)&pf
->textbuf
);
2192 pf
->text
= he
->realaddr
;
2194 else if(index
== N_DATE
){
2195 sending_order
[2] = pf
;
2196 pf
->text
= (char **) &outgoing
->date
;
2199 else if(index
== N_INREPLY
){
2200 sending_order
[NN
+9] = pf
;
2201 pf
->text
= &outgoing
->in_reply_to
;
2204 else if(index
== N_MSGID
){
2205 sending_order
[NN
+10] = pf
;
2206 pf
->text
= &outgoing
->message_id
;
2209 else if(index
== N_REF
){
2210 sending_order
[NN
+11] = pf
;
2211 pf
->text
= &outgoing
->references
;
2214 else if(index
== N_PRIORITY
){
2215 sending_order
[NN
+12] = pf
;
2216 pf
->text
= &pf
->textbuf
;
2219 else if(index
== N_USERAGENT
){
2220 sending_order
[NN
+13] = pf
;
2221 pf
->text
= &pf
->textbuf
;
2222 pf
->textbuf
= generate_user_agent();
2225 else if(index
== N_POSTERR
){
2226 sending_order
[NN
+14] = pf
;
2228 pf
->text
= &pf
->textbuf
;
2231 else if(index
== N_RPLUID
){
2232 sending_order
[NN
+15] = pf
;
2234 pf
->text
= &pf
->textbuf
;
2237 else if(index
== N_RPLMBOX
){
2238 sending_order
[NN
+16] = pf
;
2240 pf
->text
= &pf
->textbuf
;
2243 else if(index
== N_SMTP
){
2244 sending_order
[NN
+17] = pf
;
2245 pf_smtp_server
= pf
;
2246 pf
->text
= &pf
->textbuf
;
2249 else if(index
== N_NNTP
){
2250 sending_order
[NN
+18] = pf
;
2251 pf_nntp_server
= pf
;
2252 pf
->text
= &pf
->textbuf
;
2255 else if(index
== N_CURPOS
){
2256 sending_order
[NN
+19] = pf
;
2258 pf
->text
= &pf
->textbuf
;
2261 else if(index
== N_OURREPLYTO
){
2262 sending_order
[NN
+20] = pf
;
2264 pf
->text
= &pf
->textbuf
;
2267 else if(index
== N_OURHDRS
){
2268 sending_order
[NN
+21] = pf
;
2270 pf
->text
= &pf
->textbuf
;
2273 else if(index
== N_AUTHRCVD
){
2274 sending_order
[0] = pf
;
2276 pf
->text
= &pf
->textbuf
;
2280 q_status_message(SM_ORDER
| SM_DING
, 3, 7,
2281 "Botched: Unmatched FreeText header in pine_send");
2286 /* can't do a default for this one */
2288 /* If there is an attachment already, we'd better show them */
2289 if(body
&& *body
&& (*body
)->type
!= TYPETEXT
)
2290 he
->rich_header
= 0; /* force on by default */
2297 sending_order
[3] = pf
;
2298 pf
->addr
= &outgoing
->from
;
2299 if(role
&& role
->from
){
2300 if(ps_global
->never_allow_changing_from
)
2301 q_status_message(SM_ORDER
, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2303 outgoing
->from
= copyaddrlist(role
->from
);
2304 he
->display_it
= 1; /* show it */
2305 he
->rich_header
= 0;
2313 sending_order
[NN
+2] = pf
;
2314 pf
->addr
= &outgoing
->to
;
2315 /* If already set, make it act like we typed it in */
2317 && outgoing
->to
->mailbox
2318 && outgoing
->to
->mailbox
[0]
2319 && flags
& PS_STICKY_TO
)
2327 sending_order
[NN
+5] = pf
;
2329 if(ps_global
->VAR_EMPTY_HDR_MSG
2330 && !ps_global
->VAR_EMPTY_HDR_MSG
[0]){
2334 nobody_addr
= mail_newaddr();
2335 nobody_addr
->next
= mail_newaddr();
2336 nobody_addr
->mailbox
= cpystr(rfc1522_encode(tmp_20k_buf
,
2338 (unsigned char *)(ps_global
->VAR_EMPTY_HDR_MSG
2339 ? ps_global
->VAR_EMPTY_HDR_MSG
2340 : "undisclosed-recipients"),
2341 ps_global
->posting_charmap
));
2342 pf
->addr
= &nobody_addr
;
2348 sending_order
[NN
+3] = pf
;
2349 pf
->addr
= &outgoing
->cc
;
2353 sending_order
[NN
+4] = pf
;
2354 pf
->addr
= &outgoing
->bcc
;
2355 /* if bcc exists, make sure it's exposed so nothing's
2356 * sent by mistake...
2364 sending_order
[NN
+1] = pf
;
2365 pf
->addr
= &outgoing
->reply_to
;
2366 if(role
&& role
->replyto
){
2367 if(outgoing
->reply_to
)
2368 mail_free_address(&outgoing
->reply_to
);
2370 outgoing
->reply_to
= copyaddrlist(role
->replyto
);
2371 he
->display_it
= 1; /* show it */
2372 he
->rich_header
= 0;
2378 sending_order
[NN
+7] = pf
;
2379 pf
->addr
= &lcc_addr
;
2382 build_address(lcc_arg
, &addr
, NULL
, NULL
, NULL
);
2383 rfc822_parse_adrlist(&lcc_addr
, addr
,
2384 ps_global
->maildomain
);
2385 fs_give((void **)&addr
);
2391 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2393 sending_order
[4] = pf
;
2394 pf
->addr
= &outgoing
->sender
;
2399 q_status_message1(SM_ORDER
,3,7,
2400 "Internal error: Address header %s", comatose(index
));
2405 * If this is a reply to news, don't show the regular email
2406 * recipient headers (unless they are non-empty).
2408 if((outgoing
->newsgroups
&& *outgoing
->newsgroups
)
2409 && (index
== N_TO
|| index
== N_CC
2410 || index
== N_BCC
|| index
== N_LCC
)
2411 && (pf
->addr
&& !*pf
->addr
)){
2412 if((ct
=set_default_hdrval(pf
, custom
)) >= UseAsDef
&&
2413 pf
->textbuf
&& *pf
->textbuf
){
2414 removing_trailing_white_space(pf
->textbuf
);
2415 (void)removing_double_quotes(pf
->textbuf
);
2416 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2417 rfc822_parse_adrlist(pf
->addr
, addr
,
2418 ps_global
->maildomain
);
2419 fs_give((void **)&addr
);
2424 he
->rich_header
= 1; /* hide */
2428 * If this address doesn't already have a value, then we check
2429 * for a default value assigned by the user.
2431 else if(pf
->addr
&& !*pf
->addr
){
2432 if((ct
=set_default_hdrval(pf
, custom
)) >= UseAsDef
&&
2434 (!ps_global
->never_allow_changing_from
&&
2435 F_ON(F_ALLOW_CHANGING_FROM
, ps_global
))) &&
2436 pf
->textbuf
&& *pf
->textbuf
){
2438 removing_trailing_white_space(pf
->textbuf
);
2439 (void)removing_double_quotes(pf
->textbuf
);
2442 * Try to set To based on Lcc. Don't attempt Fcc.
2444 if(index
== N_LCC
&& !he_to
->sticky
&& pf_to
&& pf_to
->addr
){
2445 BUILDER_ARG
*barg
= NULL
;
2449 ppp
= addr_list_string(*pf_to
->addr
, NULL
, 1);
2454 barg
= (BUILDER_ARG
*) fs_get(sizeof(*barg
));
2455 memset(barg
, 0, sizeof(*barg
));
2456 barg
->me
= &(he
->bldr_private
);
2457 barg
->aff
= &(he_to
->bldr_private
);
2458 barg
->tptr
= cpystr(ppp
);
2460 build_addr_lcc(pf
->textbuf
, &addr
, NULL
, barg
, NULL
);
2463 rfc822_parse_adrlist(pf
->addr
, addr
,
2464 ps_global
->maildomain
);
2466 fs_give((void **) &addr
);
2471 if(barg
&& barg
->tptr
&& strcmp(ppp
, barg
->tptr
)){
2474 rfc822_parse_adrlist(&a
, barg
->tptr
,
2475 ps_global
->maildomain
);
2478 mail_free_address(pf_to
->addr
);
2486 fs_give((void **) &barg
->tptr
);
2488 fs_give((void **) &barg
);
2492 fs_give((void **) &ppp
);
2495 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2496 rfc822_parse_adrlist(pf
->addr
, addr
,
2497 ps_global
->maildomain
);
2499 fs_give((void **) &addr
);
2506 /* if we still don't have a from */
2507 if(index
== N_FROM
&& !*pf
->addr
)
2508 *pf
->addr
= generate_from();
2512 * Addr is already set in the rest of the cases.
2514 else if((index
== N_FROM
|| index
== N_REPLYTO
) && pf
->addr
){
2515 ADDRESS
*adr
= NULL
;
2518 * We get to this case of the ifelse if the from or reply-to
2519 * addr was set by a role above.
2522 /* figure out the default value */
2523 (void)set_default_hdrval(pf
, custom
);
2524 if(pf
->textbuf
&& *pf
->textbuf
){
2525 removing_trailing_white_space(pf
->textbuf
);
2526 (void)removing_double_quotes(pf
->textbuf
);
2527 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2528 rfc822_parse_adrlist(&adr
, addr
,
2529 ps_global
->maildomain
);
2530 fs_give((void **)&addr
);
2533 /* if value set by role is different from default, show it */
2534 if(adr
&& !address_is_same(*pf
->addr
, adr
))
2535 he
->display_it
= 1; /* start this off showing */
2538 if(!(*pf
->addr
)->mailbox
){
2539 fs_give((void **)pf
->addr
);
2544 mail_free_address(&adr
);
2546 else if((index
== N_TO
|| index
== N_CC
|| index
== N_BCC
)
2548 ADDRESS
*a
= NULL
, **tail
;
2551 * These three are different from the others because we
2552 * might add the addresses to what is already there instead
2556 switch(set_default_hdrval(pf
, custom
)){
2559 mail_free_address(pf
->addr
);
2561 removing_trailing_white_space(pf
->textbuf
);
2562 (void)removing_double_quotes(pf
->textbuf
);
2563 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2564 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
2565 fs_give((void **)&addr
);
2570 removing_trailing_white_space(pf
->textbuf
);
2571 (void)removing_double_quotes(pf
->textbuf
);
2572 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2573 rfc822_parse_adrlist(&a
, addr
, ps_global
->maildomain
);
2574 fs_give((void **)&addr
);
2577 for(tail
= pf
->addr
; *tail
; tail
= &(*tail
)->next
)
2579 *tail
= reply_cp_addr(ps_global
, 0, NULL
, NULL
,
2580 *pf
->addr
, NULL
, a
, RCA_ALL
);
2581 q_status_message(SM_ORDER
, 3, 3,
2582 "Adding addresses from role");
2583 mail_free_address(&a
);
2593 he
->display_it
= 1; /* start this off showing */
2596 switch(set_default_hdrval(pf
, custom
)){
2600 mail_free_address(pf
->addr
);
2602 removing_trailing_white_space(pf
->textbuf
);
2603 (void)removing_double_quotes(pf
->textbuf
);
2604 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2605 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
2606 fs_give((void **)&addr
);
2618 if(pf
->addr
&& *pf
->addr
&& !(*pf
->addr
)->mailbox
){
2619 mail_free_address(pf
->addr
);
2620 he
->display_it
= 1; /* start this off showing */
2623 if(pf
->textbuf
) /* free default value in any case */
2624 fs_give((void **)&pf
->textbuf
);
2626 /* outgoing2strings will alloc the string pf->scratch below */
2627 he
->realaddr
= &pf
->scratch
;
2631 sending_order
[NN
+8] = pf
;
2633 if(role
&& role
->fcc
)
2636 fcc
= get_fcc(fcc_arg
);
2639 fs_give((void **)&fcc_to_free
);
2643 if(((flags
& PS_STICKY_FCC
) && fcc
[0]) || (role
&& role
->fcc
))
2649 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
) != 0)
2650 he
->display_it
= 1; /* start this off showing */
2652 he
->realaddr
= &fcc
;
2658 sending_order
[NN
+6] = pf
;
2660 switch(set_default_hdrval(pf
, custom
)){
2663 pf
->scratch
= pf
->textbuf
;
2666 if(outgoing
->subject
)
2667 fs_give((void **)&outgoing
->subject
);
2673 /* if no value, use default */
2674 if(outgoing
->subject
){
2675 pf
->scratch
= cpystr(outgoing
->subject
);
2678 pf
->scratch
= pf
->textbuf
;
2685 he
->realaddr
= &pf
->scratch
;
2686 pf
->text
= &outgoing
->subject
;
2690 q_status_message1(SM_ORDER
,3,7,
2691 "Unknown header type %d in pine_send",
2697 * We may or may not want to give the user the chance to edit
2698 * the From and Reply-To lines. If they are listed in either
2699 * Default-composer-hdrs or Customized-hdrs, then they can edit
2701 * If canedit is not set, that means that this header is not in
2702 * the user's customized-hdrs. If rich_header is set, that
2703 * means that this header is not in the user's
2704 * default-composer-hdrs (since From and Reply-To are rich
2705 * by default). So, don't give it an he to edit with in that case.
2707 * For other types, just not setting canedit will cause it to be
2708 * uneditable, regardless of what the user does.
2712 /* to allow it, we let this fall through to the reply-to case below */
2713 if(ps_global
->never_allow_changing_from
||
2714 (F_OFF(F_ALLOW_CHANGING_FROM
, ps_global
) &&
2715 !(role
&& role
->from
))){
2716 if(pf
->canedit
|| !he
->rich_header
)
2717 q_status_message(SM_ORDER
, 3, 3,
2718 _("Not allowed to change header \"From\""));
2720 memset(he
, 0, (size_t)sizeof(*he
));
2726 if(!pf
->canedit
&& he
->rich_header
){
2727 memset(he
, 0, (size_t)sizeof(*he
));
2739 memset(he
, 0, (size_t)sizeof(*he
));
2750 * This is so the builder can tell the composer to fill the affected
2751 * field based on the value in the field on the left.
2753 * Note that this mechanism isn't completely general. Each entry has
2754 * only a single next_affected, so if some other entry points an
2755 * affected entry at an entry with a next_affected, they all inherit
2756 * that next_affected. Since this isn't used much a careful ordering
2757 * of the affected fields should make it a sufficient mechanism.
2759 he_to
->affected_entry
= he_fcc
;
2760 he_news
->affected_entry
= he_fcc
;
2761 he_lcc
->affected_entry
= he_to
;
2762 he_to
->next_affected
= he_fcc
;
2764 (--pf
)->next
= (total_cnt
!= fixed_cnt
) ? header
.custom
: NULL
;
2766 i
--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2768 * Set up headerentries for custom fields.
2769 * NOTE: "i" is assumed to now index first custom field in sending
2772 for(pf
= pf
->next
; pf
&& pf
->name
; pf
= pf
->next
){
2786 if(pf
->addr
){ /* better be set */
2787 sending_order
[i
++] = pf
;
2788 *he
= he_custom_addr_templ
;
2789 /* change default text into an ADDRESS */
2790 /* strip quotes around whole default */
2791 removing_trailing_white_space(pf
->textbuf
);
2792 (void)removing_double_quotes(pf
->textbuf
);
2793 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
2794 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
2795 fs_give((void **)&addr
);
2797 fs_give((void **)&pf
->textbuf
);
2799 he
->realaddr
= &pf
->scratch
;
2800 if(F_OFF(F_ENABLE_TAB_COMPLETE
,ps_global
))
2801 he
->nickcmpl
= NULL
;
2807 sending_order
[i
++] = pf
;
2808 *he
= he_custom_free_templ
;
2809 he
->realaddr
= &pf
->textbuf
;
2810 pf
->text
= &pf
->textbuf
;
2811 if(((!pf
->val
|| !pf
->val
[0]) && pf
->textbuf
&& pf
->textbuf
[0]) ||
2812 (pf
->val
&& (!pf
->textbuf
|| strcmp(pf
->textbuf
, pf
->val
))))
2813 he
->display_it
= 1; /* show it */
2818 q_status_message1(SM_ORDER
,0,7,"Unknown custom header type %d",
2823 he
->name
= pf
->name
;
2825 /* use first 8 characters for prompt */
2826 he
->prompt
= cpystr(" : ");
2827 strncpy(he
->prompt
, he
->name
, MIN(strlen(he
->name
), he
->prwid
- 2));
2829 he
->rich_header
= view_as_rich(he
->name
, he
->rich_header
);
2834 * Make sure at least *one* field is displayable...
2836 for(index
= -1, i
=0, pf
=header
.local
; pf
&& pf
->name
; pf
=pf
->next
, i
++)
2837 if(HE(pf
) && !HE(pf
)->rich_header
){
2843 * None displayable!!! Warn and display defaults.
2846 q_status_message(SM_ORDER
,0,5,
2847 "No default-composer-hdrs matched, displaying defaults");
2848 for(i
= 0, pf
= header
.local
; pf
; pf
= pf
->next
, i
++)
2849 if((i
== N_TO
|| i
== N_CC
|| i
== N_SUBJ
|| i
== N_ATTCH
)
2851 HE(pf
)->rich_header
= 0;
2855 * Save information about body which set_mime_type_by_grope might change.
2856 * Then, if we get an error sending, we reset these things so that
2857 * grope can do it's thing again after we edit some more.
2859 if ((*body
)->type
== TYPEMULTIPART
)
2860 bp
= save_body_particulars(&(*body
)->nested
.part
->body
);
2862 bp
= save_body_particulars(*body
);
2865 local_redraft_pos
= redraft_pos
;
2867 /*----------------------------------------------------------------------
2868 Loop calling the editor until everything goes well
2871 int saved_user_timeout
;
2873 /* Reset body to what it was when we started. */
2874 if ((*body
)->type
== TYPEMULTIPART
)
2875 reset_body_particulars(bp
, &(*body
)->nested
.part
->body
);
2877 reset_body_particulars(bp
,*body
);
2879 * set initial cursor position based on how many times we've been
2882 if(reply
&& reply
->pseudo
){
2883 pbf
->pine_flags
|= reply
->data
.pico_flags
;
2885 else if(body_start
){
2886 pbf
->pine_flags
|= P_BODY
;
2887 body_start
= 0; /* maybe not next time */
2889 else if(local_redraft_pos
){
2890 pbf
->edit_offset
= local_redraft_pos
->offset
;
2891 /* set the start_here bit in correct header */
2892 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
2893 if(strcmp(pf
->name
, local_redraft_pos
->hdrname
) == 0
2895 HE(pf
)->start_here
= 1;
2899 /* If didn't find it, we start in body. */
2900 if(!pf
|| !pf
->name
)
2901 pbf
->pine_flags
|= P_BODY
;
2903 else if(reply
&& (!reply
->forw
&& !reply
->forwarded
)){
2904 pbf
->pine_flags
|= P_BODY
;
2907 /* in case these were turned on in previous pass through loop */
2909 pf_nobody
->writehdr
= 0;
2910 pf_nobody
->localcopy
= 0;
2914 pf_fcc
->localcopy
= 0;
2917 * If a sending attempt failed after we passed the message text
2918 * thru a user-defined filter, "orig_so" points to the original
2919 * text. Replace the body's encoded data with the original...
2922 STORE_S
**so
= (STORE_S
**)(((*body
)->type
== TYPEMULTIPART
)
2923 ? &(*body
)->nested
.part
->body
.contents
.text
.data
2924 : &(*body
)->contents
.text
.data
);
2931 * Convert the envelope and body to the string format that
2934 outgoing2strings(&header
, *body
, &pbf
->msgtext
, &pbf
->attachments
, 0);
2936 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
){
2938 * If this isn't the first time through this loop, we may have
2939 * freed some of the FreeText headers below so that they wouldn't
2940 * show up as empty headers in the finished message. Need to
2941 * alloc them again here so they can be edited.
2943 if(pf
->type
== FreeText
&& HE(pf
) && !*HE(pf
)->realaddr
)
2944 *HE(pf
)->realaddr
= cpystr("");
2946 if(pf
->type
!= Attachment
&& HE(pf
) && *HE(pf
)->realaddr
)
2947 HE(pf
)->maxlen
= strlen(*HE(pf
)->realaddr
);
2951 * If From is exposed, probably by a role, then start the cursor
2952 * on the first line which isn't filled in. If it isn't, then we
2953 * don't move the cursor, mostly for back-compat.
2955 if((!reply
|| reply
->forw
|| reply
->forwarded
) &&
2956 !local_redraft_pos
&& !(pbf
->pine_flags
& P_BODY
) && he_from
&&
2957 (he_from
->display_it
|| !he_from
->rich_header
)){
2958 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
2960 (HE(pf
)->display_it
|| !HE(pf
)->rich_header
) &&
2962 (!*HE(pf
)->realaddr
|| !**HE(pf
)->realaddr
)){
2963 HE(pf
)->start_here
= 1;
2969 mswin_setwindowmenu (MENU_COMPOSER
);
2972 cancel_busy_cue(-1);
2973 flush_status_messages(1);
2975 /* turn off user input timeout when in composer */
2976 saved_user_timeout
= ps_global
->hours_to_timeout
;
2977 ps_global
->hours_to_timeout
= 0;
2978 dprint((1, "\n ---- COMPOSER ----\n"));
2979 editor_result
= pico(pbf
);
2980 dprint((4, "... composer returns (0x%x)\n", editor_result
));
2981 ps_global
->hours_to_timeout
= saved_user_timeout
;
2984 mswin_setwindowmenu (MENU_DEFAULT
);
2986 fix_windsize(ps_global
);
2989 * Only reinitialize signals if we didn't receive an interesting
2990 * one while in pico, since pico's return is part of processing that
2991 * signal and it should continue to be ignored.
2993 if(!(editor_result
& COMP_GOTHUP
))
2994 init_signals(); /* Pico has it's own signal stuff */
2997 * We're going to save in DEADLETTER. Dump attachments first.
2999 if(editor_result
& COMP_CANCEL
)
3000 free_attachment_list(&pbf
->attachments
);
3002 /* Turn strings back into structures */
3003 strings2outgoing(&header
, body
, pbf
->attachments
, flowing_requested
);
3005 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3006 if(outgoing
->newsgroups
){
3007 sqzspaces(outgoing
->newsgroups
);
3008 if(!outgoing
->newsgroups
[0])
3009 fs_give((void **)&(outgoing
->newsgroups
));
3012 /* Make subject NULL if it is "" (so won't show up in headers) */
3013 if(outgoing
->subject
&& !outgoing
->subject
[0])
3014 fs_give((void **)&(outgoing
->subject
));
3016 /* remove custom fields that are empty */
3017 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
){
3018 if(pf
->type
== FreeText
&& pf
->textbuf
){
3019 if(pf
->textbuf
[0] == '\0'){
3020 fs_give((void **)&pf
->textbuf
);
3026 removing_trailing_white_space(fcc
);
3028 /*-------- Stamp it with a current date -------*/
3029 if(outgoing
->date
) /* update old date */
3030 fs_give((void **)&(outgoing
->date
));
3032 if(F_ON(F_QUELL_TIMEZONE
, ps_global
))
3033 mail_parameters(NULL
, SET_DISABLE822TZTEXT
, (void *) TRUE
);
3035 rfc822_date(tmp_20k_buf
); /* format and copy new date */
3036 if(F_ON(F_QUELL_TIMEZONE
, ps_global
))
3037 mail_parameters(NULL
, SET_DISABLE822TZTEXT
, (void *) FALSE
);
3039 outgoing
->date
= (unsigned char *) cpystr(tmp_20k_buf
);
3041 /* Set return_path based on From which is going to be used */
3042 if(outgoing
->return_path
)
3043 mail_free_address(&outgoing
->return_path
);
3045 outgoing
->return_path
= rfc822_cpy_adr(outgoing
->from
);
3048 * Don't ever believe the sender that is there.
3049 * If From doesn't look quite right, generate our own sender.
3051 if(outgoing
->sender
)
3052 mail_free_address(&outgoing
->sender
);
3055 * If the LHS of the address doesn't match, or the RHS
3056 * doesn't match one of localdomain or hostname,
3057 * then add a sender line (really X-X-Sender).
3059 * Don't add a personal_name since the user can change that.
3061 if(F_OFF(F_DISABLE_SENDER
, ps_global
)
3064 || !outgoing
->from
->mailbox
3065 || strucmp(outgoing
->from
->mailbox
, ps_global
->VAR_USER_ID
) != 0
3066 || !outgoing
->from
->host
3067 || !(strucmp(outgoing
->from
->host
, ps_global
->localdomain
) == 0
3068 || strucmp(outgoing
->from
->host
, ps_global
->hostname
) == 0))){
3070 outgoing
->sender
= mail_newaddr();
3071 outgoing
->sender
->mailbox
= cpystr(ps_global
->VAR_USER_ID
);
3072 outgoing
->sender
->host
= cpystr(ps_global
->hostname
);
3075 if(ps_global
->newthread
){
3076 if(outgoing
->in_reply_to
) fs_give((void **)&outgoing
->in_reply_to
);
3077 if(outgoing
->references
) fs_give((void **)&outgoing
->references
);
3080 /*----- Message is edited, now decide what to do with it ----*/
3081 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
3082 /*=========== Postpone or Interrupted message ============*/
3083 CONTEXT_S
*fcc_cntxt
= NULL
;
3084 char folder
[MAXPATH
+1];
3088 dprint((4, "pine_send:%s handling\n",
3089 (editor_result
& COMP_SUSPEND
)
3091 : (editor_result
& COMP_GOTHUP
)
3093 : (editor_result
& COMP_CANCEL
)
3094 ? "CANCEL" : "HUH?"));
3095 if((editor_result
& COMP_CANCEL
)
3096 && (F_ON(F_QUELL_DEAD_LETTER
, ps_global
)
3097 || ps_global
->deadlets
== 0)){
3098 q_status_message(SM_ORDER
, 0, 3, "Message cancelled");
3103 * The idea here is to use the Fcc: writing facility
3104 * to append to the special postponed message folder...
3106 * NOTE: the strategy now is to write the message and
3107 * all attachments as they exist at composition time.
3108 * In other words, attachments are postponed by value
3109 * and not reference. This may change later, but we'll
3110 * need a local "message/external-body" type that
3111 * outgoing2strings knows how to properly set up for
3112 * the composer. Maybe later...
3117 if(F_ON(F_COMPOSE_REJECTS_UNQUAL
, ps_global
)
3118 && (editor_result
& COMP_SUSPEND
)
3119 && (check_addresses(&header
) == CA_BAD
)){
3120 /*--- Addresses didn't check out---*/
3121 q_status_message(SM_ORDER
, 7, 7,
3122 _("Not allowed to postpone message until addresses are qualified"));
3127 * Build the local message copy so.
3129 * In the HUP case, we'll write the bezerk delimiter by
3130 * hand and output the message directly into the folder.
3131 * It's not only faster, we don't have to worry about
3132 * c-client reentrance and less hands paw over the data so
3133 * there's less chance of a problem.
3135 * In the Postpone case, just create it if the user wants to
3136 * and create a temporary storage object to write into. */
3138 lmc
.all_written
= lmc
.text_only
= lmc
.text_written
= 0;
3139 if(editor_result
& (COMP_GOTHUP
| COMP_CANCEL
)){
3141 time_t now
= time((time_t *)0);
3143 #if defined(DOS) || defined(OS2)
3145 * we can't assume anything about root or home dirs, so
3146 * just plunk it down in the same place as the pinerc
3148 if(!getenv("HOME")){
3149 char *lc
= last_cmpnt(ps_global
->pinerc
);
3152 strncpy(folder
,ps_global
->pinerc
,
3153 MIN(lc
-ps_global
->pinerc
,sizeof(folder
)-1));
3154 folder
[MIN(lc
-ps_global
->pinerc
,sizeof(folder
)-1)]='\0';
3157 strncat(folder
, (editor_result
& COMP_GOTHUP
)
3158 ? INTERRUPTED_MAIL
: DEADLETTER
,
3159 sizeof(folder
)-strlen(folder
)-1);
3164 ps_global
->VAR_OPER_DIR
3165 ? ps_global
->VAR_OPER_DIR
: ps_global
->home_dir
,
3166 (editor_result
& COMP_GOTHUP
)
3167 ? INTERRUPTED_MAIL
: DEADLETTER
,
3170 if(editor_result
& COMP_CANCEL
){
3171 char filename
[MAXPATH
+1], newfname
[MAXPATH
+1], nbuf
[5];
3173 if(strlen(folder
) + 1 < sizeof(filename
))
3174 for(i
= ps_global
->deadlets
- 1; i
> 0 && i
< 9; i
--){
3175 strncpy(filename
, folder
, sizeof(filename
));
3176 filename
[sizeof(filename
)-1] = '\0';
3177 strncpy(newfname
, filename
, sizeof(newfname
));
3178 newfname
[sizeof(newfname
)-1] = '\0';
3181 snprintf(nbuf
, sizeof(nbuf
), "%d", i
);
3182 nbuf
[sizeof(nbuf
)-1] = '\0';
3183 strncat(filename
, nbuf
,
3184 sizeof(filename
)-strlen(filename
)-1);
3185 filename
[sizeof(filename
)-1] = '\0';
3188 snprintf(nbuf
, sizeof(nbuf
), "%d", i
+1);
3189 nbuf
[sizeof(nbuf
)-1] = '\0';
3190 strncat(newfname
, nbuf
,
3191 sizeof(newfname
)-strlen(newfname
)-1);
3192 newfname
[sizeof(newfname
)-1] = '\0';
3193 (void) rename_file(filename
, newfname
);
3199 newfile
= can_access(folder
, ACCESS_EXISTS
);
3201 if((lmc
.so
= so_get(FCC_SOURCE
, NULL
, WRITE_ACCESS
)) != NULL
){
3202 if (outgoing
->from
){
3203 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%sFrom %s@%s %.24s\015\012",
3204 newfile
? "" : "\015\012",
3205 outgoing
->from
->mailbox
,
3206 outgoing
->from
->host
, ctime(&now
));
3207 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
3208 if(!so_puts(lmc
.so
, tmp_20k_buf
)){
3209 if(editor_result
& COMP_CANCEL
)
3210 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
3211 "Can't write \"%s\": %s",
3212 folder
, error_description(errno
));
3214 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3215 folder
? folder
: "?",
3216 error_description(errno
)));
3221 else{ /* Must be COMP_SUSPEND */
3222 if(!ps_global
->VAR_POSTPONED_FOLDER
3223 || !ps_global
->VAR_POSTPONED_FOLDER
[0]){
3224 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3225 _("No postponed file defined"));
3230 * Store the cursor position
3232 * First find the header entry with the start_here
3233 * bit set, if any. This means the editor is telling
3234 * us to start on this header field next time.
3236 start_here_name
= NULL
;
3237 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
3238 if(HE(pf
) && HE(pf
)->start_here
){
3239 start_here_name
= pf
->name
;
3243 /* If there wasn't one, ":" means we start in the body. */
3244 if(!start_here_name
|| !*start_here_name
)
3245 start_here_name
= ":";
3247 if(ps_global
->VAR_FORM_FOLDER
3248 && ps_global
->VAR_FORM_FOLDER
[0]
3249 && postpone_prompt() == 'f'){
3250 strncpy(folder
, ps_global
->VAR_FORM_FOLDER
,
3252 folder
[sizeof(folder
)-1] = '\0';
3253 strncpy(label
, "form letter", sizeof(label
));
3254 label
[sizeof(label
)-1] = '\0';
3257 strncpy(folder
, ps_global
->VAR_POSTPONED_FOLDER
,
3259 folder
[sizeof(folder
)-1] = '\0';
3260 strncpy(label
, "postponed message", sizeof(label
));
3261 label
[sizeof(label
)-1] = '\0';
3264 lmc
.so
= open_fcc(folder
,&fcc_cntxt
, 1, NULL
, NULL
);
3271 /* copy fcc line to postponed or interrupted folder */
3273 pf_fcc
->localcopy
= 1;
3275 /* plug error into header for later display to user */
3276 if((editor_result
& ~0xff) && (lmq
= last_message_queued()) != NULL
){
3277 pf_err
->writehdr
= 1;
3278 pf_err
->localcopy
= 1;
3279 pf_err
->textbuf
= lmq
;
3283 * if reply, write (UID)folder header field so we can
3284 * later flag the replied-to message \\ANSWERED
3285 * DON'T save MSGNO's.
3287 if(reply
&& reply
->uid
){
3288 char uidbuf
[MAILTMPLEN
], *p
;
3291 for(i
= 0L, p
= tmp_20k_buf
; reply
->data
.uid
.msgs
[i
]; i
++){
3293 sstrncpy(&p
, ",", SIZEOF_20KBUF
-(p
-tmp_20k_buf
));
3295 sstrncpy(&p
,ulong2string(reply
->data
.uid
.msgs
[i
]),SIZEOF_20KBUF
-(p
-tmp_20k_buf
));
3298 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
3300 pf_uid
->writehdr
= 1;
3301 pf_uid
->localcopy
= 1;
3302 snprintf(uidbuf
, sizeof(uidbuf
), "(%s%s%s)(%ld %lu %s)%s",
3303 reply
->prefix
? int2string(strlen(reply
->prefix
))
3304 : (reply
->forwarded
) ? "": "0 ",
3305 reply
->prefix
? " " : "",
3306 reply
->prefix
? reply
->prefix
: "",
3307 i
, reply
->data
.uid
.validity
,
3308 tmp_20k_buf
, reply
->mailbox
);
3309 uidbuf
[sizeof(uidbuf
)-1] = '\0';
3310 pf_uid
->textbuf
= cpystr(uidbuf
);
3313 * Logically, this ought to be part of pf_uid, but this
3314 * was added later and so had to be in a separate header
3315 * for backwards compatibility.
3317 pf_mbox
->writehdr
= 1;
3318 pf_mbox
->localcopy
= 1;
3319 pf_mbox
->textbuf
= cpystr(reply
->origmbox
3324 /* Save cursor position */
3325 if(start_here_name
&& *start_here_name
){
3326 char curposbuf
[MAILTMPLEN
];
3328 pf_curpos
->writehdr
= 1;
3329 pf_curpos
->localcopy
= 1;
3330 snprintf(curposbuf
, sizeof(curposbuf
), "%s %ld", start_here_name
,
3332 curposbuf
[sizeof(curposbuf
)-1] = '\0';
3333 pf_curpos
->textbuf
= cpystr(curposbuf
);
3337 * Work around c-client reply-to bug. C-client will
3338 * return a reply_to in an envelope even if there is
3339 * no reply-to header field. We want to note here whether
3340 * the reply-to is real or not.
3342 if(outgoing
->reply_to
|| hdr_is_in_list("reply-to", custom
)){
3343 pf_ourrep
->writehdr
= 1;
3344 pf_ourrep
->localcopy
= 1;
3345 if(outgoing
->reply_to
)
3346 pf_ourrep
->textbuf
= cpystr("Full");
3348 pf_ourrep
->textbuf
= cpystr("Empty");
3351 /* Save the role-specific smtp server */
3352 if(role
&& role
->smtp
&& role
->smtp
[0]){
3353 char *q
, *smtp
= NULL
;
3358 * Turn the list of smtp servers into a space-
3359 * delimited list in a single string.
3361 for(lp
= role
->smtp
; (q
= *lp
) != NULL
; lp
++)
3362 len
+= (strlen(q
) + 1);
3365 smtp
= (char *) fs_get(len
* sizeof(char));
3367 for(lp
= role
->smtp
; (q
= *lp
) != NULL
; lp
++){
3368 if(lp
!= role
->smtp
)
3369 strncat(smtp
, " ", len
-strlen(smtp
)-1);
3371 strncat(smtp
, q
, len
-strlen(smtp
)-1);
3377 pf_smtp_server
->writehdr
= 1;
3378 pf_smtp_server
->localcopy
= 1;
3380 pf_smtp_server
->textbuf
= smtp
;
3382 pf_smtp_server
->textbuf
= cpystr("");
3385 /* Save the role-specific nntp server */
3386 if(suggested_nntp_server
||
3387 (role
&& role
->nntp
&& role
->nntp
[0])){
3388 char *q
, *nntp
= NULL
;
3392 if(role
&& role
->nntp
&& role
->nntp
[0]){
3394 * Turn the list of nntp servers into a space-
3395 * delimited list in a single string.
3397 for(lp
= role
->nntp
; (q
= *lp
) != NULL
; lp
++)
3398 len
+= (strlen(q
) + 1);
3401 nntp
= (char *) fs_get(len
* sizeof(char));
3403 for(lp
= role
->nntp
; (q
= *lp
) != NULL
; lp
++){
3404 if(lp
!= role
->nntp
)
3405 strncat(nntp
, " ", len
-strlen(nntp
)-1);
3407 strncat(nntp
, q
, len
-strlen(nntp
)-1);
3414 nntp
= cpystr(suggested_nntp_server
);
3416 pf_nntp_server
->writehdr
= 1;
3417 pf_nntp_server
->localcopy
= 1;
3419 pf_nntp_server
->textbuf
= nntp
;
3421 pf_nntp_server
->textbuf
= cpystr("");
3425 * Write the list of custom headers to the
3426 * X-Our-Headers header so that we can recover the
3430 for(pf
= header
.custom
; pf
&& pf
->name
; pf
= pf
->next
)
3431 sz
+= strlen(pf
->name
) + 1;
3436 pf_ourhdrs
->writehdr
= 1;
3437 pf_ourhdrs
->localcopy
= 1;
3438 pf_ourhdrs
->textbuf
= (char *)fs_get(sz
);
3439 memset(pf_ourhdrs
->textbuf
, 0, sz
);
3440 q
= pf_ourhdrs
->textbuf
;
3441 for(pf
= header
.custom
; pf
&& pf
->name
; pf
= pf
->next
){
3442 if(pf
!= header
.custom
)
3443 sstrncpy(&q
, ",", sz
-(q
-pf_ourhdrs
->textbuf
));
3445 sstrncpy(&q
, pf
->name
, sz
-(q
-pf_ourhdrs
->textbuf
));
3448 pf_ourhdrs
->textbuf
[sz
-1] = '\0';;
3452 * We need to make sure any header values that got cleared
3453 * get written to the postponed message (they won't if
3454 * pf->text is NULL). Otherwise, we can't tell previously
3455 * non-existent custom headers or default values from
3456 * custom (or other) headers that got blanked in the
3459 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
3460 if(pf
->type
== FreeText
&& HE(pf
) && !*(HE(pf
)->realaddr
))
3461 *(HE(pf
)->realaddr
) = cpystr("");
3464 * We're saving the message for use later. It may be that the
3465 * characters in the message are not all convertible to the
3466 * user's posting_charmap. We'll save it as UTF-8 instead
3467 * and worry about that the next time they try to send it.
3468 * Use a different save pointer just to be sure we don't
3469 * mess up the other stuff. We should probably make the
3470 * charset an argument.
3472 * We also need to fix the charset of the body part
3473 * the user is editing so that we can read it back
3474 * successfully when we resume the composition.
3476 ps_global
->post_utf8
= 1;
3481 if((*body
)->type
== TYPEMULTIPART
)
3482 bp
= &(*body
)->nested
.part
->body
;
3486 for(pm
= bp
->parameter
;
3487 pm
&& strucmp(pm
->attribute
, "charset") != 0;
3493 fs_give((void **) &pm
->value
);
3495 pm
->value
= cpystr("UTF-8");
3499 if(pine_rfc822_output(&header
,*body
,NULL
,NULL
) >= 0L){
3500 if(editor_result
& (COMP_GOTHUP
| COMP_CANCEL
)){
3506 if(editor_result
& COMP_CANCEL
){
3507 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
3508 "Saving to \"%s\"", folder
);
3509 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
3510 we_cancel
= busy_cue((char *)tmp_20k_buf
, NULL
, 1);
3514 so_get(FileStar
, folder
, WRITE_ACCESS
|OWNER_ONLY
)) != NULL
){
3515 gf_set_so_readc(&gc
, lmc
.so
);
3516 gf_set_so_writec(&pc
, hup_so
);
3517 so_seek(lmc
.so
, 0L, 0); /* read msg copy and */
3518 so_seek(hup_so
, 0L, 2); /* append to folder */
3520 gf_link_filter(gf_nvtnl_local
, NULL
);
3521 if(!(fcc_result
= !(err
= gf_pipe(gc
, pc
))))
3522 dprint((1, "*** PIPE FAILED: %s\n",
3525 gf_clear_so_readc(lmc
.so
);
3526 gf_clear_so_writec(hup_so
);
3530 dprint((1, "*** CAN'T CREATE %s: %s\n",
3531 folder
? folder
: "?",
3532 error_description(errno
)));
3535 cancel_busy_cue(-1);
3538 fcc_result
= write_fcc(folder
, fcc_cntxt
,
3539 lmc
.so
, NULL
, label
, NULL
);
3542 /* discontinue coerced UTF-8 posting */
3543 ps_global
->post_utf8
= 0;
3548 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3549 error_description(errno
)));
3551 if(editor_result
& COMP_GOTHUP
){
3553 * Special Hack #291: if any hi-byte bits are set in
3554 * editor's result, we put them there.
3556 if(editor_result
& 0xff00)
3557 exit(editor_result
>> 8);
3559 dprint((1, "Save composition on HUP %sED\n",
3560 fcc_result
? "SUCCEED" : "FAIL"));
3561 hup_signal(); /* Do what we normally do on SIGHUP */
3563 else if((editor_result
& COMP_SUSPEND
) && fcc_result
){
3564 if(ps_global
->VAR_FORM_FOLDER
3565 && ps_global
->VAR_FORM_FOLDER
[0]
3566 && !strcmp(folder
, ps_global
->VAR_FORM_FOLDER
))
3567 q_status_message(SM_ORDER
, 0, 3,
3568 _("Composition saved to Form Letter Folder. Select Compose to send."));
3570 q_status_message(SM_ORDER
, 0, 3,
3571 _("Composition postponed. Select Compose to resume."));
3573 break; /* postpone went OK, get out of here */
3575 else if(editor_result
& COMP_CANCEL
){
3578 if(fcc_result
&& folder
)
3579 lc
= last_cmpnt(folder
);
3581 q_status_message3(SM_ORDER
, 0, 3,
3582 _("Message cancelled%s%s%s"),
3583 (lc
&& *lc
) ? " and copied to \"" : "",
3584 (lc
&& *lc
) ? lc
: "",
3585 (lc
&& *lc
) ? "\" file" : "");
3589 q_status_message(SM_ORDER
, 0, 4,
3590 _("Continuing composition. Message not postponed or sent"));
3592 continue; /* postpone failed, jump back in to composer */
3596 /*------ Must be sending mail or posting ! -----*/
3597 int result
, valid_addr
, continue_with_only_fcc
= 0;
3598 CONTEXT_S
*fcc_cntxt
= NULL
;
3601 dprint((4, "=== sending: "));
3603 /* --- If posting, confirm with user ----*/
3604 if(outgoing
->newsgroups
&& *outgoing
->newsgroups
3605 && F_OFF(F_QUELL_EXTRA_POST_PROMPT
, ps_global
)
3606 && want_to(POST_PMT
, 'n', 'n', NO_HELP
, WT_NORM
) == 'n'){
3607 q_status_message(SM_ORDER
, 0, 3, _("Message not posted"));
3608 dprint((4, "no post, continuing\n"));
3612 if(!(outgoing
->to
|| outgoing
->cc
|| outgoing
->bcc
|| lcc_addr
3613 || outgoing
->newsgroups
)){
3615 if(F_OFF(F_AUTO_FCC_ONLY
, ps_global
) &&
3616 want_to(_("No recipients, really copy only to Fcc "),
3617 'n', 'n', h_send_fcc_only
, WT_NORM
) != 'y')
3620 continue_with_only_fcc
++;
3623 q_status_message(SM_ORDER
, 3, 4,
3624 _("No recipients specified!"));
3625 dprint((4, "no recip, continuing\n"));
3630 if((valid_addr
= check_addresses(&header
)) == CA_BAD
){
3631 /*--- Addresses didn't check out---*/
3632 dprint((4, "addrs failed, continuing\n"));
3636 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC
, ps_global
)
3637 && !continue_with_only_fcc
3638 && !(outgoing
->to
|| outgoing
->cc
|| lcc_addr
3639 || outgoing
->newsgroups
)
3640 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3641 'n', 'n', h_send_check_to_cc
, WT_NORM
) != 'y')){
3642 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3643 if(local_redraft_pos
&& local_redraft_pos
!= redraft_pos
)
3644 free_redraft_pos(&local_redraft_pos
);
3647 = (REDRAFT_POS_S
*) fs_get(sizeof(*local_redraft_pos
));
3648 memset((void *) local_redraft_pos
,0,sizeof(*local_redraft_pos
));
3649 local_redraft_pos
->hdrname
= cpystr(TONAME
);
3653 if(F_ON(F_WARN_ABOUT_NO_SUBJECT
, ps_global
)
3654 && check_for_subject(&header
) == CF_MISSING
){
3655 dprint((4, "No subject, continuing\n"));
3656 if(local_redraft_pos
&& local_redraft_pos
!= redraft_pos
)
3657 free_redraft_pos(&local_redraft_pos
);
3660 = (REDRAFT_POS_S
*) fs_get(sizeof(*local_redraft_pos
));
3661 memset((void *) local_redraft_pos
,0,sizeof(*local_redraft_pos
));
3662 local_redraft_pos
->hdrname
= cpystr(SUBJNAME
);
3666 if(F_ON(F_WARN_ABOUT_NO_FCC
, ps_global
)
3667 && check_for_fcc(fcc
) == CF_MISSING
){
3668 dprint((4, "No fcc, continuing\n"));
3669 if(local_redraft_pos
&& local_redraft_pos
!= redraft_pos
)
3670 free_redraft_pos(&local_redraft_pos
);
3673 = (REDRAFT_POS_S
*) fs_get(sizeof(*local_redraft_pos
));
3674 memset((void *) local_redraft_pos
,0,sizeof(*local_redraft_pos
));
3675 local_redraft_pos
->hdrname
= cpystr("Fcc");
3681 /*---- Check out fcc -----*/
3684 * If special name "inbox" then replace it with the
3687 if(ps_global
->VAR_INBOX_PATH
3688 && strucmp(fcc
, ps_global
->inbox_name
) == 0){
3691 replace_fcc
= cpystr(ps_global
->VAR_INBOX_PATH
);
3692 fs_give((void **)&fcc
);
3696 lmc
.all_written
= lmc
.text_written
= 0;
3697 /* lmc.text_only set on command line */
3698 if(!(lmc
.so
= open_fcc(fcc
, &fcc_cntxt
, 0, NULL
, NULL
))){
3699 /* ---- Open or allocation of fcc failed ----- */
3700 dprint((4,"can't open/allocate fcc, cont'g\n"));
3703 * Find field entry associated with fcc, and start
3706 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
3707 if(pf
->type
== Fcc
&& HE(pf
))
3708 HE(pf
)->start_here
= 1;
3713 so_truncate(lmc
.so
, fcc_size_guess(*body
) + 2048);
3718 /*---- Take care of any requested prefiltering ----*/
3719 if(sending_filter_requested
3720 && !filter_message_text(sending_filter_requested
, outgoing
,
3721 *body
, &orig_so
, &header
)){
3722 q_status_message1(SM_ORDER
, 3, 3,
3723 _("Problem filtering! Nothing sent%s."),
3724 fcc
? " or saved to fcc" : "");
3728 /*------ Actually post -------*/
3729 if(outgoing
->newsgroups
){
3730 char **alt_nntp
= NULL
, *alt_nntp_p
[2];
3731 if(((role
&& role
->nntp
)
3732 || suggested_nntp_server
)){
3733 if(ps_global
->FIX_NNTP_SERVER
3734 && ps_global
->FIX_NNTP_SERVER
[0])
3735 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
3736 "Using nntp-server that is administratively fixed");
3737 else if(role
&& role
->nntp
)
3738 alt_nntp
= role
->nntp
;
3740 alt_nntp_p
[0] = suggested_nntp_server
;
3741 alt_nntp_p
[1] = NULL
;
3742 alt_nntp
= alt_nntp_p
;
3745 if(news_poster(&header
, *body
, alt_nntp
, pipe_callback
) < 0){
3746 dprint((1, "Post failed, continuing\n"));
3747 if(outgoing
->message_id
)
3748 fs_give((void **) &outgoing
->message_id
);
3750 outgoing
->message_id
= generate_message_id();
3755 result
|= P_NEWS_WIN
;
3759 * BUG: IF we've posted the message *and* an fcc was specified
3760 * then we've already got a neatly formatted message in the
3761 * lmc.so. It'd be nice not to have to re-encode everything
3762 * to insert it into the smtp slot...
3766 * Turn on "undisclosed recipients" header if no To or cc.
3768 if(!(outgoing
->to
|| outgoing
->cc
)
3769 && (outgoing
->bcc
|| lcc_addr
) && pf_nobody
&& pf_nobody
->addr
){
3770 pf_nobody
->writehdr
= 1;
3771 pf_nobody
->localcopy
= 1;
3774 if(priority_requested
){
3775 (void) set_priority_header(&header
, priority_requested
);
3776 fs_give((void **) &priority_requested
);
3779 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3781 * If requested, launch backgroud posting...
3783 if(background_requested
&& !(call_mailer_flags
& CM_VERBOSE
)){
3784 ps_global
->post
= (POST_S
*)fs_get(sizeof(POST_S
));
3785 memset(ps_global
->post
, 0, sizeof(POST_S
));
3787 ps_global
->post
->fcc
= cpystr(fcc
);
3789 if((ps_global
->post
->pid
= fork()) == 0){
3791 * Put us in new process group...
3793 setpgrp(0, ps_global
->post
->pid
);
3795 /* BUG: should fix argv[0] to indicate what we're up to */
3798 * If there are any live streams, pretend we never
3799 * knew them. Problem is two processes writing
3800 * same server process.
3801 * This is not clean but we're just going to exit
3802 * right away anyway. We just want to be sure to leave
3803 * the stuff that the parent is going to use alone.
3804 * The next three lines will disable the re-use of the
3805 * existing streams and cause us to open a new one if
3808 ps_global
->mail_stream
= NULL
;
3809 ps_global
->s_pool
.streams
= NULL
;
3810 ps_global
->s_pool
.nstream
= 0;
3812 /* quell any display output */
3813 ps_global
->in_init_seq
= 1;
3815 /*------- Actually mail the message ------*/
3816 if(valid_addr
== CA_OK
3817 && (outgoing
->to
|| outgoing
->cc
3818 || outgoing
->bcc
|| lcc_addr
)){
3819 char **alt_smtp
= NULL
;
3821 if(role
&& role
->smtp
){
3822 if(ps_global
->FIX_SMTP_SERVER
3823 && ps_global
->FIX_SMTP_SERVER
[0])
3824 q_status_message(SM_ORDER
| SM_DING
, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3826 alt_smtp
= role
->smtp
;
3829 result
|= (call_mailer(&header
, *body
, alt_smtp
,
3831 call_mailer_file_result
,
3833 ? P_MAIL_WIN
: P_MAIL_LOSE
;
3835 if(result
& P_MAIL_LOSE
)
3836 mark_address_failure_for_pico(&header
);
3839 /*----- Was there an fcc involved? -----*/
3841 /*------ Write it if at least something worked ------*/
3842 if((result
& (P_MAIL_WIN
| P_NEWS_WIN
))
3843 || (!(result
& (P_MAIL_BITS
| P_NEWS_BITS
))
3844 && pine_rfc822_output(&header
, *body
,
3848 strncpy(label
, "Fcc", sizeof(label
));
3849 label
[sizeof(label
)-1] = '\0';
3850 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
)){
3851 snprintf(label
+ 3, sizeof(label
)-3, " to %s", fcc
);
3852 label
[sizeof(label
)-1] = '\0';
3855 /*-- Now actually copy to fcc folder and close --*/
3856 result
|= (write_fcc(fcc
, fcc_cntxt
, lmc
.so
,
3858 F_ON(F_MARK_FCC_SEEN
, ps_global
)
3860 ? P_FCC_WIN
: P_FCC_LOSE
;
3862 else if(!(result
& (P_MAIL_BITS
| P_NEWS_BITS
))){
3863 q_status_message(SM_ORDER
, 3, 5,
3864 _("Fcc Failed!. No message saved."));
3866 "explicit fcc write failed!\n"));
3867 result
|= P_FCC_LOSE
;
3873 if(result
& (P_MAIL_LOSE
| P_NEWS_LOSE
| P_FCC_LOSE
)){
3875 * Encode child's result in hi-byte of
3878 editor_result
= ((result
<< 8) | COMP_GOTHUP
);
3885 if(ps_global
->post
->pid
> 0){
3886 q_status_message(SM_ORDER
, 3, 3,
3887 _("Message handed off for posting"));
3888 break; /* up to our child now */
3891 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
3892 "Can't fork for send: %s",
3893 error_description(errno
));
3894 if(ps_global
->post
->fcc
)
3895 fs_give((void **) &ps_global
->post
->fcc
);
3897 fs_give((void **) &ps_global
->post
);
3900 if(lmc
.so
) /* throw away unused store obj */
3903 if(outgoing
->message_id
)
3904 fs_give((void **) &outgoing
->message_id
);
3906 outgoing
->message_id
= generate_message_id();
3908 continue; /* if we got here, there was a prob */
3910 #endif /* BACKGROUND_POST */
3912 /*------- Actually mail the message ------*/
3913 if(valid_addr
== CA_OK
3914 && (outgoing
->to
|| outgoing
->cc
|| outgoing
->bcc
|| lcc_addr
)){
3915 char **alt_smtp
= NULL
;
3917 if(role
&& role
->smtp
){
3918 if(ps_global
->FIX_SMTP_SERVER
3919 && ps_global
->FIX_SMTP_SERVER
[0])
3920 q_status_message(SM_ORDER
| SM_DING
, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3922 alt_smtp
= role
->smtp
;
3925 result
|= (call_mailer(&header
, *body
, alt_smtp
,
3927 call_mailer_file_result
,
3929 ? P_MAIL_WIN
: P_MAIL_LOSE
;
3931 if(result
& P_MAIL_LOSE
)
3932 mark_address_failure_for_pico(&header
);
3935 /*----- Was there an fcc involved? -----*/
3937 /*------ Write it if at least something worked ------*/
3938 if((result
& (P_MAIL_WIN
| P_NEWS_WIN
))
3939 || (!(result
& (P_MAIL_BITS
| P_NEWS_BITS
))
3940 && pine_rfc822_output(&header
, *body
, NULL
, NULL
))){
3943 strncpy(label
, "Fcc", sizeof(label
));
3944 label
[sizeof(label
)-1] = '\0';
3945 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
)){
3946 snprintf(label
+ 3, sizeof(label
)-3, " to %s", fcc
);
3947 label
[sizeof(label
)-1] = '\0';
3950 /*-- Now actually copy to fcc folder and close --*/
3951 result
|= (write_fcc(fcc
, fcc_cntxt
, lmc
.so
, NULL
, label
,
3952 F_ON(F_MARK_FCC_SEEN
, ps_global
)
3954 ? P_FCC_WIN
: P_FCC_LOSE
;
3956 else if(!(result
& (P_MAIL_BITS
| P_NEWS_BITS
))){
3957 q_status_message(SM_ORDER
,3,5,
3958 _("Fcc Failed!. No message saved."));
3959 dprint((1, "explicit fcc write failed!\n"));
3960 result
|= P_FCC_LOSE
;
3966 /*----- Mail Post FAILED, back to composer -----*/
3967 if(result
& (P_MAIL_LOSE
| P_FCC_LOSE
)){
3968 dprint((1, "Send failed, continuing\n"));
3970 if(result
& P_FCC_LOSE
){
3972 * Find field entry associated with fcc, and start
3975 for(pf
= header
.local
; pf
&& pf
->name
; pf
= pf
->next
)
3976 if(pf
->type
== Fcc
&& HE(pf
))
3977 HE(pf
)->start_here
= 1;
3979 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
3980 pine_send_status(result
, fcc
,
3981 tmp_20k_buf
, SIZEOF_20KBUF
, NULL
));
3984 if(outgoing
->message_id
)
3985 fs_give((void **) &outgoing
->message_id
);
3987 outgoing
->message_id
= generate_message_id();
3993 * If message sent *completely* successfully, there's a
3994 * reply struct AND we're allowed to write back state, do it.
3995 * But also protect against shifted message numbers due
3996 * to new mail arrival. Since the number passed is based
3997 * on the real imap msg no, AND we're sure no expunge has
3998 * been done, just fix up the sorted number...
4000 update_answered_flags(reply
);
4002 /*----- Signed, sealed, delivered! ------*/
4003 q_status_message(SM_ORDER
, 0, 3,
4004 pine_send_status(result
, fcc
, tmp_20k_buf
, SIZEOF_20KBUF
, NULL
));
4006 break; /* All's well, pop out of here */
4014 fs_give((void **)&fcc
);
4016 free_body_particulars(bp
);
4018 free_attachment_list(&pbf
->attachments
);
4020 standard_picobuf_teardown(pbf
);
4022 for(i
=0; i
< fixed_cnt
; i
++){
4023 if(pfields
[i
].textbuf
)
4024 fs_give((void **)&pfields
[i
].textbuf
);
4026 fs_give((void **)&pfields
[i
].name
);
4030 mail_free_address(&lcc_addr
);
4033 mail_free_address(&nobody_addr
);
4035 free_prompts(header
.custom
);
4036 free_customs(header
.custom
);
4037 fs_give((void **)&pfields
);
4038 free_headents(&headents
);
4039 fs_give((void **)&sending_order
);
4040 if(suggested_nntp_server
)
4041 fs_give((void **)&suggested_nntp_server
);
4043 fs_give((void **)&title
);
4045 if(local_redraft_pos
&& local_redraft_pos
!= redraft_pos
)
4046 free_redraft_pos(&local_redraft_pos
);
4048 pbf
= save_previous_pbuf
;
4051 dprint((4, "=== send returning ===\n"));
4056 * Check for subject in outgoing message.
4058 * Asks user whether to proceed with no subject.
4061 check_for_subject(METAENV
*header
)
4066 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
4067 if(pf
->type
== Subject
){
4068 if(pf
->text
&& *pf
->text
&& **pf
->text
)
4071 if(want_to("No Subject, send anyway ",
4072 'n', 'n', h_send_check_subj
, WT_NORM
) == 'y')
4087 * Check for fcc in outgoing message.
4089 * Asks user whether to proceed with no fcc.
4092 check_for_fcc(char *fcc
)
4099 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc
, WT_NORM
) == 'y')
4110 * Confirm that the user wants to send to MAILER-DAEMON
4113 confirm_daemon_send(void)
4115 return(want_to("Really send this message to the MAILER-DAEMON",
4116 'n', 'n', NO_HELP
, WT_NORM
) == 'y');
4121 free_prompts(PINEFIELD
*head
)
4125 for(pf
= head
; pf
&& pf
->name
; pf
= pf
->next
){
4126 if(HE(pf
) && HE(pf
)->prompt
)
4127 fs_give((void **)& HE(pf
)->prompt
);
4133 postpone_prompt(void)
4135 static ESCKEY_S pstpn_form_opt
[] = { {'p', 'p', "P", N_("Postponed Folder")},
4136 {'f', 'f', "F", N_("Form Letter Folder")},
4137 {-1, 0, NULL
, NULL
} };
4139 return(radio_buttons(PSTPN_FORM_PMT
, -FOOTER_ROWS(ps_global
),
4140 pstpn_form_opt
, 'p', 0, NO_HELP
, RB_FLUSH_IN
));
4145 * call__mailer_file_result - some results from call_mailer might be in a file.
4146 * dislplay that file.
4149 call_mailer_file_result(char *filename
, int style
)
4152 if(style
& CM_BR_VERBOSE
){
4153 display_output_file(filename
, "Verbose SMTP Interaction", NULL
, DOF_BRIEF
);
4156 display_output_file(filename
, "POSTING ERRORS", "Posting Error", DOF_EMPTY
);
4162 mark_address_failure_for_pico(METAENV
*header
)
4166 int error_count
= 0;
4167 struct headerentry
*last_he
= NULL
;
4169 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
4170 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
)
4171 for(a
= *pf
->addr
; a
!= NULL
; a
= a
->next
)
4173 && error_count
++ < MAX_ADDR_ERROR
4175 if(last_he
) /* start last reported err */
4176 last_he
->start_here
= 0;
4178 (last_he
= HE(pf
))->start_here
= 1;
4185 * This is specialized routine. It assumes that the only things that we
4186 * care about restoring are the body type, subtype, encoding and the
4187 * state of the charset parameter. It also assumes that if the charset
4188 * parameter exists when we save it, it won't be removed later.
4190 BODY_PARTICULARS_S
*
4191 save_body_particulars(struct mail_bodystruct
*body
)
4193 BODY_PARTICULARS_S
*bp
;
4196 bp
= (BODY_PARTICULARS_S
*)fs_get(sizeof(BODY_PARTICULARS_S
));
4198 bp
->type
= body
->type
;
4199 bp
->encoding
= body
->encoding
;
4200 bp
->subtype
= body
->subtype
? cpystr(body
->subtype
) : NULL
;
4201 bp
->parameter
= body
->parameter
;
4202 for(pm
= bp
->parameter
;
4203 pm
&& strucmp(pm
->attribute
, "charset") != 0;
4205 ;/* searching for possible charset parameter */
4207 if(pm
){ /* found one */
4208 bp
->had_csp
= 1; /* saved body had charset parameter */
4209 bp
->charset
= pm
->value
? cpystr(pm
->value
) : NULL
;
4221 reset_body_particulars(BODY_PARTICULARS_S
*bp
, struct mail_bodystruct
*body
)
4223 body
->type
= bp
->type
;
4224 body
->encoding
= bp
->encoding
;
4226 fs_give((void **)&body
->subtype
);
4228 body
->subtype
= bp
->subtype
? cpystr(bp
->subtype
) : NULL
;
4231 PARAMETER
*pm
, *pm_prev
= NULL
;
4233 for(pm
= body
->parameter
;
4234 pm
&& strucmp(pm
->attribute
, "charset") != 0;
4238 if(pm
){ /* body has charset parameter */
4239 if(bp
->had_csp
){ /* reset to what it used to be */
4241 fs_give((void **)&pm
->value
);
4243 pm
->value
= bp
->charset
? cpystr(bp
->charset
) : NULL
;
4245 else{ /* remove charset parameter */
4247 pm_prev
->next
= pm
->next
;
4249 body
->parameter
= pm
->next
;
4251 mail_free_body_parameter(&pm
);
4257 * This can't happen because grope never removes
4258 * the charset parameter.
4260 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
4261 "Programmer error: saved charset but no current charset param in pine_send");
4265 ok, still no parameter
4272 mail_free_body_parameter(&body
->parameter
);
4274 body
->parameter
= NULL
;
4280 free_body_particulars(BODY_PARTICULARS_S
*bp
)
4284 fs_give((void **)&bp
->subtype
);
4287 fs_give((void **)&bp
->charset
);
4289 fs_give((void **)&bp
);
4294 /*----------------------------------------------------------------------
4295 Build a status message suitable for framing
4297 Returns: pointer to resulting buffer
4300 pine_send_status(int result
, char *fcc_name
, char *buf
, size_t buflen
, int *goodorbad
)
4302 int avail
= ps_global
->ttyo
->screen_cols
- 2;
4303 int fixedneed
, need
, lenfcc
;
4304 char *part1
, *part2
, *part3
, *part4
, *part5
;
4305 char fbuf
[MAILTMPLEN
+1];
4307 part1
= (result
& P_NEWS_WIN
)
4309 : (result
& P_NEWS_LOSE
)
4312 part2
= ((result
& P_NEWS_BITS
) && (result
& P_MAIL_BITS
)
4313 && (result
& P_FCC_BITS
))
4315 : ((result
& P_NEWS_BITS
) && (result
& (P_MAIL_BITS
| P_FCC_BITS
)))
4318 part3
= (result
& P_MAIL_WIN
)
4320 : (result
& P_MAIL_LOSE
)
4323 part4
= ((result
& P_MAIL_BITS
) && (result
& P_FCC_BITS
))
4326 part5
= ((result
& P_FCC_WIN
) && !(result
& (P_MAIL_WIN
| P_NEWS_WIN
)))
4328 : (result
& P_FCC_WIN
)
4330 : (result
& P_FCC_LOSE
)
4333 lenfcc
= MIN(sizeof(fbuf
)-1, (result
& P_FCC_BITS
) ? strlen(fcc_name
) : 0);
4335 fixedneed
= 9 + strlen(part1
) + strlen(part2
) + strlen(part3
) +
4336 strlen(part4
) + strlen(part5
);
4337 need
= fixedneed
+ ((result
& P_FCC_BITS
) ? 2 : 0) + lenfcc
;
4339 if(need
> avail
&& fixedneed
+ 3 >= avail
){
4340 /* dots on end of fixed, no fcc */
4341 snprintf(fbuf
, sizeof(fbuf
), "Message %s%s%s%s%s ",
4342 part1
, part2
, part3
, part4
, part5
);
4343 short_str(fbuf
, buf
, buflen
, avail
, EndDots
);
4345 else if(need
> avail
){
4346 /* include whole fixed part, quotes and dots at end of fcc name */
4348 lenfcc
= MAX(1, lenfcc
-(need
-avail
));
4350 snprintf(buf
, buflen
, "Message %s%s%s%s%s%s%s%s.",
4351 part1
, part2
, part3
, part4
, part5
,
4352 (result
& P_FCC_BITS
) ? "\"" : "",
4353 short_str((result
& P_FCC_BITS
) ? fcc_name
: "",
4354 fbuf
, sizeof(fbuf
), lenfcc
, FrontDots
),
4355 (result
& P_FCC_BITS
) ? "\"" : "");
4359 snprintf(buf
, buflen
, "Message %s%s%s%s%s%s%s%s.",
4360 part1
, part2
, part3
, part4
, part5
,
4361 (result
& P_FCC_BITS
) ? "\"" : "",
4362 (result
& P_FCC_BITS
) ? fcc_name
: "",
4363 (result
& P_FCC_BITS
) ? "\"" : "");
4367 *goodorbad
= (result
& (P_MAIL_LOSE
| P_NEWS_LOSE
| P_FCC_LOSE
)) == 0;
4372 /* Callback from Pico to set the conditions for Alpine to start a new thread
4376 new_thread_on_blank_subject(void)
4378 ps_global
->newthread
= F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT
, ps_global
);
4383 /*----------------------------------------------------------------------
4384 Call back for pico to insert the specified message's text
4386 Args: n -- message number to format
4387 f -- function to use to output the formatted message
4390 Returns: returns msg number formatted on success, zero on error.
4393 message_format_for_pico(long int n
, int (*f
) (int))
4397 char *old_quote
= NULL
;
4400 if(!(n
> 0L && n
<= mn_get_total(ps_global
->msgmap
)
4401 && (e
= pine_mail_fetchstructure(ps_global
->mail_stream
,
4402 mn_m2raw(ps_global
->msgmap
, n
), &b
)))){
4403 q_status_message(SM_ORDER
|SM_DING
,3,3,"Error inserting Message");
4404 flush_status_messages(0);
4408 /* temporarily assign a new quote string */
4409 old_quote
= pbf
->quote_str
;
4410 pbf
->quote_str
= reply_quote_str(e
);
4412 /* build separator line */
4413 reply_delimiter(e
, NULL
, f
);
4415 /* actually write message text */
4416 if(!format_message(mn_m2raw(ps_global
->msgmap
, n
), e
, b
, NULL
,
4417 FM_NEW_MESS
| FM_DISPLAY
| FM_NOCOLOR
| FM_NOINDENT
, f
)){
4418 q_status_message(SM_ORDER
|SM_DING
,3,3,"Error inserting Message");
4419 flush_status_messages(0);
4423 fs_give((void **)&pbf
->quote_str
);
4424 pbf
->quote_str
= old_quote
;
4429 /*----------------------------------------------------------------------
4430 Call back for pico to prompt the user for exit confirmation
4432 Args: dflt -- default answer for confirmation prompt
4434 Returns: either NULL if the user accepts exit, or string containing
4435 reason why the user declined.
4438 send_exit_for_pico(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
4441 int i
, rv
, c
, verbose_label
= 0, bg_label
= 0, old_suspend
;
4442 int dsn_label
= 0, fcc_label
= 0, lparen
;
4443 int flowing_label
= 0, double_rad
;
4444 char *rstr
= NULL
, *p
, *lc
, *optp
;
4445 char dsn_string
[30];
4446 void (*redraw
)(void) = ps_global
->redrawer
;
4451 struct filters
*prev
, *next
;
4452 } *filters
= NULL
, *fp
;
4454 sending_filter_requested
= NULL
;
4455 call_mailer_flags
= 0;
4456 background_requested
= 0;
4457 flowing_requested
= allow_flowed
? 1 : 0;
4458 lmc
.text_only
= F_ON(F_NO_FCC_ATTACH
, ps_global
) != 0;
4459 if(priority_requested
)
4460 fs_give((void **) &priority_requested
);
4462 if(background_posting(FALSE
)){
4464 *result
= "Can't send while background posting. Use postpone.";
4469 if(F_ON(F_SEND_WO_CONFIRM
, ps_global
)){
4476 ps_global
->redrawer
= redraw_pico
;
4478 if((old_suspend
= F_ON(F_CAN_SUSPEND
, ps_global
)) != 0)
4479 (void) F_SET(F_CAN_SUSPEND
, ps_global
, 0);
4482 * Build list of available filters...
4484 for(i
=0; ps_global
->VAR_SEND_FILTER
&& ps_global
->VAR_SEND_FILTER
[i
]; i
++){
4485 for(p
= ps_global
->VAR_SEND_FILTER
[i
];
4486 *p
&& !isspace((unsigned char)*p
); p
++)
4491 if(!(is_absolute_path(ps_global
->VAR_SEND_FILTER
[i
])
4492 && can_access(ps_global
->VAR_SEND_FILTER
[i
],EXECUTE_ACCESS
) ==0)){
4497 fp
= (struct filters
*)fs_get(sizeof(struct filters
));
4499 if((lc
= last_cmpnt(ps_global
->VAR_SEND_FILTER
[i
])) != NULL
){
4500 fp
->filter
= cpystr(lc
);
4502 else if((p
- ps_global
->VAR_SEND_FILTER
[i
]) > 20){
4503 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "...%s", p
- 17);
4504 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
4505 fp
->filter
= cpystr(tmp_20k_buf
);
4508 fp
->filter
= cpystr(ps_global
->VAR_SEND_FILTER
[i
]);
4514 fp
->prev
= filters
->prev
;
4515 fp
->prev
->next
= filters
->prev
= fp
;
4518 filters
= (struct filters
*)fs_get(sizeof(struct filters
));
4519 filters
->index
= -1;
4520 filters
->filter
= NULL
;
4521 filters
->next
= filters
->prev
= fp
;
4522 fp
->next
= fp
->prev
= filters
;
4530 opts
[i
++].label
= N_("Yes");
4535 opts
[i
++].label
= N_("No");
4538 /* set global_filter_pointer to desired filter or NULL if none */
4539 /* prepare two keymenu slots for selecting filter */
4540 opts
[i
].ch
= ctrl('P');
4542 opts
[i
].name
= "^P";
4543 opts
[i
++].label
= N_("Prev Filter");
4545 opts
[i
].ch
= ctrl('N');
4547 opts
[i
].name
= "^N";
4548 opts
[i
++].label
= N_("Next Filter");
4550 if(F_ON(F_FIRST_SEND_FILTER_DFLT
, ps_global
))
4551 filters
= filters
->next
;
4554 if(F_ON(F_VERBOSE_POST
, ps_global
)){
4555 /* setup keymenu slot to toggle verbose mode */
4556 opts
[i
].ch
= ctrl('W');
4558 opts
[i
].name
= "^W";
4559 verbose_label
= i
++;
4563 /* setup keymenu slot to toggle flowed mode */
4564 opts
[i
].ch
= ctrl('V');
4566 opts
[i
].name
= "^V";
4567 flowing_label
= i
++;
4568 flowing_requested
= 1;
4571 if(F_ON(F_NO_FCC_ATTACH
, ps_global
)){
4572 /* setup keymenu slot to toggle attacment on fcc */
4573 opts
[i
].ch
= ctrl('F');
4575 opts
[i
].name
= "^F";
4579 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4580 if(F_ON(F_BACKGROUND_POST
, ps_global
)){
4581 opts
[i
].ch
= ctrl('R');
4583 opts
[i
].name
= "^R";
4591 opts
[i
++].label
= N_("Priority");
4594 if(F_OFF(F_DONT_DO_SMIME
, ps_global
)){
4598 opts
[i
++].label
= "Encrypt";
4603 opts
[i
++].label
= "Sign";
4605 if(ps_global
->smime
){
4606 ps_global
->smime
->do_encrypt
= F_ON(F_ENCRYPT_DEFAULT_ON
, ps_global
);
4607 ps_global
->smime
->do_sign
= F_ON(F_SIGN_DEFAULT_ON
, ps_global
);
4614 if(F_ON(F_DSN
, ps_global
)){
4615 /* setup keymenu slots to toggle dsn bits */
4619 opts
[i
].label
= N_("DSNOpts");
4624 opts
[i
++].label
= "";
4628 opts
[i
++].label
= "";
4632 opts
[i
++].label
= "";
4636 opts
[i
].ch
= KEY_UP
;
4639 opts
[i
++].label
= "";
4641 opts
[i
].ch
= KEY_DOWN
;
4644 opts
[i
++].label
= "";
4649 fix_windsize(ps_global
);
4652 if(filters
&& filters
->filter
&& (p
= strindex(filters
->filter
, ' ')))
4658 strncpy(tmp_20k_buf
, "Send message", SIZEOF_20KBUF
);
4659 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
4660 optp
= tmp_20k_buf
+ strlen(tmp_20k_buf
);
4662 if(F_ON(F_NO_FCC_ATTACH
, ps_global
) && !lmc
.text_only
)
4663 sstrncpy(&optp
, " and Fcc Atmts", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4665 if(allow_flowed
&& !flowing_requested
){
4666 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4676 sstrncpy(&optp
, "not flowed", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4680 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4692 if(filters
->filter
){
4693 sstrncpy(&optp
, "filtered thru \"", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4694 sstrncpy(&optp
, filters
->filter
, SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4695 if((optp
-tmp_20k_buf
) < SIZEOF_20KBUF
)
4699 sstrncpy(&optp
, "unfiltered", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4702 if((call_mailer_flags
& CM_VERBOSE
) || background_requested
){
4703 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4713 sstrncpy(&optp
, "in ", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4714 if(call_mailer_flags
& CM_VERBOSE
)
4715 sstrncpy(&optp
, "verbose ", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4717 if(background_requested
)
4718 sstrncpy(&optp
, "background ", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4720 sstrncpy(&optp
, "mode", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4723 if(g_rolenick
&& !(he
&& he
[N_FROM
].dirty
)){
4724 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4734 sstrncpy(&optp
, "as \"", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4735 sstrncpy(&optp
, g_rolenick
, SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4736 sstrncpy(&optp
, "\"", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4739 if(call_mailer_flags
& CM_DSN_SHOW
){
4740 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4752 sstrncpy(&optp
, dsn_string
, SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4756 if(ps_global
->smime
&& ps_global
->smime
->do_encrypt
){
4757 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4769 sstrncpy(&optp
, "Encrypted", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4772 if(ps_global
->smime
&& ps_global
->smime
->do_sign
){
4773 if((optp
-tmp_20k_buf
)+2 < SIZEOF_20KBUF
){
4785 sstrncpy(&optp
, "Signed", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4789 if(lparen
&& (optp
-tmp_20k_buf
) < SIZEOF_20KBUF
)
4792 sstrncpy(&optp
, "? ", SIZEOF_20KBUF
-(optp
-tmp_20k_buf
));
4793 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
4799 opts
[flowing_label
].label
= flowing_requested
? N_("NoFlow") : N_("Flow");
4802 opts
[verbose_label
].label
= (call_mailer_flags
& CM_VERBOSE
) ? N_("Normal") : N_("Verbose");
4805 opts
[bg_label
].label
= background_requested
4806 ? N_("Foreground") : N_("Background");
4809 opts
[fcc_label
].label
= lmc
.text_only
? N_("Fcc Attchmnts")
4810 : N_("No Fcc Atmts ");
4812 if(F_ON(F_DSN
, ps_global
)){
4813 if(call_mailer_flags
& CM_DSN_SHOW
){
4814 opts
[dsn_label
].label
= (call_mailer_flags
& CM_DSN_DELAY
)
4815 ? N_("NoDelay") : N_("Delay");
4816 opts
[dsn_label
+1].ch
= 's';
4817 opts
[dsn_label
+1].label
= (call_mailer_flags
& CM_DSN_SUCCESS
)
4818 ? N_("NoSuccess") : N_("Success");
4819 opts
[dsn_label
+2].ch
= 'x';
4820 opts
[dsn_label
+2].label
= (call_mailer_flags
& CM_DSN_NEVER
)
4821 ? N_("ErrRets") : N_("NoErrRets");
4822 opts
[dsn_label
+3].ch
= 'h';
4823 opts
[dsn_label
+3].label
= (call_mailer_flags
& CM_DSN_FULL
)
4824 ? N_("RetHdrs") : N_("RetFull");
4829 ((call_mailer_flags
& CM_DSN_SHOW
)
4830 ? 4 : F_ON(F_DSN
, ps_global
) ? 1 : 0) > 11)
4831 rv
= double_radio_buttons(tmp_20k_buf
, -FOOTER_ROWS(ps_global
), opts
,
4833 (F_ON(F_DSN
, ps_global
) && allow_flowed
)
4834 ? h_send_prompt_dsn_flowed
:
4835 F_ON(F_DSN
, ps_global
) ? h_send_prompt_dsn
:
4836 allow_flowed
? h_send_prompt_flowed
:
4840 rv
= radio_buttons(tmp_20k_buf
, -FOOTER_ROWS(ps_global
), opts
,
4843 ((call_mailer_flags
& CM_DSN_SHOW
)
4844 ? 4 : F_ON(F_DSN
, ps_global
) ? 1 : 0) == 11)
4846 (F_ON(F_DSN
, ps_global
) && allow_flowed
)
4847 ? h_send_prompt_dsn_flowed
:
4848 F_ON(F_DSN
, ps_global
) ? h_send_prompt_dsn
:
4849 allow_flowed
? h_send_prompt_flowed
:
4853 if(rv
== 'y'){ /* user ACCEPTS! */
4856 else if(rv
== 'n'){ /* Declined! */
4857 rstr
= _("No Message Sent");
4860 else if(rv
== 'z'){ /* Cancelled! */
4861 rstr
= _("Send Cancelled");
4864 else if(rv
== 10){ /* PREVIOUS filter */
4865 filters
= filters
->prev
;
4867 else if(rv
== 11){ /* NEXT filter */
4868 filters
= filters
->next
;
4871 lmc
.text_only
= !lmc
.text_only
;
4873 else if(rv
== 12){ /* flip verbose bit */
4874 if(call_mailer_flags
& CM_VERBOSE
)
4875 call_mailer_flags
&= ~CM_VERBOSE
;
4877 call_mailer_flags
|= CM_VERBOSE
;
4879 if((call_mailer_flags
& CM_VERBOSE
) && background_requested
)
4880 background_requested
= 0;
4882 else if(rv
== 22){ /* flip flowing bit */
4883 flowing_requested
= !flowing_requested
;
4886 if((background_requested
= !background_requested
)
4887 && (call_mailer_flags
& CM_VERBOSE
))
4888 call_mailer_flags
&= ~CM_VERBOSE
; /* clear verbose */
4890 else if(call_mailer_flags
& CM_DSN_SHOW
){
4891 if(rv
== 's'){ /* flip success bit */
4892 call_mailer_flags
^= CM_DSN_SUCCESS
;
4893 /* turn off related bits */
4894 if(call_mailer_flags
& CM_DSN_SUCCESS
)
4895 call_mailer_flags
&= ~(CM_DSN_NEVER
);
4897 else if(rv
== 'd'){ /* flip delay bit */
4898 call_mailer_flags
^= CM_DSN_DELAY
;
4899 /* turn off related bits */
4900 if(call_mailer_flags
& CM_DSN_DELAY
)
4901 call_mailer_flags
&= ~(CM_DSN_NEVER
);
4903 else if(rv
== 'x'){ /* flip never bit */
4904 call_mailer_flags
^= CM_DSN_NEVER
;
4905 /* turn off related bits */
4906 if(call_mailer_flags
& CM_DSN_NEVER
)
4907 call_mailer_flags
&= ~(CM_DSN_SUCCESS
| CM_DSN_DELAY
);
4909 else if(rv
== 'h'){ /* flip full bit */
4910 call_mailer_flags
^= CM_DSN_FULL
;
4913 else if(rv
== 'd'){ /* show dsn options */
4915 * When you turn on DSN, the default is to notify on
4916 * failure, success, or delay; and to return the whole
4919 call_mailer_flags
|= (CM_DSN_SHOW
| CM_DSN_SUCCESS
| CM_DSN_DELAY
| CM_DSN_FULL
);
4921 else if(rv
== 'p'){ /* choose X-Priority */
4924 prio
= choose_a_priority(priority_requested
);
4925 if((ps_global
->redrawer
= redraw_pico
) != NULL
){
4926 (*ps_global
->redrawer
)();
4927 fix_windsize(ps_global
);
4931 if(priority_requested
)
4932 fs_give((void **) &priority_requested
);
4935 priority_requested
= prio
;
4937 fs_give((void **) &prio
);
4942 if(ps_global
->smime
)
4943 ps_global
->smime
->do_encrypt
= !ps_global
->smime
->do_encrypt
;
4946 if(ps_global
->smime
)
4947 ps_global
->smime
->do_sign
= !ps_global
->smime
->do_sign
;
4951 snprintf(dsn_string
, sizeof(dsn_string
), "DSN requested[%s%s%s%s]",
4952 (call_mailer_flags
& CM_DSN_NEVER
)
4954 (call_mailer_flags
& CM_DSN_DELAY
)
4956 (call_mailer_flags
& CM_DSN_SUCCESS
)
4958 (call_mailer_flags
& CM_DSN_NEVER
)
4960 : (call_mailer_flags
& CM_DSN_FULL
) ? "-Full"
4962 dsn_string
[sizeof(dsn_string
)-1] = '\0';
4965 /* remember selection */
4966 if(filters
&& filters
->index
> -1)
4967 sending_filter_requested
= ps_global
->VAR_SEND_FILTER
[filters
->index
];
4970 filters
->prev
->next
= NULL
; /* tie off list */
4971 while(filters
){ /* then free it */
4974 fs_give((void **)&filters
->filter
);
4976 fs_give((void **)&filters
);
4982 (void) F_SET(F_CAN_SUSPEND
, ps_global
, 1);
4987 ps_global
->redrawer
= redraw
;
4989 return((rstr
== NULL
) ? 0 : 1);
4994 * Allow user to choose a priority for sending.
4996 * Returns an allocated priority on success, NULL otherwise.
4999 choose_a_priority(char *default_val
)
5001 char *choice
= NULL
;
5002 char **priority_list
, **lp
;
5003 char *starting_val
= NULL
;
5008 for(cnt
= 0, p
= priorities
; p
&& p
->desc
; p
++)
5011 cnt
++; /* for NONE entry */
5012 lp
= priority_list
= (char **) fs_get((cnt
+ 1) * sizeof(*priority_list
));
5013 memset(priority_list
, 0, (cnt
+1) * sizeof(*priority_list
));
5015 for(i
= 0, p
= priorities
; p
&& p
->desc
; p
++){
5016 *lp
= cpystr(p
->desc
);
5017 if(default_val
&& !strcmp(default_val
, p
->desc
))
5018 starting_val
= (*lp
);
5023 none
= _("NONE - No X-Priority header included");
5026 starting_val
= (*lp
);
5028 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5029 TRANSLATORS: Print something1 using something2.
5030 "priorities" is something1 */
5031 choice
= choose_item_from_list(priority_list
, NULL
, _("SELECT A PRIORITY"),
5032 _("priorities"), h_select_priority_screen
,
5033 _("HELP FOR SELECTING A PRIORITY"),
5037 q_status_message(SM_ORDER
, 1, 4, _("No change"));
5038 else if(!strcmp(choice
, none
))
5041 free_list_array(&priority_list
);
5048 dont_flow_this_time(void)
5050 return(flowing_requested
? 0 : 1);
5054 /*----------------------------------------------------------------------
5055 Call back for pico to display mime type of attachment
5057 Args: file -- filename being attached
5059 Returns: returns 1 on success (message queued), zero otherwise (don't know
5060 type so nothing queued).
5063 mime_type_for_pico(char *file
)
5067 void *file_contents
;
5069 body
= mail_newbody();
5070 body
->type
= TYPEOTHER
;
5071 body
->encoding
= ENCOTHER
;
5073 /* don't know where the cursor's been, reset it */
5075 if(!set_mime_type_by_extension(body
, file
)){
5076 if((file_contents
=(void *)so_get(FileStar
,file
,READ_ACCESS
|READ_FROM_LOCALE
)) != NULL
){
5077 body
->contents
.text
.data
= file_contents
;
5078 set_mime_type_by_grope(body
);
5082 if(body
->type
!= TYPEOTHER
){
5084 q_status_message3(SM_ORDER
, 0, 3,
5085 _("File %s attached as type %s/%s"), file
,
5086 body_types
[body
->type
],
5087 body
->subtype
? body
->subtype
: rfc822_default_subtype(body
->type
));
5092 pine_free_body(&body
);
5097 /*----------------------------------------------------------------------
5098 Call back for pico to receive an uploaded message
5100 Args: fname -- name for uploaded file (empty if they want us to assign it)
5101 size -- pointer to long to hold the attachment's size
5103 Notes: the attachment is uploaded to a temp file, and
5105 Returns: TRUE on success, FALSE otherwise
5108 upload_msg_to_pico(char *fname
, size_t fnlen
, long int *size
)
5110 char cmd
[MAXPATH
+1], *fnp
= NULL
;
5111 char *locale_name
= NULL
;
5115 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5116 fname
? fname
: "<NO FILE>"));
5118 if(!fname
) /* no place for file name */
5121 if(!*fname
){ /* caller wants temp file */
5122 if((fnp
= temp_nam(NULL
, "pu")) != NULL
){
5123 strncpy(fname
, fnp
, fnlen
);
5124 fname
[fnlen
-1] = '\0';
5126 fs_give((void **)&fnp
);
5130 locale_name
= convert_to_locale(fname
);
5132 build_updown_cmd(cmd
, sizeof(cmd
), ps_global
->VAR_UPLOAD_CMD_PREFIX
,
5133 ps_global
->VAR_UPLOAD_CMD
, locale_name
? locale_name
: fname
);
5134 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
, PIPE_USER
| PIPE_RESET
,
5135 0, pipe_callback
, pipe_report_error
)) != NULL
){
5136 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
5137 if((l
= name_file_size(locale_name
? locale_name
: fname
)) < 0L){
5138 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
5139 "Error determining size of %s: %s", fname
,
5140 fnp
= error_description(errno
));
5142 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5144 fname
? fname
: "?",
5151 fs_give((void **) &locale_name
);
5156 q_status_message(SM_ORDER
| SM_DING
, 3, 4, _("Error opening pipe"));
5159 fs_give((void **) &locale_name
);
5166 cancel_for_pico(void (*redraw_pico
)(void))
5171 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5172 void (*redraw
)(void) = ps_global
->redrawer
;
5173 static ESCKEY_S opts
[] = {
5174 {'c', 'c', "C", N_("Confirm")},
5175 {'n', 'n', "N", N_("No")},
5180 ps_global
->redrawer
= redraw_pico
;
5181 fix_windsize(ps_global
);
5184 rv
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), opts
,
5185 'n', 'x', h_confirm_cancel
, RB_NORM
);
5186 if(rv
== 'c'){ /* user ACCEPTS! */
5191 q_status_message(SM_INFO
, 1, 3, _(" Type \"C\" to cancel message "));
5192 display_message('x');
5198 ps_global
->redrawer
= redraw
;
5203 /*----------------------------------------------------------------------
5204 Pass the first text segment of the message thru the "send filter"
5206 Args: body pointer and address for storage object of old data
5208 Returns: returns 1 on success, zero on error.
5211 filter_message_text(char *fcmd
, ENVELOPE
*outgoing
, struct mail_bodystruct
*body
,
5212 STORE_S
**old
, METAENV
*header
)
5214 char *cmd
, *tmpf
= NULL
, *resultf
= NULL
, *errstr
= NULL
, *mtf
= NULL
;
5215 int key
= 0, include_hdrs
= 0;
5217 STORE_S
**so
= (STORE_S
**)((body
->type
== TYPEMULTIPART
)
5218 ? &body
->nested
.part
->body
.contents
.text
.data
5219 : &body
->contents
.text
.data
),
5220 *tmp_so
= NULL
, *tmpf_so
,
5221 *save_local_so
, *readthis_so
, *our_tmpf_so
= NULL
;
5222 #define DO_HEADERS 1
5225 && (cmd
=expand_filter_tokens(fcmd
, outgoing
, &tmpf
, &resultf
, &mtf
,
5226 &key
, &include_hdrs
, NULL
))){
5229 * We need WRITE_TO_LOCALE here because the user is going to
5230 * be operating on tmpf. We need to change it back after they
5233 if((tmpf_so
= so_get(FileStar
, tmpf
, EDIT_ACCESS
|OWNER_ONLY
|WRITE_TO_LOCALE
)) != NULL
){
5235 so_puts(tmpf_so
, filter_session_key());
5236 so_puts(tmpf_so
, NEWLINE
);
5240 * If the headers are wanted for filtering, we can just
5241 * stick them in the tmpf file that is already there before
5242 * putting the body in.
5245 save_local_so
= lmc
.so
;
5246 lmc
.so
= tmpf_so
; /* write it to tmpf_so */
5247 lmc
.all_written
= lmc
.text_written
= lmc
.text_only
= 0;
5248 pine_rfc822_header(header
, body
, NULL
, NULL
);
5249 lmc
.so
= save_local_so
;
5252 so_seek(*so
, 0L, 0);
5253 gf_set_so_readc(&gc
, *so
);
5254 gf_set_so_writec(&pc
, tmpf_so
);
5256 errstr
= gf_pipe(gc
, pc
);
5257 gf_clear_so_readc(*so
);
5258 gf_clear_so_writec(tmpf_so
);
5262 errstr
= "Can't create space for filter temporary file.";
5264 else if(include_hdrs
){
5266 * Gf_filter wants a single storage object to read from.
5267 * If headers are wanted for filtering we'll have to put them
5268 * and the body into a temp file first and then use that
5269 * as the storage object for gf_filter.
5270 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5271 * takes care of that.
5273 if((our_tmpf_so
= so_get(TmpFileStar
, NULL
, EDIT_ACCESS
|OWNER_ONLY
)) != NULL
){
5274 /* put headers in our_tmpf_so */
5275 save_local_so
= lmc
.so
;
5276 lmc
.so
= our_tmpf_so
; /* write it to our_tmpf_so */
5277 lmc
.all_written
= lmc
.text_written
= lmc
.text_only
= 0;
5278 pine_rfc822_header(header
, body
, NULL
, NULL
);
5279 lmc
.so
= save_local_so
;
5281 /* put body in our_tmpf_so */
5282 so_seek(*so
, 0L, 0);
5283 gf_set_so_readc(&gc
, *so
);
5284 gf_set_so_writec(&pc
, our_tmpf_so
);
5286 errstr
= gf_pipe(gc
, pc
);
5287 gf_clear_so_readc(*so
);
5288 gf_clear_so_writec(our_tmpf_so
);
5290 /* tell gf_filter to read from our_tmpf_so instead of *so */
5291 readthis_so
= our_tmpf_so
;
5294 errstr
= "Can't create space for temporary file.";
5300 if((tmp_so
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
5301 gf_set_so_writec(&pc
, tmp_so
);
5302 ps_global
->mangled_screen
= 1;
5309 if((fpipe
= open_system_pipe(cmd
, NULL
, NULL
,
5310 PIPE_NOSHELL
| PIPE_RESET
,
5311 0, pipe_callback
, pipe_report_error
)) != NULL
){
5312 if(close_system_pipe(&fpipe
, NULL
, pipe_callback
) == 0){
5314 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5315 if((tmpf_so
= so_get(FileStar
, tmpf
, READ_ACCESS
|READ_FROM_LOCALE
)) != NULL
){
5316 gf_set_so_readc(&gc
, tmpf_so
);
5318 errstr
= gf_pipe(gc
, pc
);
5319 gf_clear_so_readc(tmpf_so
);
5323 errstr
= "Can't open temp file filter wrote.";
5326 errstr
= "Filter command returned error.";
5329 errstr
= "Can't exec filter text.";
5332 errstr
= gf_filter(cmd
, key
? filter_session_key() : NULL
,
5333 readthis_so
, pc
, NULL
, 0, 0,
5337 so_give(&our_tmpf_so
);
5339 gf_clear_so_writec(tmp_so
);
5344 * Can't really be using stdout, so don't print message that
5345 * gets printed in the else. Ideally the program being called
5346 * will wait after showing the message, we might want to look
5347 * into doing the waiting in console based apps... or not.
5353 fprintf(stdout
, "\r\n%s Hit return to continue.", errstr
);
5355 while((ch
= read_char(300)) != ctrl('M')
5356 && ch
!= NO_OP_IDLE
)
5360 #endif /* _WINDOWS */
5361 BODY
*b
= (body
->type
== TYPEMULTIPART
)
5362 ? &body
->nested
.part
->body
: body
;
5364 *old
= *so
; /* save old so */
5365 *so
= tmp_so
; /* return new one */
5366 (*so
)->attr
= copy_parameters((*old
)->attr
);
5369 * If the command said it would return new MIME
5370 * mime type data, check it out...
5373 char buf
[MAILTMPLEN
], *s
;
5376 if((fp
= our_fopen(mtf
, "rb")) != NULL
){
5377 if(fgets(buf
, sizeof(buf
), fp
)
5378 && !struncmp(buf
, "content-", 8)
5379 && (s
= strchr(buf
+8, ':'))){
5380 BODY
*nb
= mail_newbody();
5382 for(*s
++ = '\0'; *s
== ' '; s
++)
5385 rfc822_parse_content_header(nb
,
5386 (char *) ucase((unsigned char *) buf
+8),s
);
5387 if(nb
->type
== TYPETEXT
5390 || strucmp(b
->subtype
, nb
->subtype
))){
5392 fs_give((void **) &b
->subtype
);
5394 b
->subtype
= nb
->subtype
;
5397 mail_free_body_parameter(&b
->parameter
);
5398 b
->parameter
= nb
->parameter
;
5399 nb
->parameter
= NULL
;
5400 mail_free_body_parameter(&nb
->parameter
);
5403 mail_free_body(&nb
);
5411 * Reevaluate the encoding in case form's changed...
5413 b
->encoding
= ENCOTHER
;
5414 set_mime_type_by_grope(b
);
5421 errstr
= "Can't create space for filtered text.";
5424 fs_give((void **)&cmd
);
5431 fs_give((void **)&tmpf
);
5436 fs_give((void **) &mtf
);
5440 if(name_file_size(resultf
) > 0L)
5441 display_output_file(resultf
, "Filter", NULL
, DOF_BRIEF
);
5442 our_unlink(resultf
);
5443 fs_give((void **)&resultf
);
5449 q_status_message1(SM_ORDER
| SM_DING
, 3, 6, _("Problem filtering: %s"),
5451 dprint((1, "Filter FAILED: %s\n",
5452 errstr
? errstr
: "?"));
5455 return(errstr
== NULL
);
5459 /*----------------------------------------------------------------------
5460 Copy the newsgroup name of the given mailbox into the given buffer
5467 pine_send_newsgroup_name(char *mailbox
, char *group_name
, size_t len
)
5471 if(*mailbox
== '#'){ /* Strip the leading "#news." */
5472 strncpy(group_name
, mailbox
+ 6, len
-1);
5473 group_name
[len
-1] = '\0';
5475 else if(mail_valid_net_parse(mailbox
, &mb
)){
5476 pine_send_newsgroup_name(mb
.mailbox
, group_name
, len
);
5483 /*----------------------------------------------------------------------
5484 Generate and send a message back to the pine development team
5491 phone_home(char *addr
)
5493 char tmp
[MAX_ADDRESS
];
5497 outgoing
= mail_newenvelope();
5498 if(!addr
|| !strindex(addr
, '@')){
5499 snprintf(addr
= tmp
, sizeof(tmp
), "alpine%s@%s", PHONE_HOME_VERSION
, PHONE_HOME_HOST
);
5500 tmp
[sizeof(tmp
)-1] = '\0';
5503 rfc822_parse_adrlist(&outgoing
->to
, addr
, ps_global
->maildomain
);
5505 outgoing
->message_id
= generate_message_id();
5506 outgoing
->subject
= cpystr("Document Request");
5507 outgoing
->from
= phone_home_from();
5509 body
= mail_newbody();
5510 body
->type
= TYPETEXT
;
5512 if((body
->contents
.text
.data
= (void *)so_get(PicoText
,NULL
,EDIT_ACCESS
)) != NULL
){
5513 so_puts((STORE_S
*)body
->contents
.text
.data
, "Document request: ");
5514 so_puts((STORE_S
*)body
->contents
.text
.data
, "Alpine-");
5515 so_puts((STORE_S
*)body
->contents
.text
.data
, ALPINE_VERSION
);
5516 if(ps_global
->first_time_user
)
5517 so_puts((STORE_S
*)body
->contents
.text
.data
, " for New Users");
5519 if(ps_global
->VAR_INBOX_PATH
&& ps_global
->VAR_INBOX_PATH
[0] == '{')
5520 so_puts((STORE_S
*)body
->contents
.text
.data
, " and IMAP");
5522 if(ps_global
->VAR_NNTP_SERVER
&& ps_global
->VAR_NNTP_SERVER
[0]
5523 && ps_global
->VAR_NNTP_SERVER
[0][0])
5524 so_puts((STORE_S
*)body
->contents
.text
.data
, " and NNTP");
5526 (void) pine_simple_send(outgoing
, &body
, NULL
,NULL
,NULL
,NULL
, SS_NULLRP
);
5528 q_status_message(SM_ORDER
, 1, 3, "Thanks for being counted!");
5531 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
5532 "Problem creating space for message text.");
5534 mail_free_envelope(&outgoing
);
5535 pine_free_body(&body
);
5540 /*----------------------------------------------------------------------
5541 Set up fields for passing to pico. Assumes first text part is
5542 intended to be passed along for editing, and is in the form of
5543 of a storage object brought into existence sometime before pico_send().
5546 outgoing2strings(METAENV
*header
, struct mail_bodystruct
*bod
, void **text
,
5547 PATMT
**pico_a
, int from_bounce
)
5552 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5553 * is guaranteed to be of type PicoText!
5555 if(bod
->type
== TYPETEXT
){
5556 *text
= so_text((STORE_S
*) bod
->contents
.text
.data
);
5558 /* mark storage object as user edited */
5560 (void) so_attr((STORE_S
*) bod
->contents
.text
.data
, "edited", "1");
5562 else if(bod
->type
== TYPEMULTIPART
){
5565 char *type
, *name
, *p
;
5569 * We used to jump out the window if the first part wasn't text,
5570 * but that may not be the case when bouncing a message with
5571 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5572 * contents of the first part to send is still ALWAYS in a
5573 * PicoText storage object, *AND* if that object doesn't contain
5574 * data of type text, then it must contain THE ENCODED NON-TEXT
5575 * DATA of the piece being sent.
5577 * It's up to the programmer to make sure that such a message is
5578 * sent via pine_simple_send and never get to the composer via
5583 *text
= so_text((STORE_S
*) bod
->nested
.part
->body
.contents
.text
.data
);
5585 /* mark storage object as user edited */
5587 (void) so_attr((STORE_S
*) bod
->nested
.part
->body
.contents
.text
.data
, "edited", "1");
5590 * If we already had a list, blast it now, so we can build a new
5591 * attachment list that reflects what's really there...
5594 free_attachment_list(pico_a
);
5597 /* Simplifyihg assumption #28e. (see cross reference)
5598 All parts in the body passed in here that are not already
5599 in the attachments list are added to the end of the attachments
5600 list. Attachment items not in the body list will be taken care
5601 of in strings2outgoing, but they are unlikey to occur
5604 for(part
= bod
->nested
.part
->next
; part
!= NULL
; part
= part
->next
) {
5605 /* Already in list? */
5607 *ppa
&& strcmp((*ppa
)->id
, part
->body
.id
);
5608 ppa
= &(*ppa
)->next
)
5611 if(!*ppa
){ /* Not in the list! append it... */
5612 *ppa
= (PATMT
*)fs_get(sizeof(PATMT
));
5614 if(part
->body
.description
){
5618 len
= 4*strlen(part
->body
.description
)+1;
5619 p
= (char *)fs_get(len
*sizeof(char));
5620 if(rfc1522_decode_to_utf8((unsigned char *)p
,
5621 len
, part
->body
.description
) == (unsigned char *) p
){
5622 (*ppa
)->description
= p
;
5625 fs_give((void **)&p
);
5626 (*ppa
)->description
= cpystr(part
->body
.description
);
5630 (*ppa
)->description
= cpystr("");
5632 type
= type_desc(part
->body
.type
, part
->body
.subtype
,
5633 part
->body
.parameter
, NULL
, 0);
5636 * If we can find a "name" parm, display that too...
5638 if((name
= parameter_val(part
->body
.parameter
, "name")) != NULL
){
5639 /* Convert any [ or ]'s the name contained */
5640 for(p
= name
; *p
; p
++)
5651 (*ppa
)->filename
= fs_get(strlen(type
) + name_l
+ 5);
5653 snprintf((*ppa
)->filename
, strlen(type
) + name_l
+ 5, "[%s%s%s]", type
,
5654 name
? ": " : "", name
? name
: "");
5655 (*ppa
)->filename
[strlen(type
) + name_l
+ 5 - 1] = '\0';
5658 fs_give((void **) &name
);
5660 (*ppa
)->flags
= A_FLIT
;
5661 (*ppa
)->size
= cpystr(byte_string(
5662 send_body_size(&part
->body
)));
5664 part
->body
.id
= generate_message_id();
5666 (*ppa
)->id
= cpystr(part
->body
.id
);
5667 (*ppa
)->next
= NULL
;
5674 /*------------------------------------------------------------------
5675 Malloc strings to pass to composer editor because it expects
5676 such strings so it can realloc them
5677 -----------------------------------------------------------------*/
5679 * turn any address fields into text strings
5682 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5683 * NOT to be RFC1522 decoded. Said differently, they're understood
5684 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5685 * original charset tagging as far into the compose/send pipe as
5688 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
5696 pf
->scratch
= addr_list_string(*pf
->addr
, NULL
, 1);
5699 * Scan for and fix-up patently bogus fields.
5701 * NOTE: collaboration with this code and what's done in
5702 * reply.c:reply_cp_addr to package up the bogus stuff
5705 for(p
= pf
->scratch
; (p
= strstr(p
, "@" RAWFIELD
)); )
5707 if(*t
== '&'){ /* find "leading" token */
5711 * Rfc822_cat has been changed so that it now quotes
5712 * this sometimes. So we have to look out for quotes
5713 * which confuse the decoder. It was only quoting
5714 * because we were putting \r \n in the input, I think.
5716 if(t
> pf
->scratch
&& t
[-1] == '\"' && p
[-1] == '\"')
5717 t
[-1] = p
[-1] = ' ';
5719 *t
++ = ' '; /* replace token */
5720 *p
= '\0'; /* tie off string */
5721 u
= rfc822_base64((unsigned char *) t
,
5722 (unsigned long) strlen(t
),
5723 (unsigned long *) &l
);
5727 replacelen
= strlen(t
);
5728 *p
= '@'; /* restore 'p' */
5729 rplstr(p
, strlen(p
), 12, ""); /* clear special token */
5730 rplstr(t
, strlen(u
)-replacelen
+1, replacelen
, u
);
5732 fs_give((void **) &u
);
5735 HE(pf
)->start_here
= 1;
5739 else if(t
== pf
->scratch
)
5742 removing_leading_white_space(pf
->scratch
);
5747 * Replace control characters with ^C notation, unless
5748 * some conditions are met (see istrncpy).
5749 * If user doesn't edit, then we switch back to the
5750 * original version. If user does edit, then all bets
5753 iutf8ncpy((char *)tmp_20k_buf
, pf
->scratch
, SIZEOF_20KBUF
-1);
5754 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
5755 if((l
=strlen((char *)tmp_20k_buf
)) > strlen(pf
->scratch
)){
5756 fs_give((void **)&pf
->scratch
);
5757 pf
->scratch
= (char *)fs_get((l
+1) * sizeof(char));
5760 strncpy(pf
->scratch
, (char *)tmp_20k_buf
, l
+1);
5771 src
= pf
->scratch
? pf
->scratch
5772 : (*pf
->text
) ? *pf
->text
: "";
5774 len
= 4*strlen(src
)+1;
5775 p
= (char *)fs_get(len
* sizeof(char));
5776 if(rfc1522_decode_to_utf8((unsigned char *)p
, len
, src
) == (unsigned char *) p
){
5778 fs_give((void **)&pf
->scratch
);
5783 fs_give((void **)&p
);
5785 pf
->scratch
= cpystr(src
);
5792 * Replace control characters with ^C notation, unless
5793 * some conditions are met (see istrncpy).
5794 * If user doesn't edit, then we switch back to the
5795 * original version. If user does edit, then all bets
5798 iutf8ncpy((char *)tmp_20k_buf
, pf
->scratch
, SIZEOF_20KBUF
-1);
5799 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
5800 if((l
=strlen((char *)tmp_20k_buf
)) > strlen(pf
->scratch
)){
5801 fs_give((void **)&pf
->scratch
);
5802 pf
->scratch
= (char *)fs_get((l
+1) * sizeof(char));
5805 strncpy(pf
->scratch
, (char *)tmp_20k_buf
, l
+1);
5817 /*----------------------------------------------------------------------
5818 Restore fields returned from pico to form useful to sending
5822 strings2outgoing(METAENV
*header
, struct mail_bodystruct
**bod
, PATMT
*attach
, int flow_it
)
5827 we_cancel
= busy_cue(NULL
, NULL
, 1);
5830 * turn any local address strings into address lists
5832 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
5834 char *the_address
= NULL
;
5838 removing_trailing_white_space(pf
->scratch
);
5840 if((the_address
|| *pf
->scratch
) && pf
->addr
){
5841 ADDRESS
*new_addr
= NULL
;
5842 static char *fakedomain
= "@";
5845 the_address
= pf
->scratch
;
5847 rfc822_parse_adrlist(&new_addr
, the_address
,
5848 (F_ON(F_COMPOSE_REJECTS_UNQUAL
, ps_global
))
5849 ? fakedomain
: ps_global
->maildomain
);
5850 mail_free_address(pf
->addr
); /* free old addrs */
5851 *pf
->addr
= new_addr
; /* assign new addr */
5854 mail_free_address(pf
->addr
); /* free old addrs */
5860 fs_give((void **)pf
->text
);
5863 *pf
->text
= cpystr(pf
->scratch
);
5872 fs_give((void **)&pf
->scratch
); /* free now useless text */
5875 create_message_body(bod
, attach
, flow_it
);
5878 cancel_busy_cue(-1);
5882 /*----------------------------------------------------------------------
5884 The head of the body list here is always either TEXT or MULTIPART. It may be
5885 changed from TEXT to MULTIPART if there are attachments to be added
5886 and it is not already multipart.
5889 create_message_body(struct mail_bodystruct
**b
, PATMT
*attach
, int flow_it
)
5893 BODY
*tmp_body
, *text_body
= NULL
;
5894 void *file_contents
;
5898 TIME_STAMP("create_body start.", 1);
5900 * if conditions are met short circuit MIME wrapping
5902 if((*b
)->type
!= TYPEMULTIPART
&& !attach
){
5904 /* only override assigned encoding if it might need upgrading */
5905 if((*b
)->type
== TYPETEXT
&& (*b
)->encoding
== ENC7BIT
)
5906 (*b
)->encoding
= ENCOTHER
;
5908 create_message_body_text(*b
, flow_it
);
5910 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE
, ps_global
)
5911 || !((*b
)->encoding
== ENC8BIT
5912 || (*b
)->encoding
== ENCBINARY
)){
5913 TIME_STAMP("create_body end.", 1);
5916 else /* protect 8bit in multipart */
5920 if((*b
)->type
== TYPETEXT
) {
5921 /*-- Current type is text, but there are attachments to add --*/
5922 /*-- Upgrade to a TYPEMULTIPART --*/
5923 tmp_body
= (BODY
*)mail_newbody();
5924 tmp_body
->type
= TYPEMULTIPART
;
5925 tmp_body
->nested
.part
= mail_newbody_part();
5928 * Why do we do this?
5929 * The problem is that base64 or quoted-printable encoding is
5930 * sensitive to having random data appended to it's end. If
5931 * we use a single part TEXT message and something in between
5932 * us and the end appends advertising without adjusting for
5933 * the encoding, the message is screwed up. So we wrap the
5934 * text part inside a multipart and then the appended data
5935 * will come after the boundary.
5937 * We wish we could do this on the way out the door in a
5938 * child of post_rfc822_output because at that point we know
5939 * the character set and the encoding being used. For example,
5940 * iso-2022-jp is an encoding that is not sensitive to data
5941 * appended to the end, so it wouldn't need to be wrapped.
5942 * We could conceivably have post_rfc822_body inspect the
5943 * body and change it before doing the output. It would work
5944 * but would be very fragile. We'd be passed a body from
5945 * c-client to output and instead of just doing the output
5946 * we'd change the body and then output it. Not worth it
5947 * since the multipart wrapping is completely correct for
5948 * MIME-aware mailers.
5950 (void) copy_body(&(tmp_body
->nested
.part
->body
), *b
);
5951 /* move contents which were NOT copied */
5952 tmp_body
->nested
.part
->body
.contents
.text
.data
= (*b
)->contents
.text
.data
;
5953 (*b
)->contents
.text
.data
= NULL
;
5956 tmp_body
->nested
.part
->body
= **b
;
5958 (*b
)->subtype
= (*b
)->id
= (*b
)->description
= NULL
;
5959 (*b
)->parameter
= NULL
;
5960 (*b
)->contents
.text
.data
= NULL
;
5968 /*-- Now type must be MULTIPART with first part text --*/
5969 (*b
)->nested
.part
->body
.encoding
= ENCOTHER
;
5970 create_message_body_text(&((*b
)->nested
.part
->body
), flow_it
);
5973 /*------ Go through the parts list remove those to be deleted -----*/
5974 for(pp
= &(*b
)->nested
.part
->next
; *pp
;){
5975 for(pa
= attach
; pa
&& (*pp
)->body
.id
; pa
= pa
->next
)
5976 /* already existed? */
5977 if(pa
->id
&& strcmp(pa
->id
, (*pp
)->body
.id
) == 0){
5978 char *orig_descp
= NULL
, *cs
= NULL
;
5981 * decode original to see if it matches what was decoded
5982 * when we sent it in.
5985 if((*pp
)->body
.description
)
5986 orig_descp
= (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
5987 SIZEOF_20KBUF
, (*pp
)->body
.description
);
5989 if(!(*pp
)->body
.description
/* update description? */
5990 || (pa
->description
&& strcmp(pa
->description
, orig_descp
))){
5991 if((*pp
)->body
.description
)
5992 fs_give((void **) &(*pp
)->body
.description
);
5994 /* encoding happens as msg text is written */
5995 (*pp
)->body
.description
= cpystr(pa
->description
);
5999 fs_give((void **) &cs
);
6005 p
= *pp
; /* prepare to zap *pp */
6006 *pp
= p
->next
; /* pull next one in list up */
6007 p
->next
= NULL
; /* tie off removed node */
6009 pine_free_body_data(&p
->body
); /* clean up contained data */
6010 mail_free_body_part(&p
); /* free up the part */
6016 /*---------- Now add any new attachments ---------*/
6017 for(p
= (*b
)->nested
.part
; p
->next
!= NULL
; p
= p
->next
);
6018 for(pa
= attach
; pa
!= NULL
; pa
= pa
->next
) {
6020 continue; /* Has an ID, it's old */
6023 * the idea is handle ALL attachments as open FILE *'s. Actual
6024 * encoding and such is handled at the time the message
6025 * is shoved into the mail slot or written to disk...
6027 * Also, we never unlink a file, so it's up to whoever opens
6028 * it to deal with tmpfile issues.
6030 if((file_contents
= (void *)so_get(FileStar
, pa
->filename
,
6031 READ_ACCESS
)) == NULL
){
6032 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
6033 _("Error \"%s\", couldn't attach file \"%s\""),
6034 error_description(errno
), pa
->filename
);
6035 display_message('x');
6039 p
->next
= mail_newbody_part();
6041 p
->body
.id
= generate_message_id();
6042 p
->body
.contents
.text
.data
= file_contents
;
6045 * Set type to unknown and let set_mime_type_by_* figure it out.
6046 * Always encode attachments we add as BINARY.
6048 p
->body
.type
= TYPEOTHER
;
6049 p
->body
.encoding
= ENCBINARY
;
6050 p
->body
.size
.bytes
= name_file_size(pa
->filename
);
6051 if(!set_mime_type_by_extension(&p
->body
, pa
->filename
)){
6052 set_mime_type_by_grope(&p
->body
);
6053 set_charset_possibly_to_ascii(&p
->body
, ps_global
->keyboard_charmap
);
6056 so_release((STORE_S
*)p
->body
.contents
.text
.data
);
6058 if(pa
->description
) /* encoding happens when msg written */
6059 p
->body
.description
= cpystr(pa
->description
);
6061 /* Add name attribute for backward compatibility */
6062 for(parmp
= &p
->body
.parameter
; *parmp
; )
6063 if(!struncmp((*parmp
)->attribute
, "name", 4)
6064 && (!*((*parmp
)->attribute
+ 4)
6065 || *((*parmp
)->attribute
+ 4) == '*')){
6066 PARAMETER
*free_me
= *parmp
;
6067 *parmp
= (*parmp
)->next
;
6068 free_me
->next
= NULL
;
6069 mail_free_body_parameter(&free_me
);
6072 parmp
= &(*parmp
)->next
;
6074 set_parameter(parmp
, "name",
6076 ? ((lc
= last_cmpnt(pa
->filename
)) ? lc
: pa
->filename
)
6079 /* Then set the Content-Disposition ala RFC1806 */
6080 if(!p
->body
.disposition
.type
){
6081 p
->body
.disposition
.type
= cpystr("attachment");
6082 for(parmp
= &p
->body
.disposition
.parameter
; *parmp
; )
6083 if(!struncmp((*parmp
)->attribute
, "filename", 4)
6084 && (!*((*parmp
)->attribute
+ 4)
6085 || *((*parmp
)->attribute
+ 4) == '*')){
6086 PARAMETER
*free_me
= *parmp
;
6087 *parmp
= (*parmp
)->next
;
6088 free_me
->next
= NULL
;
6089 mail_free_body_parameter(&free_me
);
6092 parmp
= &(*parmp
)->next
;
6094 set_parameter(parmp
, "filename",
6096 ? ((lc
= last_cmpnt(pa
->filename
)) ? lc
: pa
->filename
)
6101 pa
->id
= cpystr(p
->body
.id
);
6105 * Now, if this multipart has but one text piece (that is, no
6106 * attachments), then downgrade from a composite type to a discrete
6107 * text/plain message if CTE is not 8bit.
6109 if(!(*b
)->nested
.part
->next
6110 && (*b
)->nested
.part
->body
.type
== TYPETEXT
6111 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE
, ps_global
)
6112 || !((*b
)->nested
.part
->body
.encoding
== ENC8BIT
6113 || (*b
)->nested
.part
->body
.encoding
== ENCBINARY
))){
6114 /* Clone the interesting body part */
6115 tmp_body
= mail_newbody();
6116 *tmp_body
= (*b
)->nested
.part
->body
;
6117 /* and rub out what we don't want cleaned up when it's free'd */
6118 mail_initbody(&(*b
)->nested
.part
->body
);
6124 TIME_STAMP("create_body end.", 1);
6129 * Fill in text BODY part's structure
6133 create_message_body_text(struct mail_bodystruct
*b
, int flow_it
)
6135 set_mime_type_by_grope(b
);
6136 if(F_OFF(F_QUELL_FLOWED_TEXT
, ps_global
)
6137 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps_global
)
6139 set_parameter(b
? &b
->parameter
: NULL
, "format", "flowed");
6146 * free_attachment_list - free attachments in given list
6149 free_attachment_list(PATMT
**alist
)
6153 while(alist
&& *alist
){ /* pointer pointing to something */
6154 leading
= (*alist
)->next
;
6155 if((*alist
)->description
)
6156 fs_give((void **)&(*alist
)->description
);
6158 if((*alist
)->filename
){
6159 if((*alist
)->flags
& A_TMP
)
6160 if(our_unlink((*alist
)->filename
) < 0)
6161 dprint((1, "-- Can't unlink(%s): %s\n",
6162 (*alist
)->filename
? (*alist
)->filename
: "?",
6163 error_description(errno
)));
6165 fs_give((void **)&(*alist
)->filename
);
6169 fs_give((void **)&(*alist
)->size
);
6172 fs_give((void **)&(*alist
)->id
);
6174 fs_give((void **)alist
);
6182 set_body_size(struct mail_bodystruct
*b
)
6187 we_cancel
= busy_cue(NULL
, NULL
, 1);
6188 so_seek((STORE_S
*)b
->contents
.text
.data
, 0L, 0);
6190 while(so_readc(&c
, (STORE_S
*)b
->contents
.text
.data
))
6194 cancel_busy_cue(-1);
6199 * view_as_rich - set the rich_header flag
6201 * name - name of the header field
6202 * deflt - default value to return if user didn't set it
6204 * Note: if the user tries to turn them all off with "", then
6205 * we take that to mean default, since otherwise there is no
6206 * way to get to the headers.
6209 view_as_rich(char *name
, int deflt
)
6214 p
= ps_global
->VAR_COMP_HDRS
;
6217 for(; (q
= *p
) != NULL
; p
++){
6218 if(!struncmp(q
, name
, strlen(name
)))
6219 return 0; /* 0 means we *do* view it by default */
6222 return 1; /* 1 means it starts out hidden */
6229 * background_posting - return whether or not we're already in the process
6233 background_posting(int gripe
)
6235 if(ps_global
->post
){
6237 q_status_message(SM_ORDER
|SM_DING
, 3, 3,
6238 _("Can't post while posting!"));
6246 /*----------------------------------------------------------------------
6247 Validate the given subject relative to any news groups.
6251 Returns: always returns 1, but also returns error if
6254 valid_subject(char *given
, char **expanded
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
6256 struct headerentry
*hp
;
6259 *expanded
= cpystr(given
);
6263 * Now look for any header entry we passed to pico that has to do
6264 * with news. If there's no subject, gripe.
6266 for(hp
= pbf
->headents
; hp
->prompt
; hp
++)
6267 if(hp
->help
== h_composer_news
){
6268 if(hp
->hd_text
->text
[0] && !*given
)
6270 _("News postings MUST have a subject! Please add one!"));
6281 * This is the build_address used by the composer to check for an address
6284 * Args: to -- the passed in line to parse
6285 * full_to -- Address of a pointer to return the full address in.
6286 * This will be allocated here and freed by the caller.
6287 * error -- Address of a pointer to return an error message in.
6288 * This will be allocated here and freed by the caller.
6289 * barg -- Address of a pointer to return the fcc in is in
6290 * fcc->tptr. It will have already been allocated by the
6291 * caller but we may free it and reallocate if we wish.
6292 * Caller will free it.
6294 * Result: 0 is returned if address was OK,
6295 * -1 if address wasn't OK.
6297 * Side effect: Can flush addrbook entry cache entries so they need to be
6298 * re-fetched afterwords.
6301 build_address(char *to
, char **full_to
, char **error
, BUILDER_ARG
*barg
, int *mangled
)
6304 int ret_val
, no_repo
= 0, *save_nesting_level
;
6306 PrivateTop
*pt
= NULL
;
6307 PrivateAffector
*af
= NULL
;
6308 char *fcc_local
= NULL
;
6309 jmp_buf save_jmp_buf
;
6311 dprint((5, "- build_address - (%s)\n", to
? to
: "nul"));
6313 /* check to see if to string is empty to avoid work */
6314 for(p
= to
; p
&& *p
&& isspace((unsigned char)*p
); p
++)
6319 *full_to
= cpystr(to
? to
: ""); /* because pico does a strcmp() */
6325 *full_to
= (char *)NULL
;
6328 *error
= (char *)NULL
;
6330 /* No guarantee cursor or status line is how we saved it */
6332 mark_status_unknown();
6334 if(ps_global
->remote_abook_validity
> 0 &&
6335 adrbk_check_and_fix_all(ab_nesting_level
== 0, 0, 0) && mangled
)
6336 *mangled
|= BUILDER_SCREEN_MANGLED
;
6339 * If we end up jumping back here because somebody else changed one of
6340 * our addrbooks out from underneath us, we may well leak some memory.
6341 * That's probably ok since this will be very rare.
6343 * The reason for the memcpy of the jmp_buf is that we may actually
6344 * be indirectly calling this function from within the address book.
6345 * For example, we may be in the address book screen and then run
6346 * the ComposeTo command which puts us in the composer, then we call
6347 * build_address from there which resets addrbook_changed_unexpectedly.
6348 * Once we leave build_address we need to reset addrbook_changed_un...
6349 * because this position on the stack will no longer be valid.
6350 * Same is true of the other setjmp's in this file which are wrapped
6353 save_nesting_level
= cpyint(ab_nesting_level
);
6354 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
6355 if(setjmp(addrbook_changed_unexpectedly
)){
6361 *error
= (char *)NULL
;
6363 if(full_to
&& *full_to
)
6364 fs_give((void **)full_to
);
6366 q_status_message(SM_ORDER
, 3, 5, "Resetting address book...");
6368 "RESETTING address book... build_address(%s)!\n", to
? to
: "?"));
6370 ab_nesting_level
= *save_nesting_level
;
6376 ret_val
= build_address_internal(bldto
, full_to
, error
,
6377 barg
? &fcc_local
: NULL
,
6378 &no_repo
, NULL
, save_and_restore
,
6381 if(save_nesting_level
)
6382 fs_give((void **)&save_nesting_level
);
6385 * Have to rfc1522_decode the full_to string before sending it back.
6387 if(full_to
&& *full_to
){
6391 len
= 4*strlen(*full_to
)+1;
6392 q
= (char *)fs_get(len
* sizeof(char));
6393 p
= (char *)rfc1522_decode_to_utf8((unsigned char *)q
, len
, *full_to
);
6395 /* p == q means that decoding happened, p is decoded *full_to */
6397 fs_give((void **)full_to
);
6401 fs_give((void **)&q
);
6407 /* Pt will point to headents[Fcc].bldr_private */
6409 if(barg
&& barg
->aff
)
6410 pt
= (PrivateTop
*)(*barg
->aff
);
6413 * If *barg->aff is set, that means fcc was set from a list
6414 * during some previous builder call.
6415 * If the current To line contains the old expansion as a prefix, then
6416 * we should leave things as they are. In order to decide that,
6417 * we look at a hash value computed from the strings.
6419 if(pt
&& (af
=pt
->affector
) && af
->who
== BP_To
){
6423 if(len
>= af
->cksumlen
){
6426 save
= to
[af
->cksumlen
];
6427 to
[af
->cksumlen
] = '\0';
6428 csum
= line_hash(to
);
6429 to
[af
->cksumlen
] = save
;
6432 csum
= af
->cksumval
+ 1; /* something not equal to cksumval */
6437 (pt
->affector
->who
== BP_To
&& csum
!= pt
->affector
->cksumval
)){
6439 /* replace fcc value */
6441 fs_give((void **)&barg
->tptr
);
6443 barg
->tptr
= fcc_local
;
6447 *barg
->aff
= (void *)fs_get(sizeof(PrivateTop
));
6448 pt
= (PrivateTop
*)(*barg
->aff
);
6449 memset((void *)pt
, 0, sizeof(PrivateTop
));
6455 (PrivateAffector
*)fs_get(sizeof(PrivateAffector
));
6459 af
->cksumlen
= strlen(((full_to
&& *full_to
)
6461 af
->cksumval
= line_hash(((full_to
&& *full_to
)
6466 * If result is reproducible, we don't keep track here.
6469 fs_give((void **)&pt
->affector
);
6474 fs_give((void **)&fcc_local
); /* unused in this case */
6477 /* This is so pico will erase the old message */
6478 if(error
!= NULL
&& *error
== NULL
)
6479 *error
= cpystr("");
6481 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
6482 flush_status_messages(1);
6488 * This is the builder used by the composer for the Lcc line.
6490 * Args: lcc -- the passed in Lcc line to parse
6491 * full_lcc -- Address of a pointer to return the full address in.
6492 * This will be allocated here and freed by the caller.
6493 * error -- Address of a pointer to return an error message in.
6494 * This is not allocated so should not be freed by the caller.
6495 * barg -- This is a pointer to text for affected entries which
6496 * we may be changing. The first one in the list is the
6497 * To entry. We may put the name of the list in empty
6498 * group syntax form there (like List Name: ;).
6499 * The second one in the list is the fcc field.
6500 * The tptr members already point to text allocated in the
6501 * caller. We may free and reallocate here, caller will
6502 * free the result in any case.
6504 * Result: 0 is returned if address was OK,
6505 * -1 if address wasn't OK.
6507 * Side effect: Can flush addrbook entry cache entries so they need to be
6508 * re-fetched afterwords.
6511 build_addr_lcc(char *lcc
, char **full_lcc
, char **error
, BUILDER_ARG
*barg
, int *mangled
)
6514 no_repo
= 0; /* fcc or lcc not reproducible */
6515 int *save_nesting_level
;
6517 PrivateTop
*pt
= NULL
;
6518 PrivateAffector
*af
= NULL
;
6523 jmp_buf save_jmp_buf
;
6525 dprint((5, "- build_addr_lcc - (%s)\n", lcc
? lcc
: "nul"));
6527 /* check to see if to string is empty to avoid work */
6528 for(p
= lcc
; p
&& *p
&& isspace((unsigned char)*p
); p
++)
6533 *full_lcc
= cpystr(lcc
? lcc
: ""); /* because pico does a strcmp() */
6539 *error
= (char *)NULL
;
6541 if(ps_global
->remote_abook_validity
> 0 &&
6542 adrbk_check_and_fix_all(ab_nesting_level
== 0, 0, 0) && mangled
)
6543 *mangled
|= BUILDER_SCREEN_MANGLED
;
6546 * If we end up jumping back here because somebody else changed one of
6547 * our addrbooks out from underneath us, we may well leak some memory.
6548 * That's probably ok since this will be very rare.
6550 save_nesting_level
= cpyint(ab_nesting_level
);
6551 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
6552 if(setjmp(addrbook_changed_unexpectedly
)){
6559 *error
= (char *)NULL
;
6561 if(full_lcc
&& *full_lcc
)
6562 fs_give((void **)full_lcc
);
6564 q_status_message(SM_ORDER
, 3, 5, "Resetting address book...");
6566 "RESETTING address book... build_address(%s)!\n", lcc
? lcc
: "?"));
6568 ab_nesting_level
= *save_nesting_level
;
6573 bldlcc
.arg
.str
= lcc
;
6576 * To is first affected_entry and Fcc is second.
6577 * The conditional stuff for the fcc argument says to only change the
6578 * fcc if the fcc pointer is passed in non-null, and the To pointer
6579 * is also non-null. If they are null, that means they've already been
6580 * entered (are sticky). We don't affect fcc if either fcc or To has
6583 ret_val
= build_address_internal(bldlcc
,
6586 (barg
&& barg
->next
&& barg
->next
->tptr
&& barg
->tptr
)
6587 ? &fcc_local
: NULL
,
6589 (barg
&& barg
->tptr
) ? &to
: NULL
,
6590 save_and_restore
, 0, mangled
);
6593 if(save_nesting_level
)
6594 fs_give((void **)&save_nesting_level
);
6596 /* full_lcc is what ends up in the Lcc: line */
6597 if(full_lcc
&& *full_lcc
){
6601 * Have to rfc1522_decode the full_lcc string before sending it back.
6603 len
= 4*strlen(*full_lcc
)+1;
6604 p
= (char *)fs_get(len
* sizeof(char));
6605 if(rfc1522_decode_to_utf8((unsigned char *)p
, len
, *full_lcc
) == (unsigned char *)p
){
6606 fs_give((void **)full_lcc
);
6610 fs_give((void **)&p
);
6613 /* to is what ends up in the To: line */
6619 * Have to rfc1522_decode the full_to string before sending it back.
6621 len
= 4*strlen(to
)+1;
6622 p
= (char *)fs_get(len
* sizeof(char));
6624 if(rfc1522_decode_to_utf8((unsigned char *)p
, len
, to
) == (unsigned char *)p
){
6626 * If the caller wants us to try to preserve the charset
6627 * information (they set aff) we copy it into encoded->etext.
6628 * We don't have to worry about pasting together pieces of
6629 * etext like we do in build_address because whenever the
6630 * Lcc line is setting the To line it will be setting the
6631 * whole line, not modifying it.
6632 * Pt will point to headents[To].bldr_private.
6634 if(barg
&& barg
->aff
){
6635 pt
= (PrivateTop
*)(*barg
->aff
);
6638 *barg
->aff
= (void *)fs_get(sizeof(PrivateTop
));
6639 pt
= (PrivateTop
*)(*barg
->aff
);
6640 memset((void *)pt
, 0, sizeof(PrivateTop
));
6644 fs_give((void **)&to
);
6648 fs_give((void **)&p
);
6651 fs_give((void **)&dummy
);
6655 * This part is recording the fact that the To line was set to
6656 * what it is by entering something on the Lcc line. In particular,
6657 * if a list alias was entered here then the fullname of the list
6658 * goes in the To line. We save this affector information so that
6659 * we can tell it shouldn't be modified if we call build_addr_lcc
6660 * again unless we actually modified what's in the Lcc line so that
6661 * it doesn't start with the same thing. The problem we're solving
6662 * is that the contents of the Lcc line no longer look like the
6663 * list they were derived from.
6664 * Pt will point to headents[To].bldr_private.
6666 if(barg
&& barg
->aff
)
6667 pt
= (PrivateTop
*)(*barg
->aff
);
6669 if(pt
&& (af
=pt
->affector
) && af
->who
== BP_Lcc
){
6673 if(len
>= af
->cksumlen
){
6676 save
= lcc
[af
->cksumlen
];
6677 lcc
[af
->cksumlen
] = '\0';
6678 csum
= line_hash(lcc
);
6679 lcc
[af
->cksumlen
] = save
;
6682 csum
= af
->cksumval
+ 1; /* so they aren't equal */
6687 pt
->affector
->who
!= BP_Lcc
||
6688 (pt
->affector
->who
== BP_Lcc
&& csum
!= pt
->affector
->cksumval
)){
6690 /* replace to value */
6691 if(barg
->tptr
&& barg
->tptr
[0]){
6695 l
= strlen(barg
->tptr
) + strlen(to
? to
: "") + 2;
6696 t
= (char *)fs_get((l
+1) * sizeof(char));
6697 snprintf(t
, l
+1, "%s%s%s",
6699 (to
&& *to
) ? ", " : "",
6700 (to
&& *to
) ? to
: "");
6701 fs_give((void **)&barg
->tptr
);
6703 fs_give((void **)&to
);
6709 fs_give((void **)&barg
->tptr
);
6716 *barg
->aff
= (void *)fs_get(sizeof(PrivateTop
));
6717 pt
= (PrivateTop
*)(*barg
->aff
);
6718 memset((void *)pt
, 0, sizeof(PrivateTop
));
6724 (PrivateAffector
*)fs_get(sizeof(PrivateAffector
));
6728 af
->cksumlen
= strlen(((full_lcc
&& *full_lcc
)
6730 af
->cksumval
= line_hash(((full_lcc
&& *full_lcc
)
6735 * If result is reproducible, we don't keep track here.
6738 fs_give((void **)&pt
->affector
);
6743 fs_give((void **)&to
); /* unused in this case */
6750 * If *barg->next->aff is set, that means fcc was set from a list
6751 * during some previous builder call. If the current Lcc line
6752 * contains the old expansion as a prefix, then we should leave
6753 * things as they are. In order to decide that we look at a hash
6754 * value computed from the strings.
6755 * Pt will point to headents[Fcc].bldr_private
6758 if(barg
&& barg
->next
&& barg
->next
->aff
)
6759 pt
= (PrivateTop
*)(*barg
->next
->aff
);
6761 if(pt
&& (af
=pt
->affector
) && af
->who
== BP_Lcc
){
6765 if(len
>= af
->cksumlen
){
6768 save
= lcc
[af
->cksumlen
];
6769 lcc
[af
->cksumlen
] = '\0';
6770 csum
= line_hash(lcc
);
6771 lcc
[af
->cksumlen
] = save
;
6774 csum
= af
->cksumval
+ 1; /* something not equal to cksumval */
6779 pt
->affector
->who
!= BP_Lcc
||
6780 (pt
->affector
->who
== BP_Lcc
&& csum
!= pt
->affector
->cksumval
)){
6782 /* replace fcc value */
6783 if(barg
->next
->tptr
)
6784 fs_give((void **)&barg
->next
->tptr
);
6786 barg
->next
->tptr
= fcc_local
;
6788 if(barg
->next
->aff
){
6790 *barg
->next
->aff
= (void *)fs_get(sizeof(PrivateTop
));
6791 pt
= (PrivateTop
*)(*barg
->next
->aff
);
6792 memset((void *)pt
, 0, sizeof(PrivateTop
));
6798 (PrivateAffector
*)fs_get(sizeof(PrivateAffector
));
6802 af
->cksumlen
= strlen(((full_lcc
&& *full_lcc
)
6804 af
->cksumval
= line_hash(((full_lcc
&& *full_lcc
)
6809 * If result is reproducible, we don't keep track here.
6812 fs_give((void **)&pt
->affector
);
6817 fs_give((void **)&fcc_local
); /* unused in this case */
6821 if(error
!= NULL
&& *error
== NULL
)
6822 *error
= cpystr("");
6824 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
6825 flush_status_messages(0);
6830 /*----------------------------------------------------------------------
6831 Verify and canonicalize news groups names.
6832 Called from the message composer
6834 Args: given_group -- List of groups typed by user
6835 expanded_group -- pointer to point to expanded list, which will be
6836 allocated here and freed in caller. If this is
6837 NULL, don't attempt to validate.
6838 error -- pointer to store error message
6839 fcc -- pointer to point to fcc, which will be
6840 allocated here and freed in caller
6842 Returns: 0 if all is OK
6843 -1 if addresses weren't valid
6845 Test the given list of newstroups against those recognized by our nntp
6846 servers. Testing by actually trying to open the list is much cheaper, both
6847 in bandwidth and memory, than yanking the whole list across the wire.
6850 news_build(char *given_group
, char **expanded_group
, char **error
, BUILDER_ARG
*fcc
, int *mangled
)
6853 char *fccptr
= NULL
;
6855 if(fcc
&& fcc
->tptr
)
6856 fccptr
= cpystr(fcc
->tptr
);
6860 rv
= news_grouper(given_group
, expanded_group
, error
, &fccptr
, news_build_busy
);
6862 /* assign any new fcc to the BUILDER_ARG */
6866 if(fcc
->tptr
&& strcmp(fcc
->tptr
, fccptr
)){
6867 fs_give((void **) &fcc
->tptr
);
6874 fs_give((void **) &fccptr
);
6877 /* deal with any busy indicator */
6881 mark_status_dirty();
6882 display_message('x');
6884 *mangled
|= BUILDER_MESSAGE_DISPLAYED
;
6893 news_build_busy(void)
6895 news_busy_cue
= busy_cue("Validating newsgroup(s)", NULL
, 0);
6899 #if defined(DOS) || defined(OS2)
6901 /*----------------------------------------------------------------------
6902 Verify that the necessary pieces are around to allow for
6903 message sending under DOS
6905 Args: strict -- tells us if a remote stream is required before
6906 sending is permitted.
6908 The idea is to make sure pine knows enough to put together a valid
6909 from line. The things we MUST know are a user-id, user-domain and
6910 smtp server to dump the message off on. Typically these are
6911 provided in pine's configuration file, but if not, the user is
6917 char prompt
[100], answer
[80];
6922 * query for user name portion of address, use IMAP login
6925 if(!ps_global
->VAR_USER_ID
|| ps_global
->VAR_USER_ID
[0] == '\0'){
6927 int no_prompt_user_id
= 0;
6929 if(ps_global
->mail_stream
&& ps_global
->mail_stream
->mailbox
6930 && mail_valid_net_parse(ps_global
->mail_stream
->mailbox
, &mb
)
6932 strncpy(answer
, mb
.user
, sizeof(answer
)-1);
6933 answer
[sizeof(answer
)-1] = '\0';
6935 else if(F_ON(F_QUELL_USER_ID_PROMPT
, ps_global
)){
6936 /* no user-id prompting if set */
6937 no_prompt_user_id
= 1;
6939 if(!ps_global
->mail_stream
)
6940 do_broach_folder(ps_global
->inbox_name
,
6941 ps_global
->context_list
, NULL
, DB_INBOXWOCNTXT
);
6942 if(ps_global
->mail_stream
&& ps_global
->mail_stream
->mailbox
6943 && mail_valid_net_parse(ps_global
->mail_stream
->mailbox
, &mb
)
6945 strncpy(answer
, mb
.user
, sizeof(answer
)-1);
6946 answer
[sizeof(answer
)-1] = '\0';
6954 if(F_ON(F_QUELL_USER_ID_PROMPT
, ps_global
) && answer
[0]){
6955 /* No prompt, just assume mailbox login is user-id */
6956 no_prompt_user_id
= 1;
6960 snprintf(prompt
,sizeof(prompt
),_("User-id for From address : "));
6961 prompt
[sizeof(prompt
)-1] = '\0';
6964 while(!no_prompt_user_id
) {
6965 flags
= OE_APPEND_CURRENT
;
6966 rc
= optionally_enter(answer
,-FOOTER_ROWS(ps_global
),0,
6967 sizeof(answer
),prompt
,NULL
,help
,&flags
);
6972 help
= (help
== NO_HELP
) ? h_sticky_user_id
: NO_HELP
;
6980 if(rc
== 1 || (rc
== 0 && !answer
[0])) {
6981 q_status_message(SM_ORDER
, 3, 4,
6982 _("Send cancelled (User-id must be provided before sending)"));
6987 snprintf(prompt
, sizeof(prompt
), _("Preserve %.*s as \"user-id\" in PINERC"),
6988 sizeof(prompt
)-50, answer
);
6989 prompt
[sizeof(prompt
)-1] = '\0';
6990 if(ps_global
->blank_user_id
6991 && !no_prompt_user_id
6992 && want_to(prompt
, 'y', 'n', NO_HELP
, WT_NORM
) == 'y'){
6993 set_variable(V_USER_ID
, answer
, 1, 1, Main
);
6996 fs_give((void **)&(ps_global
->VAR_USER_ID
));
6997 ps_global
->VAR_USER_ID
= cpystr(answer
);
7001 /* query for personal name */
7002 if(!ps_global
->VAR_PERSONAL_NAME
|| ps_global
->VAR_PERSONAL_NAME
[0]=='\0'
7003 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT
, ps_global
)){
7005 snprintf(prompt
, sizeof(prompt
), _("Personal name for From address : "));
7006 prompt
[sizeof(prompt
)-1] = '\0';
7010 flags
= OE_APPEND_CURRENT
;
7011 rc
= optionally_enter(answer
,-FOOTER_ROWS(ps_global
),0,
7012 sizeof(answer
),prompt
,NULL
,help
,&flags
);
7017 help
= (help
== NO_HELP
) ? h_sticky_personal_name
: NO_HELP
;
7025 if(rc
== 0 && answer
){ /* save the name */
7026 snprintf(prompt
, sizeof(prompt
), _("Preserve %.*s as \"personal-name\" in PINERC"),
7027 sizeof(prompt
)-50, answer
);
7028 prompt
[sizeof(prompt
)-1] = '\0';
7029 if(ps_global
->blank_personal_name
7030 && want_to(prompt
, 'y', 'n', NO_HELP
, WT_NORM
) == 'y'){
7031 set_variable(V_PERSONAL_NAME
, answer
, 1, 1, Main
);
7034 fs_give((void **)&(ps_global
->VAR_PERSONAL_NAME
));
7035 ps_global
->VAR_PERSONAL_NAME
= cpystr(answer
);
7041 * query for host/domain portion of address, using IMAP
7044 if(ps_global
->blank_user_domain
7045 || ps_global
->maildomain
== ps_global
->localdomain
7046 || ps_global
->maildomain
== ps_global
->hostname
){
7047 if(ps_global
->inbox_name
[0] == '{'){
7049 i
< sizeof(answer
)-1 && ps_global
->inbox_name
[i
+1] != '}'; i
++)
7050 answer
[i
] = ps_global
->inbox_name
[i
+1];
7057 snprintf(prompt
,sizeof(prompt
),_("Host/domain for From address : "));
7058 prompt
[sizeof(prompt
)-1] = '\0';
7062 flags
= OE_APPEND_CURRENT
;
7063 rc
= optionally_enter(answer
,-FOOTER_ROWS(ps_global
),0,
7064 sizeof(answer
),prompt
,NULL
,help
,&flags
);
7069 help
= (help
== NO_HELP
) ? h_sticky_domain
: NO_HELP
;
7077 if(rc
== 1 || (rc
== 0 && !answer
[0])) {
7078 q_status_message(SM_ORDER
, 3, 4,
7079 _("Send cancelled (Host/domain name must be provided before sending)"));
7084 snprintf(prompt
, sizeof(prompt
), _("Preserve %.*s as \"user-domain\" in PINERC"),
7085 sizeof(prompt
)-50, answer
);
7086 prompt
[sizeof(prompt
)-1] = '\0';
7087 if(!ps_global
->userdomain
&& !ps_global
->blank_user_domain
7088 && want_to(prompt
, 'y', 'n', NO_HELP
, WT_NORM
) == 'y'){
7089 set_variable(V_USER_DOMAIN
, answer
, 1, 1, Main
);
7090 fs_give((void **)&(ps_global
->maildomain
)); /* blast old val */
7091 ps_global
->userdomain
= cpystr(answer
);
7092 ps_global
->maildomain
= ps_global
->userdomain
;
7095 fs_give((void **)&(ps_global
->maildomain
));
7096 ps_global
->userdomain
= cpystr(answer
);
7097 ps_global
->maildomain
= ps_global
->userdomain
;
7101 /* check for smtp server */
7102 if(!ps_global
->VAR_SMTP_SERVER
||
7103 !ps_global
->VAR_SMTP_SERVER
[0] ||
7104 !ps_global
->VAR_SMTP_SERVER
[0][0]){
7107 if(ps_global
->inbox_name
[0] == '{'){
7109 i
< sizeof(answer
)-1 && ps_global
->inbox_name
[i
+1] != '}'; i
++)
7110 answer
[i
] = ps_global
->inbox_name
[i
+1];
7117 snprintf(prompt
,sizeof(prompt
),_("SMTP server to forward message : "));
7118 prompt
[sizeof(prompt
)-1] = '\0';
7122 flags
= OE_APPEND_CURRENT
;
7123 rc
= optionally_enter(answer
,-FOOTER_ROWS(ps_global
),0,
7124 sizeof(answer
),prompt
,NULL
,help
,&flags
);
7129 help
= (help
== NO_HELP
) ? h_sticky_smtp
: NO_HELP
;
7137 if(rc
== 1 || (rc
== 0 && answer
[0] == '\0')) {
7138 q_status_message(SM_ORDER
, 3, 4,
7139 _("Send cancelled (SMTP server must be provided before sending)"));
7144 list
= (char **) fs_get(2 * sizeof(char *));
7145 list
[0] = cpystr(answer
);
7147 set_variable_list(V_SMTP_SERVER
, list
, TRUE
, Main
);
7148 fs_give((void *)&list
[0]);
7149 fs_give((void *)list
);
7155 #endif /* defined(DOS) || defined(OS2) */