1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Collect input from standard input, handling ~ escapes.
3 *@ TODO This needs a complete rewrite, with carriers, etc.
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7 * SPDX-License-Identifier: BSD-3-Clause
10 * Copyright (c) 1980, 1993
11 * The Regents of the University of California. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #define n_FILE collect
40 #ifndef HAVE_AMALGAMATION
44 struct a_coll_fmt_ctx
{ /* xxx This is temporary until v15 has objects */
47 struct message
*cfc_mp
;
53 char const *cfc_msgid
; /* Or NULL */
56 struct a_coll_ocs_arg
{
57 sighandler_type coa_opipe
;
58 sighandler_type coa_oint
;
59 FILE *coa_stdin
; /* The descriptor (pipe(2)+Fdopen()) we read from */
60 FILE *coa_stdout
; /* The Popen()ed pipe through which we write to the hook */
61 int coa_pipe
[2]; /* ..backing .coa_stdin */
62 si8_t
*coa_senderr
; /* Set to 1 on failure */
63 char coa_cmd
[n_VFIELD_SIZE(0)];
66 /* The following hookiness with global variables is so that on receipt of an
67 * interrupt signal, the partial message can be salted away on *DEAD* */
69 static sighandler_type _coll_saveint
; /* Previous SIGINT value */
70 static sighandler_type _coll_savehup
; /* Previous SIGHUP value */
71 static FILE *_coll_fp
; /* File for saving away */
72 static int volatile _coll_hadintr
; /* Have seen one SIGINT so far */
73 static sigjmp_buf _coll_jmp
; /* To get back to work */
74 static sigjmp_buf _coll_abort
; /* To end collection with error */
75 static char const *a_coll_ocs__macname
; /* *on-compose-splice* */
77 /* Handle `~:', `~_' and some hooks; hp may be NULL */
78 static void _execute_command(struct header
*hp
, char const *linebuf
,
82 static si32_t
a_coll_include_file(char const *name
, bool_t indent
,
85 /* Execute cmd and insert its standard output into fp, return errno */
86 static si32_t
a_coll_insert_cmd(FILE *fp
, char const *cmd
);
89 static void print_collf(FILE *collf
, struct header
*hp
);
91 /* Write a file, ex-like if f set */
92 static si32_t
a_coll_write(char const *name
, FILE *fp
, int f
);
94 /* *message-inject-head* */
95 static bool_t
a_coll_message_inject_head(FILE *fp
);
97 /* With bells and whistles */
98 static bool_t
a_coll_quote_message(FILE *fp
, struct message
*mp
, bool_t isfwd
);
100 /* *{forward,quote}-inject-{head,tail}*.
101 * fmt may be NULL or the empty string, in which case no output is produced */
102 static bool_t
a_coll__fmt_inj(struct a_coll_fmt_ctx
const *cfcp
);
104 /* Parse off the message header from fp and store relevant fields in hp,
105 * replace _coll_fp with a shiny new version without any header.
106 * Takes care for closing of fp and _coll_fp as necessary */
107 static bool_t
a_coll_makeheader(FILE *fp
, struct header
*hp
,
108 si8_t
*checkaddr_err
, bool_t do_delayed_due_t
);
110 /* Edit the message being collected on fp.
111 * If c=='|' pipecmd must be set and is passed through to n_run_editor().
112 * On successful return, make the edit file the new temp file; return errno */
113 static si32_t
a_coll_edit(int c
, struct header
*hp
, char const *pipecmd
);
115 /* Pipe the message through the command. Old message is on stdin of command,
116 * new message collected from stdout. Shell must return 0 to accept new msg */
117 static si32_t
a_coll_pipe(char const *cmd
);
119 /* Interpolate the named messages into the current message, possibly doing
120 * indent stuff. The flag argument is one of the command escapes: [mMfFuU].
122 static si32_t
a_coll_forward(char const *ms
, FILE *fp
, int f
);
124 /* On interrupt, come here to save the partial message in ~/dead.letter.
125 * Then jump out of the collection loop */
126 static void _collint(int s
);
128 static void collhup(int s
);
130 /* ~[AaIi], *message-inject-**: put value, expand \[nt] if *posix* */
131 static bool_t
a_coll_putesc(char const *s
, bool_t addnl
, FILE *stream
);
133 /* *on-compose-splice* driver and *on-compose-splice(-shell)?* finalizer */
134 static int a_coll_ocs__mac(void);
135 static void a_coll_ocs__finalize(void *vp
);
138 _execute_command(struct header
*hp
, char const *linebuf
, size_t linesize
){
139 /* The problem arises if there are rfc822 message attachments and the
140 * user uses `~:' to change the current file. TODO Unfortunately we
141 * TODO cannot simply keep a pointer to, or increment a reference count
142 * TODO of the current `file' (mailbox that is) object, because the
143 * TODO codebase doesn't deal with that at all; so, until some far
144 * TODO later time, copy the name of the path, and warn the user if it
145 * TODO changed; we COULD use the AC_TMPFILE attachment type, i.e.,
146 * TODO copy the message attachments over to temporary files, but that
147 * TODO would require more changes so that the user still can recognize
148 * TODO in `~@' etc. that its a rfc822 message attachment; see below */
150 struct attachment
*ap
;
151 char * volatile mnbuf
;
157 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_HUP
| n_SIGMAN_INT
| n_SIGMAN_QUIT
){
161 n_pstate_err_no
= n_ERR_INTR
;
166 /* If the above todo is worked, remove or outsource to attachment.c! */
167 if(hp
!= NULL
&& (ap
= hp
->h_attach
) != NULL
) do
169 mnbuf
= sstrdup(mailname
);
172 while((ap
= ap
->a_flink
) != NULL
);
174 n_go_command(n_GO_INPUT_CTX_COMPOSE
, linebuf
);
176 n_sigman_cleanup_ping(&sm
);
179 if(strcmp(mnbuf
, mailname
))
180 n_err(_("Mailbox changed: it is likely that existing "
181 "rfc822 attachments became invalid!\n"));
185 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
189 a_coll_include_file(char const *name
, bool_t indent
, bool_t writestat
){
191 char const *heredb
, *indb
;
192 size_t linesize
, heredl
, indl
, cnt
, linelen
;
200 linebuf
= NULL
; /* TODO line pool */
205 /* The -M case is special */
206 if(name
== (char*)-1){
209 }else if(name
[0] == '-' &&
210 (name
[1] == '\0' || blankspacechar(name
[1]))){
213 if(!(n_psonce
& n_PSO_INTERACTIVE
)){
214 n_err(_("~< -: HERE-delimiter required in non-interactive mode\n"));
219 for(heredb
= &name
[2]; *heredb
!= '\0' && blankspacechar(*heredb
);
222 if((heredl
= strlen(heredb
)) == 0){
224 n_err(_("~< - HERE-delimiter: delimiter must not be empty\n"));
230 for(indb
= ++heredb
; *indb
!= '\0' && *indb
!= '\''; ++indb
)
233 n_err(_("~< - HERE-delimiter: missing trailing quote\n"));
236 }else if(indb
[1] != '\0'){
237 n_err(_("~< - HERE-delimiter: trailing characters after "
242 if((heredl
= PTR2SIZE(indb
- heredb
)) == 0)
244 heredb
= savestrbuf(heredb
, heredl
);
248 }else if((fbuf
= Fopen(name
, "r")) == NULL
){
249 n_perr(name
, rv
= n_err_no
);
253 indl
= indent
? strlen(indb
= ok_vlook(indentprefix
)) : 0;
257 while(fgetline(&linebuf
, &linesize
, (fbuf
== n_stdin
? NULL
: &cnt
),
258 &linelen
, fbuf
, 0) != NULL
){
259 if(heredl
> 0 && heredl
== linelen
- 1 &&
260 !memcmp(heredb
, linebuf
, heredl
)){
266 if(fwrite(indb
, sizeof *indb
, indl
, _coll_fp
) != indl
){
273 if(fwrite(linebuf
, sizeof *linebuf
, linelen
, _coll_fp
) != linelen
){
280 if(fflush(_coll_fp
)){
286 rv
= n_ERR_NOTOBACCO
;
298 fprintf(n_stdout
, "%s%s %" PRId64
"/%" PRId64
"\n",
299 n_shexp_quote_cp(name
, FAL0
), (rv
? " " n_ERROR
: n_empty
), lc
, cc
);
305 a_coll_insert_cmd(FILE *fp
, char const *cmd
){
314 if((ibuf
= Popen(cmd
, "r", ok_vlook(SHELL
), NULL
, 0)) != NULL
){
317 while((c
= getc(ibuf
)) != EOF
){ /* XXX bytewise, yuck! */
318 if(putc(c
, fp
) == EOF
){
326 if(!feof(ibuf
) || ferror(ibuf
)){
330 if(!Pclose(ibuf
, TRU1
)){
335 n_perr(cmd
, rv
= n_err_no
);
337 fprintf(n_stdout
, "CMD%s %" PRId64
"/%" PRId64
"\n",
338 (rv
== n_ERR_NONE
? n_empty
: " " n_ERROR
), lc
, cc
);
344 print_collf(FILE *cf
, struct header
*hp
)
348 size_t cnt
, linesize
, linelen
;
352 cnt
= (size_t)fsize(cf
);
354 if((obuf
= Ftmp(NULL
, "collfp", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) == NULL
){
355 n_perr(_("Can't create temporary file for `~p' command"), 0);
361 fprintf(obuf
, _("-------\nMessage contains:\n")); /* XXX112 */
362 n_puthead(TRU1
, hp
, obuf
,
363 (GIDENT
| GTO
| GSUBJECT
| GCC
| GBCC
| GBCC_IS_FCC
| GNL
| GFILES
|
364 GCOMMA
), SEND_TODISP
, CONV_NONE
, NULL
, NULL
);
368 while(fgetline(&lbuf
, &linesize
, &cnt
, &linelen
, cf
, 1))
369 prout(lbuf
, linelen
, obuf
);
373 if(hp
->h_attach
!= NULL
){
374 fputs(_("-------\nAttachments:\n"), obuf
);
375 n_attachment_list_print(hp
->h_attach
, obuf
);
380 page_or_print(obuf
, 0);
388 a_coll_write(char const *name
, FILE *fp
, int f
)
399 fprintf(n_stdout
, "%s ", n_shexp_quote_cp(name
, FAL0
));
403 if ((of
= Fopen(name
, "a")) == NULL
) {
404 n_perr(name
, rv
= n_err_no
);
409 while ((c
= getc(fp
)) != EOF
) {
413 if (putc(c
, of
) == EOF
) {
414 n_perr(name
, rv
= n_err_no
);
418 fprintf(n_stdout
, "%" PRId64
"/%" PRId64
"\n", lc
, cc
);
428 putc('\n', n_stdout
);
433 a_coll_message_inject_head(FILE *fp
){
435 char const *cp
, *cp_obsolete
;
438 cp_obsolete
= ok_vlook(NAIL_HEAD
);
439 if(cp_obsolete
!= NULL
)
440 n_OBSOLETE(_("please use *message-inject-head*, not *NAIL_HEAD*"));
442 if(((cp
= ok_vlook(message_inject_head
)) != NULL
||
443 (cp
= cp_obsolete
) != NULL
) && !a_coll_putesc(cp
, TRU1
, fp
))
452 a_coll_quote_message(FILE *fp
, struct message
*mp
, bool_t isfwd
){
453 struct a_coll_fmt_ctx cfc
;
455 struct n_ignore
const *quoteitp
;
456 enum sendaction action
;
462 if(isfwd
|| (cp
= ok_vlook(quote
)) != NULL
){
463 quoteitp
= n_IGNORE_ALL
;
467 char const *cp_v15compat
;
469 if((cp_v15compat
= ok_vlook(fwdheading
)) != NULL
)
470 n_OBSOLETE(_("please use *forward-inject-head* instead of "
472 if((cp
= ok_vlook(forward_inject_head
)) == NULL
&&
473 (cp
= cp_v15compat
) == NULL
)
474 cp
= n_FORWARD_INJECT_HEAD
;
475 quoteitp
= n_IGNORE_FWD
;
477 if(!strcmp(cp
, "noheading")){
479 }else if(!strcmp(cp
, "headers")){
480 quoteitp
= n_IGNORE_TYPE
;
482 }else if(!strcmp(cp
, "allheaders")){
484 action
= SEND_QUOTE_ALL
;
486 }else if((cp
= ok_vlook(quote_inject_head
)) == NULL
)
487 cp
= n_QUOTE_INJECT_HEAD
;
489 /* We we pass through our formatter? */
490 if((cfc
.cfc_fmt
= cp
) != NULL
){
491 /* TODO In v15 [-textual_-]sender_info() should only create a list
492 * TODO of matching header objects, and the formatter should simply
493 * TODO iterate over this list and call OBJ->to_ui_str(FLAGS) or so.
494 * TODO For now fully initialize this thing once (grrrr!!) */
497 n_header_textual_sender_info(cfc
.cfc_mp
= mp
, &cfc
.cfc_cumul
,
498 &cfc
.cfc_addr
, &cfc
.cfc_real
, &cfc
.cfc_full
, NULL
);
499 cfc
.cfc_date
= n_header_textual_date_info(mp
, NULL
);
504 if((msgid
= hfield1("message-id", mp
)) != NULL
&&
505 (np
= lextract(msgid
, GREF
)) != NULL
)
509 cfc
.cfc_msgid
= msgid
;
512 if(!a_coll__fmt_inj(&cfc
) || fflush(fp
))
516 if(sendmp(mp
, fp
, quoteitp
, (isfwd
? NULL
: ok_vlook(indentprefix
)),
521 if((cp
= ok_vlook(forward_inject_tail
)) == NULL
)
522 cp
= n_FORWARD_INJECT_TAIL
;
523 }else if(cp
!= NULL
&& (cp
= ok_vlook(quote_inject_tail
)) == NULL
)
524 cp
= n_QUOTE_INJECT_TAIL
;
525 if((cfc
.cfc_fmt
= cp
) != NULL
&& (!a_coll__fmt_inj(&cfc
) || fflush(fp
)))
536 a_coll__fmt_inj(struct a_coll_fmt_ctx
const *cfcp
){
538 struct n_string s_b
, *sp
;
543 if((fmt
= cfcp
->cfc_fmt
) == NULL
|| *fmt
== '\0')
546 sp
= n_string_book(n_string_creat_auto(&s_b
), 127);
548 while((c
= *fmt
++) != '\0'){
549 if(c
!= '%' || (c
= *fmt
++) == '%'){
551 sp
= n_string_push_c(sp
, c
);
554 sp
= n_string_push_cp(sp
, cfcp
->cfc_addr
);
557 sp
= n_string_push_cp(sp
, cfcp
->cfc_date
);
560 sp
= n_string_push_cp(sp
, cfcp
->cfc_full
);
563 if(cfcp
->cfc_msgid
!= NULL
)
564 sp
= n_string_push_cp(sp
, cfcp
->cfc_msgid
);
567 sp
= n_string_push_cp(sp
, cfcp
->cfc_cumul
);
570 sp
= n_string_push_cp(sp
, cfcp
->cfc_real
);
577 n_err(_("*{forward,quote}-inject-{head,tail}*: "
578 "unknown format: %c (in: %s)\n"),
579 c
, n_shexp_quote_cp(cfcp
->cfc_fmt
, FAL0
));
584 quoteflt_init(&qf
, NULL
, FAL0
);
585 quoteflt_reset(&qf
, cfcp
->cfc_fp
);
586 if(quoteflt_push(&qf
, sp
->s_dat
, sp
->s_len
) < 0 || quoteflt_flush(&qf
) < 0)
588 quoteflt_destroy(&qf
);
590 /*n_string_gut(sp);*/
593 return (cfcp
!= NULL
);
597 a_coll_makeheader(FILE *fp
, struct header
*hp
, si8_t
*checkaddr_err
,
598 bool_t do_delayed_due_t
)
607 if ((nf
= Ftmp(NULL
, "colhead", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==NULL
) {
608 n_perr(_("temporary mail edit file"), 0);
612 n_header_extract(((do_delayed_due_t
613 ? n_HEADER_EXTRACT_FULL
| n_HEADER_EXTRACT_PREFILL_RECEIVERS
614 : n_HEADER_EXTRACT_EXTENDED
) |
615 n_HEADER_EXTRACT_IGNORE_SHELL_COMMENTS
), fp
, hp
, checkaddr_err
);
616 if (checkaddr_err
!= NULL
&& *checkaddr_err
!= 0)
619 /* In template mode some things have been delayed until the template has
621 if(do_delayed_due_t
){
624 if((cp
= ok_vlook(on_compose_enter
)) != NULL
){
625 setup_from_and_sender(hp
);
626 temporary_compose_mode_hook_call(cp
, &n_temporary_compose_hook_varset
,
630 if(!a_coll_message_inject_head(nf
))
634 while ((c
= getc(fp
)) != EOF
) /* XXX bytewise, yuck! */
643 if (check_from_and_sender(hp
->h_from
, hp
->h_sender
) == NULL
)
654 a_coll_edit(int c
, struct header
*hp
, char const *pipecmd
) /* TODO errret */
658 sighandler_type
volatile sigint
;
659 struct name
*saved_in_reply_to
;
665 n_UNINIT(sigint
, SIG_ERR
);
666 saved_filrec
= ok_blook(add_file_recipients
);
668 n_SIGMAN_ENTER_SWITCH(&sm
, n_SIGMAN_ALL
){
670 sigint
= safe_signal(SIGINT
, SIG_IGN
);
678 ok_bset(add_file_recipients
);
680 saved_in_reply_to
= NULL
;
684 if((np
= hp
->h_in_reply_to
) == NULL
)
685 hp
->h_in_reply_to
= np
= n_header_setup_in_reply_to(hp
);
687 saved_in_reply_to
= ndup(np
, np
->n_type
);
691 nf
= n_run_editor(_coll_fp
, (off_t
)-1, c
, FAL0
, hp
, NULL
, SEND_MBOX
, sigint
,
695 /* Overtaking of nf->_coll_fp is done by a_coll_makeheader()! */
696 if(!a_coll_makeheader(nf
, hp
, NULL
, FAL0
))
698 /* Break the thread if In-Reply-To: has been modified */
699 if(hp
->h_in_reply_to
== NULL
|| (saved_in_reply_to
!= NULL
&&
700 asccasecmp(hp
->h_in_reply_to
->n_fullname
,
701 saved_in_reply_to
->n_fullname
)))
704 fseek(nf
, 0L, SEEK_END
);
711 n_sigman_cleanup_ping(&sm
);
714 ok_bclear(add_file_recipients
);
715 safe_signal(SIGINT
, sigint
);
717 n_sigman_leave(&sm
, n_SIGMAN_VIPSIGS_NTTYOUT
);
722 a_coll_pipe(char const *cmd
)
726 sighandler_type sigint
;
731 sigint
= safe_signal(SIGINT
, SIG_IGN
);
733 if ((nf
= Ftmp(NULL
, "colpipe", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==NULL
) {
735 n_perr(_("temporary mail edit file"), rv
= n_err_no
);
739 /* stdin = current message. stdout = new message */
740 if(fflush(_coll_fp
) == EOF
)
743 if (n_child_run(ok_vlook(SHELL
), 0, fileno(_coll_fp
), fileno(nf
), "-c",
744 cmd
, NULL
, NULL
, &ws
) < 0 || WEXITSTATUS(ws
) != 0) {
750 if (fsize(nf
) == 0) {
751 n_err(_("No bytes from %s !?\n"), n_shexp_quote_cp(cmd
, FAL0
));
758 fseek(nf
, 0L, SEEK_END
);
762 safe_signal(SIGINT
, sigint
);
768 a_coll_forward(char const *ms
, FILE *fp
, int f
)
771 struct n_ignore
const *itp
;
773 enum sendaction action
;
776 if ((rv
= n_getmsglist(ms
, n_msgvec
, 0, NULL
)) < 0) {
777 rv
= n_ERR_NOENT
; /* XXX not really, should be handled there! */
781 *n_msgvec
= first(0, MMNORM
);
782 if (*n_msgvec
== 0) {
783 n_err(_("No appropriate messages\n"));
789 msgvec
= n_autorec_calloc(rv
+1, sizeof *msgvec
);
791 msgvec
[rv
] = n_msgvec
[rv
];
794 if (f
== 'f' || f
== 'F' || f
== 'u')
797 tabst
= ok_vlook(indentprefix
);
798 if (f
== 'u' || f
== 'U')
801 itp
= upperchar(f
) ? NULL
: n_IGNORE_TYPE
;
802 action
= (upperchar(f
) && f
!= 'U') ? SEND_QUOTE_ALL
: SEND_QUOTE
;
804 fprintf(n_stdout
, A_("Interpolating:"));
806 for(; *msgvec
!= 0; ++msgvec
){
809 mp
= &message
[*msgvec
- 1];
812 fprintf(n_stdout
, " %d", *msgvec
);
815 if(!a_coll_quote_message(fp
, mp
, FAL0
)){
819 }else if(sendmp(mp
, fp
, itp
, tabst
, action
, NULL
) < 0){
820 n_perr(_("forward: temporary mail file"), 0);
827 fprintf(n_stdout
, "\n");
836 NYD_X
; /* Signal handler */
838 /* the control flow is subtle, because we can be called from ~q */
839 if (_coll_hadintr
== 0) {
840 if (ok_blook(ignore
)) {
841 fputs("@\n", n_stdout
);
846 siglongjmp(_coll_jmp
, 1);
848 n_exit_status
|= n_EXIT_SEND_ERROR
;
850 savedeadletter(_coll_fp
, TRU1
);
851 /* Aborting message, no need to fflush() .. */
852 siglongjmp(_coll_abort
, 1);
858 NYD_X
; /* Signal handler */
861 savedeadletter(_coll_fp
, TRU1
);
862 /* Let's pretend nobody else wants to clean up, a true statement at
868 a_coll_putesc(char const *s
, bool_t addnl
, FILE *stream
){
873 isposix
= ok_blook(posix
);
875 while((c1
= *s
++) != '\0'){
876 if(c1
== '\\' && ((c2
= *s
) == 't' || c2
== 'n')){
878 isposix
= TRU1
; /* TODO v15 OBSOLETE! */
879 n_err(_("Compose mode warning: expanding \\t or \\n in variable "
881 " Support remains only for ~A,~a,~I,~i in *posix* mode!\n"
882 " Please use \"wysh set X=y..\" instead\n"));
885 c1
= (c2
== 't') ? '\t' : '\n';
888 if(putc(c1
, stream
) == EOF
)
892 if(addnl
&& putc('\n', stream
) == EOF
)
901 a_coll_ocs__mac(void){
902 /* Executes in a fork(2)ed child TODO if remains, global MASKs for those! */
903 setvbuf(n_stdin
, NULL
, _IOLBF
, 0);
904 setvbuf(n_stdout
, NULL
, _IOLBF
, 0);
905 n_psonce
&= ~(n_PSO_INTERACTIVE
| n_PSO_TTYIN
| n_PSO_TTYOUT
);
906 n_pstate
|= n_PS_COMPOSE_FORKHOOK
;
907 n_readctl_read_overlay
= NULL
; /* TODO need OnForkEvent! See c_readctl() */
908 n_digmsg_read_overlay
= NULL
; /* TODO need OnForkEvent! See c_digmsg() */
909 if(n_poption
& n_PO_D_VV
){
912 snprintf(buf
, sizeof buf
, "[%d]%s", getpid(), ok_vlook(log_prefix
));
913 ok_vset(log_prefix
, buf
);
915 /* TODO If that uses `!' it will effectively SIG_IGN SIGINT, ...and such */
916 temporary_compose_mode_hook_call(a_coll_ocs__macname
, NULL
, NULL
);
921 a_coll_ocs__finalize(void *vp
){
922 /* Note we use this for destruction upon setup errors, thus */
923 sighandler_type opipe
;
924 sighandler_type oint
;
925 struct a_coll_ocs_arg
**coapp
, *coap
;
928 temporary_compose_mode_hook_call((char*)-1, NULL
, NULL
);
930 coap
= *(coapp
= vp
);
931 *coapp
= (struct a_coll_ocs_arg
*)-1;
933 if(coap
->coa_stdin
!= NULL
)
934 Fclose(coap
->coa_stdin
);
935 else if(coap
->coa_pipe
[0] != -1)
936 close(coap
->coa_pipe
[0]);
938 if(coap
->coa_stdout
!= NULL
&& !Pclose(coap
->coa_stdout
, TRU1
))
939 *coap
->coa_senderr
= 111;
940 if(coap
->coa_pipe
[1] != -1)
941 close(coap
->coa_pipe
[1]);
943 opipe
= coap
->coa_opipe
;
944 oint
= coap
->coa_oint
;
949 safe_signal(SIGPIPE
, opipe
);
950 safe_signal(SIGINT
, oint
);
956 n_temporary_compose_hook_varset(void *arg
){ /* TODO v15: drop */
963 if((val
= hp
->h_subject
) == NULL
)
965 ok_vset(mailx_subject
, val
);
966 if((val
= detract(hp
->h_from
, GNAMEONLY
)) == NULL
)
968 ok_vset(mailx_from
, val
);
969 if((val
= detract(hp
->h_sender
, GNAMEONLY
)) == NULL
)
971 ok_vset(mailx_sender
, val
);
972 if((val
= detract(hp
->h_to
, GNAMEONLY
)) == NULL
)
974 ok_vset(mailx_to
, val
);
975 if((val
= detract(hp
->h_cc
, GNAMEONLY
)) == NULL
)
977 ok_vset(mailx_cc
, val
);
978 if((val
= detract(hp
->h_bcc
, GNAMEONLY
)) == NULL
)
980 ok_vset(mailx_bcc
, val
);
982 if((val
= hp
->h_mailx_command
) == NULL
)
984 ok_vset(mailx_command
, val
);
986 if((val
= detract(hp
->h_mailx_raw_to
, GNAMEONLY
)) == NULL
)
988 ok_vset(mailx_raw_to
, val
);
989 if((val
= detract(hp
->h_mailx_raw_cc
, GNAMEONLY
)) == NULL
)
991 ok_vset(mailx_raw_cc
, val
);
992 if((val
= detract(hp
->h_mailx_raw_bcc
, GNAMEONLY
)) == NULL
)
994 ok_vset(mailx_raw_bcc
, val
);
996 if((val
= detract(hp
->h_mailx_orig_from
, GNAMEONLY
)) == NULL
)
998 ok_vset(mailx_orig_from
, val
);
999 if((val
= detract(hp
->h_mailx_orig_to
, GNAMEONLY
)) == NULL
)
1001 ok_vset(mailx_orig_to
, val
);
1002 if((val
= detract(hp
->h_mailx_orig_cc
, GNAMEONLY
)) == NULL
)
1004 ok_vset(mailx_orig_cc
, val
);
1005 if((val
= detract(hp
->h_mailx_orig_bcc
, GNAMEONLY
)) == NULL
)
1007 ok_vset(mailx_orig_bcc
, val
);
1012 n_collect(enum n_mailsend_flags msf
, struct header
*hp
, struct message
*mp
,
1013 char const *quotefile
, si8_t
*checkaddr_err
)
1015 struct n_dig_msg_ctx dmc
;
1016 struct n_string s
, * volatile sp
;
1017 struct a_coll_ocs_arg
*coap
;
1019 int volatile t
, eofcnt
, getfields
;
1020 char volatile escape
;
1025 a_COAP_NOSIGTERM
= 1u<<8
1026 #define a_HARDERR() ((flags & (a_ERREXIT | a_IGNERR)) == a_ERREXIT)
1029 char const *cp
, *cp_base
, * volatile coapm
, * volatile ifs_saved
;
1030 size_t i
, linesize
; /* TODO line pool */
1032 sigset_t oset
, nset
;
1033 FILE * volatile sigfp
;
1036 n_DIG_MSG_COMPOSE_CREATE(&dmc
, hp
);
1044 ifs_saved
= coapm
= NULL
;
1048 /* Start catching signals from here, but we still die on interrupts
1049 * until we're in the main loop */
1051 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
1052 if ((_coll_saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
1053 safe_signal(SIGINT
, &_collint
);
1054 if ((_coll_savehup
= safe_signal(SIGHUP
, SIG_IGN
)) != SIG_IGN
)
1055 safe_signal(SIGHUP
, collhup
);
1056 if (sigsetjmp(_coll_abort
, 1))
1058 if (sigsetjmp(_coll_jmp
, 1))
1060 n_pstate
|= n_PS_COMPOSE_MODE
;
1061 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
1063 if ((_coll_fp
= Ftmp(NULL
, "collect", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
1065 n_perr(_("collect: temporary mail file"), 0);
1069 /* If we are going to prompt for a subject, refrain from printing a newline
1070 * after the headers (since some people mind) */
1072 if(!(n_poption
& n_PO_t_FLAG
)){
1073 t
= GTO
| GSUBJECT
| GCC
| GNL
;
1074 if(ok_blook(fullnames
))
1077 if((n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
)){
1078 if(hp
->h_subject
== NULL
&& ok_blook(asksub
)/* *ask* auto warped! */)
1079 t
&= ~GNL
, getfields
|= GSUBJECT
;
1081 if(hp
->h_to
== NULL
)
1082 t
&= ~GNL
, getfields
|= GTO
;
1084 if(!ok_blook(bsdcompat
) && !ok_blook(askatend
)){
1085 if(ok_blook(askbcc
))
1086 t
&= ~GNL
, getfields
|= GBCC
;
1088 t
&= ~GNL
, getfields
|= GCC
;
1097 if (!sigsetjmp(_coll_jmp
, 1)) {
1098 /* Ask for some headers first, as necessary */
1100 grab_headers(n_GO_INPUT_CTX_COMPOSE
, hp
, getfields
, 1);
1102 /* Execute compose-enter; delayed for -t mode */
1103 if(!(n_poption
& n_PO_t_FLAG
) &&
1104 (cp
= ok_vlook(on_compose_enter
)) != NULL
){
1105 setup_from_and_sender(hp
);
1106 temporary_compose_mode_hook_call(cp
, &n_temporary_compose_hook_varset
,
1110 /* TODO Mm: nope since it may require turning this into a multipart one */
1111 if(!(n_poption
& (n_PO_Mm_FLAG
| n_PO_t_FLAG
))){
1112 if(!a_coll_message_inject_head(_coll_fp
))
1115 /* Quote an original message */
1116 if(mp
!= NULL
&& !a_coll_quote_message(_coll_fp
, mp
,
1117 ((msf
& n_MAILSEND_IS_FWD
) != 0)))
1121 if (quotefile
!= NULL
) {
1122 if((n_pstate_err_no
= a_coll_include_file(quotefile
, FAL0
, FAL0
)
1127 if(n_psonce
& n_PSO_INTERACTIVE
){
1128 if(!(n_pstate
& n_PS_SOURCING
)){
1129 sp
= n_string_creat_auto(&s
);
1130 sp
= n_string_reserve(sp
, 80);
1133 if(!(n_poption
& n_PO_Mm_FLAG
) && !(n_pstate
& n_PS_ROBOT
)){
1134 /* Print what we have sofar also on the terminal (if useful) */
1135 if((cp
= ok_vlook(editalong
)) == NULL
){
1136 if(msf
& n_MAILSEND_HEADERS_PRINT
)
1137 n_puthead(TRU1
, hp
, n_stdout
, t
, SEND_TODISP
, CONV_NONE
,
1141 while ((c
= getc(_coll_fp
)) != EOF
) /* XXX bytewise, yuck! */
1143 if (fseek(_coll_fp
, 0, SEEK_END
))
1146 if(a_coll_edit(((*cp
== 'v') ? 'v' : 'e'), hp
, NULL
1149 /* Print msg mandated by the Mail Reference Manual */
1151 if((n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
))
1152 fputs(_("(continue)\n"), n_stdout
);
1158 /* Come here for printing the after-signal message. Duplicate messages
1159 * won't be printed because the write is aborted if we get a SIGTTOU */
1160 if(_coll_hadintr
&& (n_psonce
& n_PSO_INTERACTIVE
) &&
1161 !(n_pstate
& n_PS_ROBOT
))
1162 n_err(_("\n(Interrupt -- one more to kill letter)\n"));
1165 /* If not under shell hook control */
1167 /* We're done with -M or -m TODO because: we are too stupid yet, above */
1168 if(n_poption
& n_PO_Mm_FLAG
)
1170 /* No command escapes, interrupts not expected. Simply copy STDIN */
1171 if(!(n_psonce
& n_PSO_INTERACTIVE
) &&
1172 !(n_poption
& (n_PO_t_FLAG
| n_PO_TILDE_FLAG
))){
1173 linebuf
= n_realloc(linebuf
, linesize
= LINESIZE
);
1174 while ((i
= fread(linebuf
, sizeof *linebuf
, linesize
, n_stdin
)) > 0) {
1175 if (i
!= fwrite(linebuf
, sizeof *linebuf
, i
, _coll_fp
))
1182 /* The interactive collect loop */
1184 escape
= *ok_vlook(escape
);
1185 flags
= ok_blook(errexit
) ? a_ERREXIT
: a_NONE
;
1188 enum {a_HIST_NONE
, a_HIST_ADD
= 1u<<0, a_HIST_GABBY
= 1u<<1} hist
;
1191 enum n_go_input_flags gif
;
1194 /* TODO optimize: no need to evaluate that anew for each loop tick! */
1195 gif
= n_GO_INPUT_CTX_COMPOSE
;
1196 histadd
= (sp
!= NULL
);
1197 if((n_psonce
& n_PSO_INTERACTIVE
) || (n_poption
& n_PO_TILDE_FLAG
)){
1198 if(!(n_poption
& n_PO_t_FLAG
) || (n_psonce
& n_PSO_t_FLAG_DONE
))
1199 gif
|= n_GO_INPUT_NL_ESC
;
1201 cnt
= n_go_input(gif
, n_empty
, &linebuf
, &linesize
, NULL
, &histadd
);
1202 hist
= histadd
? a_HIST_ADD
| a_HIST_GABBY
: a_HIST_NONE
;
1208 if((n_poption
& n_PO_t_FLAG
) && !(n_psonce
& n_PSO_t_FLAG_DONE
)){
1209 fflush_rewind(_coll_fp
);
1210 n_psonce
|= n_PSO_t_FLAG_DONE
;
1211 if(!a_coll_makeheader(_coll_fp
, hp
, checkaddr_err
, TRU1
))
1214 }else if((n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
) &&
1215 ok_blook(ignoreeof
) && ++eofcnt
< 4){
1217 _("*ignoreeof* set, use `~.' to terminate letter\n"));
1218 n_go_input_clearerr();
1229 else if(coap
== NULL
){
1230 if(!(n_psonce
& n_PSO_INTERACTIVE
) && !(n_poption
& n_PO_TILDE_FLAG
))
1232 else if(cp
[0] == '.'){
1233 if(cnt
== 1 && (ok_blook(dot
) ||
1234 (ok_blook(posix
) && ok_blook(ignoreeof
))))
1238 if(cp
[0] != escape
){
1240 if(fwrite(cp
, sizeof *cp
, cnt
, _coll_fp
) != (size_t)cnt
)
1242 /* TODO n_PS_READLINE_NL is a terrible hack to ensure that _in_all_-
1243 * TODO _code_paths_ a file without trailing newline isn't modified
1244 * TODO to contain one; the "saw-newline" needs to be part of an
1245 * TODO I/O input machinery object */
1247 if(n_pstate
& n_PS_READLINE_NL
){
1248 if(putc('\n', _coll_fp
) == EOF
)
1254 c
= *(cp_base
= ++cp
);
1258 /* Avoid history entry? */
1259 while(spacechar(c
)){
1261 c
= *(cp_base
= ++cp
);
1266 /* It may just be an escaped escaped character, do that quick */
1270 /* Avoid hard *errexit*? */
1279 /* Trim input, also to gain a somewhat normalized history entry */
1284 x
.s
= n_UNCONST(cp
);
1286 n_str_trim_ifs(&x
, TRU1
);
1289 cnt
= (int)/*XXX*/x
.l
;
1292 if(hist
!= a_HIST_NONE
){
1293 sp
= n_string_assign_c(sp
, escape
);
1294 if(flags
& a_IGNERR
)
1295 sp
= n_string_push_c(sp
, '-');
1296 sp
= n_string_push_c(sp
, c
);
1298 sp
= n_string_push_c(sp
, ' ');
1299 sp
= n_string_push_buf(sp
, cp
, cnt
);
1303 /* Switch over all command escapes */
1307 char buf
[sizeof(n_UNIREPL
)];
1310 buf
[0] = c
, buf
[1] = '\0';
1311 else if(n_psonce
& n_PSO_UNICODE
)
1312 memcpy(buf
, n_unirepl
, sizeof n_unirepl
);
1314 buf
[0] = '?', buf
[1] = '\0';
1315 n_err(_("Unknown command escape: `%c%s'\n"), escape
, buf
);
1318 n_err(_("Invalid command escape usage: %s\n"),
1319 n_shexp_quote_cp(linebuf
, FAL0
));
1322 n_pstate_err_no
= n_ERR_INVAL
;
1326 /* Shell escape, send the balance of line to sh -c */
1327 if(cnt
== 0 || coap
!= NULL
)
1330 char const *argv
[2];
1334 n_pstate_ex_no
= c_shell(argv
); /* TODO history norm.; errexit? */
1338 /* Simulate end of file on input */
1339 if(cnt
!= 0 || coap
!= NULL
)
1341 goto jout
; /* TODO does not enter history, thus */
1344 /* Escape to command mode, but be nice! *//* TODO command expansion
1345 * TODO should be handled here so that we have unique history! */
1348 _execute_command(hp
, cp
, cnt
);
1349 if(ok_blook(errexit
))
1352 flags
&= ~a_ERREXIT
;
1353 if(n_pstate_ex_no
!= 0 && a_HARDERR())
1356 escape
= *ok_vlook(escape
);
1357 hist
&= ~a_HIST_GABBY
;
1359 /* case '<': <> 'd' */
1361 #ifdef HAVE_UISTRINGS
1363 "COMMAND ESCAPES (to be placed after a newline) excerpt:\n"
1364 "~. Commit and send message\n"
1365 "~: <command> Execute an internal command\n"
1366 "~< <file> Insert <file> (\"~<! <command>\": insert shell command)\n"
1367 "~@ [<files>] Edit [Add] attachments (file[=in-charset[#out-charset]])\n"
1368 "~c <users> Add users to Cc: list (`~b': to Bcc:)\n"
1369 "~e, ~v Edit message via $EDITOR / $VISUAL\n"
1372 "~F <msglist> Read in with headers, do not *indentprefix* lines\n"
1373 "~f <msglist> Like `~F', but honour `headerpick' configuration\n"
1374 "~H Edit From:, Reply-To: and Sender:\n"
1375 "~h Prompt for Subject:, To:, Cc: and Bcc:\n"
1376 "~i <variable> Insert a value and a newline (`~I': insert value)\n"
1377 "~M <msglist> Read in with headers, *indentprefix* (`~m': use `headerpick')\n"
1378 "~p Show current message compose buffer\n"
1379 "~Q <msglist> Read in using normal *quote* algorithm\n"
1382 "~r <file> Insert <file> (`~R': *indentprefix* lines)\n"
1383 " <file> may also be <- [HERE-DELIMITER]>\n"
1384 "~s <subject> Set Subject:\n"
1385 "~t <users> Add users to To: list\n"
1386 "~u <msglist> Read in without headers (`~U': *indentprefix* lines)\n"
1387 "~w <file> Write message onto file\n"
1388 "~x Abort composition, discard message (`~q': save in $DEAD)\n"
1389 "~| <command> Pipe message through shell filter (`~||': with headers)\n"
1391 #endif /* HAVE_UISTRINGS */
1394 n_pstate_err_no
= n_ERR_NONE
;
1398 struct attachment
*aplist
;
1400 /* Edit the attachment list */
1401 aplist
= hp
->h_attach
;
1402 hp
->h_attach
= NULL
;
1404 hp
->h_attach
= n_attachment_append_list(aplist
, cp
);
1406 hp
->h_attach
= n_attachment_list_edit(aplist
,
1407 n_GO_INPUT_CTX_COMPOSE
);
1408 n_pstate_err_no
= n_ERR_NONE
; /* XXX ~@ does NOT handle $!/$?! */
1409 n_pstate_ex_no
= 0; /* XXX */
1412 if(!n_dig_msg_circumflex(&dmc
, n_stdout
, cp
)){
1413 if(ferror(_coll_fp
))
1417 n_pstate_err_no
= n_ERR_NONE
; /* XXX */
1418 n_pstate_ex_no
= 0; /* XXX */
1419 hist
&= ~a_HIST_GABBY
;
1421 /* case '_': <> ':' */
1423 /* Pipe message through command. Collect output as new message */
1426 /* Is this request to do a "stream equivalent" to 'e' and 'v'? */
1431 if((n_pstate_err_no
= a_coll_pipe(cp
)) == n_ERR_NONE
)
1433 else if(ferror(_coll_fp
))
1435 else if(a_HARDERR())
1439 hist
&= ~a_HIST_GABBY
;
1443 /* Insert the contents of a sign variable */
1446 cp
= (c
== 'a') ? ok_vlook(sign
) : ok_vlook(Sign
);
1449 /* Add stuff to blind carbon copies list TODO join 'c' */
1457 if((np
= checkaddrs(lextract(cp
, GBCC
| GFULL
), EACM_NORMAL
, &soe
)
1459 hp
->h_bcc
= cat(hp
->h_bcc
, np
);
1461 n_pstate_err_no
= n_ERR_NONE
;
1465 n_pstate_err_no
= (soe
< 0) ? n_ERR_PERM
: n_ERR_INVAL
;
1468 hist
&= ~a_HIST_GABBY
;
1471 /* Add to the CC list TODO join 'b' */
1479 if((np
= checkaddrs(lextract(cp
, GCC
| GFULL
), EACM_NORMAL
, &soe
)
1481 hp
->h_cc
= cat(hp
->h_cc
, np
);
1483 n_pstate_err_no
= n_ERR_NONE
;
1487 n_pstate_err_no
= (soe
< 0) ? n_ERR_PERM
: n_ERR_INVAL
;
1490 hist
&= ~a_HIST_GABBY
;
1495 cp
= n_getdeadletter();
1500 /* Invoke a file: Search for the file name, then open it and copy
1501 * the contents to _coll_fp */
1503 n_err(_("Interpolate what file?\n"));
1506 n_pstate_err_no
= n_ERR_NOENT
;
1510 if(*cp
== '!' && c
== '<'){
1511 /* TODO hist. normalization */
1512 if((n_pstate_err_no
= a_coll_insert_cmd(_coll_fp
, ++cp
)
1514 if(ferror(_coll_fp
))
1523 /* Note this also expands things like
1524 * !:vput vexpr delim random 0
1526 if((cp
= fexpand(cp
, FEXP_LOCAL
| FEXP_NOPROTO
| FEXP_NSHELL
)
1530 n_pstate_err_no
= n_ERR_INVAL
;
1535 /* XXX race, and why not test everywhere, then? */
1536 if(n_is_dir(cp
, FAL0
)){
1537 n_err(_("%s: is a directory\n"), n_shexp_quote_cp(cp
, FAL0
));
1540 n_pstate_err_no
= n_ERR_ISDIR
;
1544 if((n_pstate_err_no
= a_coll_include_file(cp
, (c
== 'R'), TRU1
)
1546 if(ferror(_coll_fp
))
1553 n_pstate_err_no
= n_ERR_NONE
; /* XXX */
1554 n_pstate_ex_no
= 0; /* XXX */
1558 /* Edit the current message. 'e' -> use EDITOR, 'v' -> use VISUAL */
1559 if(cnt
!= 0 || coap
!= NULL
)
1562 if((n_pstate_err_no
= a_coll_edit(c
,
1563 ((c
== '|' || ok_blook(editheaders
)) ? hp
: NULL
), cp
)
1566 else if(ferror(_coll_fp
))
1568 else if(a_HARDERR())
1580 /* Interpolate the named messages, if we are in receiving mail mode.
1581 * Does the standard list processing garbage. If ~f is given, we
1582 * don't shift over */
1583 if((n_pstate_err_no
= a_coll_forward(cp
, _coll_fp
, c
)) == n_ERR_NONE
)
1585 else if(ferror(_coll_fp
))
1587 else if(a_HARDERR())
1593 /* Grab extra headers */
1597 grab_headers(n_GO_INPUT_CTX_COMPOSE
, hp
, GEXTRA
, 0);
1598 while(check_from_and_sender(hp
->h_from
, hp
->h_sender
) == NULL
);
1599 n_pstate_err_no
= n_ERR_NONE
; /* XXX */
1600 n_pstate_ex_no
= 0; /* XXX */
1603 /* Grab a bunch of headers */
1607 grab_headers(n_GO_INPUT_CTX_COMPOSE
, hp
,
1608 (GTO
| GSUBJECT
| GCC
| GBCC
),
1609 (ok_blook(bsdcompat
) && ok_blook(bsdorder
)));
1610 while(hp
->h_to
== NULL
);
1611 n_pstate_err_no
= n_ERR_NONE
; /* XXX */
1612 n_pstate_ex_no
= 0; /* XXX */
1616 /* Insert a variable into the file */
1619 cp
= n_var_vlook(cp
, TRU1
);
1621 if(cp
== NULL
|| *cp
== '\0')
1623 if(!a_coll_putesc(cp
, (c
!= 'I'), _coll_fp
))
1625 if((n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
) &&
1626 (!a_coll_putesc(cp
, (c
!= 'I'), n_stdout
) ||
1627 fflush(n_stdout
) == EOF
))
1629 n_pstate_err_no
= n_ERR_NONE
; /* XXX */
1630 n_pstate_ex_no
= 0; /* XXX */
1632 /* case 'M': <> 'F' */
1633 /* case 'm': <> 'f' */
1635 /* Print current state of the message without altering anything */
1638 print_collf(_coll_fp
, hp
); /* XXX pstate_err_no ++ */
1639 if(ferror(_coll_fp
))
1641 n_pstate_err_no
= n_ERR_NONE
; /* XXX */
1642 n_pstate_ex_no
= 0; /* XXX */
1644 /* case 'Q': <> 'F' */
1647 /* Force a quit, act like an interrupt had happened */
1650 /* If we are running a splice hook, assume it quits on its own now,
1651 * otherwise we (no true out-of-band IPC to signal this state, XXX sic)
1652 * have to SIGTERM it in order to stop this wild beast */
1653 flags
|= a_COAP_NOSIGTERM
;
1655 _collint((c
== 'x') ? 0 : SIGINT
);
1658 /* case 'R': <> 'd' */
1659 /* case 'r': <> 'd' */
1661 /* Set the Subject list */
1664 /* Subject:; take care for Debian #419840 and strip any \r and \n */
1665 if(n_anyof_cp("\n\r", hp
->h_subject
= savestr(cp
))){
1668 n_err(_("-s: normalizing away invalid ASCII NL / CR bytes\n"));
1669 for(xp
= hp
->h_subject
; *xp
!= '\0'; ++xp
)
1670 if(*xp
== '\n' || *xp
== '\r')
1672 n_pstate_err_no
= n_ERR_INVAL
;
1675 n_pstate_err_no
= n_ERR_NONE
;
1680 /* Add to the To: list TODO join 'b', 'c' */
1688 if((np
= checkaddrs(lextract(cp
, GTO
| GFULL
), EACM_NORMAL
, &soe
)
1690 hp
->h_to
= cat(hp
->h_to
, np
);
1692 n_pstate_err_no
= n_ERR_NONE
;
1696 n_pstate_err_no
= (soe
< 0) ? n_ERR_PERM
: n_ERR_INVAL
;
1699 hist
&= ~a_HIST_GABBY
;
1701 /* case 'U': <> 'F' */
1702 /* case 'u': <> 'f' */
1703 /* case 'v': <> 'e' */
1705 /* Write the message on a file */
1708 if((cp
= fexpand(cp
, FEXP_LOCAL
| FEXP_NOPROTO
)) == NULL
){
1709 n_err(_("Write what file!?\n"));
1712 n_pstate_err_no
= n_ERR_INVAL
;
1717 if((n_pstate_err_no
= a_coll_write(cp
, _coll_fp
, 1)) == n_ERR_NONE
)
1719 else if(ferror(_coll_fp
))
1721 else if(a_HARDERR())
1726 /* case 'x': <> 'q' */
1729 /* Finally place an entry in history as applicable */
1735 if(hist
& a_HIST_ADD
){
1736 /* Do not add *escape* to the history in order to allow history search
1737 * to be handled generically in the MLE regardless of actual *escape*
1739 n_tty_addhist(&n_string_cp(sp
)[1], (n_GO_INPUT_CTX_COMPOSE
|
1740 (hist
& a_HIST_GABBY
? n_GO_INPUT_HIST_GABBY
: n_GO_INPUT_NONE
)));
1747 /* Do we have *on-compose-splice-shell*, or *on-compose-splice*?
1748 * TODO Usual f...ed up state of signals and terminal etc. */
1749 if(coap
== NULL
&& (cp
= ok_vlook(on_compose_splice_shell
)) != NULL
) Jocs
:{
1750 union {int (*ptf
)(void); char const *sh
;} u
;
1753 /* Reset *escape* and more to their defaults. On change update manual! */
1754 if(ifs_saved
== NULL
)
1755 ifs_saved
= savestr(ok_vlook(ifs
));
1756 escape
= n_ESCAPE
[0];
1760 /* XXX Due Popen() fflush(NULL) in PTF mode, ensure nothing to flush */
1761 /*if(!n_real_seek(_coll_fp, 0, SEEK_END))
1763 u
.ptf
= &a_coll_ocs__mac
;
1765 a_coll_ocs__macname
= cp
= coapm
;
1767 u
.sh
= ok_vlook(SHELL
);
1772 coap
= n_lofi_alloc(n_VSTRUCT_SIZEOF(struct a_coll_ocs_arg
, coa_cmd
1774 coap
->coa_pipe
[0] = coap
->coa_pipe
[1] = -1;
1775 coap
->coa_stdin
= coap
->coa_stdout
= NULL
;
1776 coap
->coa_senderr
= checkaddr_err
;
1777 memcpy(coap
->coa_cmd
, cp
, i
);
1780 coap
->coa_opipe
= safe_signal(SIGPIPE
, SIG_IGN
);
1781 coap
->coa_oint
= safe_signal(SIGINT
, SIG_IGN
);
1784 if(pipe_cloexec(coap
->coa_pipe
) != -1 &&
1785 (coap
->coa_stdin
= Fdopen(coap
->coa_pipe
[0], "r", FAL0
)) != NULL
&&
1786 (coap
->coa_stdout
= Popen(cmd
, "W", u
.sh
, NULL
, coap
->coa_pipe
[1])
1788 close(coap
->coa_pipe
[1]);
1789 coap
->coa_pipe
[1] = -1;
1791 temporary_compose_mode_hook_call(NULL
, NULL
, NULL
);
1792 n_go_splice_hack(coap
->coa_cmd
, coap
->coa_stdin
, coap
->coa_stdout
,
1793 (n_psonce
& ~(n_PSO_INTERACTIVE
| n_PSO_TTYIN
| n_PSO_TTYOUT
)),
1794 &a_coll_ocs__finalize
, &coap
);
1795 /* Hook version protocol for ~^: update manual upon change! */
1796 fputs(n_DIG_MSG_PLUMBING_VERSION
"\n", n_stdout
/*coap->coa_stdout*/);
1801 a_coll_ocs__finalize(coap
);
1802 n_perr(_("Cannot invoke *on-compose-splice(-shell)?*"), c
);
1805 if(*checkaddr_err
!= 0){
1809 if(coapm
== NULL
&& (coapm
= ok_vlook(on_compose_splice
)) != NULL
)
1812 ok_vset(ifs
, ifs_saved
);
1817 * Note: the order of the following steps is documented for `~.'.
1818 * Adjust the manual on change!!
1821 /* Final chance to edit headers, if not already above; and *asksend* */
1822 if((n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
)){
1823 if(ok_blook(bsdcompat
) || ok_blook(askatend
)){
1829 if(ok_blook(askbcc
))
1832 grab_headers(n_GO_INPUT_CTX_COMPOSE
, hp
, gf
, 1);
1835 if(ok_blook(askattach
))
1836 hp
->h_attach
= n_attachment_list_edit(hp
->h_attach
,
1837 n_GO_INPUT_CTX_COMPOSE
);
1839 if(ok_blook(asksend
)){
1842 ifs_saved
= coapm
= NULL
;
1845 fprintf(n_stdout
, _("-------\nEnvelope contains:\n")); /* XXX112 */
1846 if(!n_puthead(TRU1
, hp
, n_stdout
,
1847 GIDENT
| GREF_IRT
| GSUBJECT
| GTO
| GCC
| GBCC
| GBCC_IS_FCC
|
1848 GCOMMA
, SEND_TODISP
, CONV_NONE
, NULL
, NULL
))
1852 if(n_go_input(n_GO_INPUT_CTX_COMPOSE
| n_GO_INPUT_NL_ESC
,
1853 _("Send this message [yes/no, empty: recompose]? "),
1854 &linebuf
, &linesize
, NULL
, NULL
) < 0){
1855 if(!n_go_input_is_eof())
1860 if((b
= n_boolify(linebuf
, UIZ_MAX
, TRUM1
)) < FAL0
)
1869 /* Execute compose-leave */
1870 if((cp
= ok_vlook(on_compose_leave
)) != NULL
){
1871 setup_from_and_sender(hp
);
1872 temporary_compose_mode_hook_call(cp
, &n_temporary_compose_hook_varset
,
1876 /* Add automatic receivers */
1877 if ((cp
= ok_vlook(autocc
)) != NULL
&& *cp
!= '\0')
1878 hp
->h_cc
= cat(hp
->h_cc
, checkaddrs(lextract(cp
, GCC
|
1879 (ok_blook(fullnames
) ? GFULL
| GSKIN
: GSKIN
)),
1880 EACM_NORMAL
, checkaddr_err
));
1881 if ((cp
= ok_vlook(autobcc
)) != NULL
&& *cp
!= '\0')
1882 hp
->h_bcc
= cat(hp
->h_bcc
, checkaddrs(lextract(cp
, GBCC
|
1883 (ok_blook(fullnames
) ? GFULL
| GSKIN
: GSKIN
)),
1884 EACM_NORMAL
, checkaddr_err
));
1885 if (*checkaddr_err
!= 0)
1888 /* TODO Cannot do since it may require turning this into a multipart one */
1889 if(n_poption
& n_PO_Mm_FLAG
)
1892 /* Place signature? */
1893 if((cp
= ok_vlook(signature
)) != NULL
&& *cp
!= '\0'){ /* TODO OBSOLETE */
1896 n_OBSOLETE(_("please use *on-compose-{leave,splice}* and/or "
1897 "*message-inject-tail*, not *signature*"));
1899 if((cpq
= fexpand(cp
, FEXP_LOCAL
| FEXP_NOPROTO
)) == NULL
){
1900 n_err(_("*signature* expands to invalid file: %s\n"),
1901 n_shexp_quote_cp(cp
, FAL0
));
1904 cpq
= n_shexp_quote_cp(cp
= cpq
, FAL0
);
1906 if((sigfp
= Fopen(cp
, "r")) == NULL
){
1907 n_err(_("Can't open *signature* %s: %s\n"),
1908 cpq
, n_err_to_doc(n_err_no
));
1913 linebuf
= n_alloc(linesize
= LINESIZE
);
1915 while((i
= fread(linebuf
, sizeof *linebuf
, linesize
, n_UNVOLATILE(sigfp
)))
1918 if(i
!= fwrite(linebuf
, sizeof *linebuf
, i
, _coll_fp
))
1923 FILE *x
= n_UNVOLATILE(sigfp
);
1924 int e
= n_err_no
, ise
= ferror(x
);
1930 n_err(_("Errors while reading *signature* %s: %s\n"),
1931 cpq
, n_err_to_doc(e
));
1936 if(c
!= '\0' && c
!= '\n')
1937 putc('\n', _coll_fp
);
1940 { char const *cp_obsolete
= ok_vlook(NAIL_TAIL
);
1942 if(cp_obsolete
!= NULL
)
1943 n_OBSOLETE(_("please use *message-inject-tail*, not *NAIL_TAIL*"));
1945 if((cp
= ok_vlook(message_inject_tail
)) != NULL
||
1946 (cp
= cp_obsolete
) != NULL
){
1947 if(!a_coll_putesc(cp
, TRU1
, _coll_fp
))
1949 if((n_psonce
& n_PSO_INTERACTIVE
) && !(n_pstate
& n_PS_ROBOT
) &&
1950 (!a_coll_putesc(cp
, TRU1
, n_stdout
) || fflush(n_stdout
) == EOF
))
1956 if(fflush(_coll_fp
))
1960 if(mp
!= NULL
&& ok_blook(quote_as_attachment
)){
1961 struct attachment
*ap
;
1963 ap
= n_autorec_calloc(1, sizeof *ap
);
1964 if((ap
->a_flink
= hp
->h_attach
) != NULL
)
1965 hp
->h_attach
->a_blink
= ap
;
1967 ap
->a_msgno
= (int)PTR2SIZE(mp
- message
+ 1);
1968 ap
->a_content_description
= _("Original message content");
1972 if (linebuf
!= NULL
)
1974 sigprocmask(SIG_BLOCK
, &nset
, NULL
);
1975 n_DIG_MSG_COMPOSE_GUT(&dmc
);
1976 n_pstate
&= ~n_PS_COMPOSE_MODE
;
1977 safe_signal(SIGINT
, _coll_saveint
);
1978 safe_signal(SIGHUP
, _coll_savehup
);
1979 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
1986 if(coap
!= NULL
&& coap
!= (struct a_coll_ocs_arg
*)-1){
1987 if(!(flags
& a_COAP_NOSIGTERM
))
1988 n_psignal(coap
->coa_stdout
, SIGTERM
);
1989 n_go_splice_hack_remove_after_jump();
1992 if(ifs_saved
!= NULL
){
1993 ok_vset(ifs
, ifs_saved
);
1997 Fclose(n_UNVOLATILE(sigfp
));
2000 if(_coll_fp
!= NULL
){
2007 assert(checkaddr_err
!= NULL
);
2008 /* TODO We don't save in $DEAD upon error because msg not readily composed?
2009 * TODO But this no good, it should go ZOMBIE / DRAFT / POSTPONED or what! */
2010 if(*checkaddr_err
!= 0){
2011 if(*checkaddr_err
== 111)
2012 n_err(_("Compose mode splice hook failure\n"));
2014 n_err(_("Some addressees were classified as \"hard error\"\n"));
2015 }else if(_coll_hadintr
== 0){
2016 *checkaddr_err
= TRU1
; /* TODO ugly: "sendout_error" now.. */
2017 n_err(_("Failed to prepare composed message\n"));