1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: send.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
3 #endif
4
5 /*
6 * ========================================================================
7 * Copyright 2013-2021 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
9 *
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
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * ========================================================================
17 */
18
19 /*======================================================================
20 Functions for composing and sending mail
21
22 ====*/
23
24
25 #include "headers.h"
26 #include "send.h"
27 #include "status.h"
28 #include "mailview.h"
29 #include "mailindx.h"
30 #include "dispfilt.h"
31 #include "keymenu.h"
32 #include "folder.h"
33 #include "radio.h"
34 #include "addrbook.h"
35 #include "reply.h"
36 #include "titlebar.h"
37 #include "signal.h"
38 #include "mailcmd.h"
39 #include "roleconf.h"
40 #include "adrbkcmd.h"
41 #include "busy.h"
42 #include "../pith/debug.h"
43 #include "../pith/state.h"
44 #include "../pith/conf.h"
45 #include "../pith/flag.h"
46 #include "../pith/bldaddr.h"
47 #include "../pith/copyaddr.h"
48 #include "../pith/detach.h"
49 #include "../pith/mimedesc.h"
50 #include "../pith/pipe.h"
51 #include "../pith/addrstring.h"
52 #include "../pith/news.h"
53 #include "../pith/detoken.h"
54 #include "../pith/util.h"
55 #include "../pith/init.h"
56 #include "../pith/mailcmd.h"
57 #include "../pith/ablookup.h"
58 #include "../pith/reply.h"
59 #include "../pith/hist.h"
60 #include "../pith/list.h"
61 #include "../pith/icache.h"
62 #include "../pith/busy.h"
63 #include "../pith/mimetype.h"
64 #include "../pith/send.h"
65 #include "../pith/smime.h"
66
67
68 typedef struct body_particulars {
69 unsigned short type, encoding, had_csp;
70 char *subtype, *charset;
71 PARAMETER *parameter;
72 } BODY_PARTICULARS_S;
73
74 /*
75 * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook
76 */
77 #define HE(PF) ((struct headerentry *)((PF)->extdata))
78
79
80 /*
81 * Internal Prototypes
82 */
83 int redraft(MAILSTREAM **, ENVELOPE **, BODY **, char **, char **, REPLY_S **,
84 REDRAFT_POS_S **, PINEFIELD **, ACTION_S **, int);
85 int redraft_prompt(char *, char *, int);
86 int check_for_subject(METAENV *);
87 int check_for_fcc(char *);
88 void free_prompts(PINEFIELD *);
89 int postpone_prompt(void);
90 METAENV *pine_simple_send_header(ENVELOPE *, char **, char ***);
91 void call_mailer_file_result(char *, int);
92 void mark_address_failure_for_pico(METAENV *);
93 BODY_PARTICULARS_S
94 *save_body_particulars(BODY *);
95 void reset_body_particulars(BODY_PARTICULARS_S *, BODY *);
96 void free_body_particulars(BODY_PARTICULARS_S *);
97 long message_format_for_pico(long, int (*)(int));
98 int send_exit_for_pico(struct headerentry *, void (*)(void), int, char **);
99 void new_thread_on_blank_subject(void);
100 char *choose_a_priority(char *);
101 int dont_flow_this_time(void);
102 int mime_type_for_pico(char *);
103 char *cancel_for_pico(void (*)(void));
104 int filter_message_text(char *, ENVELOPE *, BODY *, STORE_S **, METAENV *);
105 void pine_send_newsgroup_name(char *, char*, size_t);
106 void outgoing2strings(METAENV *, BODY *, void **, PATMT **, int);
107 void strings2outgoing(METAENV *, BODY **, PATMT *, int);
108 void create_message_body_text(BODY *, int);
109 void set_body_size(BODY *);
110 int view_as_rich(char *, int);
111 int background_posting(int);
112 int valid_subject(char *, char **, char **,BUILDER_ARG *,int *);
113 int build_addr_lcc(char *, char **, char **, BUILDER_ARG *, int *);
114 int news_build(char *, char **, char **, BUILDER_ARG *, int *);
115 void news_build_busy(void);
116 #if defined(DOS) || defined(OS2)
117 int dos_valid_from(void);
118 #endif /* defined(DOS) || defined(OS2) */
119
120
121 /*
122 * Pointer to buffer to hold pointers into pine data that's needed by pico.
123 */
124 static PICO *pbf;
125
126
127 static char *g_rolenick = NULL;
128
129
130 static char *sending_filter_requested;
131 static char background_requested, flowing_requested;
132 static unsigned call_mailer_flags;
133 static char *priority_requested;
134
135 /* local global to save busy_cue state */
136 static int news_busy_cue = 0;
137
138
139 /*
140 * Various useful strings
141 */
142 #define INTRPT_PMT \
143 _("Continue INTERRUPTED composition (answering \"n\" won't erase it)")
144 #define PSTPND_PMT \
145 _("Continue postponed composition (answering \"No\" won't erase it)")
146 #define FORM_PMT \
147 _("Start composition from Form Letter Folder")
148 #define PSTPN_FORM_PMT \
149 _("Save to Postponed or Form letter folder? ")
150 #define POST_PMT \
151 _("Posted message may go to thousands of readers. Really post")
152 #define INTR_DEL_PMT \
153 _("Deleted messages will be removed from folder after use. Proceed")
154
155
156 /*
157 * Macros to help sort out posting results
158 */
159 #define P_MAIL_WIN 0x01
160 #define P_MAIL_LOSE 0x02
161 #define P_MAIL_BITS 0x03
162 #define P_NEWS_WIN 0x04
163 #define P_NEWS_LOSE 0x08
164 #define P_NEWS_BITS 0x0C
165 #define P_FCC_WIN 0x10
166 #define P_FCC_LOSE 0x20
167 #define P_FCC_BITS 0x30
168
169
170 #define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE"
171
172
173 /*
174 * For check_for_subject and check_for_fcc
175 */
176 #define CF_OK 0x1
177 #define CF_MISSING 0x2
178
179
180 /*----------------------------------------------------------------------
181 Compose screen (not forward or reply). Set up envelope, call composer
182
183 Args: pine_state -- The usual pine structure
184
185 Little front end for the compose screen
186 ---*/
187 void
compose_screen(struct pine * pine_state)188 compose_screen(struct pine *pine_state)
189 {
190 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
191 (*redraw)(void) = pine_state->redrawer;
192
193 pine_state->redrawer = NULL;
194 ps_global->next_screen = SCREEN_FUN_NULL;
195 mailcap_free(); /* free resources we won't be using for a while */
196 compose_mail(NULL, NULL, NULL, NULL, NULL);
197 pine_state->next_screen = prev_screen;
198 pine_state->redrawer = redraw;
199 }
200
201
202 /*----------------------------------------------------------------------
203 Alternate compose screen. Set up role and call regular compose.
204
205 Args: pine_state -- The usual pine structure
206 ---*/
207 void
alt_compose_screen(struct pine * pine_state)208 alt_compose_screen(struct pine *pine_state)
209 {
210 ACTION_S *role = NULL;
211 void (*prev_screen)(struct pine *) = pine_state->prev_screen,
212 (*redraw)(void) = pine_state->redrawer;
213
214 pine_state->redrawer = NULL;
215 ps_global->next_screen = SCREEN_FUN_NULL;
216 mailcap_free(); /* free resources we won't be using for a while */
217
218 /* Setup role */
219 if(role_select_screen(pine_state, &role, MC_COMPOSE) < 0){
220 cmd_cancelled("Composition");
221 pine_state->next_screen = prev_screen;
222 pine_state->redrawer = redraw;
223 return;
224 }
225
226 /*
227 * If default role was selected (NULL) we need to make up a role which
228 * won't do anything, but will cause compose_mail to think there's
229 * already a role so that it won't try to confirm the default.
230 */
231 if(role)
232 role = combine_inherited_role(role);
233 else{
234 role = (ACTION_S *)fs_get(sizeof(*role));
235 memset((void *)role, 0, sizeof(*role));
236 role->nick = cpystr("Default Role");
237 }
238
239 pine_state->redrawer = NULL;
240 compose_mail(NULL, NULL, role, NULL, NULL);
241 free_action(&role);
242 pine_state->next_screen = prev_screen;
243 pine_state->redrawer = redraw;
244 }
245
246
247 /*----------------------------------------------------------------------
248 Format envelope for outgoing message and call editor
249
250 Args: given_to -- An address to send mail to (usually from command line
251 invocation)
252 fcc_arg -- The fcc that goes with this address.
253
254 If a "To" line is given format that into the envelope and get ready to call
255 the composer
256 If there's a message postponed, offer to continue it, and set it up,
257 otherwise just fill in the outgoing envelope as blank.
258
259 NOTE: we ignore postponed and interrupted messages in nr mode
260 ----*/
261 void
compose_mail(char * given_to,char * fcc_arg,ACTION_S * role_arg,PATMT * attach,gf_io_t inc_text_getc)262 compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
263 PATMT *attach, gf_io_t inc_text_getc)
264 {
265 BODY *body = NULL;
266 ENVELOPE *outgoing = NULL;
267 PINEFIELD *custom = NULL;
268 REPLY_S *reply = NULL;
269 REDRAFT_POS_S *redraft_pos = NULL;
270 ACTION_S *role = NULL;
271 MAILSTREAM *stream;
272 char *fcc_to_free,
273 *fcc = NULL,
274 *lcc = NULL,
275 *sig = NULL;
276 int fcc_is_sticky = 0,
277 to_is_sticky = 0,
278 intrptd = 0,
279 postponed = 0,
280 form = 0;
281
282 dprint((1,
283 "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n"));
284
285 /*-- Check for INTERRUPTED mail --*/
286 if(!role_arg && !(given_to || attach)){
287 char file_path[MAXPATH+1];
288
289 /* build filename and see if it exists. build_path creates
290 * an explicit local path name, so all c-client access is thru
291 * local drivers.
292 */
293 file_path[0] = '\0';
294 build_path(file_path,
295 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
296 : ps_global->home_dir,
297 INTERRUPTED_MAIL, sizeof(file_path));
298
299 /* check to see if the folder exists, the user wants to continue
300 * and that we can actually read something in...
301 */
302 if(folder_exists(NULL, file_path) & FEX_ISFILE)
303 intrptd = 1;
304 }
305
306 /*-- Check for postponed mail --*/
307 if(!role_arg
308 && !outgoing /* not replying/forwarding */
309 && !(given_to || attach) /* not command line send */
310 && ps_global->VAR_POSTPONED_FOLDER /* folder to look in */
311 && ps_global->VAR_POSTPONED_FOLDER[0])
312 postponed = 1;
313
314 /*-- Check for form letter folder --*/
315 if(!role_arg
316 && !outgoing /* not replying/forwarding */
317 && !(given_to || attach) /* not command line send */
318 && ps_global->VAR_FORM_FOLDER /* folder to look in */
319 && ps_global->VAR_FORM_FOLDER[0])
320 form = 1;
321
322 if(!outgoing && !(given_to || attach)
323 && !role_arg && F_ON(F_ALT_COMPOSE_MENU, ps_global)){
324 char prompt[80];
325 char letters[30];
326 char chosen_task;
327 char *new = "New";
328 char *intrpt = "Interrupted";
329 char *postpnd = "Postponed";
330 char *formltr = "FormLetter";
331 char *roles = "setRole";
332 HelpType help = h_composer_browse;
333 ESCKEY_S compose_style[6];
334 unsigned which_help;
335 int ekey_num;
336
337 ekey_num = 0;
338 compose_style[ekey_num].ch = 'n';
339 compose_style[ekey_num].rval = 'n';
340 compose_style[ekey_num].name = "N";
341 compose_style[ekey_num++].label = new;
342
343 if(intrptd){
344 compose_style[ekey_num].ch = 'i';
345 compose_style[ekey_num].rval = 'i';
346 compose_style[ekey_num].name = "I";
347 compose_style[ekey_num++].label = intrpt;
348 }
349
350 if(postponed){
351 compose_style[ekey_num].ch = 'p';
352 compose_style[ekey_num].rval = 'p';
353 compose_style[ekey_num].name = "P";
354 compose_style[ekey_num++].label = postpnd;
355 }
356
357 if(form){
358 compose_style[ekey_num].ch = 'f';
359 compose_style[ekey_num].rval = 'f';
360 compose_style[ekey_num].name = "F";
361 compose_style[ekey_num++].label = formltr;
362 }
363
364 compose_style[ekey_num].ch = 'r';
365 compose_style[ekey_num].rval = 'r';
366 compose_style[ekey_num].name = "R";
367 compose_style[ekey_num++].label = roles;
368
369 compose_style[ekey_num].ch = -1;
370
371 if(F_ON(F_BLANK_KEYMENU,ps_global)){
372 char *p;
373
374 p = letters;
375 *p = '\0';
376 for(ekey_num = 0; compose_style[ekey_num].ch != -1; ekey_num++){
377 if(p - letters < sizeof(letters))
378 *p++ = (char) compose_style[ekey_num].ch;
379
380 if(compose_style[ekey_num + 1].ch != -1 && p - letters < sizeof(letters))
381 *p++ = ',';
382 }
383
384 if(p - letters < sizeof(letters))
385 *p = '\0';
386 }
387
388 which_help = intrptd + 2 * postponed + 4 * form;
389 switch(which_help){
390 case 1:
391 help = h_compose_intrptd;
392 break;
393 case 2:
394 help = h_compose_postponed;
395 break;
396 case 3:
397 help = h_compose_intrptd_postponed;
398 break;
399 case 4:
400 help = h_compose_form;
401 break;
402 case 5:
403 help = h_compose_intrptd_form;
404 break;
405 case 6:
406 help = h_compose_postponed_form;
407 break;
408 case 7:
409 help = h_compose_intrptd_postponed_form;
410 break;
411 default:
412 help = h_compose_default;
413 break;
414 }
415
416 snprintf(prompt, sizeof(prompt),
417 "Choose a compose method from %s : ",
418 F_ON(F_BLANK_KEYMENU,ps_global) ? letters : "the menu below");
419 prompt[sizeof(prompt)-1] = '\0';
420
421 chosen_task = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
422 compose_style, 'n', 'x', help, RB_NORM);
423 intrptd = postponed = form = 0;
424
425 switch(chosen_task){
426 case 'i':
427 intrptd = 1;
428 break;
429 case 'p':
430 postponed = 1;
431 break;
432 case 'r':
433 {
434 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
435 (*redraw)(void) = ps_global->redrawer;
436
437 ps_global->redrawer = NULL;
438 ps_global->next_screen = SCREEN_FUN_NULL;
439 if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
440 cmd_cancelled("Composition");
441 ps_global->next_screen = prev_screen;
442 ps_global->redrawer = redraw;
443 return;
444 }
445
446 ps_global->next_screen = prev_screen;
447 ps_global->redrawer = redraw;
448 if(role)
449 role = combine_inherited_role(role);
450 }
451 break;
452
453 case 'f':
454 form = 1;
455 break;
456
457 case 'x':
458 q_status_message(SM_ORDER, 0, 3,
459 "Composition cancelled");
460 return;
461 break;
462
463 default:
464 break;
465 }
466 }
467
468 if(intrptd && !outgoing){
469 char file_path[MAXPATH+1];
470 int ret = 'n';
471
472 file_path[0] = '\0';
473 build_path(file_path,
474 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
475 : ps_global->home_dir,
476 INTERRUPTED_MAIL, sizeof(file_path));
477 if(folder_exists(NULL, file_path) & FEX_ISFILE){
478 if((stream = pine_mail_open(NULL, file_path,
479 SP_USEPOOL|SP_TEMPUSE, NULL))
480 && !stream->halfopen){
481
482 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
483 (ret = redraft_prompt("Interrupted",INTRPT_PMT,'n')) =='y'){
484 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
485 &redraft_pos, &custom, &role, REDRAFT_DEL)){
486 if(stream)
487 pine_mail_close(stream);
488
489 return;
490 }
491
492 to_is_sticky++;
493
494 /* redraft() may or may not have closed stream */
495 if(stream)
496 pine_mail_close(stream);
497
498 postponed = form = 0;
499 }
500 else{
501 pine_mail_close(stream);
502 if(ret == 'x'){
503 q_status_message(SM_ORDER, 0, 3,
504 _("Composition cancelled"));
505 return;
506 }
507 }
508 }
509 else{
510 q_status_message1(SM_ORDER | SM_DING, 3, 3,
511 _("Can't open Interrupted mailbox: %s"),
512 file_path);
513 if(stream)
514 pine_mail_close(stream);
515 }
516 }
517 }
518
519 if(postponed && !outgoing){
520 int ret = 'n', done = 0;
521 int exists;
522
523 if((exists=postponed_stream(&stream,
524 ps_global->VAR_POSTPONED_FOLDER,
525 "Postponed", 0)) & FEX_ISFILE){
526 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
527 (ret = redraft_prompt("Postponed",PSTPND_PMT,'n')) == 'y'){
528 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
529 &redraft_pos, &custom, &role,
530 REDRAFT_DEL | REDRAFT_PPND))
531 done++;
532
533 /* stream may or may not be closed in redraft() */
534 if(stream && (stream != ps_global->mail_stream))
535 pine_mail_close(stream);
536
537 to_is_sticky++;
538 intrptd = form = 0;
539 }
540 else{
541 if(stream != ps_global->mail_stream)
542 pine_mail_close(stream);
543
544 if(ret == 'x'){
545 q_status_message(SM_ORDER, 0, 3,
546 _("Composition cancelled"));
547 done++;
548 }
549 }
550 }
551 else if(F_ON(F_ALT_COMPOSE_MENU, ps_global))
552 done++;
553
554 if(done)
555 return;
556 }
557
558 if(form && !outgoing){
559 int ret = 'n', done = 0;
560 int exists;
561
562 if((exists=postponed_stream(&stream,
563 ps_global->VAR_FORM_FOLDER,
564 "Form letter", 1)) & FEX_ISFILE){
565 if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
566 (ret = want_to(FORM_PMT,'y','x',NO_HELP,WT_NORM))=='y'){
567 if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
568 &redraft_pos, &custom, &role, REDRAFT_NONE))
569 done++;
570
571 /* stream may or may not be closed in redraft() */
572 if(stream && (stream != ps_global->mail_stream))
573 pine_mail_close(stream);
574
575 to_is_sticky++;
576 intrptd = postponed = 0;
577 }
578 else{
579 if(stream != ps_global->mail_stream)
580 pine_mail_close(stream);
581
582 if(ret == 'x'){
583 q_status_message(SM_ORDER, 0, 3,
584 _("Composition cancelled"));
585 done++;
586 }
587 }
588 }
589 else{
590 if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
591 q_status_message(SM_ORDER | SM_DING, 3, 3,
592 _("Form letter folder doesn't exist!"));
593 return;
594 }
595 }
596
597 if(done)
598 return;
599 }
600
601 /*-- normal composition --*/
602 if(!outgoing){
603 int impl, template_len = 0;
604 long rflags = ROLE_COMPOSE;
605 PAT_STATE dummy;
606
607 /*================= Compose new message ===============*/
608 body = mail_newbody();
609 outgoing = mail_newenvelope();
610
611 if(given_to)
612 rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
613
614 /*
615 * Setup possible role
616 */
617 if(role_arg)
618 role = copy_action(role_arg);
619
620 if(!role){
621 /* Setup possible compose role */
622 if(nonempty_patterns(rflags, &dummy)){
623 /*
624 * setup default role
625 * Msgno = -1 means there is no msg.
626 * This will match roles which have the Compose Use turned
627 * on, and have no patterns set, and match the Current
628 * Folder Type.
629 */
630 role = set_role_from_msg(ps_global, rflags, -1L, NULL);
631
632 if(confirm_role(rflags, &role))
633 role = combine_inherited_role(role);
634 else{ /* cancel reply */
635 role = NULL;
636 cmd_cancelled("Composition");
637 return;
638 }
639 }
640 }
641
642 if(role)
643 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
644 role->nick);
645
646 outgoing->message_id = generate_message_id(role);
647 /*
648 * The type of storage object allocated below is vitally
649 * important. See SIMPLIFYING ASSUMPTION #37
650 */
651 if((body->contents.text.data = (void *) so_get(PicoText,
652 NULL, EDIT_ACCESS)) != NULL){
653 char ch;
654
655 if(inc_text_getc){
656 while((*inc_text_getc)(&ch))
657 if(!so_writec(ch, (STORE_S *)body->contents.text.data)){
658 break;
659 }
660 }
661 }
662 else{
663 q_status_message(SM_ORDER | SM_DING, 3, 4,
664 _("Problem creating space for message text."));
665 return;
666 }
667
668 if(role && role->template){
669 char *filtered;
670
671 impl = 1; /* leave cursor in header if not explicit */
672 filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
673 if(filtered){
674 if(*filtered){
675 so_puts((STORE_S *)body->contents.text.data, filtered);
676 if(impl == 1)
677 template_len = strlen(filtered);
678 }
679
680 fs_give((void **)&filtered);
681 }
682 }
683 else
684 impl = 1;
685
686 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
687 if(impl == 2)
688 redraft_pos->offset += template_len;
689
690 if(*sig)
691 so_puts((STORE_S *)body->contents.text.data, sig);
692
693 fs_give((void **)&sig);
694 }
695
696 body->type = TYPETEXT;
697
698 if(attach)
699 create_message_body(&body, attach, 0);
700 }
701
702 ps_global->prev_screen = compose_screen;
703 if(!(fcc_to_free = fcc) && !(role && role->fcc))
704 fcc = fcc_arg; /* Didn't pick up fcc, use given */
705
706 /*
707 * check whether a build_address-produced fcc is different from
708 * fcc. If same, do nothing, if different, set sticky bit in pine_send.
709 */
710 if(fcc){
711 char *tmp_fcc = NULL;
712
713 if(outgoing->to){
714 tmp_fcc = get_fcc_based_on_to(outgoing->to);
715 if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
716 fcc_is_sticky++; /* cause sticky bit to get set */
717
718 }
719 else if((tmp_fcc = get_fcc(NULL)) != NULL &&
720 !strcmp(fcc, tmp_fcc)){
721 /* not sticky */
722 }
723 else
724 fcc_is_sticky++;
725
726 if(tmp_fcc)
727 fs_give((void **)&tmp_fcc);
728 }
729
730 pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc,
731 reply, redraft_pos, lcc, custom,
732 (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0));
733
734 if(reply){
735 if(reply->mailbox)
736 fs_give((void **) &reply->mailbox);
737 if(reply->origmbox)
738 fs_give((void **) &reply->origmbox);
739 if(reply->prefix)
740 fs_give((void **) &reply->prefix);
741 if(reply->data.uid.msgs)
742 fs_give((void **) &reply->data.uid.msgs);
743 fs_give((void **) &reply);
744 }
745
746 if(fcc_to_free)
747 fs_give((void **)&fcc_to_free);
748
749 if(lcc)
750 fs_give((void **)&lcc);
751
752 mail_free_envelope(&outgoing);
753 pine_free_body(&body);
754 free_redraft_pos(&redraft_pos);
755 free_action(&role);
756 }
757
758
759 /*----------------------------------------------------------------------
760 Args: stream -- This is where we get the postponed messages from
761 We'll expunge and close it here unless it is mail_stream.
762
763 These are all return values:
764 ================
765 outgoing --
766 body --
767 fcc --
768 lcc --
769 reply --
770 redraft_pos --
771 custom --
772 role --
773 ================
774
775 flags --
776
777 ----*/
778 int
redraft(MAILSTREAM ** streamp,ENVELOPE ** outgoing,struct mail_bodystruct ** body,char ** fcc,char ** lcc,REPLY_S ** reply,REDRAFT_POS_S ** redraft_pos,PINEFIELD ** custom,ACTION_S ** role,int flags)779 redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body,
780 char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos,
781 PINEFIELD **custom, ACTION_S **role, int flags)
782 {
783 MAILSTREAM *stream;
784 long cont_msg = 1L;
785 STORE_S *so;
786
787 if(!(streamp && *streamp))
788 return(0);
789
790 stream = *streamp;
791
792 /*
793 * If we're manipulating the current folder, don't bother
794 * with index
795 */
796 if(!stream->nmsgs){
797 if(REDRAFT_PPND&flags)
798 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!"));
799 else
800 q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!"));
801
802 return(redraft_cleanup(streamp, FALSE, flags));
803 }
804 else if(stream == ps_global->mail_stream
805 && ps_global->prev_screen == mail_index_screen){
806 /*
807 * Since the user's got this folder already opened and they're
808 * on a selected message, pick that one rather than rebuild
809 * another index screen...
810 */
811 cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
812 }
813 else if(stream->nmsgs > 1L){ /* offer browser ? */
814 int rv;
815
816 if(REDRAFT_PPND&flags){ /* set to last message postponed */
817 mn_set_cur(sp_msgmap(stream),
818 mn_get_revsort(sp_msgmap(stream))
819 ? 1L : mn_get_total(sp_msgmap(stream)));
820 }
821 else{ /* set to top form letter */
822 mn_set_cur(sp_msgmap(stream), 1L);
823 }
824
825 clear_index_cache(stream, 0);
826 while(1){
827 void *ti;
828
829 ti = stop_threading_temporarily();
830 rv = index_lister(ps_global, NULL, stream->mailbox,
831 stream, sp_msgmap(stream));
832 restore_threading(&ti);
833
834 cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream)));
835 if(count_flagged(stream, F_DEL)
836 && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){
837 if(REDRAFT_PPND&flags)
838 q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
839 else
840 q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
841
842 continue;
843 }
844
845 break;
846 }
847
848 clear_index_cache(stream, 0);
849
850 if(rv){
851 q_status_message(SM_ORDER, 0, 3, _("Composition cancelled"));
852 (void) redraft_cleanup(streamp, FALSE, flags);
853
854 if(!*streamp && !ps_global->mail_stream){
855 q_status_message2(SM_ORDER, 3, 7,
856 "No more %.200s, returning to \"%.200s\"",
857 (REDRAFT_PPND&flags) ? "postponed messages"
858 : "form letters",
859 ps_global->inbox_name);
860 if(ps_global && ps_global->ttyo){
861 blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
862 ps_global->mangled_footer = 1;
863 }
864
865 do_broach_folder(ps_global->inbox_name,
866 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
867
868 ps_global->next_screen = mail_index_screen;
869 }
870
871 return(0); /* special case */
872 }
873 }
874
875 if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL)
876 return(redraft_work(streamp, cont_msg, outgoing, body,
877 fcc, lcc, reply, redraft_pos, custom,
878 role, flags, so));
879 else
880 return(0);
881 }
882
883
884 int
redraft_prompt(char * type,char * prompt,int failure)885 redraft_prompt(char *type, char *prompt, int failure)
886 {
887 if(background_posting(FALSE)){
888 q_status_message1(SM_ORDER, 0, 3,
889 _("%s folder unavailable while background posting"),
890 type);
891 return(failure);
892 }
893
894 return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM));
895 }
896
897
898 /* this is for initializing the fixed header elements in pine_send() */
899 /*
900 prompt::name::help::prwid::maxlen::realaddr::
901 builder::affected_entry::next_affected::selector::key_label::fileedit::
902 display_it::break_on_comma::is_attach::rich_header::only_file_chars::
903 single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
904 */
905 static struct headerentry he_template[]={
906 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
907 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
908 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
909 {"From : ", "From", h_composer_from, 10, 0, NULL,
910 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
911 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
912 {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
913 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
914 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
915 {"To : ", "To", h_composer_to, 10, 0, NULL,
916 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
917 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK},
918 {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL,
919 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
920 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
921 {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL,
922 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
923 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
924 {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL,
925 news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL,
926 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
927 {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL,
928 NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, fcc_tab_complete,
929 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE},
930 {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL,
931 build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete,
932 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
933 {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL,
934 NULL, NULL, NULL, NULL, "To Files", NULL, NULL,
935 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE},
936 {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL,
937 valid_subject, NULL, NULL, NULL, NULL, NULL, NULL,
938 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
939 {"", "References", NO_HELP, 10, 0, NULL,
940 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
941 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
942 {"", "Date", NO_HELP, 10, 0, NULL,
943 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
944 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
945 {"", "In-Reply-To", NO_HELP, 10, 0, NULL,
946 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
947 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
948 {"", "Message-ID", NO_HELP, 10, 0, NULL,
949 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
950 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
951 {"", "X-Priority", NO_HELP, 10, 0, NULL,
952 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
953 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
954 {"", "User-Agent", NO_HELP, 10, 0, NULL,
955 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
956 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
957 {"", "To", NO_HELP, 10, 0, NULL,
958 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
959 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
960 {"", "X-Post-Error",NO_HELP, 10, 0, NULL,
961 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
962 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
963 {"", "X-Reply-UID", NO_HELP, 10, 0, NULL,
964 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
965 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
966 {"", "X-Reply-Mbox", NO_HELP, 10, 0, NULL,
967 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
968 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
969 {"", "X-SMTP-Server", NO_HELP, 10, 0, NULL,
970 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
971 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
972 {"", "X-Cursor-Pos", NO_HELP, 10, 0, NULL,
973 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
974 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
975 {"", "X-Our-ReplyTo", NO_HELP, 10, 0, NULL,
976 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
977 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
978 {"", OUR_HDRS_LIST, NO_HELP, 10, 0, NULL,
979 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
980 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
981 {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
982 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
983 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
984 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
985 {"", "Sender", NO_HELP, 10, 0, NULL,
986 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
987 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
988 #endif
989 };
990
991
992 static struct headerentry he_custom_addr_templ={
993 NULL, NULL, h_composer_custom_addr,10, 0, NULL,
994 build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
995 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK};
996
997 static struct headerentry he_custom_free_templ={
998 NULL, NULL, h_composer_custom_free,10, 0, NULL,
999 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1000 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE};
1001
1002
1003 /*----------------------------------------------------------------------
1004 Get addressee for message, then post message
1005
1006 Args: outgoing -- Partially formatted outgoing ENVELOPE
1007 body -- Body of outgoing message
1008 prmpt_who -- Optional prompt for optionally_enter call
1009 prmpt_cnf -- Optional prompt for confirmation call
1010 used_tobufval -- The string that the to was eventually set equal to.
1011 This gets passed back if non-NULL on entry.
1012 flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
1013 SS_NULLRP - Use null return-path so we'll send an
1014 SMTP MAIL FROM: <>
1015
1016 Result: message "To: " field is provided and message is sent or cancelled.
1017
1018 Fields:
1019 remail -
1020 return_path -
1021 date added here
1022 from added here
1023 sender -
1024 reply_to -
1025 subject passed in, NOT edited but maybe canonized here
1026 to possibly passed in, edited and canonized here
1027 cc -
1028 bcc -
1029 in_reply_to -
1030 message_id -
1031
1032 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1033 with the first part TYPETEXT! All newlines in the text here also end with
1034 CRLF.
1035
1036 Returns 0 on success, -1 on failure.
1037 ----*/
1038 int
pine_simple_send(ENVELOPE * outgoing,struct mail_bodystruct ** body,ACTION_S ** rolep,char * prmpt_who,char * prmpt_cnf,char ** used_tobufval,int flagsarg)1039 pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */
1040 struct mail_bodystruct **body,
1041 ACTION_S **rolep,
1042 char *prmpt_who,
1043 char *prmpt_cnf,
1044 char **used_tobufval,
1045 int flagsarg)
1046 {
1047 char **tobufp, *p, tmp[MAILTMPLEN];
1048 void *messagebuf;
1049 int done = 0, retval = 0, x;
1050 int lastrc, rc = 0, ku, i, resize_len, result, fcc_result = 0;
1051 int og2s_done = 0;
1052 HelpType help;
1053 static HISTORY_S *history = NULL;
1054 ESCKEY_S ekey[6];
1055 BUILDER_ARG ba_fcc;
1056 METAENV *header;
1057 ACTION_S *role = rolep ? *rolep : NULL;
1058 PAT_STATE pstate;
1059
1060 dprint((1,"\n === simple send called === \n"));
1061
1062 memset(&ba_fcc, 0, sizeof(BUILDER_ARG));
1063
1064 init_hist(&history, HISTSIZE);
1065
1066 header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp);
1067
1068 /*----- Fill in a few general parts of the envelope ----*/
1069 if(!outgoing->date){
1070 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1071 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
1072
1073 rfc822_date(tmp_20k_buf); /* format and copy new date */
1074 if(F_ON(F_QUELL_TIMEZONE, ps_global))
1075 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
1076
1077 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
1078 }
1079
1080 if(!outgoing->from){
1081 if(role && role->from){
1082 if(ps_global->never_allow_changing_from)
1083 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
1084 else
1085 outgoing->from = copyaddrlist(role->from);
1086 }
1087 else
1088 outgoing->from = generate_from();
1089 }
1090
1091 if(!(flagsarg & SS_NULLRP))
1092 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1093
1094 ekey[i = 0].ch = ctrl('T');
1095 ekey[i].rval = 2;
1096 ekey[i].name = "^T";
1097 ekey[i++].label = N_("To AddrBk");
1098
1099 if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
1100 ekey[i].ch = ctrl('I');
1101 ekey[i].rval = 11;
1102 ekey[i].name = "TAB";
1103 ekey[i++].label = N_("Complete");
1104 }
1105
1106 if(nonempty_patterns(ROLE_DO_ROLES, &pstate) && first_pattern(&pstate)){
1107 ekey[i].ch = ctrl('R');
1108 ekey[i].rval = 15;
1109 ekey[i].name = "^R";
1110 ekey[i++].label = "Set Role";
1111 }
1112
1113 ekey[i].ch = KEY_UP;
1114 ekey[i].rval = 30;
1115 ekey[i].name = "";
1116 ku = i;
1117 ekey[i++].label = "";
1118
1119 ekey[i].ch = KEY_DOWN;
1120 ekey[i].rval = 31;
1121 ekey[i].name = "";
1122 ekey[i++].label = "";
1123
1124 ekey[i].ch = -1;
1125
1126 if(outgoing->remail == NULL)
1127 strcpy(tmp, _("FORWARD (as e-mail) to : "));
1128
1129 /*----------------------------------------------------------------------
1130 Loop editing the "To: " field until everything goes well
1131 ----*/
1132 help = NO_HELP;
1133
1134 while(!done){
1135 int flags;
1136
1137 if(outgoing->message_id)
1138 fs_give((void **) &outgoing->message_id);
1139
1140 outgoing->message_id = generate_message_id(role);
1141
1142 if(outgoing->remail){
1143 if(role)
1144 snprintf(tmp, sizeof(tmp), _("BOUNCE (redirect) message using role \"%s\" to : "), role->nick);
1145 else
1146 strncpy(tmp, _("BOUNCE (redirect) message to : "), sizeof(tmp));
1147 tmp[sizeof(tmp)-1] = '\0';
1148 }
1149
1150 if(!og2s_done){
1151 og2s_done++;
1152 outgoing2strings(header, *body, &messagebuf, NULL, 1);
1153 }
1154
1155 lastrc = rc;
1156 if(flagsarg & SS_PROMPTFORTO){
1157 if(!*tobufp)
1158 *tobufp = cpystr("");
1159
1160 resize_len = MAX(MAXPATH, strlen(*tobufp));
1161 fs_resize((void **) tobufp, resize_len+1);
1162
1163 if(items_in_hist(history) > 0){
1164 ekey[ku].name = HISTORY_UP_KEYNAME;
1165 ekey[ku].label = HISTORY_KEYLABEL;
1166 ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
1167 ekey[ku+1].label = HISTORY_KEYLABEL;
1168 }
1169 else{
1170 ekey[ku].name = "";
1171 ekey[ku].label = "";
1172 ekey[ku+1].name = "";
1173 ekey[ku+1].label = "";
1174 }
1175
1176 flags = OE_APPEND_CURRENT;
1177
1178 rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
1179 0, resize_len,
1180 prmpt_who
1181 ? prmpt_who
1182 : tmp,
1183 ekey, help, &flags);
1184 }
1185 else
1186 rc = 0;
1187
1188 switch(rc){
1189 case -1:
1190 q_status_message(SM_ORDER | SM_DING, 3, 4,
1191 "Internal problem encountered");
1192 retval = -1;
1193 done++;
1194 break;
1195
1196 case 15 : /* set a role */
1197 {void (*prev_screen)(struct pine *) = NULL, (*redraw)(void) = NULL;
1198
1199 redraw = ps_global->redrawer;
1200 ps_global->redrawer = NULL;
1201 prev_screen = ps_global->prev_screen;
1202 role = NULL;
1203 ps_global->next_screen = SCREEN_FUN_NULL;
1204
1205 if(role_select_screen(ps_global, &role,
1206 outgoing->remail ? MC_BOUNCE : MC_FORWARD) < 0)
1207 cmd_cancelled(_("Set Role"));
1208 else{
1209 if(role)
1210 role = combine_inherited_role(role);
1211 else{
1212 role = (ACTION_S *) fs_get(sizeof(*role));
1213 memset((void *) role, 0, sizeof(*role));
1214 role->nick = cpystr("Default Role");
1215 }
1216 }
1217
1218 if(redraw)
1219 (*redraw)();
1220
1221 ps_global->next_screen = prev_screen;
1222 ps_global->redrawer = redraw;
1223 ps_global->mangled_screen = 1;
1224
1225 if(role && role->from && !ps_global->never_allow_changing_from){
1226 mail_free_address (&outgoing->from);
1227 outgoing->from = copyaddrlist(role->from);
1228 if(!(flagsarg & SS_NULLRP)){
1229 fs_give((void **) &outgoing->return_path);
1230 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
1231 }
1232 }
1233 if(rolep) *rolep = role;
1234 }
1235 break;
1236
1237 case 30 :
1238 if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){
1239 strncpy(*tobufp, p, resize_len);
1240 (*tobufp)[resize_len-1] = '\0';
1241 }
1242 else
1243 Writechar(BELL, 0);
1244
1245 break;
1246
1247 case 31 :
1248 if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){
1249 strncpy(*tobufp, p, resize_len);
1250 (*tobufp)[resize_len-1] = '\0';
1251 }
1252 else
1253 Writechar(BELL, 0);
1254
1255 break;
1256
1257 case 2: /* ^T */
1258 case 0:
1259 {void (*redraw) (void) = ps_global->redrawer;
1260 char *returned_addr = NULL;
1261 int len, l;
1262
1263 if(rc == 2){
1264 int got_something = 0;
1265
1266 push_titlebar_state();
1267 returned_addr = addr_book_bounce();
1268
1269 /*
1270 * Just make it look like user typed this list in.
1271 */
1272 if(returned_addr){
1273 got_something++;
1274 if((l=resize_len) < (len = strlen(returned_addr)) + 1){
1275 l = len;
1276 fs_resize((void **) tobufp, (size_t) (l+1));
1277 }
1278
1279 strncpy(*tobufp, returned_addr, l);
1280 (*tobufp)[l] = '\0';
1281 fs_give((void **)&returned_addr);
1282 }
1283
1284 ClearScreen();
1285 pop_titlebar_state();
1286 redraw_titlebar();
1287 if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
1288 (*ps_global->redrawer)();
1289
1290 if(!got_something)
1291 continue;
1292 }
1293
1294 if(*tobufp && **tobufp != '\0'){
1295 char *errbuf, *addr;
1296 int tolen;
1297
1298 save_hist(history, *tobufp, 0, NULL);
1299
1300 errbuf = NULL;
1301
1302 /*
1303 * If role has an fcc, use it instead of what build_address
1304 * tells us.
1305 */
1306 if(role && role->fcc){
1307 if(ba_fcc.tptr)
1308 fs_give((void **) &ba_fcc.tptr);
1309
1310 ba_fcc.tptr = cpystr(role->fcc);
1311 }
1312
1313 if(build_address(*tobufp, &addr, &errbuf,
1314 (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){
1315 int sendit = 0;
1316
1317 if(errbuf)
1318 fs_give((void **)&errbuf);
1319
1320 if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){
1321 l = tolen;
1322 fs_resize((void **) tobufp, (size_t) (l+1));
1323 }
1324
1325 strncpy(*tobufp, addr, l);
1326 (*tobufp)[l] = '\0';
1327 if(used_tobufval)
1328 *used_tobufval = cpystr(addr);
1329
1330 /* confirm address */
1331 if(flagsarg & SS_PROMPTFORTO){
1332 char dsn_string[30];
1333 int dsn_label = 0, dsn_show, i;
1334 int verbose_label = 0;
1335 ESCKEY_S opts[13];
1336
1337 strings2outgoing(header, body, NULL, 0);
1338
1339 if((flagsarg & SS_PROMPTFORTO)
1340 && ((x = check_addresses(header)) == CA_BAD
1341 || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE,
1342 ps_global))))
1343 /*--- Addresses didn't check out---*/
1344 continue;
1345
1346 i = 0;
1347 opts[i].ch = 'y';
1348 opts[i].rval = 'y';
1349 opts[i].name = "Y";
1350 opts[i++].label = N_("Yes");
1351
1352 opts[i].ch = 'n';
1353 opts[i].rval = 'n';
1354 opts[i].name = "N";
1355 opts[i++].label = N_("No");
1356
1357 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
1358 if(F_ON(F_VERBOSE_POST, ps_global)){
1359 /* setup keymenu slot to toggle verbose mode */
1360 opts[i].ch = ctrl('W');
1361 opts[i].rval = 12;
1362 opts[i].name = "^W";
1363 verbose_label = i++;
1364 if(F_ON(F_DSN, ps_global)){
1365 opts[i].ch = 0;
1366 opts[i].rval = 0;
1367 opts[i].name = "";
1368 opts[i++].label = "";
1369 }
1370 }
1371
1372 /* clear DSN flags */
1373 call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL);
1374 if(F_ON(F_DSN, ps_global)){
1375 /* setup keymenu slots to toggle dsn bits */
1376 opts[i].ch = 'd';
1377 opts[i].rval = 'd';
1378 opts[i].name = "D";
1379 opts[i].label = "DSNOpts";
1380 dsn_label = i++;
1381 opts[i].ch = -2;
1382 opts[i].rval = 's';
1383 opts[i].name = "S";
1384 opts[i++].label = "";
1385 opts[i].ch = -2;
1386 opts[i].rval = 'x';
1387 opts[i].name = "X";
1388 opts[i++].label = "";
1389 opts[i].ch = -2;
1390 opts[i].rval = 'h';
1391 opts[i].name = "H";
1392 opts[i++].label = "";
1393 }
1394
1395 opts[i].ch = -1;
1396
1397 while(1){
1398 int rv;
1399
1400 dsn_show = (call_mailer_flags & CM_DSN_SHOW);
1401 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
1402 "%s%s%s%s%s%sto \"%s\" ? ",
1403 prmpt_cnf ? prmpt_cnf : "Send message ",
1404 ((call_mailer_flags & CM_VERBOSE)
1405 || (dsn_show))
1406 ? "(" : "",
1407 (call_mailer_flags & CM_VERBOSE)
1408 ? "in verbose mode" : "",
1409 (dsn_show && (call_mailer_flags & CM_VERBOSE))
1410 ? ", " : "",
1411 (dsn_show) ? dsn_string : "",
1412 ((call_mailer_flags & CM_VERBOSE) || dsn_show)
1413 ? ") " : "",
1414 (addr && *addr)
1415 ? addr
1416 : (F_ON(F_FCC_ON_BOUNCE, ps_global)
1417 && ba_fcc.tptr && ba_fcc.tptr[0])
1418 ? ba_fcc.tptr
1419 : "");
1420 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1421
1422 if((strlen(tmp_20k_buf) >
1423 ps_global->ttyo->screen_cols - 2) &&
1424 ps_global->ttyo->screen_cols >= 7)
1425 strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7,
1426 "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7);
1427
1428 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1429
1430 if(verbose_label)
1431 opts[verbose_label].label =
1432 /* TRANSLATORS: several possible key labels follow */
1433 (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
1434
1435 if(F_ON(F_DSN, ps_global)){
1436 if(call_mailer_flags & CM_DSN_SHOW){
1437 opts[dsn_label].label =
1438 (call_mailer_flags & CM_DSN_DELAY)
1439 ? N_("NoDelay") : N_("Delay");
1440 opts[dsn_label+1].ch = 's';
1441 opts[dsn_label+1].label =
1442 (call_mailer_flags & CM_DSN_SUCCESS)
1443 ? N_("NoSuccess") : N_("Success");
1444 opts[dsn_label+2].ch = 'x';
1445 opts[dsn_label+2].label =
1446 (call_mailer_flags & CM_DSN_NEVER)
1447 ? N_("ErrRets") : N_("NoErrRets");
1448 opts[dsn_label+3].ch = 'h';
1449 opts[dsn_label+3].label =
1450 (call_mailer_flags & CM_DSN_FULL)
1451 ? N_("RetHdrs") : N_("RetFull");
1452 }
1453 }
1454
1455 rv = radio_buttons(tmp_20k_buf,
1456 -FOOTER_ROWS(ps_global), opts,
1457 'y', 'z', NO_HELP, RB_NORM);
1458 if(rv == 'y'){ /* user ACCEPTS! */
1459 sendit = 1;
1460 break;
1461 }
1462 else if(rv == 'n'){ /* Declined! */
1463 break;
1464 }
1465 else if(rv == 'z'){ /* Cancelled! */
1466 break;
1467 }
1468 else if(rv == 12){ /* flip verbose bit */
1469 if(call_mailer_flags & CM_VERBOSE)
1470 call_mailer_flags &= ~CM_VERBOSE;
1471 else
1472 call_mailer_flags |= CM_VERBOSE;
1473 }
1474 else if(call_mailer_flags & CM_DSN_SHOW){
1475 if(rv == 's'){ /* flip success bit */
1476 call_mailer_flags ^= CM_DSN_SUCCESS;
1477 /* turn off related bits */
1478 if(call_mailer_flags & CM_DSN_SUCCESS)
1479 call_mailer_flags &= ~(CM_DSN_NEVER);
1480 }
1481 else if(rv == 'd'){ /* flip delay bit */
1482 call_mailer_flags ^= CM_DSN_DELAY;
1483 /* turn off related bits */
1484 if(call_mailer_flags & CM_DSN_DELAY)
1485 call_mailer_flags &= ~(CM_DSN_NEVER);
1486 }
1487 else if(rv == 'x'){ /* flip never bit */
1488 call_mailer_flags ^= CM_DSN_NEVER;
1489 /* turn off related bits */
1490 if(call_mailer_flags & CM_DSN_NEVER)
1491 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
1492 }
1493 else if(rv == 'h'){ /* flip full bit */
1494 call_mailer_flags ^= CM_DSN_FULL;
1495 }
1496 }
1497 else if(rv == 'd'){ /* show dsn options */
1498 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
1499 }
1500
1501 snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"),
1502 (call_mailer_flags & CM_DSN_NEVER)
1503 ? _("Never") : "F",
1504 (call_mailer_flags & CM_DSN_DELAY)
1505 ? "D" : "",
1506 (call_mailer_flags & CM_DSN_SUCCESS)
1507 ? "S" : "",
1508 (call_mailer_flags & CM_DSN_NEVER)
1509 ? ""
1510 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
1511 : "-Hdrs");
1512 dsn_string[sizeof(dsn_string)-1] = '\0';
1513 }
1514 }
1515
1516 if(addr)
1517 fs_give((void **)&addr);
1518
1519 if(!(flagsarg & SS_PROMPTFORTO) || sendit){
1520 char *fcc = NULL;
1521 CONTEXT_S *fcc_cntxt = NULL;
1522
1523 if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
1524 if(ba_fcc.tptr)
1525 fcc = cpystr(ba_fcc.tptr);
1526
1527 set_last_fcc(fcc);
1528
1529 /*
1530 * If special name "inbox" then replace it with the
1531 * real inbox path.
1532 */
1533 if(ps_global->VAR_INBOX_PATH
1534 && strucmp(fcc, ps_global->inbox_name) == 0){
1535 char *replace_fcc;
1536
1537 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
1538 fs_give((void **) &fcc);
1539 fcc = replace_fcc;
1540 }
1541 }
1542
1543 /*---- Check out fcc -----*/
1544 if(fcc && *fcc){
1545 (void) commence_fcc(fcc, &fcc_cntxt, FALSE);
1546 if(!lmc.so){
1547 dprint((4,"can't open fcc, cont\n"));
1548 if(!(flagsarg & SS_PROMPTFORTO)){
1549 retval = -1;
1550 fs_give((void **)&fcc);
1551 fcc = NULL;
1552 goto finish;
1553 }
1554 else
1555 continue;
1556 }
1557 else
1558 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
1559 }
1560 else
1561 lmc.so = NULL;
1562
1563 if(!(outgoing->to || outgoing->cc || outgoing->bcc
1564 || lmc.so)){
1565 q_status_message(SM_ORDER, 3, 5, _("No recipients specified!"));
1566 continue;
1567 }
1568
1569 if(outgoing->to || outgoing->cc || outgoing->bcc){
1570 char **alt_smtp = NULL;
1571
1572 if(role && role->smtp){
1573 if(ps_global->FIX_SMTP_SERVER
1574 && ps_global->FIX_SMTP_SERVER[0])
1575 q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
1576 else
1577 alt_smtp = role->smtp;
1578 }
1579
1580 result = call_mailer(header, *body, alt_smtp,
1581 call_mailer_flags,
1582 call_mailer_file_result,
1583 pipe_callback);
1584 mark_address_failure_for_pico(header);
1585 }
1586 else
1587 result = 0;
1588
1589 if(result == 1 && !lmc.so)
1590 q_status_message(SM_ORDER, 0, 3, _("Message sent"));
1591
1592 /*----- Was there an fcc involved? -----*/
1593 if(lmc.so){
1594 if(result == 1
1595 || (result == 0
1596 && pine_rfc822_output(header, *body, NULL, NULL))){
1597 char label[50];
1598
1599 strncpy(label, "Fcc", sizeof(label));
1600 label[sizeof(label)-1] = '\0';
1601 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
1602 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
1603 label[sizeof(label)-1] = '\0';
1604 }
1605
1606 /* Now actually copy to fcc folder and close */
1607 fcc_result =
1608 write_fcc(fcc, fcc_cntxt, lmc.so, NULL,
1609 label,
1610 F_ON(F_MARK_FCC_SEEN, ps_global)
1611 ? "\\SEEN" : NULL);
1612 }
1613 else if(result == 0){
1614 q_status_message(SM_ORDER,3,5,
1615 _("Fcc Failed!. No message saved."));
1616 retval = -1;
1617 dprint((1, "explicit fcc write failed!\n"));
1618 }
1619
1620 so_give(&lmc.so);
1621 }
1622
1623 if(result < 0){
1624 dprint((1, "Bounce failed\n"));
1625 if(!(flagsarg & SS_PROMPTFORTO))
1626 retval = -1;
1627 else
1628 continue;
1629 }
1630 else if(result == 1){
1631 if(!fcc)
1632 q_status_message(SM_ORDER, 0, 3,
1633 _("Message sent"));
1634 else{
1635 int avail = ps_global->ttyo->screen_cols-2;
1636 int need, fcclen;
1637 char *part1 = "Message sent and ";
1638 char *part2 = fcc_result ? "" : "NOT ";
1639 char *part3 = "copied to ";
1640 fcclen = strlen(fcc);
1641
1642 need = 2 + strlen(part1) + strlen(part2) +
1643 strlen(part3) + fcclen;
1644
1645 if(need > avail && fcclen > 6)
1646 fcclen -= MIN(fcclen-6, need-avail);
1647
1648 q_status_message4(SM_ORDER, 0, 3,
1649 "%s%s%s\"%s\"",
1650 part1, part2, part3,
1651 short_str(fcc,
1652 (char *)tmp_20k_buf,
1653 SIZEOF_20KBUF,
1654 fcclen, FrontDots));
1655 }
1656 }
1657
1658 if(fcc)
1659 fs_give((void **)&fcc);
1660 }
1661 else{
1662 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1663 retval = -1;
1664 }
1665 }
1666 else{
1667 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1668 _("Error in address: %s"), errbuf);
1669 if(errbuf)
1670 fs_give((void **)&errbuf);
1671
1672 if(!(flagsarg & SS_PROMPTFORTO))
1673 retval = -1;
1674 else
1675 continue;
1676 }
1677
1678 }
1679 else{
1680 q_status_message(SM_ORDER | SM_DING, 3, 5,
1681 _("No addressee! No e-mail sent."));
1682 retval = -1;
1683 }
1684 }
1685
1686 done++;
1687 break;
1688
1689 case 1:
1690 q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
1691 done++;
1692 retval = -1;
1693 break;
1694
1695 case 3:
1696 help = (help == NO_HELP)
1697 ? (outgoing->remail == NULL
1698 ? h_anon_forward
1699 : h_bounce)
1700 : NO_HELP;
1701 break;
1702
1703 case 11:
1704 if(**tobufp){
1705 char *new_nickname = NULL;
1706 int l;
1707 int ambiguity;
1708
1709 ambiguity = abook_nickname_complete(*tobufp, &new_nickname,
1710 (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA);
1711 if(new_nickname){
1712 if(*new_nickname){
1713 if((l=strlen(new_nickname)) > resize_len){
1714 resize_len = l;
1715 fs_resize((void **) tobufp, resize_len+1);
1716 }
1717
1718 strncpy(*tobufp, new_nickname, l);
1719 (*tobufp)[l] = '\0';
1720 }
1721
1722 fs_give((void **) &new_nickname);
1723 }
1724
1725 if(ambiguity != 2)
1726 Writechar(BELL, 0);
1727 }
1728
1729 break;
1730
1731 case 4: /* can't suspend */
1732 default:
1733 break;
1734 }
1735 }
1736
1737 finish:
1738 if(ba_fcc.tptr)
1739 fs_give((void **)&ba_fcc.tptr);
1740
1741 pine_free_env(&header);
1742
1743 return(retval);
1744 }
1745
1746
1747 /*
1748 * pine_simple_send_header - generate header suitable for simple_sending
1749 */
1750 METAENV *
pine_simple_send_header(ENVELOPE * outgoing,char ** fccp,char *** tobufpp)1751 pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp)
1752 {
1753 METAENV *header;
1754 PINEFIELD *pf;
1755 static struct headerentry he_dummy;
1756
1757 header = pine_new_env(outgoing, fccp, tobufpp, NULL);
1758
1759 /* assign he_dummy to "To:" field "he" for strings2outgoing */
1760 for(pf = header->local; pf && pf->name; pf = pf->next)
1761 if(pf->type == Address && !strucmp(pf->name, "to")){
1762 memset((void *) &he_dummy, 0, sizeof(he_dummy));
1763 pf->extdata = (void *) &he_dummy;
1764 HE(pf)->dirty = 1;
1765 break;
1766 }
1767
1768 return(header);
1769 }
1770
1771
1772
1773 /*----------------------------------------------------------------------
1774 Prepare data structures for pico, call pico, then post message
1775
1776 Args: outgoing -- Partially formatted outgoing ENVELOPE
1777 body -- Body of outgoing message
1778 editor_title -- Title for anchor line in composer
1779 fcc_arg -- The file carbon copy field
1780 reply -- Struct describing set of msgs being replied-to
1781 lcc_arg --
1782 custom -- custom header list.
1783 sticky_fcc --
1784
1785 Result: message is edited, then postponed, cancelled or sent.
1786
1787 Fields:
1788 remail -
1789 return_path -
1790 date added here
1791 from added here
1792 sender -
1793 reply_to -
1794 subject passed in, edited and cannonized here
1795 to possibly passed in, edited and cannonized here
1796 cc possibly passed in, edited and cannonized here
1797 bcc edited and cannonized here
1798 in_reply_to generated in reply() and passed in
1799 message_id -
1800
1801 Storage for these fields comes from anywhere outside. It is remalloced
1802 here so the composer can realloc them if needed. The copies here are also
1803 freed here.
1804
1805 Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
1806 with the first part TYPETEXT! All newlines in the text here also end with
1807 CRLF.
1808
1809 There's a further assumption that the text in the TYPETEXT part is
1810 stored in a storage object (see filter.c).
1811 ----*/
1812 void
pine_send(ENVELOPE * outgoing,struct mail_bodystruct ** body,char * editor_title,ACTION_S * role,char * fcc_arg,REPLY_S * reply,REDRAFT_POS_S * redraft_pos,char * lcc_arg,PINEFIELD * custom,int flags)1813 pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
1814 char *editor_title, ACTION_S *role, char *fcc_arg,
1815 REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg,
1816 PINEFIELD *custom, int flags)
1817 {
1818 int i, fixed_cnt, total_cnt, index,
1819 editor_result = 0, body_start = 0, use_news_order = 0;
1820 char *p, *addr, *fcc, *fcc_to_free = NULL;
1821 char *start_here_name = NULL;
1822 char *suggested_nntp_server = NULL;
1823 char *title = NULL;
1824 struct headerentry *he, *headents, *he_to = NULL, *he_fcc = NULL, *he_news = NULL, *he_lcc = NULL,
1825 *he_from = NULL;
1826 PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL,
1827 *pf_smtp_server = NULL, *pf_nntp_server = NULL,
1828 *pf_fcc = NULL, *pf_err = NULL, *pf_uid = NULL, *pf_mbox = NULL, *pf_curpos = NULL,
1829 *pf_ourrep = NULL, *pf_ourhdrs = NULL, **sending_order;
1830 METAENV header;
1831 ADDRESS *lcc_addr = NULL;
1832 ADDRESS *nobody_addr = NULL;
1833 BODY_PARTICULARS_S *bp;
1834 STORE_S *orig_so = NULL;
1835 PICO pbuf1, *save_previous_pbuf;
1836 CustomType ct;
1837 REDRAFT_POS_S *local_redraft_pos = NULL;
1838
1839 dprint((1,"\n=== send called ===\n"));
1840
1841 save_previous_pbuf = pbf;
1842 pbf = &pbuf1;
1843 standard_picobuf_setup(pbf);
1844
1845 /*
1846 * Cancel any pending initial commands since pico uses a different
1847 * input routine. If we didn't cancel them, they would happen after
1848 * we returned from the editor, which would be confusing.
1849 */
1850 if(ps_global->in_init_seq){
1851 ps_global->in_init_seq = 0;
1852 ps_global->save_in_init_seq = 0;
1853 clear_cursor_pos();
1854 if(ps_global->initial_cmds){
1855 if(ps_global->free_initial_cmds)
1856 fs_give((void **)&(ps_global->free_initial_cmds));
1857
1858 ps_global->initial_cmds = 0;
1859 }
1860
1861 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1862 }
1863
1864 #if defined(DOS) || defined(OS2)
1865 if(!dos_valid_from()){
1866 pbf = save_previous_pbuf;
1867 return;
1868 }
1869
1870 pbf->upload = NULL;
1871 #else
1872 pbf->upload = (ps_global->VAR_UPLOAD_CMD
1873 && ps_global->VAR_UPLOAD_CMD[0])
1874 ? upload_msg_to_pico : NULL;
1875 #endif
1876
1877 pbf->msgntext = message_format_for_pico;
1878 pbf->mimetype = mime_type_for_pico;
1879 pbf->exittest = send_exit_for_pico;
1880 pbf->user_says_noflow = dont_flow_this_time;
1881 pbf->newthread = new_thread_on_blank_subject;
1882 ps_global->newthread = 0; /* reset this value */
1883 if(F_OFF(F_CANCEL_CONFIRM, ps_global))
1884 pbf->canceltest = cancel_for_pico;
1885 #ifdef _WINDOWS
1886 pbf->dict = (ps_global->VAR_DICTIONARY
1887 && ps_global->VAR_DICTIONARY[0]
1888 && ps_global->VAR_DICTIONARY[0][0])
1889 ? ps_global->VAR_DICTIONARY : NULL;
1890 pbf->chosen_dict = -1; /* not chosen yet */
1891 #endif /* _WINDOWS */
1892 pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] &&
1893 ps_global->VAR_EDITOR[0][0])
1894 ? ps_global->VAR_EDITOR : NULL;
1895 pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0])
1896 ? ps_global->VAR_SPELLER : NULL;
1897 pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
1898 pbf->quote_str = reply && reply->prefix ? reply->prefix : "> ";
1899 /* We actually want to set this only if message we're sending is flowed */
1900 pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
1901 pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
1902 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
1903 && (strcmp(pbf->quote_str, "> ") == 0
1904 || strcmp(pbf->quote_str, ">") == 0));
1905 pbf->edit_offset = 0;
1906 title = cpystr(set_titlebar(editor_title,
1907 ps_global->mail_stream,
1908 ps_global->context_current,
1909 ps_global->cur_folder,ps_global->msgmap,
1910 0, FolderName, 0, 0, NULL));
1911 pbf->pine_anchor = title;
1912
1913 #if defined(DOS) || defined(OS2)
1914 if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){
1915 pbf->oper_dir = ps_global->VAR_FILE_DIR;
1916 }
1917 #endif
1918
1919 if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE))
1920 pbf->pine_flags |= P_CHKPTNOW;
1921
1922 /* NOTE: initial cursor position set below */
1923
1924 dprint((9, "flags: %x\n", pbf->pine_flags));
1925
1926 /*
1927 * When user runs compose and the current folder is a newsgroup,
1928 * offer to post to the current newsgroup.
1929 */
1930 if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups))
1931 && IS_NEWS(ps_global->mail_stream)){
1932 char prompt[200], news_group[MAILTMPLEN];
1933
1934 pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group,
1935 sizeof(news_group));
1936
1937 /*
1938 * Replies don't get this far because To or Newsgroups will already
1939 * be filled in. So must be either ordinary compose or forward.
1940 * Forward sets subject, so use that to tell the difference.
1941 */
1942 if(news_group[0] && !outgoing->subject){
1943 int ch = 'y';
1944 int ret_val;
1945 char *errmsg = NULL;
1946 BUILDER_ARG *fcc_build = NULL;
1947
1948 if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
1949 snprintf(prompt, sizeof(prompt),
1950 _("Post to current newsgroup (%s)"), news_group);
1951 prompt[sizeof(prompt)-1] = '\0';
1952 ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM);
1953 }
1954
1955 switch(ch){
1956 case 'y':
1957 if(outgoing->newsgroups)
1958 fs_give((void **)&outgoing->newsgroups);
1959
1960 if(!fcc_arg && !(role && role->fcc)){
1961 fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
1962 memset((void *)fcc_build, 0, sizeof(BUILDER_ARG));
1963 fcc_build->tptr = fcc_to_free;
1964 }
1965
1966 ret_val = news_build(news_group, &outgoing->newsgroups,
1967 &errmsg, fcc_build, NULL);
1968
1969 if(ret_val == -1){
1970 if(outgoing->newsgroups)
1971 fs_give((void **)&outgoing->newsgroups);
1972
1973 outgoing->newsgroups = cpystr(news_group);
1974 }
1975
1976 if(!fcc_arg && !(role && role->fcc)){
1977 fcc_arg = fcc_to_free = fcc_build->tptr;
1978 fs_give((void **)&fcc_build);
1979 }
1980
1981 if(errmsg){
1982 if(*errmsg){
1983 q_status_message(SM_ORDER, 3, 3, errmsg);
1984 display_message(NO_OP_COMMAND);
1985 }
1986
1987 fs_give((void **)&errmsg);
1988 }
1989
1990 break;
1991
1992 case 'x': /* ^C */
1993 q_status_message(SM_ORDER, 0, 3, _("Message cancelled"));
1994 dprint((4, "=== send: cancelled\n"));
1995 pbf = save_previous_pbuf;
1996 return;
1997
1998 case 'n':
1999 break;
2000
2001 default:
2002 break;
2003 }
2004 }
2005 }
2006 if(F_ON(F_PREDICT_NNTP_SERVER, ps_global)
2007 && outgoing->newsgroups && *outgoing->newsgroups
2008 && IS_NEWS(ps_global->mail_stream)){
2009 NETMBX news_mb;
2010
2011 if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox,
2012 &news_mb))
2013 if(!strucmp(news_mb.service, "nntp")){
2014 if(*ps_global->mail_stream->original_mailbox == '{'){
2015 char *svcp = NULL, *psvcp;
2016
2017 suggested_nntp_server =
2018 cpystr(ps_global->mail_stream->original_mailbox + 1);
2019 if((p = strindex(suggested_nntp_server, '}')) != NULL)
2020 *p = '\0';
2021 for(p = strindex(suggested_nntp_server, '/'); p && *p;
2022 p = strindex(p, '/')){
2023 /* take out /nntp, which gets added in nntp_open */
2024 if(!struncmp(p, "/nntp", 5))
2025 svcp = p + 5;
2026 else if(!struncmp(p, "/service=nntp", 13))
2027 svcp = p + 13;
2028 else if(!struncmp(p, "/service=\"nntp\"", 15))
2029 svcp = p + 15;
2030 else
2031 p++;
2032 if(svcp){
2033 if(*svcp == '\0')
2034 *p = '\0';
2035 else if(*svcp == '/' || *svcp == ':'){
2036 for(psvcp = p; *svcp; svcp++, psvcp++)
2037 *psvcp = *svcp;
2038 *psvcp = '\0';
2039 }
2040 svcp = NULL;
2041 }
2042 }
2043 }
2044 else
2045 suggested_nntp_server = cpystr(news_mb.orighost);
2046 }
2047 }
2048
2049 /*
2050 * If we don't already have custom headers set and the role has custom
2051 * headers, then incorporate those custom headers into "custom".
2052 */
2053 if(!custom){
2054 PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL;
2055
2056 dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
2057 /*
2058 * If we allow the Combine argument here, we're saying that we want to
2059 * combine the values from the envelope and the role for the fields To,
2060 * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
2061 * we'll have a To in the envelope because we're replying. If our role also
2062 * has a To action, then Combine would combine those two and offer both
2063 * to the user. We've decided against doing this. Instead, we always use
2064 * Replace, and the role's header value replaces the value from the
2065 * envelope. It might also make sense in some cases to do the opposite,
2066 * which would be treating the role headers as defaults, just like
2067 * customized-hdrs.
2068 */
2069 #ifdef WANT_TO_COMBINE_ADDRESSES
2070 if(role && role->cstm)
2071 rolehdrs = parse_custom_hdrs(role->cstm, Combine);
2072 #else
2073 if(role && role->cstm)
2074 rolehdrs = parse_custom_hdrs(role->cstm, Replace);
2075 #endif
2076
2077 if(rolehdrs){
2078 custom = combine_custom_headers(dflthdrs, rolehdrs);
2079 if(dflthdrs){
2080 free_prompts(dflthdrs);
2081 free_customs(dflthdrs);
2082 }
2083
2084 if(rolehdrs){
2085 free_prompts(rolehdrs);
2086 free_customs(rolehdrs);
2087 }
2088 }
2089 else
2090 custom = dflthdrs;
2091 }
2092
2093 g_rolenick = role ? role->nick : NULL;
2094
2095 /* how many fixed fields are there? */
2096 for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++)
2097 ;
2098
2099 total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1);
2100
2101 /* the fixed part of the PINEFIELDs */
2102 i = fixed_cnt * sizeof(PINEFIELD);
2103 pfields = (PINEFIELD *)fs_get((size_t) i);
2104 memset(pfields, 0, (size_t) i);
2105
2106 /* temporary headerentry array for pico */
2107 i = (total_cnt + 1) * sizeof(struct headerentry);
2108 headents = (struct headerentry *)fs_get((size_t) i);
2109 memset(headents, 0, (size_t) i);
2110
2111 i = total_cnt * sizeof(PINEFIELD *);
2112 sending_order = (PINEFIELD **)fs_get((size_t) i);
2113 memset(sending_order, 0, (size_t) i);
2114
2115 pbf->headents = headents;
2116 header.env = outgoing;
2117 header.local = pfields;
2118 header.sending_order = sending_order;
2119
2120 /* custom part of PINEFIELDs */
2121 header.custom = custom;
2122
2123 he = headents;
2124 pf = pfields;
2125
2126 /*
2127 * For Address types, pf->addr points to an ADDRESS *.
2128 * If that address is in the "outgoing" envelope, it will
2129 * be freed by the caller, otherwise, it should be freed here.
2130 * Pf->textbuf for an Address is used a little to set up a default,
2131 * but then is freed right away below. Pf->scratch is used for a
2132 * pointer to some alloced space for pico to edit in. Addresses in
2133 * the custom area are freed by free_customs().
2134 *
2135 * For FreeText types, pf->addr is not used. Pf->text points to a
2136 * pointer that points to the text. Pf->textbuf points to a copy of
2137 * the text that must be freed before we leave, otherwise, it is
2138 * probably a pointer into the envelope and that gets freed by the
2139 * caller.
2140 *
2141 * He->realaddr is the pointer to the text that pico actually edits.
2142 */
2143
2144 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2145 # define NN 4
2146 #else
2147 # define NN 3
2148 #endif
2149
2150 if(outgoing->newsgroups && *outgoing->newsgroups)
2151 use_news_order++;
2152
2153 /* initialize the fixed header elements of the two temp arrays */
2154 for(i=0; i < fixed_cnt; i++, pf++){
2155 static int news_order[] = {
2156 N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC,
2157 N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY,
2158 N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX,
2159 N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS
2160 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2161 , N_SENDER
2162 #endif
2163 };
2164
2165 index = i;
2166 /* slightly different editing order if sending to news */
2167 if(use_news_order &&
2168 index >= 0 && index < sizeof(news_order)/sizeof(news_order[0]))
2169 index = news_order[i];
2170
2171 /* copy the templates */
2172 *he = he_template[index];
2173
2174 pf->name = cpystr(pf_template[index].name);
2175 if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global)){
2176 /* slide string over so it is Sender instead of X-X-Sender */
2177 for(p = pf->name+4; *p != '\0'; p++)
2178 *(p-4) = *p;
2179 *(p-4) = '\0';
2180 }
2181 pf->type = pf_template[index].type;
2182 pf->canedit = pf_template[index].canedit;
2183 pf->rcptto = pf_template[index].rcptto;
2184 pf->writehdr = pf_template[index].writehdr;
2185 pf->localcopy = pf_template[index].localcopy;
2186 pf->extdata = he;
2187 pf->next = pf + 1;
2188
2189 he->rich_header = view_as_rich(pf->name, he->rich_header);
2190 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2191 he->nickcmpl = NULL;
2192
2193 switch(pf->type){
2194 case FreeText: /* realaddr points to c-client env */
2195 if(index == N_NEWS){
2196 sending_order[1] = pf;
2197 he->realaddr = &outgoing->newsgroups;
2198 he_news = he;
2199
2200 switch(set_default_hdrval(pf, custom)){
2201 case Replace:
2202 if(*he->realaddr)
2203 fs_give((void **)he->realaddr);
2204
2205 *he->realaddr = pf->textbuf;
2206 pf->textbuf = NULL;
2207 he->sticky = 1;
2208 break;
2209
2210 case Combine:
2211 if(*he->realaddr){ /* combine values */
2212 if(pf->textbuf && *pf->textbuf){
2213 char *combined_hdr;
2214 size_t l;
2215
2216 l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
2217 combined_hdr = (char *) fs_get((l+1) * sizeof(char));
2218 strncpy(combined_hdr, *he->realaddr, l);
2219 combined_hdr[l] = '\0';
2220 strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
2221 combined_hdr[l] = '\0';
2222 strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
2223 combined_hdr[l] = '\0';
2224
2225 fs_give((void **)he->realaddr);
2226 *he->realaddr = combined_hdr;
2227 q_status_message(SM_ORDER, 3, 3,
2228 "Adding newsgroup from role");
2229 he->sticky = 1;
2230 }
2231 }
2232 else{
2233 *he->realaddr = pf->textbuf;
2234 pf->textbuf = NULL;
2235 }
2236
2237 break;
2238
2239 case UseAsDef:
2240 /* if no value, use default */
2241 if(!*he->realaddr){
2242 *he->realaddr = pf->textbuf;
2243 pf->textbuf = NULL;
2244 }
2245
2246 break;
2247
2248 case NoMatch:
2249 break;
2250 }
2251
2252 /* If there is a newsgroup, we'd better show it */
2253 if(outgoing->newsgroups && *outgoing->newsgroups)
2254 he->rich_header = 0; /* force on by default */
2255
2256 if(pf->textbuf)
2257 fs_give((void **)&pf->textbuf);
2258
2259 pf->text = he->realaddr;
2260 }
2261 else if(index == N_DATE){
2262 sending_order[2] = pf;
2263 pf->text = (char **) &outgoing->date;
2264 pf->extdata = NULL;
2265 }
2266 else if(index == N_INREPLY){
2267 sending_order[NN+9] = pf;
2268 pf->text = &outgoing->in_reply_to;
2269 pf->extdata = NULL;
2270 }
2271 else if(index == N_MSGID){
2272 sending_order[NN+10] = pf;
2273 pf->text = &outgoing->message_id;
2274 pf->extdata = NULL;
2275 }
2276 else if(index == N_REF){
2277 sending_order[NN+11] = pf;
2278 pf->text = &outgoing->references;
2279 pf->extdata = NULL;
2280 }
2281 else if(index == N_PRIORITY){
2282 sending_order[NN+12] = pf;
2283 pf->text = &pf->textbuf;
2284 pf->extdata = NULL;
2285 }
2286 else if(index == N_USERAGENT){
2287 sending_order[NN+13] = pf;
2288 pf->text = &pf->textbuf;
2289 pf->textbuf = generate_user_agent();
2290 pf->extdata = NULL;
2291 }
2292 else if(index == N_POSTERR){
2293 sending_order[NN+14] = pf;
2294 pf_err = pf;
2295 pf->text = &pf->textbuf;
2296 pf->extdata = NULL;
2297 }
2298 else if(index == N_RPLUID){
2299 sending_order[NN+15] = pf;
2300 pf_uid = pf;
2301 pf->text = &pf->textbuf;
2302 pf->extdata = NULL;
2303 }
2304 else if(index == N_RPLMBOX){
2305 sending_order[NN+16] = pf;
2306 pf_mbox = pf;
2307 pf->text = &pf->textbuf;
2308 pf->extdata = NULL;
2309 }
2310 else if(index == N_SMTP){
2311 sending_order[NN+17] = pf;
2312 pf_smtp_server = pf;
2313 pf->text = &pf->textbuf;
2314 pf->extdata = NULL;
2315 }
2316 else if(index == N_NNTP){
2317 sending_order[NN+18] = pf;
2318 pf_nntp_server = pf;
2319 pf->text = &pf->textbuf;
2320 pf->extdata = NULL;
2321 }
2322 else if(index == N_CURPOS){
2323 sending_order[NN+19] = pf;
2324 pf_curpos = pf;
2325 pf->text = &pf->textbuf;
2326 pf->extdata = NULL;
2327 }
2328 else if(index == N_OURREPLYTO){
2329 sending_order[NN+20] = pf;
2330 pf_ourrep = pf;
2331 pf->text = &pf->textbuf;
2332 pf->extdata = NULL;
2333 }
2334 else if(index == N_OURHDRS){
2335 sending_order[NN+21] = pf;
2336 pf_ourhdrs = pf;
2337 pf->text = &pf->textbuf;
2338 pf->extdata = NULL;
2339 }
2340 else if(index == N_AUTHRCVD){
2341 sending_order[0] = pf;
2342 pf_ourhdrs = pf;
2343 pf->text = &pf->textbuf;
2344 pf->extdata = NULL;
2345 }
2346 else{
2347 q_status_message(SM_ORDER | SM_DING, 3, 7,
2348 "Botched: Unmatched FreeText header in pine_send");
2349 }
2350
2351 break;
2352
2353 /* can't do a default for this one */
2354 case Attachment:
2355 /* If there is an attachment already, we'd better show them */
2356 if(body && *body && (*body)->type != TYPETEXT)
2357 he->rich_header = 0; /* force on by default */
2358
2359 break;
2360
2361 case Address:
2362 switch(index){
2363 case N_FROM:
2364 sending_order[3] = pf;
2365 pf->addr = &outgoing->from;
2366 if(role && role->from){
2367 if(ps_global->never_allow_changing_from)
2368 q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
2369 else{
2370 outgoing->from = copyaddrlist(role->from);
2371 he->display_it = 1; /* show it */
2372 he->rich_header = 0;
2373 }
2374 }
2375
2376 he_from = he;
2377 break;
2378
2379 case N_TO:
2380 sending_order[NN+2] = pf;
2381 pf->addr = &outgoing->to;
2382 /* If already set, make it act like we typed it in */
2383 if(outgoing->to
2384 && outgoing->to->mailbox
2385 && outgoing->to->mailbox[0]
2386 && flags & PS_STICKY_TO)
2387 he->sticky = 1;
2388
2389 he_to = he;
2390 pf_to = pf;
2391 break;
2392
2393 case N_NOBODY:
2394 sending_order[NN+5] = pf;
2395 pf_nobody = pf;
2396 if(ps_global->VAR_EMPTY_HDR_MSG
2397 && !ps_global->VAR_EMPTY_HDR_MSG[0]){
2398 pf->addr = NULL;
2399 }
2400 else{
2401 nobody_addr = mail_newaddr();
2402 nobody_addr->next = mail_newaddr();
2403 nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
2404 SIZEOF_20KBUF,
2405 (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
2406 ? ps_global->VAR_EMPTY_HDR_MSG
2407 : "undisclosed-recipients"),
2408 ps_global->posting_charmap));
2409 pf->addr = &nobody_addr;
2410 }
2411
2412 break;
2413
2414 case N_CC:
2415 sending_order[NN+3] = pf;
2416 pf->addr = &outgoing->cc;
2417 break;
2418
2419 case N_BCC:
2420 sending_order[NN+4] = pf;
2421 pf->addr = &outgoing->bcc;
2422 /* if bcc exists, make sure it's exposed so nothing's
2423 * sent by mistake...
2424 */
2425 if(outgoing->bcc)
2426 he->display_it = 1;
2427
2428 break;
2429
2430 case N_REPLYTO:
2431 sending_order[NN+1] = pf;
2432 pf->addr = &outgoing->reply_to;
2433 if(role && role->replyto){
2434 if(outgoing->reply_to)
2435 mail_free_address(&outgoing->reply_to);
2436
2437 outgoing->reply_to = copyaddrlist(role->replyto);
2438 he->display_it = 1; /* show it */
2439 he->rich_header = 0;
2440 }
2441
2442 break;
2443
2444 case N_LCC:
2445 sending_order[NN+7] = pf;
2446 pf->addr = &lcc_addr;
2447 he_lcc = he;
2448 if(lcc_arg){
2449 build_address(lcc_arg, &addr, NULL, NULL, NULL);
2450 rfc822_parse_adrlist(&lcc_addr, addr,
2451 ps_global->maildomain);
2452 fs_give((void **)&addr);
2453 he->display_it = 1;
2454 }
2455
2456 break;
2457
2458 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
2459 case N_SENDER:
2460 sending_order[4] = pf;
2461 pf->addr = &outgoing->sender;
2462 break;
2463 #endif
2464
2465 default:
2466 q_status_message1(SM_ORDER,3,7,
2467 "Internal error: Address header %s", comatose(index));
2468 break;
2469 }
2470
2471 /*
2472 * If this is a reply to news, don't show the regular email
2473 * recipient headers (unless they are non-empty).
2474 */
2475 if((outgoing->newsgroups && *outgoing->newsgroups)
2476 && (index == N_TO || index == N_CC
2477 || index == N_BCC || index == N_LCC)
2478 && (pf->addr && !*pf->addr)){
2479 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2480 pf->textbuf && *pf->textbuf){
2481 removing_trailing_white_space(pf->textbuf);
2482 (void)removing_double_quotes(pf->textbuf);
2483 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2484 rfc822_parse_adrlist(pf->addr, addr,
2485 ps_global->maildomain);
2486 fs_give((void **)&addr);
2487 if(ct > UseAsDef)
2488 he->sticky = 1;
2489 }
2490 else
2491 he->rich_header = 1; /* hide */
2492 }
2493
2494 /*
2495 * If this address doesn't already have a value, then we check
2496 * for a default value assigned by the user.
2497 */
2498 else if(pf->addr && !*pf->addr){
2499 if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
2500 (index != N_FROM ||
2501 (!ps_global->never_allow_changing_from &&
2502 F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
2503 pf->textbuf && *pf->textbuf){
2504
2505 removing_trailing_white_space(pf->textbuf);
2506 (void)removing_double_quotes(pf->textbuf);
2507
2508 /*
2509 * Try to set To based on Lcc. Don't attempt Fcc.
2510 */
2511 if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
2512 BUILDER_ARG *barg = NULL;
2513 char *ppp = NULL;
2514
2515 if(*pf_to->addr)
2516 ppp = addr_list_string(*pf_to->addr, NULL, 1);
2517
2518 if(!ppp)
2519 ppp = cpystr("");
2520
2521 barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
2522 memset(barg, 0, sizeof(*barg));
2523 barg->me = &(he->bldr_private);
2524 barg->aff = &(he_to->bldr_private);
2525 barg->tptr = cpystr(ppp);
2526
2527 build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
2528 he->display_it = 1;
2529
2530 rfc822_parse_adrlist(pf->addr, addr,
2531 ps_global->maildomain);
2532 if(addr)
2533 fs_give((void **) &addr);
2534
2535 if(ct > UseAsDef)
2536 he->sticky = 1;
2537
2538 if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
2539 ADDRESS *a = NULL;
2540
2541 rfc822_parse_adrlist(&a, barg->tptr,
2542 ps_global->maildomain);
2543 if(a){
2544 if(pf_to->addr)
2545 mail_free_address(pf_to->addr);
2546
2547 *pf_to->addr = a;
2548 }
2549 }
2550
2551 if(barg){
2552 if(barg->tptr)
2553 fs_give((void **) &barg->tptr);
2554
2555 fs_give((void **) &barg);
2556 }
2557
2558 if(ppp)
2559 fs_give((void **) &ppp);
2560 }
2561 else{
2562 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2563 rfc822_parse_adrlist(pf->addr, addr,
2564 ps_global->maildomain);
2565 if(addr)
2566 fs_give((void **) &addr);
2567
2568 if(ct > UseAsDef)
2569 he->sticky = 1;
2570 }
2571 }
2572
2573 /* if we still don't have a from */
2574 if(index == N_FROM && !*pf->addr)
2575 *pf->addr = generate_from();
2576 }
2577
2578 /*
2579 * Addr is already set in the rest of the cases.
2580 */
2581 else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
2582 ADDRESS *adr = NULL;
2583
2584 /*
2585 * We get to this case of the ifelse if the from or reply-to
2586 * addr was set by a role above.
2587 */
2588
2589 /* figure out the default value */
2590 (void)set_default_hdrval(pf, custom);
2591 if(pf->textbuf && *pf->textbuf){
2592 removing_trailing_white_space(pf->textbuf);
2593 (void)removing_double_quotes(pf->textbuf);
2594 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2595 rfc822_parse_adrlist(&adr, addr,
2596 ps_global->maildomain);
2597 fs_give((void **)&addr);
2598 }
2599
2600 /* if value set by role is different from default, show it */
2601 if(adr && !address_is_same(*pf->addr, adr))
2602 he->display_it = 1; /* start this off showing */
2603
2604 /* malformed */
2605 if(!(*pf->addr)->mailbox){
2606 fs_give((void **)pf->addr);
2607 he->display_it = 1;
2608 }
2609
2610 if(adr)
2611 mail_free_address(&adr);
2612 }
2613 else if((index == N_TO || index == N_CC || index == N_BCC)
2614 && pf->addr){
2615 ADDRESS *a = NULL, **tail;
2616
2617 /*
2618 * These three are different from the others because we
2619 * might add the addresses to what is already there instead
2620 * of replacing.
2621 */
2622
2623 switch(set_default_hdrval(pf, custom)){
2624 case Replace:
2625 if(*pf->addr)
2626 mail_free_address(pf->addr);
2627
2628 removing_trailing_white_space(pf->textbuf);
2629 (void)removing_double_quotes(pf->textbuf);
2630 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2631 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2632 fs_give((void **)&addr);
2633 he->sticky = 1;
2634 break;
2635
2636 case Combine:
2637 removing_trailing_white_space(pf->textbuf);
2638 (void)removing_double_quotes(pf->textbuf);
2639 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2640 rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
2641 fs_give((void **)&addr);
2642 he->sticky = 1;
2643 if(a){
2644 for(tail = pf->addr; *tail; tail = &(*tail)->next)
2645 ;
2646 *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
2647 *pf->addr, NULL, a, RCA_ALL);
2648 q_status_message(SM_ORDER, 3, 3,
2649 "Adding addresses from role");
2650 mail_free_address(&a);
2651 }
2652
2653 break;
2654
2655 case UseAsDef:
2656 case NoMatch:
2657 break;
2658 }
2659
2660 he->display_it = 1; /* start this off showing */
2661 }
2662 else if(pf->addr){
2663 switch(set_default_hdrval(pf, custom)){
2664 case Replace:
2665 case Combine:
2666 if(*pf->addr)
2667 mail_free_address(pf->addr);
2668
2669 removing_trailing_white_space(pf->textbuf);
2670 (void)removing_double_quotes(pf->textbuf);
2671 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2672 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2673 fs_give((void **)&addr);
2674 he->sticky = 1;
2675 break;
2676
2677 case UseAsDef:
2678 case NoMatch:
2679 break;
2680 }
2681
2682 he->display_it = 1;
2683 }
2684
2685 if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
2686 mail_free_address(pf->addr);
2687 he->display_it = 1; /* start this off showing */
2688 }
2689
2690 if(pf->textbuf) /* free default value in any case */
2691 fs_give((void **)&pf->textbuf);
2692
2693 /* outgoing2strings will alloc the string pf->scratch below */
2694 he->realaddr = &pf->scratch;
2695 break;
2696
2697 case Fcc:
2698 sending_order[NN+8] = pf;
2699 pf_fcc = pf;
2700 if(role && role->fcc)
2701 fcc = role->fcc;
2702 else
2703 fcc = get_fcc(fcc_arg);
2704
2705 if(fcc_to_free){
2706 fs_give((void **)&fcc_to_free);
2707 fcc_arg = NULL;
2708 }
2709
2710 if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
2711 he->sticky = 1;
2712
2713 if(role)
2714 role->fcc = NULL;
2715
2716 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
2717 he->display_it = 1; /* start this off showing */
2718
2719 he->realaddr = &fcc;
2720 pf->text = &fcc;
2721 he_fcc = he;
2722 break;
2723
2724 case Subject :
2725 sending_order[NN+6] = pf;
2726
2727 switch(set_default_hdrval(pf, custom)){
2728 case Replace:
2729 case Combine:
2730 pf->scratch = pf->textbuf;
2731 pf->textbuf = NULL;
2732 he->sticky = 1;
2733 if(outgoing->subject)
2734 fs_give((void **)&outgoing->subject);
2735
2736 break;
2737
2738 case UseAsDef:
2739 case NoMatch:
2740 /* if no value, use default */
2741 if(outgoing->subject){
2742 pf->scratch = cpystr(outgoing->subject);
2743 }
2744 else{
2745 pf->scratch = pf->textbuf;
2746 pf->textbuf = NULL;
2747 }
2748
2749 break;
2750 }
2751
2752 he->realaddr = &pf->scratch;
2753 pf->text = &outgoing->subject;
2754 break;
2755
2756 default:
2757 q_status_message1(SM_ORDER,3,7,
2758 "Unknown header type %d in pine_send",
2759 (void *)pf->type);
2760 break;
2761 }
2762
2763 /*
2764 * We may or may not want to give the user the chance to edit
2765 * the From and Reply-To lines. If they are listed in either
2766 * Default-composer-hdrs or Customized-hdrs, then they can edit
2767 * them, else no.
2768 * If canedit is not set, that means that this header is not in
2769 * the user's customized-hdrs. If rich_header is set, that
2770 * means that this header is not in the user's
2771 * default-composer-hdrs (since From and Reply-To are rich
2772 * by default). So, don't give it an he to edit with in that case.
2773 *
2774 * For other types, just not setting canedit will cause it to be
2775 * uneditable, regardless of what the user does.
2776 */
2777 switch(index){
2778 case N_FROM:
2779 /* to allow it, we let this fall through to the reply-to case below */
2780 if(ps_global->never_allow_changing_from ||
2781 (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
2782 !(role && role->from))){
2783 if(pf->canedit || !he->rich_header)
2784 q_status_message(SM_ORDER, 3, 3,
2785 _("Not allowed to change header \"From\""));
2786
2787 memset(he, 0, (size_t)sizeof(*he));
2788 pf->extdata = NULL;
2789 break;
2790 }
2791
2792 case N_REPLYTO:
2793 if(!pf->canedit && he->rich_header){
2794 memset(he, 0, (size_t)sizeof(*he));
2795 pf->extdata = NULL;
2796 }
2797 else{
2798 pf->canedit = 1;
2799 he++;
2800 }
2801
2802 break;
2803
2804 default:
2805 if(!pf->canedit){
2806 memset(he, 0, (size_t)sizeof(*he));
2807 pf->extdata = NULL;
2808 }
2809 else
2810 he++;
2811
2812 break;
2813 }
2814 }
2815
2816 /*
2817 * This is so the builder can tell the composer to fill the affected
2818 * field based on the value in the field on the left.
2819 *
2820 * Note that this mechanism isn't completely general. Each entry has
2821 * only a single next_affected, so if some other entry points an
2822 * affected entry at an entry with a next_affected, they all inherit
2823 * that next_affected. Since this isn't used much a careful ordering
2824 * of the affected fields should make it a sufficient mechanism.
2825 */
2826 he_to->affected_entry = he_fcc;
2827 he_news->affected_entry = he_fcc;
2828 he_lcc->affected_entry = he_to;
2829 he_to->next_affected = he_fcc;
2830
2831 (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
2832
2833 i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
2834 /*
2835 * Set up headerentries for custom fields.
2836 * NOTE: "i" is assumed to now index first custom field in sending
2837 * order.
2838 */
2839 for(pf = pf->next; pf && pf->name; pf = pf->next){
2840 char *addr;
2841
2842 if(pf->standard)
2843 continue;
2844
2845 pf->extdata = he;
2846 pf->canedit = 1;
2847 pf->rcptto = 0;
2848 pf->writehdr = 1;
2849 pf->localcopy = 1;
2850
2851 switch(pf->type){
2852 case Address:
2853 if(pf->addr){ /* better be set */
2854 sending_order[i++] = pf;
2855 *he = he_custom_addr_templ;
2856 /* change default text into an ADDRESS */
2857 /* strip quotes around whole default */
2858 removing_trailing_white_space(pf->textbuf);
2859 (void)removing_double_quotes(pf->textbuf);
2860 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
2861 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
2862 fs_give((void **)&addr);
2863 if(pf->textbuf)
2864 fs_give((void **)&pf->textbuf);
2865
2866 he->realaddr = &pf->scratch;
2867 if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
2868 he->nickcmpl = NULL;
2869 }
2870
2871 break;
2872
2873 case FreeText:
2874 sending_order[i++] = pf;
2875 *he = he_custom_free_templ;
2876 he->realaddr = &pf->textbuf;
2877 pf->text = &pf->textbuf;
2878 if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
2879 (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
2880 he->display_it = 1; /* show it */
2881
2882 break;
2883
2884 default:
2885 q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
2886 (void *)pf->type);
2887 break;
2888 }
2889
2890 he->name = pf->name;
2891
2892 /* use first 8 characters for prompt */
2893 he->prompt = cpystr(" : ");
2894 strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
2895
2896 he->rich_header = view_as_rich(he->name, he->rich_header);
2897 he++;
2898 }
2899
2900 /*
2901 * Make sure at least *one* field is displayable...
2902 */
2903 for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
2904 if(HE(pf) && !HE(pf)->rich_header){
2905 index = i;
2906 break;
2907 }
2908
2909 /*
2910 * None displayable!!! Warn and display defaults.
2911 */
2912 if(index == -1){
2913 q_status_message(SM_ORDER,0,5,
2914 "No default-composer-hdrs matched, displaying defaults");
2915 for(i = 0, pf = header.local; pf; pf = pf->next, i++)
2916 if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
2917 && HE(pf))
2918 HE(pf)->rich_header = 0;
2919 }
2920
2921 /*
2922 * Save information about body which set_mime_type_by_grope might change.
2923 * Then, if we get an error sending, we reset these things so that
2924 * grope can do it's thing again after we edit some more.
2925 */
2926 if ((*body)->type == TYPEMULTIPART)
2927 bp = save_body_particulars(&(*body)->nested.part->body);
2928 else
2929 bp = save_body_particulars(*body);
2930
2931
2932 local_redraft_pos = redraft_pos;
2933
2934 /*----------------------------------------------------------------------
2935 Loop calling the editor until everything goes well
2936 ----*/
2937 while(1){
2938 int saved_user_timeout;
2939
2940 /* Reset body to what it was when we started. */
2941 if ((*body)->type == TYPEMULTIPART)
2942 reset_body_particulars(bp, &(*body)->nested.part->body);
2943 else
2944 reset_body_particulars(bp,*body);
2945 /*
2946 * set initial cursor position based on how many times we've been
2947 * thru the loop...
2948 */
2949 if(reply && reply->pseudo){
2950 pbf->pine_flags |= reply->data.pico_flags;
2951 }
2952 else if(body_start){
2953 pbf->pine_flags |= P_BODY;
2954 body_start = 0; /* maybe not next time */
2955 }
2956 else if(local_redraft_pos){
2957 pbf->edit_offset = local_redraft_pos->offset;
2958 /* set the start_here bit in correct header */
2959 for(pf = header.local; pf && pf->name; pf = pf->next)
2960 if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
2961 && HE(pf)){
2962 HE(pf)->start_here = 1;
2963 break;
2964 }
2965
2966 /* If didn't find it, we start in body. */
2967 if(!pf || !pf->name)
2968 pbf->pine_flags |= P_BODY;
2969 }
2970 else if(reply && (!reply->forw && !reply->forwarded)){
2971 pbf->pine_flags |= P_BODY;
2972 }
2973
2974 /* in case these were turned on in previous pass through loop */
2975 if(pf_nobody){
2976 pf_nobody->writehdr = 0;
2977 pf_nobody->localcopy = 0;
2978 }
2979
2980 if(pf_fcc)
2981 pf_fcc->localcopy = 0;
2982
2983 /*
2984 * If a sending attempt failed after we passed the message text
2985 * thru a user-defined filter, "orig_so" points to the original
2986 * text. Replace the body's encoded data with the original...
2987 */
2988 if(orig_so){
2989 STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
2990 ? &(*body)->nested.part->body.contents.text.data
2991 : &(*body)->contents.text.data);
2992 so_give(so);
2993 *so = orig_so;
2994 orig_so = NULL;
2995 }
2996
2997 /*
2998 * Convert the envelope and body to the string format that
2999 * pico can edit
3000 */
3001 outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
3002
3003 for(pf = header.local; pf && pf->name; pf = pf->next){
3004 /*
3005 * If this isn't the first time through this loop, we may have
3006 * freed some of the FreeText headers below so that they wouldn't
3007 * show up as empty headers in the finished message. Need to
3008 * alloc them again here so they can be edited.
3009 */
3010 if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
3011 *HE(pf)->realaddr = cpystr("");
3012
3013 if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
3014 HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
3015 }
3016
3017 /*
3018 * If From is exposed, probably by a role, then start the cursor
3019 * on the first line which isn't filled in. If it isn't, then we
3020 * don't move the cursor, mostly for back-compat.
3021 */
3022 if((!reply || reply->forw || reply->forwarded) &&
3023 !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
3024 (he_from->display_it || !he_from->rich_header)){
3025 for(pf = header.local; pf && pf->name; pf = pf->next)
3026 if(HE(pf) &&
3027 (HE(pf)->display_it || !HE(pf)->rich_header) &&
3028 HE(pf)->realaddr &&
3029 (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
3030 HE(pf)->start_here = 1;
3031 break;
3032 }
3033 }
3034
3035 #ifdef _WINDOWS
3036 mswin_setwindowmenu (MENU_COMPOSER);
3037 #endif
3038
3039 cancel_busy_cue(-1);
3040 flush_status_messages(1);
3041
3042 /* turn off user input timeout when in composer */
3043 saved_user_timeout = ps_global->hours_to_timeout;
3044 ps_global->hours_to_timeout = 0;
3045 dprint((1, "\n ---- COMPOSER ----\n"));
3046 editor_result = pico(pbf);
3047 dprint((4, "... composer returns (0x%x)\n", editor_result));
3048 ps_global->hours_to_timeout = saved_user_timeout;
3049
3050 #ifdef _WINDOWS
3051 mswin_setwindowmenu (MENU_DEFAULT);
3052 #endif
3053 fix_windsize(ps_global);
3054
3055 /*
3056 * Only reinitialize signals if we didn't receive an interesting
3057 * one while in pico, since pico's return is part of processing that
3058 * signal and it should continue to be ignored.
3059 */
3060 if(!(editor_result & COMP_GOTHUP))
3061 init_signals(); /* Pico has it's own signal stuff */
3062
3063 /*
3064 * We're going to save in DEADLETTER. Dump attachments first.
3065 */
3066 if(editor_result & COMP_CANCEL)
3067 free_attachment_list(&pbf->attachments);
3068
3069 /* Turn strings back into structures */
3070 strings2outgoing(&header, body, pbf->attachments, flowing_requested);
3071
3072 /* Make newsgroups NULL if it is "" (so won't show up in headers) */
3073 if(outgoing->newsgroups){
3074 sqzspaces(outgoing->newsgroups);
3075 if(!outgoing->newsgroups[0])
3076 fs_give((void **)&(outgoing->newsgroups));
3077 }
3078
3079 /* Make subject NULL if it is "" (so won't show up in headers) */
3080 if(outgoing->subject && !outgoing->subject[0])
3081 fs_give((void **)&(outgoing->subject));
3082
3083 /* remove custom fields that are empty */
3084 for(pf = header.local; pf && pf->name; pf = pf->next){
3085 if(pf->type == FreeText && pf->textbuf){
3086 if(pf->textbuf[0] == '\0'){
3087 fs_give((void **)&pf->textbuf);
3088 pf->text = NULL;
3089 }
3090 }
3091 }
3092
3093 removing_trailing_white_space(fcc);
3094
3095 /*-------- Stamp it with a current date -------*/
3096 if(outgoing->date) /* update old date */
3097 fs_give((void **)&(outgoing->date));
3098
3099 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3100 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
3101
3102 rfc822_date(tmp_20k_buf); /* format and copy new date */
3103 if(F_ON(F_QUELL_TIMEZONE, ps_global))
3104 mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
3105
3106 outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
3107
3108 /* Set return_path based on From which is going to be used */
3109 if(outgoing->return_path)
3110 mail_free_address(&outgoing->return_path);
3111
3112 outgoing->return_path = rfc822_cpy_adr(outgoing->from);
3113
3114 /*
3115 * Don't ever believe the sender that is there.
3116 * If From doesn't look quite right, generate our own sender.
3117 */
3118 if(outgoing->sender)
3119 mail_free_address(&outgoing->sender);
3120
3121 /*
3122 * If the LHS of the address doesn't match, or the RHS
3123 * doesn't match one of localdomain or hostname,
3124 * then add a sender line (really X-X-Sender).
3125 *
3126 * Don't add a personal_name since the user can change that.
3127 */
3128 if(F_OFF(F_DISABLE_SENDER, ps_global)
3129 &&
3130 (!outgoing->from
3131 || !outgoing->from->mailbox
3132 || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
3133 || !outgoing->from->host
3134 || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
3135 || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
3136
3137 outgoing->sender = mail_newaddr();
3138 outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
3139 outgoing->sender->host = cpystr(ps_global->hostname);
3140 }
3141
3142 if(ps_global->newthread){
3143 if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
3144 if(outgoing->references) fs_give((void **)&outgoing->references);
3145 }
3146
3147 /*----- Message is edited, now decide what to do with it ----*/
3148 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
3149 /*=========== Postpone or Interrupted message ============*/
3150 CONTEXT_S *fcc_cntxt = NULL;
3151 char folder[MAXPATH+1];
3152 int fcc_result = 0;
3153 char label[50];
3154
3155 dprint((4, "pine_send:%s handling\n",
3156 (editor_result & COMP_SUSPEND)
3157 ? "SUSPEND"
3158 : (editor_result & COMP_GOTHUP)
3159 ? "HUP"
3160 : (editor_result & COMP_CANCEL)
3161 ? "CANCEL" : "HUH?"));
3162 if((editor_result & COMP_CANCEL)
3163 && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
3164 || ps_global->deadlets == 0)){
3165 q_status_message(SM_ORDER, 0, 3, "Message cancelled");
3166 break;
3167 }
3168
3169 /*
3170 * The idea here is to use the Fcc: writing facility
3171 * to append to the special postponed message folder...
3172 *
3173 * NOTE: the strategy now is to write the message and
3174 * all attachments as they exist at composition time.
3175 * In other words, attachments are postponed by value
3176 * and not reference. This may change later, but we'll
3177 * need a local "message/external-body" type that
3178 * outgoing2strings knows how to properly set up for
3179 * the composer. Maybe later...
3180 */
3181
3182 label[0] = '\0';
3183
3184 if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
3185 && (editor_result & COMP_SUSPEND)
3186 && (check_addresses(&header) == CA_BAD)){
3187 /*--- Addresses didn't check out---*/
3188 q_status_message(SM_ORDER, 7, 7,
3189 _("Not allowed to postpone message until addresses are qualified"));
3190 continue;
3191 }
3192
3193 /*
3194 * Build the local message copy so.
3195 *
3196 * In the HUP case, we'll write the bezerk delimiter by
3197 * hand and output the message directly into the folder.
3198 * It's not only faster, we don't have to worry about
3199 * c-client reentrance and less hands paw over the data so
3200 * there's less chance of a problem.
3201 *
3202 * In the Postpone case, just create it if the user wants to
3203 * and create a temporary storage object to write into. */
3204 fake_hup:
3205 fcc_result = 0;
3206 lmc.all_written = lmc.text_only = lmc.text_written = 0;
3207 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3208 int newfile = 1;
3209 time_t now = time((time_t *)0);
3210
3211 #if defined(DOS) || defined(OS2)
3212 /*
3213 * we can't assume anything about root or home dirs, so
3214 * just plunk it down in the same place as the pinerc
3215 */
3216 if(!getenv("HOME")){
3217 char *lc = last_cmpnt(ps_global->pinerc);
3218 folder[0] = '\0';
3219 if(lc != NULL){
3220 strncpy(folder,ps_global->pinerc,
3221 MIN(lc-ps_global->pinerc,sizeof(folder)-1));
3222 folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
3223 }
3224
3225 strncat(folder, (editor_result & COMP_GOTHUP)
3226 ? INTERRUPTED_MAIL : DEADLETTER,
3227 sizeof(folder)-strlen(folder)-1);
3228 }
3229 else
3230 #endif
3231 build_path(folder,
3232 ps_global->VAR_OPER_DIR
3233 ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
3234 (editor_result & COMP_GOTHUP)
3235 ? INTERRUPTED_MAIL : DEADLETTER,
3236 sizeof(folder));
3237
3238 if(editor_result & COMP_CANCEL){
3239 char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
3240
3241 if(strlen(folder) + 1 < sizeof(filename))
3242 for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
3243 strncpy(filename, folder, sizeof(filename));
3244 filename[sizeof(filename)-1] = '\0';
3245 strncpy(newfname, filename, sizeof(newfname));
3246 newfname[sizeof(newfname)-1] = '\0';
3247
3248 if(i > 1){
3249 snprintf(nbuf, sizeof(nbuf), "%d", i);
3250 nbuf[sizeof(nbuf)-1] = '\0';
3251 strncat(filename, nbuf,
3252 sizeof(filename)-strlen(filename)-1);
3253 filename[sizeof(filename)-1] = '\0';
3254 }
3255
3256 snprintf(nbuf, sizeof(nbuf), "%d", i+1);
3257 nbuf[sizeof(nbuf)-1] = '\0';
3258 strncat(newfname, nbuf,
3259 sizeof(newfname)-strlen(newfname)-1);
3260 newfname[sizeof(newfname)-1] = '\0';
3261 (void) rename_file(filename, newfname);
3262 }
3263
3264 our_unlink(folder);
3265 }
3266 else
3267 newfile = can_access(folder, ACCESS_EXISTS);
3268
3269 if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
3270 if (outgoing->from){
3271 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
3272 newfile ? "" : "\015\012",
3273 outgoing->from->mailbox,
3274 outgoing->from->host, ctime(&now));
3275 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3276 if(!so_puts(lmc.so, tmp_20k_buf)){
3277 if(editor_result & COMP_CANCEL)
3278 q_status_message2(SM_ORDER | SM_DING, 3, 3,
3279 "Can't write \"%s\": %s",
3280 folder, error_description(errno));
3281 else
3282 dprint((1, "* * * CAN'T WRITE %s: %s\n",
3283 folder ? folder : "?",
3284 error_description(errno)));
3285 }
3286 }
3287 }
3288 }
3289 else{ /* Must be COMP_SUSPEND */
3290 if(!ps_global->VAR_POSTPONED_FOLDER
3291 || !ps_global->VAR_POSTPONED_FOLDER[0]){
3292 q_status_message(SM_ORDER | SM_DING, 3, 3,
3293 _("No postponed file defined"));
3294 continue;
3295 }
3296
3297 /*
3298 * Store the cursor position
3299 *
3300 * First find the header entry with the start_here
3301 * bit set, if any. This means the editor is telling
3302 * us to start on this header field next time.
3303 */
3304 start_here_name = NULL;
3305 for(pf = header.local; pf && pf->name; pf = pf->next)
3306 if(HE(pf) && HE(pf)->start_here){
3307 start_here_name = pf->name;
3308 break;
3309 }
3310
3311 /* If there wasn't one, ":" means we start in the body. */
3312 if(!start_here_name || !*start_here_name)
3313 start_here_name = ":";
3314
3315 if(ps_global->VAR_FORM_FOLDER
3316 && ps_global->VAR_FORM_FOLDER[0]
3317 && postpone_prompt() == 'f'){
3318 strncpy(folder, ps_global->VAR_FORM_FOLDER,
3319 sizeof(folder)-1);
3320 folder[sizeof(folder)-1] = '\0';
3321 strncpy(label, "form letter", sizeof(label));
3322 label[sizeof(label)-1] = '\0';
3323 }
3324 else{
3325 strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
3326 sizeof(folder)-1);
3327 folder[sizeof(folder)-1] = '\0';
3328 strncpy(label, "postponed message", sizeof(label));
3329 label[sizeof(label)-1] = '\0';
3330 }
3331
3332 lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
3333 }
3334
3335 if(lmc.so){
3336 size_t sz;
3337 char *lmq = NULL;
3338
3339 /* copy fcc line to postponed or interrupted folder */
3340 if(pf_fcc)
3341 pf_fcc->localcopy = 1;
3342
3343 /* plug error into header for later display to user */
3344 if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
3345 pf_err->writehdr = 1;
3346 pf_err->localcopy = 1;
3347 pf_err->textbuf = lmq;
3348 }
3349
3350 /*
3351 * if reply, write (UID)folder header field so we can
3352 * later flag the replied-to message \\ANSWERED
3353 * DON'T save MSGNO's.
3354 */
3355 if(reply && reply->uid){
3356 char uidbuf[MAILTMPLEN], *p;
3357 long i;
3358
3359 for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
3360 if(i)
3361 sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
3362
3363 sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
3364 }
3365
3366 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3367
3368 pf_uid->writehdr = 1;
3369 pf_uid->localcopy = 1;
3370 snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
3371 reply->prefix ? int2string(strlen(reply->prefix))
3372 : (reply->forwarded) ? "": "0 ",
3373 reply->prefix ? " " : "",
3374 reply->prefix ? reply->prefix : "",
3375 i, reply->data.uid.validity,
3376 tmp_20k_buf, reply->mailbox);
3377 uidbuf[sizeof(uidbuf)-1] = '\0';
3378 pf_uid->textbuf = cpystr(uidbuf);
3379
3380 /*
3381 * Logically, this ought to be part of pf_uid, but this
3382 * was added later and so had to be in a separate header
3383 * for backwards compatibility.
3384 */
3385 pf_mbox->writehdr = 1;
3386 pf_mbox->localcopy = 1;
3387 pf_mbox->textbuf = cpystr(reply->origmbox
3388 ? reply->origmbox
3389 : reply->mailbox);
3390 }
3391
3392 /* Save cursor position */
3393 if(start_here_name && *start_here_name){
3394 char curposbuf[MAILTMPLEN];
3395
3396 pf_curpos->writehdr = 1;
3397 pf_curpos->localcopy = 1;
3398 snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
3399 pbf->edit_offset);
3400 curposbuf[sizeof(curposbuf)-1] = '\0';
3401 pf_curpos->textbuf = cpystr(curposbuf);
3402 }
3403
3404 /*
3405 * Work around c-client reply-to bug. C-client will
3406 * return a reply_to in an envelope even if there is
3407 * no reply-to header field. We want to note here whether
3408 * the reply-to is real or not.
3409 */
3410 if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
3411 pf_ourrep->writehdr = 1;
3412 pf_ourrep->localcopy = 1;
3413 if(outgoing->reply_to)
3414 pf_ourrep->textbuf = cpystr("Full");
3415 else
3416 pf_ourrep->textbuf = cpystr("Empty");
3417 }
3418
3419 /* Save the role-specific smtp server */
3420 if(role && role->smtp && role->smtp[0]){
3421 char *q, *smtp = NULL;
3422 char **lp;
3423 size_t len = 0;
3424
3425 /*
3426 * Turn the list of smtp servers into a space-
3427 * delimited list in a single string.
3428 */
3429 for(lp = role->smtp; (q = *lp) != NULL; lp++)
3430 len += (strlen(q) + 1);
3431
3432 if(len){
3433 smtp = (char *) fs_get(len * sizeof(char));
3434 smtp[0] = '\0';
3435 for(lp = role->smtp; (q = *lp) != NULL; lp++){
3436 if(lp != role->smtp)
3437 strncat(smtp, " ", len-strlen(smtp)-1);
3438
3439 strncat(smtp, q, len-strlen(smtp)-1);
3440 }
3441
3442 smtp[len-1] = '\0';
3443 }
3444
3445 pf_smtp_server->writehdr = 1;
3446 pf_smtp_server->localcopy = 1;
3447 if(smtp)
3448 pf_smtp_server->textbuf = smtp;
3449 else
3450 pf_smtp_server->textbuf = cpystr("");
3451 }
3452
3453 /* Save the role-specific nntp server */
3454 if(suggested_nntp_server ||
3455 (role && role->nntp && role->nntp[0])){
3456 char *q, *nntp = NULL;
3457 char **lp;
3458 size_t len = 0;
3459
3460 if(role && role->nntp && role->nntp[0]){
3461 /*
3462 * Turn the list of nntp servers into a space-
3463 * delimited list in a single string.
3464 */
3465 for(lp = role->nntp; (q = *lp) != NULL; lp++)
3466 len += (strlen(q) + 1);
3467
3468 if(len){
3469 nntp = (char *) fs_get(len * sizeof(char));
3470 nntp[0] = '\0';
3471 for(lp = role->nntp; (q = *lp) != NULL; lp++){
3472 if(lp != role->nntp)
3473 strncat(nntp, " ", len-strlen(nntp)-1);
3474
3475 strncat(nntp, q, len-strlen(nntp)-1);
3476 }
3477
3478 nntp[len-1] = '\0';
3479 }
3480 }
3481 else
3482 nntp = cpystr(suggested_nntp_server);
3483
3484 pf_nntp_server->writehdr = 1;
3485 pf_nntp_server->localcopy = 1;
3486 if(nntp)
3487 pf_nntp_server->textbuf = nntp;
3488 else
3489 pf_nntp_server->textbuf = cpystr("");
3490 }
3491
3492 /*
3493 * Write the list of custom headers to the
3494 * X-Our-Headers header so that we can recover the
3495 * list in redraft.
3496 */
3497 sz = 0;
3498 for(pf = header.custom; pf && pf->name; pf = pf->next)
3499 sz += strlen(pf->name) + 1;
3500
3501 if(sz){
3502 char *q;
3503
3504 pf_ourhdrs->writehdr = 1;
3505 pf_ourhdrs->localcopy = 1;
3506 pf_ourhdrs->textbuf = (char *)fs_get(sz);
3507 memset(pf_ourhdrs->textbuf, 0, sz);
3508 q = pf_ourhdrs->textbuf;
3509 for(pf = header.custom; pf && pf->name; pf = pf->next){
3510 if(pf != header.custom)
3511 sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
3512
3513 sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
3514 }
3515
3516 pf_ourhdrs->textbuf[sz-1] = '\0';;
3517 }
3518
3519 /*
3520 * We need to make sure any header values that got cleared
3521 * get written to the postponed message (they won't if
3522 * pf->text is NULL). Otherwise, we can't tell previously
3523 * non-existent custom headers or default values from
3524 * custom (or other) headers that got blanked in the
3525 * composer...
3526 */
3527 for(pf = header.local; pf && pf->name; pf = pf->next)
3528 if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
3529 *(HE(pf)->realaddr) = cpystr("");
3530
3531 /*
3532 * We're saving the message for use later. It may be that the
3533 * characters in the message are not all convertible to the
3534 * user's posting_charmap. We'll save it as UTF-8 instead
3535 * and worry about that the next time they try to send it.
3536 * Use a different save pointer just to be sure we don't
3537 * mess up the other stuff. We should probably make the
3538 * charset an argument.
3539 *
3540 * We also need to fix the charset of the body part
3541 * the user is editing so that we can read it back
3542 * successfully when we resume the composition.
3543 */
3544 ps_global->post_utf8 = 1;
3545
3546 {
3547 PARAMETER *pm;
3548 BODY *bp;
3549 if((*body)->type == TYPEMULTIPART)
3550 bp = &(*body)->nested.part->body;
3551 else
3552 bp = *body;
3553
3554 for(pm = bp->parameter;
3555 pm && strucmp(pm->attribute, "charset") != 0;
3556 pm = pm->next)
3557 ;
3558
3559 if(pm){
3560 if(pm->value)
3561 fs_give((void **) &pm->value);
3562
3563 pm->value = cpystr("UTF-8");
3564 }
3565 }
3566
3567 if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
3568 if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
3569 char *err;
3570 STORE_S *hup_so;
3571 gf_io_t gc, pc;
3572 int we_cancel = 0;
3573
3574 if(editor_result & COMP_CANCEL){
3575 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
3576 "Saving to \"%s\"", folder);
3577 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
3578 we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
3579 }
3580
3581 if((hup_so =
3582 so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
3583 gf_set_so_readc(&gc, lmc.so);
3584 gf_set_so_writec(&pc, hup_so);
3585 so_seek(lmc.so, 0L, 0); /* read msg copy and */
3586 so_seek(hup_so, 0L, 2); /* append to folder */
3587 gf_filter_init();
3588 gf_link_filter(gf_nvtnl_local, NULL);
3589 if(!(fcc_result = !(err = gf_pipe(gc, pc))))
3590 dprint((1, "*** PIPE FAILED: %s\n",
3591 err ? err : "?"));
3592
3593 gf_clear_so_readc(lmc.so);
3594 gf_clear_so_writec(hup_so);
3595 so_give(&hup_so);
3596 }
3597 else
3598 dprint((1, "*** CAN'T CREATE %s: %s\n",
3599 folder ? folder : "?",
3600 error_description(errno)));
3601
3602 if(we_cancel)
3603 cancel_busy_cue(-1);
3604 }
3605 else
3606 fcc_result = write_fcc(folder, fcc_cntxt,
3607 lmc.so, NULL, label, NULL);
3608 }
3609
3610 /* discontinue coerced UTF-8 posting */
3611 ps_global->post_utf8 = 0;
3612
3613 so_give(&lmc.so);
3614 }
3615 else
3616 dprint((1, "***CAN'T ALLOCATE temp store: %s ",
3617 error_description(errno)));
3618
3619 if(editor_result & COMP_GOTHUP){
3620 /*
3621 * Special Hack #291: if any hi-byte bits are set in
3622 * editor's result, we put them there.
3623 */
3624 if(editor_result & 0xff00)
3625 exit(editor_result >> 8);
3626
3627 dprint((1, "Save composition on HUP %sED\n",
3628 fcc_result ? "SUCCEED" : "FAIL"));
3629 hup_signal(); /* Do what we normally do on SIGHUP */
3630 }
3631 else if((editor_result & COMP_SUSPEND) && fcc_result){
3632 if(ps_global->VAR_FORM_FOLDER
3633 && ps_global->VAR_FORM_FOLDER[0]
3634 && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
3635 q_status_message(SM_ORDER, 0, 3,
3636 _("Composition saved to Form Letter Folder. Select Compose to send."));
3637 else
3638 q_status_message(SM_ORDER, 0, 3,
3639 _("Composition postponed. Select Compose to resume."));
3640
3641 break; /* postpone went OK, get out of here */
3642 }
3643 else if(editor_result & COMP_CANCEL){
3644 char *lc = NULL;
3645
3646 if(fcc_result && folder)
3647 lc = last_cmpnt(folder);
3648
3649 q_status_message3(SM_ORDER, 0, 3,
3650 _("Message cancelled%s%s%s"),
3651 (lc && *lc) ? " and copied to \"" : "",
3652 (lc && *lc) ? lc : "",
3653 (lc && *lc) ? "\" file" : "");
3654 break;
3655 }
3656 else{
3657 q_status_message(SM_ORDER, 0, 4,
3658 _("Continuing composition. Message not postponed or sent"));
3659 body_start = 1;
3660 continue; /* postpone failed, jump back in to composer */
3661 }
3662 }
3663 else{
3664 /*------ Must be sending mail or posting ! -----*/
3665 int result, valid_addr, continue_with_only_fcc = 0;
3666 CONTEXT_S *fcc_cntxt = NULL;
3667
3668 result = 0;
3669 dprint((4, "=== sending: "));
3670
3671 /* --- If posting, confirm with user ----*/
3672 if(outgoing->newsgroups && *outgoing->newsgroups
3673 && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
3674 && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
3675 q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
3676 dprint((4, "no post, continuing\n"));
3677 continue;
3678 }
3679
3680 if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
3681 || outgoing->newsgroups)){
3682 if(fcc && fcc[0]){
3683 if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
3684 want_to(_("No recipients, really copy only to Fcc "),
3685 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
3686 continue;
3687
3688 continue_with_only_fcc++;
3689 }
3690 else{
3691 q_status_message(SM_ORDER, 3, 4,
3692 _("No recipients specified!"));
3693 dprint((4, "no recip, continuing\n"));
3694 continue;
3695 }
3696 }
3697
3698 if((valid_addr = check_addresses(&header)) == CA_BAD){
3699 /*--- Addresses didn't check out---*/
3700 dprint((4, "addrs failed, continuing\n"));
3701 continue;
3702 }
3703
3704 if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
3705 && !continue_with_only_fcc
3706 && !(outgoing->to || outgoing->cc || lcc_addr
3707 || outgoing->newsgroups)
3708 && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
3709 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
3710 dprint((4, "No To or CC or Newsgroup, continuing\n"));
3711 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3712 free_redraft_pos(&local_redraft_pos);
3713
3714 local_redraft_pos
3715 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3716 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3717 local_redraft_pos->hdrname = cpystr(TONAME);
3718 continue;
3719 }
3720
3721 if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
3722 && check_for_subject(&header) == CF_MISSING){
3723 dprint((4, "No subject, continuing\n"));
3724 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3725 free_redraft_pos(&local_redraft_pos);
3726
3727 local_redraft_pos
3728 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3729 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3730 local_redraft_pos->hdrname = cpystr(SUBJNAME);
3731 continue;
3732 }
3733
3734 if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
3735 && check_for_fcc(fcc) == CF_MISSING){
3736 dprint((4, "No fcc, continuing\n"));
3737 if(local_redraft_pos && local_redraft_pos != redraft_pos)
3738 free_redraft_pos(&local_redraft_pos);
3739
3740 local_redraft_pos
3741 = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
3742 memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
3743 local_redraft_pos->hdrname = cpystr("Fcc");
3744 continue;
3745 }
3746
3747 set_last_fcc(fcc);
3748
3749 /*---- Check out fcc -----*/
3750 if(fcc && *fcc){
3751 /*
3752 * If special name "inbox" then replace it with the
3753 * real inbox path.
3754 */
3755 if(ps_global->VAR_INBOX_PATH
3756 && strucmp(fcc, ps_global->inbox_name) == 0){
3757 char *replace_fcc;
3758
3759 replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
3760 fs_give((void **)&fcc);
3761 fcc = replace_fcc;
3762 }
3763
3764 lmc.all_written = lmc.text_written = 0;
3765 /* lmc.text_only set on command line */
3766 if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
3767 /* ---- Open or allocation of fcc failed ----- */
3768 dprint((4,"can't open/allocate fcc, cont'g\n"));
3769
3770 /*
3771 * Find field entry associated with fcc, and start
3772 * composer on it...
3773 */
3774 for(pf = header.local; pf && pf->name; pf = pf->next)
3775 if(pf->type == Fcc && HE(pf))
3776 HE(pf)->start_here = 1;
3777
3778 continue;
3779 }
3780 else
3781 so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
3782 }
3783 else
3784 lmc.so = NULL;
3785
3786 /*---- Take care of any requested prefiltering ----*/
3787 if(sending_filter_requested
3788 && !filter_message_text(sending_filter_requested, outgoing,
3789 *body, &orig_so, &header)){
3790 q_status_message1(SM_ORDER, 3, 3,
3791 _("Problem filtering! Nothing sent%s."),
3792 fcc ? " or saved to fcc" : "");
3793 continue;
3794 }
3795
3796 /*------ Actually post -------*/
3797 if(outgoing->newsgroups){
3798 char **alt_nntp = NULL, *alt_nntp_p[2];
3799 if(((role && role->nntp)
3800 || suggested_nntp_server)){
3801 if(ps_global->FIX_NNTP_SERVER
3802 && ps_global->FIX_NNTP_SERVER[0])
3803 q_status_message(SM_ORDER | SM_DING, 5, 5,
3804 "Using nntp-server that is administratively fixed");
3805 else if(role && role->nntp)
3806 alt_nntp = role->nntp;
3807 else{
3808 alt_nntp_p[0] = suggested_nntp_server;
3809 alt_nntp_p[1] = NULL;
3810 alt_nntp = alt_nntp_p;
3811 }
3812 }
3813 if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
3814 dprint((1, "Post failed, continuing\n"));
3815 if(outgoing->message_id)
3816 fs_give((void **) &outgoing->message_id);
3817
3818 outgoing->message_id = generate_message_id(role);
3819
3820 continue;
3821 }
3822 else
3823 result |= P_NEWS_WIN;
3824 }
3825
3826 /*
3827 * BUG: IF we've posted the message *and* an fcc was specified
3828 * then we've already got a neatly formatted message in the
3829 * lmc.so. It'd be nice not to have to re-encode everything
3830 * to insert it into the smtp slot...
3831 */
3832
3833 /*
3834 * Turn on "undisclosed recipients" header if no To or cc.
3835 */
3836 if(!(outgoing->to || outgoing->cc)
3837 && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
3838 pf_nobody->writehdr = 1;
3839 pf_nobody->localcopy = 1;
3840 }
3841
3842 if(priority_requested){
3843 (void) set_priority_header(&header, priority_requested);
3844 fs_give((void **) &priority_requested);
3845 }
3846
3847 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
3848 /*
3849 * If requested, launch background posting...
3850 */
3851 if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
3852 ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
3853 memset(ps_global->post, 0, sizeof(POST_S));
3854 if(fcc)
3855 ps_global->post->fcc = cpystr(fcc);
3856
3857 if((ps_global->post->pid = fork()) == 0){
3858 /*
3859 * Put us in new process group...
3860 */
3861 setpgrp(0, ps_global->post->pid);
3862
3863 /* BUG: should fix argv[0] to indicate what we're up to */
3864
3865 /*
3866 * If there are any live streams, pretend we never
3867 * knew them. Problem is two processes writing
3868 * same server process.
3869 * This is not clean but we're just going to exit
3870 * right away anyway. We just want to be sure to leave
3871 * the stuff that the parent is going to use alone.
3872 * The next three lines will disable the re-use of the
3873 * existing streams and cause us to open a new one if
3874 * needed.
3875 */
3876 ps_global->mail_stream = NULL;
3877 ps_global->s_pool.streams = NULL;
3878 ps_global->s_pool.nstream = 0;
3879
3880 /* quell any display output */
3881 ps_global->in_init_seq = 1;
3882
3883 /*------- Actually mail the message ------*/
3884 if(valid_addr == CA_OK
3885 && (outgoing->to || outgoing->cc
3886 || outgoing->bcc || lcc_addr)){
3887 char **alt_smtp = NULL;
3888
3889 if(role && role->smtp){
3890 if(ps_global->FIX_SMTP_SERVER
3891 && ps_global->FIX_SMTP_SERVER[0])
3892 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3893 else
3894 alt_smtp = role->smtp;
3895 }
3896
3897 result |= (call_mailer(&header, *body, alt_smtp,
3898 call_mailer_flags,
3899 call_mailer_file_result,
3900 pipe_callback) > 0)
3901 ? P_MAIL_WIN : P_MAIL_LOSE;
3902
3903 if(result & P_MAIL_LOSE)
3904 mark_address_failure_for_pico(&header);
3905 }
3906
3907 /*----- Was there an fcc involved? -----*/
3908 if(lmc.so){
3909 /*------ Write it if at least something worked ------*/
3910 if((result & (P_MAIL_WIN | P_NEWS_WIN))
3911 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
3912 && pine_rfc822_output(&header, *body,
3913 NULL, NULL))){
3914 char label[50];
3915
3916 strncpy(label, "Fcc", sizeof(label));
3917 label[sizeof(label)-1] = '\0';
3918 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
3919 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
3920 label[sizeof(label)-1] = '\0';
3921 }
3922
3923 /*-- Now actually copy to fcc folder and close --*/
3924 result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
3925 NULL, label,
3926 F_ON(F_MARK_FCC_SEEN, ps_global)
3927 ? "\\SEEN" : NULL))
3928 ? P_FCC_WIN : P_FCC_LOSE;
3929 }
3930 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
3931 q_status_message(SM_ORDER, 3, 5,
3932 _("Fcc Failed!. No message saved."));
3933 dprint((1,
3934 "explicit fcc write failed!\n"));
3935 result |= P_FCC_LOSE;
3936 }
3937
3938 so_give(&lmc.so);
3939 }
3940
3941 if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
3942 /*
3943 * Encode child's result in hi-byte of
3944 * editor's result
3945 */
3946 editor_result = ((result << 8) | COMP_GOTHUP);
3947 goto fake_hup;
3948 }
3949
3950 exit(result);
3951 }
3952
3953 if(ps_global->post->pid > 0){
3954 q_status_message(SM_ORDER, 3, 3,
3955 _("Message handed off for posting"));
3956 break; /* up to our child now */
3957 }
3958 else{
3959 q_status_message1(SM_ORDER | SM_DING, 3, 3,
3960 "Can't fork for send: %s",
3961 error_description(errno));
3962 if(ps_global->post->fcc)
3963 fs_give((void **) &ps_global->post->fcc);
3964
3965 fs_give((void **) &ps_global->post);
3966 }
3967
3968 if(lmc.so) /* throw away unused store obj */
3969 so_give(&lmc.so);
3970
3971 if(outgoing->message_id)
3972 fs_give((void **) &outgoing->message_id);
3973
3974 outgoing->message_id = generate_message_id(role);
3975
3976 continue; /* if we got here, there was a prob */
3977 }
3978 #endif /* BACKGROUND_POST */
3979
3980 /*------- Actually mail the message ------*/
3981 if(valid_addr == CA_OK
3982 && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
3983 char **alt_smtp = NULL;
3984
3985 if(role && role->smtp){
3986 if(ps_global->FIX_SMTP_SERVER
3987 && ps_global->FIX_SMTP_SERVER[0])
3988 q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
3989 else
3990 alt_smtp = role->smtp;
3991 }
3992
3993 result |= (call_mailer(&header, *body, alt_smtp,
3994 call_mailer_flags,
3995 call_mailer_file_result,
3996 pipe_callback) > 0)
3997 ? P_MAIL_WIN : P_MAIL_LOSE;
3998
3999 if(result & P_MAIL_LOSE)
4000 mark_address_failure_for_pico(&header);
4001 }
4002
4003 /*----- Was there an fcc involved? -----*/
4004 if(lmc.so){
4005 /*------ Write it if at least something worked ------*/
4006 if((result & (P_MAIL_WIN | P_NEWS_WIN))
4007 || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
4008 && pine_rfc822_output(&header, *body, NULL, NULL))){
4009 char label[50];
4010
4011 strncpy(label, "Fcc", sizeof(label));
4012 label[sizeof(label)-1] = '\0';
4013 if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
4014 snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
4015 label[sizeof(label)-1] = '\0';
4016 }
4017
4018 /*-- Now actually copy to fcc folder and close --*/
4019 result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
4020 F_ON(F_MARK_FCC_SEEN, ps_global)
4021 ? "\\SEEN" : NULL))
4022 ? P_FCC_WIN : P_FCC_LOSE;
4023 }
4024 else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
4025 q_status_message(SM_ORDER,3,5,
4026 _("Fcc Failed!. No message saved."));
4027 dprint((1, "explicit fcc write failed!\n"));
4028 result |= P_FCC_LOSE;
4029 }
4030
4031 so_give(&lmc.so);
4032 }
4033
4034 /*----- Mail Post FAILED, back to composer -----*/
4035 if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
4036 dprint((1, "Send failed, continuing\n"));
4037
4038 if(result & P_FCC_LOSE){
4039 /*
4040 * Find field entry associated with fcc, and start
4041 * composer on it...
4042 */
4043 for(pf = header.local; pf && pf->name; pf = pf->next)
4044 if(pf->type == Fcc && HE(pf))
4045 HE(pf)->start_here = 1;
4046
4047 q_status_message(SM_ORDER | SM_DING, 3, 3,
4048 pine_send_status(result, fcc,
4049 tmp_20k_buf, SIZEOF_20KBUF, NULL));
4050 }
4051
4052 if(outgoing->message_id)
4053 fs_give((void **) &outgoing->message_id);
4054
4055 outgoing->message_id = generate_message_id(role);
4056
4057 continue;
4058 }
4059
4060 /*
4061 * If message sent *completely* successfully, there's a
4062 * reply struct AND we're allowed to write back state, do it.
4063 * But also protect against shifted message numbers due
4064 * to new mail arrival. Since the number passed is based
4065 * on the real imap msg no, AND we're sure no expunge has
4066 * been done, just fix up the sorted number...
4067 */
4068 update_answered_flags(reply);
4069
4070 /*----- Signed, sealed, delivered! ------*/
4071 q_status_message(SM_ORDER, 0, 3,
4072 pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
4073
4074 break; /* All's well, pop out of here */
4075 }
4076 }
4077
4078 if(orig_so)
4079 so_give(&orig_so);
4080
4081 if(fcc)
4082 fs_give((void **)&fcc);
4083
4084 free_body_particulars(bp);
4085
4086 free_attachment_list(&pbf->attachments);
4087
4088 standard_picobuf_teardown(pbf);
4089
4090 for(i=0; i < fixed_cnt; i++){
4091 if(pfields[i].textbuf)
4092 fs_give((void **)&pfields[i].textbuf);
4093
4094 fs_give((void **)&pfields[i].name);
4095 }
4096
4097 if(lcc_addr)
4098 mail_free_address(&lcc_addr);
4099
4100 if(nobody_addr)
4101 mail_free_address(&nobody_addr);
4102
4103 free_prompts(header.custom);
4104 free_customs(header.custom);
4105 fs_give((void **)&pfields);
4106 free_headents(&headents);
4107 fs_give((void **)&sending_order);
4108 if(suggested_nntp_server)
4109 fs_give((void **)&suggested_nntp_server);
4110 if(title)
4111 fs_give((void **)&title);
4112
4113 if(local_redraft_pos && local_redraft_pos != redraft_pos)
4114 free_redraft_pos(&local_redraft_pos);
4115
4116 pbf = save_previous_pbuf;
4117 g_rolenick = NULL;
4118
4119 dprint((4, "=== send returning ===\n"));
4120 }
4121
4122
4123 /*
4124 * Check for subject in outgoing message.
4125 *
4126 * Asks user whether to proceed with no subject.
4127 */
4128 int
check_for_subject(METAENV * header)4129 check_for_subject(METAENV *header)
4130 {
4131 PINEFIELD *pf;
4132 int rv = CF_OK;
4133
4134 for(pf = header->local; pf && pf->name; pf = pf->next)
4135 if(pf->type == Subject){
4136 if(pf->text && *pf->text && **pf->text)
4137 rv = CF_OK;
4138 else{
4139 if(want_to("No Subject, send anyway ",
4140 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
4141 rv = CF_OK;
4142 else
4143 rv = CF_MISSING;
4144 }
4145
4146 break;
4147 }
4148
4149
4150 return(rv);
4151 }
4152
4153
4154 /*
4155 * Check for fcc in outgoing message.
4156 *
4157 * Asks user whether to proceed with no fcc.
4158 */
4159 int
check_for_fcc(char * fcc)4160 check_for_fcc(char *fcc)
4161 {
4162 int rv = CF_OK;
4163
4164 if(fcc && *fcc)
4165 rv = CF_OK;
4166 else{
4167 if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
4168 rv = CF_OK;
4169 else
4170 rv = CF_MISSING;
4171 }
4172
4173 return(rv);
4174 }
4175
4176
4177 /*
4178 * Confirm that the user wants to send to MAILER-DAEMON
4179 */
4180 int
confirm_daemon_send(void)4181 confirm_daemon_send(void)
4182 {
4183 return(want_to("Really send this message to the MAILER-DAEMON",
4184 'n', 'n', NO_HELP, WT_NORM) == 'y');
4185 }
4186
4187
4188 void
free_prompts(PINEFIELD * head)4189 free_prompts(PINEFIELD *head)
4190 {
4191 PINEFIELD *pf;
4192
4193 for(pf = head; pf && pf->name; pf = pf->next){
4194 if(HE(pf) && HE(pf)->prompt)
4195 fs_give((void **)& HE(pf)->prompt);
4196 }
4197 }
4198
4199
4200 int
postpone_prompt(void)4201 postpone_prompt(void)
4202 {
4203 static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
4204 {'f', 'f', "F", N_("Form Letter Folder")},
4205 {-1, 0, NULL, NULL} };
4206
4207 return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
4208 pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
4209 }
4210
4211
4212 /*
4213 * call__mailer_file_result - some results from call_mailer might be in a file.
4214 * dislplay that file.
4215 */
4216 void
call_mailer_file_result(char * filename,int style)4217 call_mailer_file_result(char *filename, int style)
4218 {
4219 if(filename){
4220 if(style & CM_BR_VERBOSE){
4221 display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
4222 }
4223 else{
4224 display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
4225 }
4226 }
4227 }
4228
4229 void
mark_address_failure_for_pico(METAENV * header)4230 mark_address_failure_for_pico(METAENV *header)
4231 {
4232 PINEFIELD *pf;
4233 ADDRESS *a;
4234 int error_count = 0;
4235 struct headerentry *last_he = NULL;
4236
4237 for(pf = header->local; pf && pf->name; pf = pf->next)
4238 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
4239 for(a = *pf->addr; a != NULL; a = a->next)
4240 if(a->error != NULL
4241 && error_count++ < MAX_ADDR_ERROR
4242 && (HE(pf))){
4243 if(last_he) /* start last reported err */
4244 last_he->start_here = 0;
4245
4246 (last_he = HE(pf))->start_here = 1;
4247 }
4248 }
4249
4250
4251
4252 /*
4253 * This is specialized routine. It assumes that the only things that we
4254 * care about restoring are the body type, subtype, encoding and the
4255 * state of the charset parameter. It also assumes that if the charset
4256 * parameter exists when we save it, it won't be removed later.
4257 */
4258 BODY_PARTICULARS_S *
save_body_particulars(struct mail_bodystruct * body)4259 save_body_particulars(struct mail_bodystruct *body)
4260 {
4261 BODY_PARTICULARS_S *bp;
4262 PARAMETER *pm;
4263
4264 bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
4265
4266 bp->type = body->type;
4267 bp->encoding = body->encoding;
4268 bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
4269 bp->parameter = body->parameter;
4270 for(pm = bp->parameter;
4271 pm && strucmp(pm->attribute, "charset") != 0;
4272 pm = pm->next)
4273 ;/* searching for possible charset parameter */
4274
4275 if(pm){ /* found one */
4276 bp->had_csp = 1; /* saved body had charset parameter */
4277 bp->charset = pm->value ? cpystr(pm->value) : NULL;
4278 }
4279 else{
4280 bp->had_csp = 0;
4281 bp->charset = NULL;
4282 }
4283
4284 return(bp);
4285 }
4286
4287
4288 void
reset_body_particulars(BODY_PARTICULARS_S * bp,struct mail_bodystruct * body)4289 reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
4290 {
4291 body->type = bp->type;
4292 body->encoding = bp->encoding;
4293 if(body->subtype)
4294 fs_give((void **)&body->subtype);
4295
4296 body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
4297
4298 if(bp->parameter){
4299 PARAMETER *pm, *pm_prev = NULL;
4300
4301 for(pm = body->parameter;
4302 pm && strucmp(pm->attribute, "charset") != 0;
4303 pm = pm->next)
4304 pm_prev = pm;
4305
4306 if(pm){ /* body has charset parameter */
4307 if(bp->had_csp){ /* reset to what it used to be */
4308 if(pm->value)
4309 fs_give((void **)&pm->value);
4310
4311 pm->value = bp->charset ? cpystr(bp->charset) : NULL;
4312 }
4313 else{ /* remove charset parameter */
4314 if(pm_prev)
4315 pm_prev->next = pm->next;
4316 else
4317 body->parameter = pm->next;
4318
4319 mail_free_body_parameter(&pm);
4320 }
4321 }
4322 else{
4323 if(bp->had_csp){
4324 /*
4325 * This can't happen because grope never removes
4326 * the charset parameter.
4327 */
4328 q_status_message(SM_ORDER | SM_DING, 5, 5,
4329 "Programmer error: saved charset but no current charset param in pine_send");
4330 }
4331 /*
4332 else{
4333 ok, still no parameter
4334 }
4335 */
4336 }
4337 }
4338 else{
4339 if(body->parameter)
4340 mail_free_body_parameter(&body->parameter);
4341
4342 body->parameter = NULL;
4343 }
4344 }
4345
4346
4347 void
free_body_particulars(BODY_PARTICULARS_S * bp)4348 free_body_particulars(BODY_PARTICULARS_S *bp)
4349 {
4350 if(bp){
4351 if(bp->subtype)
4352 fs_give((void **)&bp->subtype);
4353
4354 if(bp->charset)
4355 fs_give((void **)&bp->charset);
4356
4357 fs_give((void **)&bp);
4358 }
4359 }
4360
4361
4362 /*----------------------------------------------------------------------
4363 Build a status message suitable for framing
4364
4365 Returns: pointer to resulting buffer
4366 ---*/
4367 char *
pine_send_status(int result,char * fcc_name,char * buf,size_t buflen,int * goodorbad)4368 pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
4369 {
4370 int avail = ps_global->ttyo->screen_cols - 2;
4371 int fixedneed, need, lenfcc;
4372 char *part1, *part2, *part3, *part4, *part5;
4373 char fbuf[MAILTMPLEN+1];
4374
4375 part1 = (result & P_NEWS_WIN)
4376 ? "posted"
4377 : (result & P_NEWS_LOSE)
4378 ? "NOT POSTED"
4379 : "";
4380 part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
4381 && (result & P_FCC_BITS))
4382 ? ", "
4383 : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
4384 ? " and "
4385 : "";
4386 part3 = (result & P_MAIL_WIN)
4387 ? "sent"
4388 : (result & P_MAIL_LOSE)
4389 ? "NOT SENT"
4390 : "";
4391 part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
4392 ? " and "
4393 : "";
4394 part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
4395 ? "ONLY copied to "
4396 : (result & P_FCC_WIN)
4397 ? "copied to "
4398 : (result & P_FCC_LOSE)
4399 ? "NOT copied to "
4400 : "";
4401 lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
4402
4403 fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
4404 strlen(part4) + strlen(part5);
4405 need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
4406
4407 if(need > avail && fixedneed + 3 >= avail){
4408 /* dots on end of fixed, no fcc */
4409 snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
4410 part1, part2, part3, part4, part5);
4411 short_str(fbuf, buf, buflen, avail, EndDots);
4412 }
4413 else if(need > avail){
4414 /* include whole fixed part, quotes and dots at end of fcc name */
4415 if(lenfcc > 0)
4416 lenfcc = MAX(1, lenfcc-(need-avail));
4417
4418 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4419 part1, part2, part3, part4, part5,
4420 (result & P_FCC_BITS) ? "\"" : "",
4421 short_str((result & P_FCC_BITS) ? fcc_name : "",
4422 fbuf, sizeof(fbuf), lenfcc, FrontDots),
4423 (result & P_FCC_BITS) ? "\"" : "");
4424 }
4425 else{
4426 /* whole thing */
4427 snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
4428 part1, part2, part3, part4, part5,
4429 (result & P_FCC_BITS) ? "\"" : "",
4430 (result & P_FCC_BITS) ? fcc_name : "",
4431 (result & P_FCC_BITS) ? "\"" : "");
4432 }
4433
4434 if(goodorbad)
4435 *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
4436
4437 return(buf);
4438 }
4439
4440 /* Callback from Pico to set the conditions for Alpine to start a new thread
4441 */
4442
4443 void
new_thread_on_blank_subject(void)4444 new_thread_on_blank_subject(void)
4445 {
4446 ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
4447 }
4448
4449
4450
4451 /*----------------------------------------------------------------------
4452 Call back for pico to insert the specified message's text
4453
4454 Args: n -- message number to format
4455 f -- function to use to output the formatted message
4456
4457
4458 Returns: returns msg number formatted on success, zero on error.
4459 ----*/
4460 long
message_format_for_pico(long int n,int (* f)(int))4461 message_format_for_pico(long int n, int (*f) (int))
4462 {
4463 ENVELOPE *e;
4464 BODY *b;
4465 char *old_quote = NULL;
4466 long rv = n;
4467
4468 if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
4469 && (e = pine_mail_fetchstructure(ps_global->mail_stream,
4470 mn_m2raw(ps_global->msgmap, n), &b)))){
4471 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4472 flush_status_messages(0);
4473 return(0L);
4474 }
4475
4476 /* temporarily assign a new quote string */
4477 old_quote = pbf->quote_str;
4478 pbf->quote_str = reply_quote_str(e);
4479
4480 /* build separator line */
4481 reply_delimiter(e, NULL, f);
4482
4483 /* actually write message text */
4484 if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
4485 FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
4486 q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
4487 flush_status_messages(0);
4488 rv = 0L;
4489 }
4490
4491 fs_give((void **)&pbf->quote_str);
4492 pbf->quote_str = old_quote;
4493 return(rv);
4494 }
4495
4496
4497 /*----------------------------------------------------------------------
4498 Call back for pico to prompt the user for exit confirmation
4499
4500 Args: dflt -- default answer for confirmation prompt
4501
4502 Returns: either NULL if the user accepts exit, or string containing
4503 reason why the user declined.
4504 ----*/
4505 int
send_exit_for_pico(struct headerentry * he,void (* redraw_pico)(void),int allow_flowed,char ** result)4506 send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
4507 char **result)
4508 {
4509 int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
4510 int dsn_label = 0, fcc_label = 0, lparen;
4511 int flowing_label = 0, double_rad;
4512 char *rstr = NULL, *p, *lc, *optp;
4513 char dsn_string[30];
4514 void (*redraw)(void) = ps_global->redrawer;
4515 ESCKEY_S opts[18];
4516 struct filters {
4517 char *filter;
4518 int index;
4519 struct filters *prev, *next;
4520 } *filters = NULL, *fp;
4521
4522 sending_filter_requested = NULL;
4523 call_mailer_flags = 0;
4524 background_requested = 0;
4525 flowing_requested = allow_flowed ? 1 : 0;
4526 lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
4527 if(priority_requested)
4528 fs_give((void **) &priority_requested);
4529
4530 if(background_posting(FALSE)){
4531 if(result)
4532 *result = "Can't send while background posting. Use postpone.";
4533
4534 return(1);
4535 }
4536
4537 if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
4538 if(result)
4539 *result = NULL;
4540
4541 return(0);
4542 }
4543
4544 ps_global->redrawer = redraw_pico;
4545
4546 if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
4547 (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
4548
4549 /*
4550 * Build list of available filters...
4551 */
4552 for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
4553 for(p = ps_global->VAR_SEND_FILTER[i];
4554 *p && !isspace((unsigned char)*p); p++)
4555 ;
4556
4557 c = *p;
4558 *p = '\0';
4559 if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
4560 && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
4561 *p = c;
4562 continue;
4563 }
4564
4565 fp = (struct filters *)fs_get(sizeof(struct filters));
4566 fp->index = i;
4567 if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
4568 fp->filter = cpystr(lc);
4569 }
4570 else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
4571 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
4572 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4573 fp->filter = cpystr(tmp_20k_buf);
4574 }
4575 else
4576 fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
4577
4578 *p = c;
4579
4580 if(filters){
4581 fp->next = filters;
4582 fp->prev = filters->prev;
4583 fp->prev->next = filters->prev = fp;
4584 }
4585 else{
4586 filters = (struct filters *)fs_get(sizeof(struct filters));
4587 filters->index = -1;
4588 filters->filter = NULL;
4589 filters->next = filters->prev = fp;
4590 fp->next = fp->prev = filters;
4591 }
4592 }
4593
4594 i = 0;
4595 opts[i].ch = 'y';
4596 opts[i].rval = 'y';
4597 opts[i].name = "Y";
4598 opts[i++].label = N_("Yes");
4599
4600 opts[i].ch = 'n';
4601 opts[i].rval = 'n';
4602 opts[i].name = "N";
4603 opts[i++].label = N_("No");
4604
4605 if(filters){
4606 /* set global_filter_pointer to desired filter or NULL if none */
4607 /* prepare two keymenu slots for selecting filter */
4608 opts[i].ch = ctrl('P');
4609 opts[i].rval = 10;
4610 opts[i].name = "^P";
4611 opts[i++].label = N_("Prev Filter");
4612
4613 opts[i].ch = ctrl('N');
4614 opts[i].rval = 11;
4615 opts[i].name = "^N";
4616 opts[i++].label = N_("Next Filter");
4617
4618 if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
4619 filters = filters->next;
4620 }
4621
4622 if(F_ON(F_VERBOSE_POST, ps_global)){
4623 /* setup keymenu slot to toggle verbose mode */
4624 opts[i].ch = ctrl('W');
4625 opts[i].rval = 12;
4626 opts[i].name = "^W";
4627 verbose_label = i++;
4628 }
4629
4630 if(allow_flowed){
4631 /* setup keymenu slot to toggle flowed mode */
4632 opts[i].ch = ctrl('V');
4633 opts[i].rval = 22;
4634 opts[i].name = "^V";
4635 flowing_label = i++;
4636 flowing_requested = 1;
4637 }
4638
4639 if(F_ON(F_NO_FCC_ATTACH, ps_global)){
4640 /* setup keymenu slot to toggle attacment on fcc */
4641 opts[i].ch = ctrl('F');
4642 opts[i].rval = 21;
4643 opts[i].name = "^F";
4644 fcc_label = i++;
4645 }
4646
4647 #if defined(BACKGROUND_POST) && defined(SIGCHLD)
4648 if(F_ON(F_BACKGROUND_POST, ps_global)){
4649 opts[i].ch = ctrl('R');
4650 opts[i].rval = 15;
4651 opts[i].name = "^R";
4652 bg_label = i++;
4653 }
4654 #endif
4655
4656 opts[i].ch = 'p';
4657 opts[i].rval = 'p';
4658 opts[i].name = "P";
4659 opts[i++].label = N_("Priority");
4660
4661 #ifdef SMIME
4662 if(F_OFF(F_DONT_DO_SMIME, ps_global)){
4663 opts[i].ch = 'e';
4664 opts[i].rval = 'e';
4665 opts[i].name = "E";
4666 opts[i++].label = "Encrypt";
4667
4668 opts[i].ch = 'g';
4669 opts[i].rval = 'g';
4670 opts[i].name = "G";
4671 opts[i++].label = "Sign";
4672
4673 if(ps_global->smime){
4674 ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
4675 ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
4676 }
4677 }
4678 #endif
4679
4680 double_rad = i;
4681
4682 if(F_ON(F_DSN, ps_global)){
4683 /* setup keymenu slots to toggle dsn bits */
4684 opts[i].ch = 'd';
4685 opts[i].rval = 'd';
4686 opts[i].name = "D";
4687 opts[i].label = N_("DSNOpts");
4688 dsn_label = i++;
4689 opts[i].ch = -2;
4690 opts[i].rval = 's';
4691 opts[i].name = "S";
4692 opts[i++].label = "";
4693 opts[i].ch = -2;
4694 opts[i].rval = 'x';
4695 opts[i].name = "X";
4696 opts[i++].label = "";
4697 opts[i].ch = -2;
4698 opts[i].rval = 'h';
4699 opts[i].name = "H";
4700 opts[i++].label = "";
4701 }
4702
4703 if(filters){
4704 opts[i].ch = KEY_UP;
4705 opts[i].rval = 10;
4706 opts[i].name = "";
4707 opts[i++].label = "";
4708
4709 opts[i].ch = KEY_DOWN;
4710 opts[i].rval = 11;
4711 opts[i].name = "";
4712 opts[i++].label = "";
4713 }
4714
4715 opts[i].ch = -1;
4716
4717 fix_windsize(ps_global);
4718
4719 while(1){
4720 if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
4721 *p = '\0';
4722 else
4723 p = NULL;
4724
4725 lparen = 0;
4726 strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
4727 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4728 optp = tmp_20k_buf + strlen(tmp_20k_buf);
4729
4730 if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
4731 sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4732
4733 if(allow_flowed && !flowing_requested){
4734 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4735 if(!lparen){
4736 *optp++ = ' ';
4737 *optp++ = '(';
4738 lparen++;
4739 }
4740 else
4741 *optp++ = ' ';
4742 }
4743
4744 sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4745 }
4746
4747 if(filters){
4748 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4749 if(!lparen){
4750 *optp++ = ' ';
4751 *optp++ = '(';
4752 lparen++;
4753 }
4754 else{
4755 *optp++ = ',';
4756 *optp++ = ' ';
4757 }
4758 }
4759
4760 if(filters->filter){
4761 sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4762 sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4763 if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
4764 *optp++ = '\"';
4765 }
4766 else
4767 sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4768 }
4769
4770 if((call_mailer_flags & CM_VERBOSE) || background_requested){
4771 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4772 if(!lparen){
4773 *optp++ = ' ';
4774 *optp++ = '(';
4775 lparen++;
4776 }
4777 else
4778 *optp++ = ' ';
4779 }
4780
4781 sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4782 if(call_mailer_flags & CM_VERBOSE)
4783 sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4784
4785 if(background_requested)
4786 sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4787
4788 sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4789 }
4790
4791 if(g_rolenick && !(he && he[N_FROM].dirty)){
4792 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4793 if(!lparen){
4794 *optp++ = ' ';
4795 *optp++ = '(';
4796 lparen++;
4797 }
4798 else
4799 *optp++ = ' ';
4800 }
4801
4802 sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4803 sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4804 sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4805 }
4806
4807 if(call_mailer_flags & CM_DSN_SHOW){
4808 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4809 if(!lparen){
4810 *optp++ = ' ';
4811 *optp++ = '(';
4812 lparen++;
4813 }
4814 else{
4815 *optp++ = ',';
4816 *optp++ = ' ';
4817 }
4818 }
4819
4820 sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
4821 }
4822
4823 #ifdef SMIME
4824 if(ps_global->smime && ps_global->smime->do_encrypt){
4825 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4826 if(!lparen){
4827 *optp++ = ' ';
4828 *optp++ = '(';
4829 lparen++;
4830 }
4831 else{
4832 *optp++ = ',';
4833 *optp++ = ' ';
4834 }
4835 }
4836
4837 sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4838 }
4839
4840 if(ps_global->smime && ps_global->smime->do_sign){
4841 if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
4842 if(!lparen){
4843 *optp++ = ' ';
4844 *optp++ = '(';
4845 lparen++;
4846 }
4847 else{
4848 *optp++ = ',';
4849 *optp++ = ' ';
4850 }
4851 }
4852
4853 sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4854 }
4855 #endif
4856
4857 if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
4858 *optp++ = ')';
4859
4860 sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
4861 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
4862
4863 if(p)
4864 *p = ' ';
4865
4866 if(flowing_label)
4867 opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
4868
4869 if(verbose_label)
4870 opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
4871
4872 if(bg_label)
4873 opts[bg_label].label = background_requested
4874 ? N_("Foreground") : N_("Background");
4875
4876 if(fcc_label)
4877 opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
4878 : N_("No Fcc Atmts ");
4879
4880 if(F_ON(F_DSN, ps_global)){
4881 if(call_mailer_flags & CM_DSN_SHOW){
4882 opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
4883 ? N_("NoDelay") : N_("Delay");
4884 opts[dsn_label+1].ch = 's';
4885 opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
4886 ? N_("NoSuccess") : N_("Success");
4887 opts[dsn_label+2].ch = 'x';
4888 opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
4889 ? N_("ErrRets") : N_("NoErrRets");
4890 opts[dsn_label+3].ch = 'h';
4891 opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
4892 ? N_("RetHdrs") : N_("RetFull");
4893 }
4894 }
4895
4896 if(double_rad +
4897 ((call_mailer_flags & CM_DSN_SHOW)
4898 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
4899 rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4900 'y', 'z',
4901 (F_ON(F_DSN, ps_global) && allow_flowed)
4902 ? h_send_prompt_dsn_flowed :
4903 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4904 allow_flowed ? h_send_prompt_flowed :
4905 h_send_prompt,
4906 RB_NORM);
4907 else
4908 rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
4909 'y', 'z',
4910 (double_rad +
4911 ((call_mailer_flags & CM_DSN_SHOW)
4912 ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
4913 ? NO_HELP :
4914 (F_ON(F_DSN, ps_global) && allow_flowed)
4915 ? h_send_prompt_dsn_flowed :
4916 F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
4917 allow_flowed ? h_send_prompt_flowed :
4918 h_send_prompt,
4919 RB_NORM);
4920
4921 if(rv == 'y'){ /* user ACCEPTS! */
4922 break;
4923 }
4924 else if(rv == 'n'){ /* Declined! */
4925 rstr = _("No Message Sent");
4926 break;
4927 }
4928 else if(rv == 'z'){ /* Cancelled! */
4929 rstr = _("Send Cancelled");
4930 break;
4931 }
4932 else if(rv == 10){ /* PREVIOUS filter */
4933 filters = filters->prev;
4934 }
4935 else if(rv == 11){ /* NEXT filter */
4936 filters = filters->next;
4937 }
4938 else if(rv == 21){
4939 lmc.text_only = !lmc.text_only;
4940 }
4941 else if(rv == 12){ /* flip verbose bit */
4942 if(call_mailer_flags & CM_VERBOSE)
4943 call_mailer_flags &= ~CM_VERBOSE;
4944 else
4945 call_mailer_flags |= CM_VERBOSE;
4946
4947 if((call_mailer_flags & CM_VERBOSE) && background_requested)
4948 background_requested = 0;
4949 }
4950 else if(rv == 22){ /* flip flowing bit */
4951 flowing_requested = !flowing_requested;
4952 }
4953 else if(rv == 15){
4954 if((background_requested = !background_requested)
4955 && (call_mailer_flags & CM_VERBOSE))
4956 call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
4957 }
4958 else if(call_mailer_flags & CM_DSN_SHOW){
4959 if(rv == 's'){ /* flip success bit */
4960 call_mailer_flags ^= CM_DSN_SUCCESS;
4961 /* turn off related bits */
4962 if(call_mailer_flags & CM_DSN_SUCCESS)
4963 call_mailer_flags &= ~(CM_DSN_NEVER);
4964 }
4965 else if(rv == 'd'){ /* flip delay bit */
4966 call_mailer_flags ^= CM_DSN_DELAY;
4967 /* turn off related bits */
4968 if(call_mailer_flags & CM_DSN_DELAY)
4969 call_mailer_flags &= ~(CM_DSN_NEVER);
4970 }
4971 else if(rv == 'x'){ /* flip never bit */
4972 call_mailer_flags ^= CM_DSN_NEVER;
4973 /* turn off related bits */
4974 if(call_mailer_flags & CM_DSN_NEVER)
4975 call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
4976 }
4977 else if(rv == 'h'){ /* flip full bit */
4978 call_mailer_flags ^= CM_DSN_FULL;
4979 }
4980 }
4981 else if(rv == 'd'){ /* show dsn options */
4982 /*
4983 * When you turn on DSN, the default is to notify on
4984 * failure, success, or delay; and to return the whole
4985 * body on failure.
4986 */
4987 call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
4988 }
4989 else if(rv == 'p'){ /* choose X-Priority */
4990 char *prio;
4991
4992 prio = choose_a_priority(priority_requested);
4993 if((ps_global->redrawer = redraw_pico) != NULL){
4994 (*ps_global->redrawer)();
4995 fix_windsize(ps_global);
4996 }
4997
4998 if(prio){
4999 if(priority_requested)
5000 fs_give((void **) &priority_requested);
5001
5002 if(prio[0])
5003 priority_requested = prio;
5004 else
5005 fs_give((void **) &prio);
5006 }
5007 }
5008 #ifdef SMIME
5009 else if(rv=='e'){
5010 if(ps_global->smime)
5011 ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
5012 }
5013 else if(rv=='g'){
5014 if(ps_global->smime)
5015 ps_global->smime->do_sign = !ps_global->smime->do_sign;
5016 }
5017 #endif
5018
5019 snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
5020 (call_mailer_flags & CM_DSN_NEVER)
5021 ? "Never" : "F",
5022 (call_mailer_flags & CM_DSN_DELAY)
5023 ? "D" : "",
5024 (call_mailer_flags & CM_DSN_SUCCESS)
5025 ? "S" : "",
5026 (call_mailer_flags & CM_DSN_NEVER)
5027 ? ""
5028 : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
5029 : "-Hdrs");
5030 dsn_string[sizeof(dsn_string)-1] = '\0';
5031 }
5032
5033 /* remember selection */
5034 if(filters && filters->index > -1)
5035 sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
5036
5037 if(filters){
5038 filters->prev->next = NULL; /* tie off list */
5039 while(filters){ /* then free it */
5040 fp = filters->next;
5041 if(filters->filter)
5042 fs_give((void **)&filters->filter);
5043
5044 fs_give((void **)&filters);
5045 filters = fp;
5046 }
5047 }
5048
5049 if(old_suspend)
5050 (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
5051
5052 if(result)
5053 *result = rstr;
5054
5055 ps_global->redrawer = redraw;
5056
5057 return((rstr == NULL) ? 0 : 1);
5058 }
5059
5060
5061 /*
5062 * Allow user to choose a priority for sending.
5063 *
5064 * Returns an allocated priority on success, NULL otherwise.
5065 */
5066 char *
choose_a_priority(char * default_val)5067 choose_a_priority(char *default_val)
5068 {
5069 char *choice = NULL;
5070 char **priority_list, **lp;
5071 char *starting_val = NULL;
5072 char *none;
5073 int i, cnt;
5074 PRIORITY_S *p;
5075
5076 for(cnt = 0, p = priorities; p && p->desc; p++)
5077 cnt++;
5078
5079 cnt++; /* for NONE entry */
5080 lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
5081 memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
5082
5083 for(i = 0, p = priorities; p && p->desc; p++){
5084 *lp = cpystr(p->desc);
5085 if(default_val && !strcmp(default_val, p->desc))
5086 starting_val = (*lp);
5087
5088 lp++;
5089 }
5090
5091 none = _("NONE - No X-Priority header included");
5092 *lp = cpystr(none);
5093 if(!starting_val)
5094 starting_val = (*lp);
5095
5096 /* TRANSLATORS: SELECT A PRIORITY is a screen title
5097 TRANSLATORS: Print something1 using something2.
5098 "priorities" is something1 */
5099 choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
5100 _("priorities"), h_select_priority_screen,
5101 _("HELP FOR SELECTING A PRIORITY"),
5102 starting_val);
5103
5104 if(!choice)
5105 q_status_message(SM_ORDER, 1, 4, _("No change"));
5106 else if(!strcmp(choice, none))
5107 choice[0] = '\0';
5108
5109 free_list_array(&priority_list);
5110
5111 return(choice);
5112 }
5113
5114
5115 int
dont_flow_this_time(void)5116 dont_flow_this_time(void)
5117 {
5118 return(flowing_requested ? 0 : 1);
5119 }
5120
5121
5122 /*----------------------------------------------------------------------
5123 Call back for pico to display mime type of attachment
5124
5125 Args: file -- filename being attached
5126
5127 Returns: returns 1 on success (message queued), zero otherwise (don't know
5128 type so nothing queued).
5129 ----*/
5130 int
mime_type_for_pico(char * file)5131 mime_type_for_pico(char *file)
5132 {
5133 BODY *body;
5134 int rv;
5135 void *file_contents;
5136
5137 body = mail_newbody();
5138 body->type = TYPEOTHER;
5139 body->encoding = ENCOTHER;
5140
5141 /* don't know where the cursor's been, reset it */
5142 clear_cursor_pos();
5143 if(!set_mime_type_by_extension(body, file)){
5144 if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5145 body->contents.text.data = file_contents;
5146 set_mime_type_by_grope(body);
5147 }
5148 }
5149
5150 if(body->type != TYPEOTHER){
5151 rv = 1;
5152 q_status_message3(SM_ORDER, 0, 3,
5153 _("File %s attached as type %s/%s"), file,
5154 body_types[body->type],
5155 body->subtype ? body->subtype : rfc822_default_subtype(body->type));
5156 }
5157 else
5158 rv = 0;
5159
5160 pine_free_body(&body);
5161 return(rv);
5162 }
5163
5164
5165 /*----------------------------------------------------------------------
5166 Call back for pico to receive an uploaded message
5167
5168 Args: fname -- name for uploaded file (empty if they want us to assign it)
5169 size -- pointer to long to hold the attachment's size
5170
5171 Notes: the attachment is uploaded to a temp file, and
5172
5173 Returns: TRUE on success, FALSE otherwise
5174 ----*/
5175 int
upload_msg_to_pico(char * fname,size_t fnlen,long int * size)5176 upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
5177 {
5178 char cmd[MAXPATH+1], *fnp = NULL;
5179 char *locale_name = NULL;
5180 long l;
5181 PIPE_S *syspipe;
5182
5183 dprint((1, "Upload cmd called to xfer \"%s\"\n",
5184 fname ? fname : "<NO FILE>"));
5185
5186 if(!fname) /* no place for file name */
5187 return(0);
5188
5189 if(!*fname){ /* caller wants temp file */
5190 if((fnp = temp_nam(NULL, "pu")) != NULL){
5191 strncpy(fname, fnp, fnlen);
5192 fname[fnlen-1] = '\0';
5193 our_unlink(fnp);
5194 fs_give((void **)&fnp);
5195 }
5196 }
5197 else
5198 locale_name = convert_to_locale(fname);
5199
5200 build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
5201 ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
5202 if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
5203 0, pipe_callback, pipe_report_error)) != NULL){
5204 (void) close_system_pipe(&syspipe, NULL, pipe_callback);
5205 if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
5206 q_status_message2(SM_ORDER | SM_DING, 3, 4,
5207 "Error determining size of %s: %s", fname,
5208 fnp = error_description(errno));
5209 dprint((1,
5210 "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
5211 cmd ? cmd : "?",
5212 fname ? fname : "?",
5213 fnp ? fnp : "?"));
5214 }
5215 else if(size)
5216 *size = l;
5217
5218 if(locale_name)
5219 fs_give((void **) &locale_name);
5220
5221 return(l >= 0);
5222 }
5223 else
5224 q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
5225
5226 if(locale_name)
5227 fs_give((void **) &locale_name);
5228
5229 return(0);
5230 }
5231
5232
5233 char *
cancel_for_pico(void (* redraw_pico)(void))5234 cancel_for_pico(void (*redraw_pico)(void))
5235 {
5236 int rv;
5237 char *rstr = NULL;
5238 char *prompt =
5239 _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
5240 void (*redraw)(void) = ps_global->redrawer;
5241 static ESCKEY_S opts[] = {
5242 {'c', 'c', "C", N_("Confirm")},
5243 {'n', 'n', "N", N_("No")},
5244 {'y', 'y', "", ""},
5245 {-1, 0, NULL, NULL}
5246 };
5247
5248 ps_global->redrawer = redraw_pico;
5249 fix_windsize(ps_global);
5250
5251 while(1){
5252 rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
5253 'n', 'x', h_confirm_cancel, RB_NORM);
5254 if(rv == 'c'){ /* user ACCEPTS! */
5255 rstr = "";
5256 break;
5257 }
5258 else if(rv == 'y'){
5259 q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
5260 display_message('x');
5261 }
5262 else
5263 break;
5264 }
5265
5266 ps_global->redrawer = redraw;
5267 return(rstr);
5268 }
5269
5270
5271 /*----------------------------------------------------------------------
5272 Pass the first text segment of the message thru the "send filter"
5273
5274 Args: body pointer and address for storage object of old data
5275
5276 Returns: returns 1 on success, zero on error.
5277 ----*/
5278 int
filter_message_text(char * fcmd,ENVELOPE * outgoing,struct mail_bodystruct * body,STORE_S ** old,METAENV * header)5279 filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
5280 STORE_S **old, METAENV *header)
5281 {
5282 char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
5283 int key = 0, include_hdrs = 0;
5284 gf_io_t gc, pc;
5285 STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
5286 ? &body->nested.part->body.contents.text.data
5287 : &body->contents.text.data),
5288 *tmp_so = NULL, *tmpf_so,
5289 *save_local_so, *readthis_so = NULL, *our_tmpf_so = NULL;
5290 #define DO_HEADERS 1
5291
5292 if(fcmd
5293 && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
5294 &key, &include_hdrs, NULL))){
5295 if(tmpf){
5296 /*
5297 * We need WRITE_TO_LOCALE here because the user is going to
5298 * be operating on tmpf. We need to change it back after they
5299 * are done.
5300 */
5301 if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
5302 if(key){
5303 so_puts(tmpf_so, filter_session_key());
5304 so_puts(tmpf_so, NEWLINE);
5305 }
5306
5307 /*
5308 * If the headers are wanted for filtering, we can just
5309 * stick them in the tmpf file that is already there before
5310 * putting the body in.
5311 */
5312 if(include_hdrs){
5313 save_local_so = lmc.so;
5314 lmc.so = tmpf_so; /* write it to tmpf_so */
5315 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5316 pine_rfc822_header(header, body, NULL, NULL);
5317 lmc.so = save_local_so;
5318 }
5319
5320 so_seek(*so, 0L, 0);
5321 gf_set_so_readc(&gc, *so);
5322 gf_set_so_writec(&pc, tmpf_so);
5323 gf_filter_init();
5324 errstr = gf_pipe(gc, pc);
5325 gf_clear_so_readc(*so);
5326 gf_clear_so_writec(tmpf_so);
5327 so_give(&tmpf_so);
5328 }
5329 else
5330 errstr = "Can't create space for filter temporary file.";
5331 }
5332 else if(include_hdrs){
5333 /*
5334 * Gf_filter wants a single storage object to read from.
5335 * If headers are wanted for filtering we'll have to put them
5336 * and the body into a temp file first and then use that
5337 * as the storage object for gf_filter.
5338 * We don't use WRITE_TO_LOCALE in this case because gf_filter
5339 * takes care of that.
5340 */
5341 if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
5342 /* put headers in our_tmpf_so */
5343 save_local_so = lmc.so;
5344 lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
5345 lmc.all_written = lmc.text_written = lmc.text_only = 0;
5346 pine_rfc822_header(header, body, NULL, NULL);
5347 lmc.so = save_local_so;
5348
5349 /* put body in our_tmpf_so */
5350 so_seek(*so, 0L, 0);
5351 gf_set_so_readc(&gc, *so);
5352 gf_set_so_writec(&pc, our_tmpf_so);
5353 gf_filter_init();
5354 errstr = gf_pipe(gc, pc);
5355 gf_clear_so_readc(*so);
5356 gf_clear_so_writec(our_tmpf_so);
5357
5358 /* tell gf_filter to read from our_tmpf_so instead of *so */
5359 readthis_so = our_tmpf_so;
5360 }
5361 else
5362 errstr = "Can't create space for temporary file.";
5363 }
5364 else
5365 readthis_so = *so;
5366
5367 if(!errstr){
5368 if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
5369 gf_set_so_writec(&pc, tmp_so);
5370 ps_global->mangled_screen = 1;
5371 suspend_busy_cue();
5372 ClearScreen();
5373 fflush(stdout);
5374 if(tmpf){
5375 PIPE_S *fpipe;
5376
5377 if((fpipe = open_system_pipe(cmd, NULL, NULL,
5378 PIPE_NOSHELL | PIPE_RESET,
5379 0, pipe_callback, pipe_report_error)) != NULL){
5380 if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
5381
5382 /* now we undo the WRITE_FROM_LOCALE change in tmpf */
5383 if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
5384 gf_set_so_readc(&gc, tmpf_so);
5385 gf_filter_init();
5386 errstr = gf_pipe(gc, pc);
5387 gf_clear_so_readc(tmpf_so);
5388 so_give(&tmpf_so);
5389 }
5390 else
5391 errstr = "Can't open temp file filter wrote.";
5392 }
5393 else
5394 errstr = "Filter command returned error.";
5395 }
5396 else
5397 errstr = "Can't exec filter text.";
5398 }
5399 else
5400 errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
5401 readthis_so, pc, NULL, 0, 0,
5402 pipe_callback);
5403
5404 if(our_tmpf_so)
5405 so_give(&our_tmpf_so);
5406
5407 gf_clear_so_writec(tmp_so);
5408
5409 #ifdef _WINDOWS
5410 if(!errstr){
5411 /*
5412 * Can't really be using stdout, so don't print message that
5413 * gets printed in the else. Ideally the program being called
5414 * will wait after showing the message, we might want to look
5415 * into doing the waiting in console based apps... or not.
5416 */
5417 #else
5418 if(errstr){
5419 unsigned long ch;
5420
5421 fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
5422 fflush(stdout);
5423 while((ch = read_char(300)) != ctrl('M')
5424 && ch != NO_OP_IDLE)
5425 putchar(BELL);
5426 }
5427 else{
5428 #endif /* _WINDOWS */
5429 BODY *b = (body->type == TYPEMULTIPART)
5430 ? &body->nested.part->body : body;
5431
5432 *old = *so; /* save old so */
5433 *so = tmp_so; /* return new one */
5434 (*so)->attr = copy_parameters((*old)->attr);
5435
5436 /*
5437 * If the command said it would return new MIME
5438 * mime type data, check it out...
5439 */
5440 if(mtf){
5441 char buf[MAILTMPLEN], *s;
5442 FILE *fp;
5443
5444 if((fp = our_fopen(mtf, "rb")) != NULL){
5445 if(fgets(buf, sizeof(buf), fp)
5446 && !struncmp(buf, "content-", 8)
5447 && (s = strchr(buf+8, ':'))){
5448 BODY *nb = mail_newbody();
5449
5450 for(*s++ = '\0'; *s == ' '; s++)
5451 ;
5452
5453 rfc822_parse_content_header(nb,
5454 (char *) ucase((unsigned char *) buf+8),s);
5455 if(nb->type == TYPETEXT
5456 && nb->subtype
5457 && (!b->subtype
5458 || strucmp(b->subtype, nb->subtype))){
5459 if(b->subtype)
5460 fs_give((void **) &b->subtype);
5461
5462 b->subtype = nb->subtype;
5463 nb->subtype = NULL;
5464
5465 mail_free_body_parameter(&b->parameter);
5466 b->parameter = nb->parameter;
5467 nb->parameter = NULL;
5468 mail_free_body_parameter(&nb->parameter);
5469 }
5470
5471 mail_free_body(&nb);
5472 }
5473
5474 fclose(fp);
5475 }
5476 }
5477
5478 /*
5479 * Reevaluate the encoding in case form's changed...
5480 */
5481 b->encoding = ENCOTHER;
5482 set_mime_type_by_grope(b);
5483 }
5484
5485 ClearScreen();
5486 resume_busy_cue(0);
5487 }
5488 else
5489 errstr = "Can't create space for filtered text.";
5490 }
5491
5492 fs_give((void **)&cmd);
5493 }
5494 else
5495 return(0);
5496
5497 if(tmpf){
5498 our_unlink(tmpf);
5499 fs_give((void **)&tmpf);
5500 }
5501
5502 if(mtf){
5503 our_unlink(mtf);
5504 fs_give((void **) &mtf);
5505 }
5506
5507 if(resultf){
5508 if(name_file_size(resultf) > 0L)
5509 display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
5510 our_unlink(resultf);
5511 fs_give((void **)&resultf);
5512 }
5513 else if(errstr){
5514 if(tmp_so)
5515 so_give(&tmp_so);
5516
5517 q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
5518 errstr);
5519 dprint((1, "Filter FAILED: %s\n",
5520 errstr ? errstr : "?"));
5521 }
5522
5523 return(errstr == NULL);
5524 }
5525
5526
5527 /*----------------------------------------------------------------------
5528 Copy the newsgroup name of the given mailbox into the given buffer
5529
5530 Args:
5531
5532 Returns:
5533 ----*/
5534 void
5535 pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
5536 {
5537 NETMBX mb;
5538
5539 if(*mailbox == '#'){ /* Strip the leading "#news." */
5540 strncpy(group_name, mailbox + 6, len-1);
5541 group_name[len-1] = '\0';
5542 }
5543 else if(mail_valid_net_parse(mailbox, &mb)){
5544 pine_send_newsgroup_name(mb.mailbox, group_name, len);
5545 }
5546 else
5547 *group_name = '\0';
5548 }
5549
5550
5551 /*----------------------------------------------------------------------
5552 Set up fields for passing to pico. Assumes first text part is
5553 intended to be passed along for editing, and is in the form of
5554 of a storage object brought into existence sometime before pico_send().
5555 -----*/
5556 void
5557 outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
5558 PATMT **pico_a, int from_bounce)
5559 {
5560 PINEFIELD *pf;
5561
5562 /*
5563 * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
5564 * is guaranteed to be of type PicoText!
5565 */
5566 if(bod->type == TYPETEXT){
5567 *text = so_text((STORE_S *) bod->contents.text.data);
5568
5569 /* mark storage object as user edited */
5570 if(!from_bounce)
5571 (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
5572 }
5573 else if(bod->type == TYPEMULTIPART){
5574 PART *part;
5575 PATMT **ppa;
5576 char *type, *name, *p;
5577 int name_l;
5578
5579 /*
5580 * We used to jump out the window if the first part wasn't text,
5581 * but that may not be the case when bouncing a message with
5582 * a leading non-text segment. So, IT'S UNDERSTOOD that the
5583 * contents of the first part to send is still ALWAYS in a
5584 * PicoText storage object, *AND* if that object doesn't contain
5585 * data of type text, then it must contain THE ENCODED NON-TEXT
5586 * DATA of the piece being sent.
5587 *
5588 * It's up to the programmer to make sure that such a message is
5589 * sent via pine_simple_send and never get to the composer via
5590 * pine_send.
5591 *
5592 * Make sense?
5593 */
5594 *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
5595
5596 /* mark storage object as user edited */
5597 if(!from_bounce)
5598 (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
5599
5600 /*
5601 * If we already had a list, blast it now, so we can build a new
5602 * attachment list that reflects what's really there...
5603 */
5604 if(pico_a){
5605 free_attachment_list(pico_a);
5606
5607
5608 /* Simplifyihg assumption #28e. (see cross reference)
5609 All parts in the body passed in here that are not already
5610 in the attachments list are added to the end of the attachments
5611 list. Attachment items not in the body list will be taken care
5612 of in strings2outgoing, but they are unlikely to occur
5613 */
5614
5615 for(part = bod->nested.part->next; part != NULL; part = part->next) {
5616 /* Already in list? */
5617 for(ppa = pico_a;
5618 *ppa && strcmp((*ppa)->id, part->body.id);
5619 ppa = &(*ppa)->next)
5620 ;
5621
5622 if(!*ppa){ /* Not in the list! append it... */
5623 *ppa = (PATMT *)fs_get(sizeof(PATMT));
5624
5625 if(part->body.description){
5626 char *p;
5627 size_t len;
5628
5629 len = 4*strlen(part->body.description)+1;
5630 p = (char *)fs_get(len*sizeof(char));
5631 if(rfc1522_decode_to_utf8((unsigned char *)p,
5632 len, part->body.description) == (unsigned char *) p){
5633 (*ppa)->description = p;
5634 }
5635 else{
5636 fs_give((void **)&p);
5637 (*ppa)->description = cpystr(part->body.description);
5638 }
5639 }
5640 else
5641 (*ppa)->description = cpystr("");
5642
5643 type = type_desc(part->body.type, part->body.subtype,
5644 part->body.parameter, NULL, 0);
5645
5646 /*
5647 * If we can find a "name" parm, display that too...
5648 */
5649 if((name = parameter_val(part->body.parameter, "name")) != NULL){
5650 /* Convert any [ or ]'s the name contained */
5651 for(p = name; *p ; p++)
5652 if(*p == '[')
5653 *p = '(';
5654 else if(*p == ']')
5655 *p = ')';
5656
5657 name_l = p - name;
5658 }
5659 else
5660 name_l = 0;
5661
5662 (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
5663
5664 snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
5665 name ? ": " : "", name ? name : "");
5666 (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
5667
5668 if(name)
5669 fs_give((void **) &name);
5670
5671 (*ppa)->flags = A_FLIT;
5672 (*ppa)->size = cpystr(byte_string(
5673 send_body_size(&part->body)));
5674 if(!part->body.id)
5675 part->body.id = generate_message_id(NULL);
5676
5677 (*ppa)->id = cpystr(part->body.id);
5678 (*ppa)->next = NULL;
5679 }
5680 }
5681 }
5682 }
5683
5684
5685 /*------------------------------------------------------------------
5686 Malloc strings to pass to composer editor because it expects
5687 such strings so it can realloc them
5688 -----------------------------------------------------------------*/
5689 /*
5690 * turn any address fields into text strings
5691 */
5692 /*
5693 * SIMPLIFYING ASSUMPTION #116: all header strings are understood
5694 * NOT to be RFC1522 decoded. Said differently, they're understood
5695 * to be RFC1522 ENCODED as necessary. The intent is to preserve
5696 * original charset tagging as far into the compose/send pipe as
5697 * we can.
5698 */
5699 for(pf = header->local; pf && pf->name; pf = pf->next)
5700 if(pf->canedit)
5701 switch(pf->type){
5702 case Address :
5703 if(pf->addr){
5704 char *p, *t, *u;
5705 long l;
5706
5707 pf->scratch = addr_list_string(*pf->addr, NULL, 1);
5708
5709 /*
5710 * Scan for and fix-up patently bogus fields.
5711 *
5712 * NOTE: collaboration with this code and what's done in
5713 * reply.c:reply_cp_addr to package up the bogus stuff
5714 * is required.
5715 */
5716 for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
5717 for(t = p; ; t--)
5718 if(*t == '&'){ /* find "leading" token */
5719 int replacelen;
5720
5721 /*
5722 * Rfc822_cat has been changed so that it now quotes
5723 * this sometimes. So we have to look out for quotes
5724 * which confuse the decoder. It was only quoting
5725 * because we were putting \r \n in the input, I think.
5726 */
5727 if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
5728 t[-1] = p[-1] = ' ';
5729
5730 *t++ = ' '; /* replace token */
5731 *p = '\0'; /* tie off string */
5732 u = rfc822_base64((unsigned char *) t,
5733 (unsigned long) strlen(t),
5734 (unsigned long *) &l);
5735 if(!u)
5736 u = "";
5737
5738 replacelen = strlen(t);
5739 *p = '@'; /* restore 'p' */
5740 rplstr(p, strlen(p), 12, ""); /* clear special token */
5741 rplstr(t, strlen(u)-replacelen+1, replacelen, u);
5742 if(u)
5743 fs_give((void **) &u);
5744
5745 if(HE(pf))
5746 HE(pf)->start_here = 1;
5747
5748 break;
5749 }
5750 else if(t == pf->scratch)
5751 break;
5752
5753 removing_leading_white_space(pf->scratch);
5754 if(pf->scratch){
5755 size_t l;
5756
5757 /*
5758 * Replace control characters with ^C notation, unless
5759 * some conditions are met (see istrncpy).
5760 * If user doesn't edit, then we switch back to the
5761 * original version. If user does edit, then all bets
5762 * are off.
5763 */
5764 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5765 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5766 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5767 fs_give((void **)&pf->scratch);
5768 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5769 }
5770
5771 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5772 }
5773 }
5774
5775 break;
5776
5777 case Subject :
5778 if(pf->text){
5779 char *p, *src;
5780 size_t len;
5781
5782 src = pf->scratch ? pf->scratch
5783 : (*pf->text) ? *pf->text : "";
5784
5785 len = 4*strlen(src)+1;
5786 p = (char *)fs_get(len * sizeof(char));
5787 if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
5788 if(pf->scratch)
5789 fs_give((void **)&pf->scratch);
5790
5791 pf->scratch = p;
5792 }
5793 else{
5794 fs_give((void **)&p);
5795 if(!pf->scratch)
5796 pf->scratch = cpystr(src);
5797 }
5798
5799 if(pf->scratch){
5800 size_t l;
5801
5802 /*
5803 * Replace control characters with ^C notation, unless
5804 * some conditions are met (see istrncpy).
5805 * If user doesn't edit, then we switch back to the
5806 * original version. If user does edit, then all bets
5807 * are off.
5808 */
5809 iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
5810 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
5811 if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
5812 fs_give((void **)&pf->scratch);
5813 pf->scratch = (char *)fs_get((l+1) * sizeof(char));
5814 }
5815
5816 strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
5817 }
5818 }
5819
5820 break;
5821
5822 default :
5823 break;
5824 }
5825 }
5826
5827
5828 /*----------------------------------------------------------------------
5829 Restore fields returned from pico to form useful to sending
5830 routines.
5831 -----*/
5832 void
5833 strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
5834 {
5835 PINEFIELD *pf;
5836 int we_cancel = 0;
5837
5838 we_cancel = busy_cue(NULL, NULL, 1);
5839
5840 /*
5841 * turn any local address strings into address lists
5842 */
5843 for(pf = header->local; pf && pf->name; pf = pf->next)
5844 if(pf->scratch){
5845 char *the_address = NULL;
5846
5847 switch(pf->type){
5848 case Address :
5849 removing_trailing_white_space(pf->scratch);
5850
5851 if((the_address || *pf->scratch) && pf->addr){
5852 ADDRESS *new_addr = NULL;
5853 static char *fakedomain = "@";
5854
5855 if(!the_address)
5856 the_address = pf->scratch;
5857
5858 rfc822_parse_adrlist(&new_addr, the_address,
5859 (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
5860 ? fakedomain : ps_global->maildomain);
5861 mail_free_address(pf->addr); /* free old addrs */
5862 *pf->addr = new_addr; /* assign new addr */
5863 }
5864 else if(pf->addr)
5865 mail_free_address(pf->addr); /* free old addrs */
5866
5867 break;
5868
5869 case Subject :
5870 if(*pf->text)
5871 fs_give((void **)pf->text);
5872
5873 if(*pf->scratch){
5874 *pf->text = cpystr(pf->scratch);
5875 }
5876
5877 break;
5878
5879 default :
5880 break;
5881 }
5882
5883 fs_give((void **)&pf->scratch); /* free now useless text */
5884 }
5885
5886 create_message_body(bod, attach, flow_it);
5887
5888 if(we_cancel)
5889 cancel_busy_cue(-1);
5890 }
5891
5892
5893 /*----------------------------------------------------------------------
5894
5895 The head of the body list here is always either TEXT or MULTIPART. It may be
5896 changed from TEXT to MULTIPART if there are attachments to be added
5897 and it is not already multipart.
5898 ----*/
5899 void
5900 create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
5901 {
5902 PART *p, **pp;
5903 PATMT *pa;
5904 BODY *tmp_body, *text_body = NULL;
5905 void *file_contents;
5906 PARAMETER **parmp;
5907 char *lc;
5908
5909 TIME_STAMP("create_body start.", 1);
5910 /*
5911 * if conditions are met short circuit MIME wrapping
5912 */
5913 if((*b)->type != TYPEMULTIPART && !attach){
5914
5915 /* only override assigned encoding if it might need upgrading */
5916 if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
5917 (*b)->encoding = ENCOTHER;
5918
5919 create_message_body_text(*b, flow_it);
5920
5921 if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
5922 || !((*b)->encoding == ENC8BIT
5923 || (*b)->encoding == ENCBINARY)){
5924 TIME_STAMP("create_body end.", 1);
5925 return;
5926 }
5927 else /* protect 8bit in multipart */
5928 text_body = *b;
5929 }
5930
5931 if((*b)->type == TYPETEXT) {
5932 /*-- Current type is text, but there are attachments to add --*/
5933 /*-- Upgrade to a TYPEMULTIPART --*/
5934 tmp_body = (BODY *)mail_newbody();
5935 tmp_body->type = TYPEMULTIPART;
5936 tmp_body->nested.part = mail_newbody_part();
5937 if(text_body){
5938 /*
5939 * Why do we do this?
5940 * The problem is that base64 or quoted-printable encoding is
5941 * sensitive to having random data appended to it's end. If
5942 * we use a single part TEXT message and something in between
5943 * us and the end appends advertising without adjusting for
5944 * the encoding, the message is screwed up. So we wrap the
5945 * text part inside a multipart and then the appended data
5946 * will come after the boundary.
5947 *
5948 * We wish we could do this on the way out the door in a
5949 * child of post_rfc822_output because at that point we know
5950 * the character set and the encoding being used. For example,
5951 * iso-2022-jp is an encoding that is not sensitive to data
5952 * appended to the end, so it wouldn't need to be wrapped.
5953 * We could conceivably have post_rfc822_body inspect the
5954 * body and change it before doing the output. It would work
5955 * but would be very fragile. We'd be passed a body from
5956 * c-client to output and instead of just doing the output
5957 * we'd change the body and then output it. Not worth it
5958 * since the multipart wrapping is completely correct for
5959 * MIME-aware mailers.
5960 */
5961 (void) copy_body(&(tmp_body->nested.part->body), *b);
5962 /* move contents which were NOT copied */
5963 tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
5964 (*b)->contents.text.data = NULL;
5965 }
5966 else{
5967 tmp_body->nested.part->body = **b;
5968
5969 (*b)->subtype = (*b)->id = (*b)->description = NULL;
5970 (*b)->parameter = NULL;
5971 (*b)->contents.text.data = NULL;
5972 }
5973
5974 pine_free_body(b);
5975 *b = tmp_body;
5976 }
5977
5978 if(!text_body){
5979 /*-- Now type must be MULTIPART with first part text --*/
5980 (*b)->nested.part->body.encoding = ENCOTHER;
5981 create_message_body_text(&((*b)->nested.part->body), flow_it);
5982 }
5983
5984 /*------ Go through the parts list remove those to be deleted -----*/
5985 for(pp = &(*b)->nested.part->next; *pp;){
5986 for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
5987 /* already existed? */
5988 if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
5989 char *orig_descp = NULL, *cs = NULL;
5990
5991 /*
5992 * decode original to see if it matches what was decoded
5993 * when we sent it in.
5994 */
5995
5996 if((*pp)->body.description)
5997 orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
5998 SIZEOF_20KBUF, (*pp)->body.description);
5999
6000 if(!(*pp)->body.description /* update description? */
6001 || (pa->description && strcmp(pa->description, orig_descp))){
6002 if((*pp)->body.description)
6003 fs_give((void **) &(*pp)->body.description);
6004
6005 /* encoding happens as msg text is written */
6006 (*pp)->body.description = cpystr(pa->description);
6007 }
6008
6009 if(cs)
6010 fs_give((void **) &cs);
6011
6012 break;
6013 }
6014
6015 if(pa == NULL){
6016 p = *pp; /* prepare to zap *pp */
6017 *pp = p->next; /* pull next one in list up */
6018 p->next = NULL; /* tie off removed node */
6019
6020 pine_free_body_data(&p->body); /* clean up contained data */
6021 mail_free_body_part(&p); /* free up the part */
6022 }
6023 else
6024 pp = &(*pp)->next;
6025 }
6026
6027 /*---------- Now add any new attachments ---------*/
6028 for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
6029 for(pa = attach; pa != NULL; pa = pa->next) {
6030 if(pa->id != NULL)
6031 continue; /* Has an ID, it's old */
6032
6033 /*
6034 * the idea is handle ALL attachments as open FILE *'s. Actual
6035 * encoding and such is handled at the time the message
6036 * is shoved into the mail slot or written to disk...
6037 *
6038 * Also, we never unlink a file, so it's up to whoever opens
6039 * it to deal with tmpfile issues.
6040 */
6041 if((file_contents = (void *)so_get(FileStar, pa->filename,
6042 READ_ACCESS)) == NULL){
6043 q_status_message2(SM_ORDER | SM_DING, 3, 4,
6044 _("Error \"%s\", couldn't attach file \"%s\""),
6045 error_description(errno), pa->filename);
6046 display_message('x');
6047 continue;
6048 }
6049
6050 p->next = mail_newbody_part();
6051 p = p->next;
6052 p->body.id = generate_message_id(NULL);
6053 p->body.contents.text.data = file_contents;
6054
6055 /*
6056 * Set type to unknown and let set_mime_type_by_* figure it out.
6057 * Always encode attachments we add as BINARY.
6058 */
6059 p->body.type = TYPEOTHER;
6060 p->body.encoding = ENCBINARY;
6061 p->body.size.bytes = name_file_size(pa->filename);
6062 if(!set_mime_type_by_extension(&p->body, pa->filename)){
6063 set_mime_type_by_grope(&p->body);
6064 set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
6065 }
6066
6067 so_release((STORE_S *)p->body.contents.text.data);
6068
6069 if(pa->description) /* encoding happens when msg written */
6070 p->body.description = cpystr(pa->description);
6071
6072 /* Add name attribute for backward compatibility */
6073 for(parmp = &p->body.parameter; *parmp; )
6074 if(!struncmp((*parmp)->attribute, "name", 4)
6075 && (!*((*parmp)->attribute + 4)
6076 || *((*parmp)->attribute + 4) == '*')){
6077 PARAMETER *free_me = *parmp;
6078 *parmp = (*parmp)->next;
6079 free_me->next = NULL;
6080 mail_free_body_parameter(&free_me);
6081 }
6082 else
6083 parmp = &(*parmp)->next;
6084
6085 set_parameter(parmp, "name",
6086 pa->filename
6087 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6088 : NULL);
6089
6090 /* Then set the Content-Disposition ala RFC1806 */
6091 if(!p->body.disposition.type){
6092 p->body.disposition.type = cpystr("attachment");
6093 for(parmp = &p->body.disposition.parameter; *parmp; )
6094 if(!struncmp((*parmp)->attribute, "filename", 4)
6095 && (!*((*parmp)->attribute + 4)
6096 || *((*parmp)->attribute + 4) == '*')){
6097 PARAMETER *free_me = *parmp;
6098 *parmp = (*parmp)->next;
6099 free_me->next = NULL;
6100 mail_free_body_parameter(&free_me);
6101 }
6102 else
6103 parmp = &(*parmp)->next;
6104
6105 set_parameter(parmp, "filename",
6106 pa->filename
6107 ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
6108 : NULL);
6109 }
6110
6111 p->next = NULL;
6112 pa->id = cpystr(p->body.id);
6113 }
6114
6115 /*
6116 * Now, if this multipart has but one text piece (that is, no
6117 * attachments), then downgrade from a composite type to a discrete
6118 * text/plain message if CTE is not 8bit.
6119 */
6120 if(!(*b)->nested.part->next
6121 && (*b)->nested.part->body.type == TYPETEXT
6122 && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
6123 || !((*b)->nested.part->body.encoding == ENC8BIT
6124 || (*b)->nested.part->body.encoding == ENCBINARY))){
6125 /* Clone the interesting body part */
6126 tmp_body = mail_newbody();
6127 *tmp_body = (*b)->nested.part->body;
6128 /* and rub out what we don't want cleaned up when it's free'd */
6129 mail_initbody(&(*b)->nested.part->body);
6130 mail_free_body(b);
6131 *b = tmp_body;
6132 }
6133
6134
6135 TIME_STAMP("create_body end.", 1);
6136 }
6137
6138
6139 /*
6140 * Fill in text BODY part's structure
6141 *
6142 */
6143 void
6144 create_message_body_text(struct mail_bodystruct *b, int flow_it)
6145 {
6146 set_mime_type_by_grope(b);
6147 if(b != NULL){
6148 remove_parameter(&b->parameter, "format"); /* we will set it up below */
6149 remove_parameter(&b->parameter, "delsp"); /* we never set this up */
6150 }
6151 if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
6152 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
6153 && flow_it)
6154 set_parameter(b ? &b->parameter : NULL, "format", "flowed");
6155
6156 set_body_size(b);
6157 }
6158
6159
6160 /*
6161 * free_attachment_list - free attachments in given list
6162 */
6163 void
6164 free_attachment_list(PATMT **alist)
6165 {
6166 PATMT *leading;
6167
6168 while(alist && *alist){ /* pointer pointing to something */
6169 leading = (*alist)->next;
6170 if((*alist)->description)
6171 fs_give((void **)&(*alist)->description);
6172
6173 if((*alist)->filename){
6174 if((*alist)->flags & A_TMP)
6175 if(our_unlink((*alist)->filename) < 0)
6176 dprint((1, "-- Can't unlink(%s): %s\n",
6177 (*alist)->filename ? (*alist)->filename : "?",
6178 error_description(errno)));
6179
6180 fs_give((void **)&(*alist)->filename);
6181 }
6182
6183 if((*alist)->size)
6184 fs_give((void **)&(*alist)->size);
6185
6186 if((*alist)->id)
6187 fs_give((void **)&(*alist)->id);
6188
6189 fs_give((void **)alist);
6190
6191 *alist = leading;
6192 }
6193 }
6194
6195
6196 void
6197 set_body_size(struct mail_bodystruct *b)
6198 {
6199 unsigned char c;
6200 int we_cancel = 0;
6201
6202 we_cancel = busy_cue(NULL, NULL, 1);
6203 so_seek((STORE_S *)b->contents.text.data, 0L, 0);
6204 b->size.bytes = 0L;
6205 while(so_readc(&c, (STORE_S *)b->contents.text.data))
6206 b->size.bytes++;
6207
6208 if(we_cancel)
6209 cancel_busy_cue(-1);
6210 }
6211
6212
6213 /*
6214 * view_as_rich - set the rich_header flag
6215 *
6216 * name - name of the header field
6217 * deflt - default value to return if user didn't set it
6218 *
6219 * Note: if the user tries to turn them all off with "", then
6220 * we take that to mean default, since otherwise there is no
6221 * way to get to the headers.
6222 */
6223 int
6224 view_as_rich(char *name, int deflt)
6225 {
6226 char **p;
6227 char *q;
6228
6229 p = ps_global->VAR_COMP_HDRS;
6230
6231 if(p && *p && **p){
6232 for(; (q = *p) != NULL; p++){
6233 if(!struncmp(q, name, strlen(name)))
6234 return 0; /* 0 means we *do* view it by default */
6235 }
6236
6237 return 1; /* 1 means it starts out hidden */
6238 }
6239 return(deflt);
6240 }
6241
6242
6243 /*
6244 * background_posting - return whether or not we're already in the process
6245 * of posting
6246 */
6247 int
6248 background_posting(int gripe)
6249 {
6250 if(ps_global->post){
6251 if(gripe)
6252 q_status_message(SM_ORDER|SM_DING, 3, 3,
6253 _("Can't post while posting!"));
6254 return(1);
6255 }
6256
6257 return(0);
6258 }
6259
6260
6261 /*----------------------------------------------------------------------
6262 Validate the given subject relative to any news groups.
6263
6264 Args: none
6265
6266 Returns: always returns 1, but also returns error if
6267 ----*/
6268 int
6269 valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
6270 {
6271 struct headerentry *hp;
6272
6273 if(expanded)
6274 *expanded = cpystr(given);
6275
6276 if(error){
6277 /*
6278 * Now look for any header entry we passed to pico that has to do
6279 * with news. If there's no subject, gripe.
6280 */
6281 for(hp = pbf->headents; hp->prompt; hp++)
6282 if(hp->help == h_composer_news){
6283 if(hp->hd_text->text[0] && !*given)
6284 *error = cpystr(
6285 _("News postings MUST have a subject! Please add one!"));
6286
6287 break;
6288 }
6289 }
6290
6291 return(0);
6292 }
6293
6294
6295 /*
6296 * This is the build_address used by the composer to check for an address
6297 * in the addrbook.
6298 *
6299 * Args: to -- the passed in line to parse
6300 * full_to -- Address of a pointer to return the full address in.
6301 * This will be allocated here and freed by the caller.
6302 * error -- Address of a pointer to return an error message in.
6303 * This will be allocated here and freed by the caller.
6304 * barg -- Address of a pointer to return the fcc in is in
6305 * fcc->tptr. It will have already been allocated by the
6306 * caller but we may free it and reallocate if we wish.
6307 * Caller will free it.
6308 *
6309 * Result: 0 is returned if address was OK,
6310 * -1 if address wasn't OK.
6311 *
6312 * Side effect: Can flush addrbook entry cache entries so they need to be
6313 * re-fetched afterwords.
6314 */
6315 int
6316 build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
6317 {
6318 char *p;
6319 int ret_val, no_repo = 0, *save_nesting_level;
6320 BuildTo bldto;
6321 PrivateTop *pt = NULL;
6322 PrivateAffector *af = NULL;
6323 char *fcc_local = NULL;
6324 jmp_buf save_jmp_buf;
6325
6326 dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
6327
6328 /* check to see if to string is empty to avoid work */
6329 for(p = to; p && *p && isspace((unsigned char)*p); p++)
6330 ;/* do nothing */
6331
6332 if(!p || !*p){
6333 if(full_to)
6334 *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
6335
6336 return 0;
6337 }
6338
6339 if(full_to != NULL)
6340 *full_to = (char *)NULL;
6341
6342 if(error != NULL)
6343 *error = (char *)NULL;
6344
6345 /* No guarantee cursor or status line is how we saved it */
6346 clear_cursor_pos();
6347 mark_status_unknown();
6348
6349 if(ps_global->remote_abook_validity > 0 &&
6350 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6351 *mangled |= BUILDER_SCREEN_MANGLED;
6352
6353 /*
6354 * If we end up jumping back here because somebody else changed one of
6355 * our addrbooks out from underneath us, we may well leak some memory.
6356 * That's probably ok since this will be very rare.
6357 *
6358 * The reason for the memcpy of the jmp_buf is that we may actually
6359 * be indirectly calling this function from within the address book.
6360 * For example, we may be in the address book screen and then run
6361 * the ComposeTo command which puts us in the composer, then we call
6362 * build_address from there which resets addrbook_changed_unexpectedly.
6363 * Once we leave build_address we need to reset addrbook_changed_un...
6364 * because this position on the stack will no longer be valid.
6365 * Same is true of the other setjmp's in this file which are wrapped
6366 * in memcpy calls.
6367 */
6368 save_nesting_level = cpyint(ab_nesting_level);
6369 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6370 if(setjmp(addrbook_changed_unexpectedly)){
6371 no_repo = 0;
6372 pt = NULL;
6373 af = NULL;
6374 fcc_local = NULL;
6375 if(error != NULL)
6376 *error = (char *)NULL;
6377
6378 if(full_to && *full_to)
6379 fs_give((void **)full_to);
6380
6381 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6382 dprint((1,
6383 "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
6384 addrbook_reset();
6385 ab_nesting_level = *save_nesting_level;
6386 }
6387
6388 ab_nesting_level++;
6389 bldto.type = Str;
6390 bldto.arg.str = to;
6391 ret_val = build_address_internal(bldto, full_to, error,
6392 barg ? &fcc_local : NULL,
6393 &no_repo, NULL, save_and_restore,
6394 0, mangled);
6395 ab_nesting_level--;
6396 if(save_nesting_level)
6397 fs_give((void **)&save_nesting_level);
6398
6399 /*
6400 * Have to rfc1522_decode the full_to string before sending it back.
6401 */
6402 if(full_to && *full_to ){
6403 char *q;
6404 size_t len;
6405
6406 len = 4*strlen(*full_to)+1;
6407 q = (char *)fs_get(len * sizeof(char));
6408 p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
6409
6410 /* p == q means that decoding happened, p is decoded *full_to */
6411 if(p == q){
6412 fs_give((void **)full_to);
6413 *full_to = p;
6414 }
6415 else
6416 fs_give((void **)&q);
6417 }
6418
6419 if(fcc_local){
6420 unsigned long csum;
6421
6422 /* Pt will point to headents[Fcc].bldr_private */
6423 pt = NULL;
6424 if(barg && barg->aff)
6425 pt = (PrivateTop *)(*barg->aff);
6426
6427 /*
6428 * If *barg->aff is set, that means fcc was set from a list
6429 * during some previous builder call.
6430 * If the current To line contains the old expansion as a prefix, then
6431 * we should leave things as they are. In order to decide that,
6432 * we look at a hash value computed from the strings.
6433 */
6434 if(pt && (af=pt->affector) && af->who == BP_To){
6435 int len;
6436
6437 len = strlen(to);
6438 if(len >= af->cksumlen){
6439 int save;
6440
6441 save = to[af->cksumlen];
6442 to[af->cksumlen] = '\0';
6443 csum = line_hash(to);
6444 to[af->cksumlen] = save;
6445 }
6446 else
6447 csum = af->cksumval + 1; /* something not equal to cksumval */
6448 }
6449
6450 if(!pt ||
6451 !pt->affector ||
6452 (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
6453
6454 /* replace fcc value */
6455 if(barg->tptr)
6456 fs_give((void **)&barg->tptr);
6457
6458 barg->tptr = fcc_local;
6459
6460 if(barg->aff){
6461 if(!pt){
6462 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6463 pt = (PrivateTop *)(*barg->aff);
6464 memset((void *)pt, 0, sizeof(PrivateTop));
6465 }
6466
6467 if(no_repo){
6468 if(!pt->affector)
6469 pt->affector =
6470 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6471
6472 af = pt->affector;
6473 af->who = BP_To;
6474 af->cksumlen = strlen(((full_to && *full_to)
6475 ? *full_to : ""));
6476 af->cksumval = line_hash(((full_to && *full_to)
6477 ? *full_to : ""));
6478 }
6479 else{
6480 /*
6481 * If result is reproducible, we don't keep track here.
6482 */
6483 if(pt->affector)
6484 fs_give((void **)&pt->affector);
6485 }
6486 }
6487 }
6488 else
6489 fs_give((void **)&fcc_local); /* unused in this case */
6490 }
6491
6492 /* This is so pico will erase the old message */
6493 if(error != NULL && *error == NULL)
6494 *error = cpystr("");
6495
6496 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6497 flush_status_messages(1);
6498 return(ret_val);
6499 }
6500
6501
6502 /*
6503 * This is the builder used by the composer for the Lcc line.
6504 *
6505 * Args: lcc -- the passed in Lcc line to parse
6506 * full_lcc -- Address of a pointer to return the full address in.
6507 * This will be allocated here and freed by the caller.
6508 * error -- Address of a pointer to return an error message in.
6509 * This is not allocated so should not be freed by the caller.
6510 * barg -- This is a pointer to text for affected entries which
6511 * we may be changing. The first one in the list is the
6512 * To entry. We may put the name of the list in empty
6513 * group syntax form there (like List Name: ;).
6514 * The second one in the list is the fcc field.
6515 * The tptr members already point to text allocated in the
6516 * caller. We may free and reallocate here, caller will
6517 * free the result in any case.
6518 *
6519 * Result: 0 is returned if address was OK,
6520 * -1 if address wasn't OK.
6521 *
6522 * Side effect: Can flush addrbook entry cache entries so they need to be
6523 * re-fetched afterwords.
6524 */
6525 int
6526 build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
6527 {
6528 int ret_val,
6529 no_repo = 0; /* fcc or lcc not reproducible */
6530 int *save_nesting_level;
6531 BuildTo bldlcc;
6532 PrivateTop *pt = NULL;
6533 PrivateAffector *af = NULL;
6534 char *p,
6535 *fcc_local = NULL,
6536 *to = NULL,
6537 *dummy;
6538 jmp_buf save_jmp_buf;
6539
6540 dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
6541
6542 /* check to see if to string is empty to avoid work */
6543 for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
6544 ;/* do nothing */
6545
6546 if(!p || !*p){
6547 if(full_lcc)
6548 *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
6549
6550 return 0;
6551 }
6552
6553 if(error != NULL)
6554 *error = (char *)NULL;
6555
6556 if(ps_global->remote_abook_validity > 0 &&
6557 adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
6558 *mangled |= BUILDER_SCREEN_MANGLED;
6559
6560 /*
6561 * If we end up jumping back here because somebody else changed one of
6562 * our addrbooks out from underneath us, we may well leak some memory.
6563 * That's probably ok since this will be very rare.
6564 */
6565 save_nesting_level = cpyint(ab_nesting_level);
6566 memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
6567 if(setjmp(addrbook_changed_unexpectedly)){
6568 no_repo = 0;
6569 pt = NULL;
6570 af = NULL;
6571 fcc_local = NULL;
6572 to = NULL;
6573 if(error != NULL)
6574 *error = (char *)NULL;
6575
6576 if(full_lcc && *full_lcc)
6577 fs_give((void **)full_lcc);
6578
6579 q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
6580 dprint((1,
6581 "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
6582 addrbook_reset();
6583 ab_nesting_level = *save_nesting_level;
6584 }
6585
6586 ab_nesting_level++;
6587 bldlcc.type = Str;
6588 bldlcc.arg.str = lcc;
6589
6590 /*
6591 * To is first affected_entry and Fcc is second.
6592 * The conditional stuff for the fcc argument says to only change the
6593 * fcc if the fcc pointer is passed in non-null, and the To pointer
6594 * is also non-null. If they are null, that means they've already been
6595 * entered (are sticky). We don't affect fcc if either fcc or To has
6596 * been typed in.
6597 */
6598 ret_val = build_address_internal(bldlcc,
6599 full_lcc,
6600 error,
6601 (barg && barg->next && barg->next->tptr && barg->tptr)
6602 ? &fcc_local : NULL,
6603 &no_repo,
6604 (barg && barg->tptr) ? &to : NULL,
6605 save_and_restore, 0, mangled);
6606
6607 ab_nesting_level--;
6608 if(save_nesting_level)
6609 fs_give((void **)&save_nesting_level);
6610
6611 /* full_lcc is what ends up in the Lcc: line */
6612 if(full_lcc && *full_lcc){
6613 size_t len;
6614
6615 /*
6616 * Have to rfc1522_decode the full_lcc string before sending it back.
6617 */
6618 len = 4*strlen(*full_lcc)+1;
6619 p = (char *)fs_get(len * sizeof(char));
6620 if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
6621 fs_give((void **)full_lcc);
6622 *full_lcc = p;
6623 }
6624 else
6625 fs_give((void **)&p);
6626 }
6627
6628 /* to is what ends up in the To: line */
6629 if(to && *to){
6630 unsigned long csum;
6631 size_t len;
6632
6633 /*
6634 * Have to rfc1522_decode the full_to string before sending it back.
6635 */
6636 len = 4*strlen(to)+1;
6637 p = (char *)fs_get(len * sizeof(char));
6638 dummy = NULL;
6639 if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
6640 /*
6641 * If the caller wants us to try to preserve the charset
6642 * information (they set aff) we copy it into encoded->etext.
6643 * We don't have to worry about pasting together pieces of
6644 * etext like we do in build_address because whenever the
6645 * Lcc line is setting the To line it will be setting the
6646 * whole line, not modifying it.
6647 * Pt will point to headents[To].bldr_private.
6648 */
6649 if(barg && barg->aff){
6650 pt = (PrivateTop *)(*barg->aff);
6651
6652 if(!pt){
6653 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6654 pt = (PrivateTop *)(*barg->aff);
6655 memset((void *)pt, 0, sizeof(PrivateTop));
6656 }
6657 }
6658
6659 fs_give((void **)&to);
6660 to = p;
6661 }
6662 else
6663 fs_give((void **)&p);
6664
6665 if(dummy)
6666 fs_give((void **)&dummy);
6667
6668
6669 /*
6670 * This part is recording the fact that the To line was set to
6671 * what it is by entering something on the Lcc line. In particular,
6672 * if a list alias was entered here then the fullname of the list
6673 * goes in the To line. We save this affector information so that
6674 * we can tell it shouldn't be modified if we call build_addr_lcc
6675 * again unless we actually modified what's in the Lcc line so that
6676 * it doesn't start with the same thing. The problem we're solving
6677 * is that the contents of the Lcc line no longer look like the
6678 * list they were derived from.
6679 * Pt will point to headents[To].bldr_private.
6680 */
6681 if(barg && barg->aff)
6682 pt = (PrivateTop *)(*barg->aff);
6683
6684 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6685 int len;
6686
6687 len = strlen(lcc);
6688 if(len >= af->cksumlen){
6689 int save;
6690
6691 save = lcc[af->cksumlen];
6692 lcc[af->cksumlen] = '\0';
6693 csum = line_hash(lcc);
6694 lcc[af->cksumlen] = save;
6695 }
6696 else
6697 csum = af->cksumval + 1; /* so they aren't equal */
6698 }
6699
6700 if(!pt ||
6701 !pt->affector ||
6702 pt->affector->who != BP_Lcc ||
6703 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6704
6705 /* replace to value */
6706 if(barg->tptr && barg->tptr[0]){
6707 size_t l;
6708 char *t;
6709
6710 l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
6711 t = (char *)fs_get((l+1) * sizeof(char));
6712 snprintf(t, l+1, "%s%s%s",
6713 barg->tptr,
6714 (to && *to) ? ", " : "",
6715 (to && *to) ? to : "");
6716 fs_give((void **)&barg->tptr);
6717 if(to)
6718 fs_give((void **)&to);
6719
6720 barg->tptr = t;
6721 }
6722 else{
6723 if(barg->tptr)
6724 fs_give((void **)&barg->tptr);
6725
6726 barg->tptr = to;
6727 }
6728
6729 if(barg->aff){
6730 if(!pt){
6731 *barg->aff = (void *)fs_get(sizeof(PrivateTop));
6732 pt = (PrivateTop *)(*barg->aff);
6733 memset((void *)pt, 0, sizeof(PrivateTop));
6734 }
6735
6736 if(no_repo){
6737 if(!pt->affector)
6738 pt->affector =
6739 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6740
6741 af = pt->affector;
6742 af->who = BP_Lcc;
6743 af->cksumlen = strlen(((full_lcc && *full_lcc)
6744 ? *full_lcc : ""));
6745 af->cksumval = line_hash(((full_lcc && *full_lcc)
6746 ? *full_lcc : ""));
6747 }
6748 else{
6749 /*
6750 * If result is reproducible, we don't keep track here.
6751 */
6752 if(pt->affector)
6753 fs_give((void **)&pt->affector);
6754 }
6755 }
6756 }
6757 else
6758 fs_give((void **)&to); /* unused in this case */
6759 }
6760
6761 if(fcc_local){
6762 unsigned long csum;
6763
6764 /*
6765 * If *barg->next->aff is set, that means fcc was set from a list
6766 * during some previous builder call. If the current Lcc line
6767 * contains the old expansion as a prefix, then we should leave
6768 * things as they are. In order to decide that we look at a hash
6769 * value computed from the strings.
6770 * Pt will point to headents[Fcc].bldr_private
6771 */
6772 pt = NULL;
6773 if(barg && barg->next && barg->next->aff)
6774 pt = (PrivateTop *)(*barg->next->aff);
6775
6776 if(pt && (af=pt->affector) && af->who == BP_Lcc){
6777 int len;
6778
6779 len = strlen(lcc);
6780 if(len >= af->cksumlen){
6781 int save;
6782
6783 save = lcc[af->cksumlen];
6784 lcc[af->cksumlen] = '\0';
6785 csum = line_hash(lcc);
6786 lcc[af->cksumlen] = save;
6787 }
6788 else
6789 csum = af->cksumval + 1; /* something not equal to cksumval */
6790 }
6791
6792 if(!pt ||
6793 !pt->affector ||
6794 pt->affector->who != BP_Lcc ||
6795 (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
6796
6797 /* replace fcc value */
6798 if(barg->next->tptr)
6799 fs_give((void **)&barg->next->tptr);
6800
6801 barg->next->tptr = fcc_local;
6802
6803 if(barg->next->aff){
6804 if(!pt){
6805 *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
6806 pt = (PrivateTop *)(*barg->next->aff);
6807 memset((void *)pt, 0, sizeof(PrivateTop));
6808 }
6809
6810 if(no_repo){
6811 if(!pt->affector)
6812 pt->affector =
6813 (PrivateAffector *)fs_get(sizeof(PrivateAffector));
6814
6815 af = pt->affector;
6816 af->who = BP_Lcc;
6817 af->cksumlen = strlen(((full_lcc && *full_lcc)
6818 ? *full_lcc : ""));
6819 af->cksumval = line_hash(((full_lcc && *full_lcc)
6820 ? *full_lcc : ""));
6821 }
6822 else{
6823 /*
6824 * If result is reproducible, we don't keep track here.
6825 */
6826 if(pt->affector)
6827 fs_give((void **)&pt->affector);
6828 }
6829 }
6830 }
6831 else
6832 fs_give((void **)&fcc_local); /* unused in this case */
6833 }
6834
6835
6836 if(error != NULL && *error == NULL)
6837 *error = cpystr("");
6838
6839 memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
6840 flush_status_messages(0);
6841 return(ret_val);
6842 }
6843
6844
6845 /*----------------------------------------------------------------------
6846 Verify and canonicalize news groups names.
6847 Called from the message composer
6848
6849 Args: given_group -- List of groups typed by user
6850 expanded_group -- pointer to point to expanded list, which will be
6851 allocated here and freed in caller. If this is
6852 NULL, don't attempt to validate.
6853 error -- pointer to store error message
6854 fcc -- pointer to point to fcc, which will be
6855 allocated here and freed in caller
6856
6857 Returns: 0 if all is OK
6858 -1 if addresses weren't valid
6859
6860 Test the given list of newstroups against those recognized by our nntp
6861 servers. Testing by actually trying to open the list is much cheaper, both
6862 in bandwidth and memory, than yanking the whole list across the wire.
6863 ----*/
6864 int
6865 news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
6866 {
6867 int rv;
6868 char *fccptr = NULL;
6869
6870 if(fcc && fcc->tptr)
6871 fccptr = cpystr(fcc->tptr);
6872
6873 clear_cursor_pos();
6874
6875 rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
6876
6877 /* assign any new fcc to the BUILDER_ARG */
6878 if(fccptr){
6879 if(fcc){
6880 /* it changed */
6881 if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
6882 fs_give((void **) &fcc->tptr);
6883 fcc->tptr = fccptr;
6884 fccptr = NULL;
6885 }
6886 }
6887
6888 if(fccptr)
6889 fs_give((void **) &fccptr);
6890 }
6891
6892 /* deal with any busy indicator */
6893 if(news_busy_cue){
6894 news_busy_cue = 0;
6895 cancel_busy_cue(0);
6896 mark_status_dirty();
6897 display_message('x');
6898 if(mangled)
6899 *mangled |= BUILDER_MESSAGE_DISPLAYED;
6900 }
6901
6902
6903 return(rv);
6904 }
6905
6906
6907 void
6908 news_build_busy(void)
6909 {
6910 news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
6911 }
6912
6913
6914 #if defined(DOS) || defined(OS2)
6915
6916 /*----------------------------------------------------------------------
6917 Verify that the necessary pieces are around to allow for
6918 message sending under DOS
6919
6920 Args: strict -- tells us if a remote stream is required before
6921 sending is permitted.
6922
6923 The idea is to make sure pine knows enough to put together a valid
6924 from line. The things we MUST know are a user-id, user-domain and
6925 smtp server to dump the message off on. Typically these are
6926 provided in pine's configuration file, but if not, the user is
6927 queried here.
6928 ----*/
6929 int
6930 dos_valid_from()
6931 {
6932 char prompt[100], answer[80];
6933 int rc, i, flags;
6934 HelpType help;
6935
6936 /*
6937 * query for user name portion of address, use IMAP login
6938 * name as default
6939 */
6940 if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
6941 NETMBX mb;
6942 int no_prompt_user_id = 0;
6943
6944 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6945 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6946 && *mb.user){
6947 strncpy(answer, mb.user, sizeof(answer)-1);
6948 answer[sizeof(answer)-1] = '\0';
6949 }
6950 else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
6951 /* no user-id prompting if set */
6952 no_prompt_user_id = 1;
6953 rc = 0;
6954 if(!ps_global->mail_stream)
6955 do_broach_folder(ps_global->inbox_name,
6956 ps_global->context_list, NULL, DB_INBOXWOCNTXT);
6957 if(ps_global->mail_stream && ps_global->mail_stream->mailbox
6958 && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
6959 && *mb.user){
6960 strncpy(answer, mb.user, sizeof(answer)-1);
6961 answer[sizeof(answer)-1] = '\0';
6962 }
6963 else
6964 answer[0] = '\0';
6965 }
6966 else
6967 answer[0] = '\0';
6968
6969 if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
6970 /* No prompt, just assume mailbox login is user-id */
6971 no_prompt_user_id = 1;
6972 rc = 0;
6973 }
6974
6975 snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
6976 prompt[sizeof(prompt)-1] = '\0';
6977
6978 help = NO_HELP;
6979 while(!no_prompt_user_id) {
6980 flags = OE_APPEND_CURRENT;
6981 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
6982 sizeof(answer),prompt,NULL,help,&flags);
6983 if(rc == 2)
6984 continue;
6985
6986 if(rc == 3){
6987 help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
6988 continue;
6989 }
6990
6991 if(rc != 4)
6992 break;
6993 }
6994
6995 if(rc == 1 || (rc == 0 && !answer[0])) {
6996 q_status_message(SM_ORDER, 3, 4,
6997 _("Send cancelled (User-id must be provided before sending)"));
6998 return(0);
6999 }
7000
7001 /* save the name */
7002 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
7003 sizeof(prompt)-50, answer);
7004 prompt[sizeof(prompt)-1] = '\0';
7005 if(ps_global->blank_user_id
7006 && !no_prompt_user_id
7007 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7008 set_variable(V_USER_ID, answer, 1, 1, Main);
7009 }
7010 else{
7011 fs_give((void **)&(ps_global->VAR_USER_ID));
7012 ps_global->VAR_USER_ID = cpystr(answer);
7013 }
7014 }
7015
7016 /* query for personal name */
7017 if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
7018 && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
7019 answer[0] = '\0';
7020 snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
7021 prompt[sizeof(prompt)-1] = '\0';
7022
7023 help = NO_HELP;
7024 while(1) {
7025 flags = OE_APPEND_CURRENT;
7026 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7027 sizeof(answer),prompt,NULL,help,&flags);
7028 if(rc == 2)
7029 continue;
7030
7031 if(rc == 3){
7032 help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
7033 continue;
7034 }
7035
7036 if(rc != 4)
7037 break;
7038 }
7039
7040 if(rc == 0 && answer){ /* save the name */
7041 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
7042 sizeof(prompt)-50, answer);
7043 prompt[sizeof(prompt)-1] = '\0';
7044 if(ps_global->blank_personal_name
7045 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7046 set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
7047 }
7048 else{
7049 fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
7050 ps_global->VAR_PERSONAL_NAME = cpystr(answer);
7051 }
7052 }
7053 }
7054
7055 /*
7056 * query for host/domain portion of address, using IMAP
7057 * host as default
7058 */
7059 if(ps_global->blank_user_domain
7060 || ps_global->maildomain == ps_global->localdomain
7061 || ps_global->maildomain == ps_global->hostname){
7062 if(ps_global->inbox_name[0] == '{'){
7063 for(i=0;
7064 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7065 answer[i] = ps_global->inbox_name[i+1];
7066
7067 answer[i] = '\0';
7068 }
7069 else
7070 answer[0] = '\0';
7071
7072 snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
7073 prompt[sizeof(prompt)-1] = '\0';
7074
7075 help = NO_HELP;
7076 while(1) {
7077 flags = OE_APPEND_CURRENT;
7078 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7079 sizeof(answer),prompt,NULL,help,&flags);
7080 if(rc == 2)
7081 continue;
7082
7083 if(rc == 3){
7084 help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
7085 continue;
7086 }
7087
7088 if(rc != 4)
7089 break;
7090 }
7091
7092 if(rc == 1 || (rc == 0 && !answer[0])) {
7093 q_status_message(SM_ORDER, 3, 4,
7094 _("Send cancelled (Host/domain name must be provided before sending)"));
7095 return(0);
7096 }
7097
7098 /* save the name */
7099 snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
7100 sizeof(prompt)-50, answer);
7101 prompt[sizeof(prompt)-1] = '\0';
7102 if(!ps_global->userdomain && !ps_global->blank_user_domain
7103 && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
7104 set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
7105 fs_give((void **)&(ps_global->maildomain)); /* blast old val */
7106 ps_global->userdomain = cpystr(answer);
7107 ps_global->maildomain = ps_global->userdomain;
7108 }
7109 else{
7110 fs_give((void **)&(ps_global->maildomain));
7111 ps_global->userdomain = cpystr(answer);
7112 ps_global->maildomain = ps_global->userdomain;
7113 }
7114 }
7115
7116 /* check for smtp server */
7117 if(!ps_global->VAR_SMTP_SERVER ||
7118 !ps_global->VAR_SMTP_SERVER[0] ||
7119 !ps_global->VAR_SMTP_SERVER[0][0]){
7120 char **list;
7121
7122 if(ps_global->inbox_name[0] == '{'){
7123 for(i=0;
7124 i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
7125 answer[i] = ps_global->inbox_name[i+1];
7126
7127 answer[i] = '\0';
7128 }
7129 else
7130 answer[0] = '\0';
7131
7132 snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
7133 prompt[sizeof(prompt)-1] = '\0';
7134
7135 help = NO_HELP;
7136 while(1) {
7137 flags = OE_APPEND_CURRENT;
7138 rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
7139 sizeof(answer),prompt,NULL,help,&flags);
7140 if(rc == 2)
7141 continue;
7142
7143 if(rc == 3){
7144 help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
7145 continue;
7146 }
7147
7148 if(rc != 4)
7149 break;
7150 }
7151
7152 if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
7153 q_status_message(SM_ORDER, 3, 4,
7154 _("Send cancelled (SMTP server must be provided before sending)"));
7155 return(0);
7156 }
7157
7158 /* save the name */
7159 list = (char **) fs_get(2 * sizeof(char *));
7160 list[0] = cpystr(answer);
7161 list[1] = NULL;
7162 set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
7163 fs_give((void *)&list[0]);
7164 fs_give((void *)list);
7165 }
7166
7167 return(1);
7168 }
7169
7170 #endif /* defined(DOS) || defined(OS2) */
7171