2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 * Mail -- a mail program
43 * Collect input from standard input, handling
52 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
53 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
54 * TODO may leave child running if fdopen() fails! */
57 * Read a message from standard output and return a read file to it
62 * The following hokiness with global variables is so that on
63 * receipt of an interrupt signal, the partial message can be salted
64 * away on dead.letter.
67 static sighandler_type saveint
; /* Previous SIGINT value */
68 static sighandler_type savehup
; /* Previous SIGHUP value */
69 static sighandler_type savetstp
; /* Previous SIGTSTP value */
70 static sighandler_type savettou
; /* Previous SIGTTOU value */
71 static sighandler_type savettin
; /* Previous SIGTTIN value */
72 static FILE *collf
; /* File for saving away */
73 static int hadintr
; /* Have seen one SIGINT so far */
75 static sigjmp_buf colljmp
; /* To get back to work */
76 static int colljmp_p
; /* whether to long jump */
77 static sigjmp_buf collabort
; /* To end collection with error */
79 static sigjmp_buf pipejmp
; /* On broken pipe */
81 static void onpipe(int signo
);
82 static void insertcommand(FILE *fp
, char *cmd
);
83 static void print_collf(FILE *collf
, struct header
*hp
);
84 static int include_file(FILE *fbuf
, char *name
, int *linecount
,
85 int *charcount
, int echo
);
86 static struct attachment
*read_attachment_data(struct attachment
*ap
,
88 static struct attachment
*append_attachments(struct attachment
*attach
,
90 static int exwrite(char *name
, FILE *fp
, int f
);
91 static enum okay
makeheader(FILE *fp
, struct header
*hp
);
92 static void mesedit(int c
, struct header
*hp
);
93 static void mespipe(char *cmd
);
94 static int forward(char *ms
, FILE *fp
, int f
);
95 static void collstop(int s
);
96 static void collint(int s
);
97 static void collhup(int s
);
98 static int putesc(const char *s
, FILE *stream
);
105 siglongjmp(pipejmp
, 1);
109 * Execute cmd and insert its standard output into fp.
112 insertcommand(FILE *fp
, char *cmd
)
119 if (sigsetjmp(pipejmp
, 1))
123 if ((obuf
= Popen(cmd
, "r", cp
, 0)) == NULL
) {
127 safe_signal(SIGPIPE
, onpipe
);
128 while ((c
= getc(obuf
)) != EOF
)
131 safe_signal(SIGPIPE
, SIG_IGN
);
133 safe_signal(SIGPIPE
, dflpipe
);
140 print_collf(FILE *collf
, struct header
*hp
)
143 FILE *volatile obuf
= stdout
;
144 struct attachment
*ap
;
147 size_t linecnt
, maxlines
, linesize
= 0, linelen
, count
, count2
;
153 count
= count2
= fsize(collf
);
154 if (is_a_tty
[0] && is_a_tty
[1] && (cp
= value("crt")) != NULL
) {
156 fgetline(&lbuf
, &linesize
, &count2
, NULL
, collf
, 0);
159 maxlines
= (*cp
== '\0' ? screensize() : atoi(cp
));
171 maxlines
-= myaddrs(hp
) != NULL
|| hp
->h_from
!= NULL
;
172 maxlines
-= value("ORGANIZATION") != NULL
||
173 hp
->h_organization
!= NULL
;
174 maxlines
-= value("replyto") != NULL
|| hp
->h_replyto
!= NULL
;
175 maxlines
-= value("sender") != NULL
|| hp
->h_sender
!= NULL
;
176 if ((long)maxlines
< 0 || linecnt
> maxlines
) {
178 if (sigsetjmp(pipejmp
, 1))
180 obuf
= Popen(cp
, "w", NULL
, 1);
185 safe_signal(SIGPIPE
, onpipe
);
188 fprintf(obuf
, catgets(catd
, CATSET
, 62,
189 "-------\nMessage contains:\n"));
190 gf
= GIDENT
|GTO
|GSUBJECT
|GCC
|GBCC
|GNL
|GFILES
;
191 if (value("fullnames"))
193 puthead(hp
, obuf
, gf
, SEND_TODISP
, CONV_NONE
, NULL
, NULL
);
194 while (fgetline(&lbuf
, &linesize
, &count
, &linelen
, collf
, 1))
195 prout(lbuf
, linelen
, obuf
);
196 if (hp
->h_attach
!= NULL
) {
197 fputs(catgets(catd
, CATSET
, 63, "Attachments:"), obuf
);
198 for (ap
= hp
->h_attach
; ap
!= NULL
; ap
= ap
->a_flink
) {
200 fprintf(obuf
, " message %u", ap
->a_msgno
);
202 fprintf(obuf
, " %s", ap
->a_name
);
209 if (obuf
!= stdout
) {
210 safe_signal(SIGPIPE
, SIG_IGN
);
212 safe_signal(SIGPIPE
, dflpipe
);
219 include_file(FILE *fbuf
, char *name
, int *linecount
, int *charcount
, int echo
)
221 char *interactive
, *linebuf
= NULL
;
222 size_t linesize
= 0, linelen
, count
;
225 fbuf
= Fopen(name
, "r");
230 interactive
= value("interactive");
236 while (fgetline(&linebuf
, &linesize
, &count
, &linelen
, fbuf
, 0)
238 if (fwrite(linebuf
, sizeof *linebuf
, linelen
, collf
)
243 if (interactive
!= NULL
&& echo
)
244 fwrite(linebuf
, sizeof *linebuf
, linelen
, stdout
);
246 (*charcount
) += linelen
;
257 * Ask the user to edit file names and other data for the given
258 * attachment. NULL is returned if no file name is given.
260 static struct attachment
*
261 read_attachment_data(struct attachment
*ap
, unsigned number
)
263 char prefix
[80], *cp
;
266 ap
= csalloc(1, sizeof *ap
);
268 printf("#%u\tmessage %u\n", number
, ap
->a_msgno
);
271 snprintf(prefix
, sizeof prefix
, catgets(catd
, CATSET
, 50,
272 "#%u\tfilename: "), number
);
275 if ((ap
->a_name
= readtty(prefix
, ap
->a_name
)) == NULL
)
277 if ((exf
= file_expand(ap
->a_name
)) == NULL
)
280 if (access(ap
->a_name
, R_OK
) == 0)
284 if ((ap
->a_name
&& (value("attachment-ask-charset"))) ||
285 ((cp
= value("sendcharsets")) != NULL
&&
286 strchr(cp
, ',') != NULL
)) {
287 snprintf(prefix
, sizeof prefix
, "#%u\tcharset: ", number
);
288 ap
->a_charset
= readtty(prefix
, ap
->a_charset
);
291 * The "attachment-ask-content-*" variables are left undocumented
292 * since they are for RFC connoisseurs only.
294 if (ap
->a_name
&& value("attachment-ask-content-type")) {
295 if (ap
->a_content_type
== NULL
)
296 ap
->a_content_type
= mime_filecontent(ap
->a_name
);
297 snprintf(prefix
, sizeof prefix
, "#%u\tContent-Type: ", number
);
298 ap
->a_content_type
= readtty(prefix
, ap
->a_content_type
);
300 if (ap
->a_name
&& value("attachment-ask-content-disposition")) {
301 snprintf(prefix
, sizeof prefix
,
302 "#%u\tContent-Disposition: ", number
);
303 ap
->a_content_disposition
= readtty(prefix
,
304 ap
->a_content_disposition
);
306 if (ap
->a_name
&& value("attachment-ask-content-id")) {
307 snprintf(prefix
, sizeof prefix
, "#%u\tContent-ID: ", number
);
308 ap
->a_content_id
= readtty(prefix
, ap
->a_content_id
);
310 if (ap
->a_name
&& value("attachment-ask-content-description")) {
311 snprintf(prefix
, sizeof prefix
,
312 "#%u\tContent-Description: ", number
);
313 ap
->a_content_description
= readtty(prefix
,
314 ap
->a_content_description
);
316 return ap
->a_name
? ap
: NULL
;
320 * Interactively edit the attachment list.
323 edit_attachments(struct attachment
*attach
)
325 struct attachment
*ap
, *nap
;
328 for (ap
= attach
; ap
; ap
= ap
->a_flink
) {
329 if ((nap
= read_attachment_data(ap
, attno
)) == NULL
) {
331 ap
->a_blink
->a_flink
= ap
->a_flink
;
333 attach
= ap
->a_flink
;
335 ap
->a_flink
->a_blink
= ap
->a_blink
;
341 while ((nap
= read_attachment_data(NULL
, attno
)) != NULL
) {
342 for (ap
= attach
; ap
&& ap
->a_flink
; ap
= ap
->a_flink
);
355 * Put the given file to the end of the attachment list.
358 add_attachment(struct attachment
*attach
, char *file
, int expand_file
)
360 struct attachment
*ap
, *nap
;
363 file
= file_expand(file
);
367 file
= savestr(file
);
368 if (access(file
, R_OK
) != 0)
371 nap
= csalloc(1, sizeof *nap
);
373 if (attach
!= NULL
) {
374 for (ap
= attach
; ap
->a_flink
!= NULL
; ap
= ap
->a_flink
);
385 * Append the whitespace-separated file names to the end of
386 * the attachment list.
388 static struct attachment
*
389 append_attachments(struct attachment
*attach
, char *names
)
393 struct attachment
*ap
;
396 while (*cp
!= '\0' && blankchar(*cp
& 0377))
398 while (*cp
!= '\0') {
400 while (*cp
!= '\0' && !blankchar(*cp
& 0377))
404 if (*names
!= '\0') {
405 if ((ap
= add_attachment(attach
, names
, 1)) == NULL
)
412 while (*cp
!= '\0' && blankchar(*cp
& 0377))
419 collect(struct header
*hp
, int printheaders
, struct message
*mp
,
420 char *quotefile
, int doprefix
, int volatile tflag
)
427 struct ignoretab
*quoteig
;
428 int lc
, cc
, eofcount
, val
, c
, t
;
429 int volatile escape
, getfields
;
430 char *linebuf
= NULL
, *cp
, *quote
= NULL
, *tempMail
= NULL
;
433 enum sendaction action
;
435 sighandler_type savedtop
;
438 if (value("interactive") != NULL
)
443 * Start catching signals from here, but we're still die on interrupts
444 * until we're in the main loop.
447 sigaddset(&nset
, SIGINT
);
448 sigaddset(&nset
, SIGHUP
);
449 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
450 handlerpush(collint
);
451 if ((saveint
= safe_signal(SIGINT
, SIG_IGN
)) != SIG_IGN
)
452 safe_signal(SIGINT
, collint
);
453 if ((savehup
= safe_signal(SIGHUP
, SIG_IGN
)) != SIG_IGN
)
454 safe_signal(SIGHUP
, collhup
);
455 savetstp
= safe_signal(SIGTSTP
, collstop
);
456 savettou
= safe_signal(SIGTTOU
, collstop
);
457 savettin
= safe_signal(SIGTTIN
, collstop
);
458 if (sigsetjmp(collabort
, 1))
460 if (sigsetjmp(colljmp
, 1))
462 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
465 if ((collf
= Ftemp(&tempMail
, "Rs", "w+", 0600, 1)) == NULL
) {
466 perror(tr(51, "temporary mail file"));
472 if ((cp
= value("NAIL_HEAD")) != NULL
&& putesc(cp
, collf
) < 0)
476 * If we are going to prompt for a subject,
477 * refrain from printing a newline after
478 * the headers (since some people mind).
482 t
= GTO
|GSUBJECT
|GCC
|GNL
;
483 if (value("fullnames"))
485 if (hp
->h_subject
== NULL
&& (val
& val_INTERACT
) &&
486 (value("ask") != NULL
|| value("asksub") != NULL
))
487 t
&= ~GNL
, getfields
|= GSUBJECT
;
488 if (hp
->h_to
== NULL
&& (val
& val_INTERACT
))
489 t
&= ~GNL
, getfields
|= GTO
;
490 if (value("bsdcompat") == NULL
&& value("askatend") == NULL
&&
491 (val
& val_INTERACT
)) {
492 if (hp
->h_bcc
== NULL
&& value("askbcc"))
493 t
&= ~GNL
, getfields
|= GBCC
;
494 if (hp
->h_cc
== NULL
&& value("askcc"))
495 t
&= ~GNL
, getfields
|= GCC
;
498 (void)puthead(hp
, stdout
, t
, SEND_TODISP
, CONV_NONE
,
500 (void)fflush(stdout
);
505 * Quote an original message
507 if (mp
!= NULL
&& (doprefix
|| (quote
= value("quote")) != NULL
)) {
512 if ((cp
= value("fwdheading")) == NULL
)
513 cp
= "-------- Original Message --------";
514 if (*cp
&& fprintf(collf
, "%s\n", cp
) < 0)
516 } else if (strcmp(quote
, "noheading") == 0) {
518 } else if (strcmp(quote
, "headers") == 0) {
520 } else if (strcmp(quote
, "allheaders") == 0) {
522 action
= SEND_QUOTE_ALL
;
524 cp
= hfield1("from", mp
);
525 if (cp
!= NULL
&& (count
= (long)strlen(cp
)) > 0) {
526 if (mime_write(cp
, count
,
527 collf
, CONV_FROMHDR
, TD_NONE
,
531 if (fprintf(collf
, tr(52, " wrote:\n\n")) < 0)
537 cp
= value("indentprefix");
538 if (cp
!= NULL
&& *cp
== '\0')
540 if (send(mp
, collf
, quoteig
, (doprefix
? NULL
: cp
), action
,
545 /* Print what we have sofar also on the terminal */
547 while ((c
= getc(collf
)) != EOF
)
548 (void)putc(c
, stdout
);
549 if (fseek(collf
, 0, SEEK_END
))
552 escape
= ((cp
= value("escape")) != NULL
) ? *cp
: ESCAPE
;
556 if (! sigsetjmp(colljmp
, 1)) {
558 grabh(hp
, getfields
, 1);
559 if (quotefile
!= NULL
) {
560 if (include_file(NULL
, quotefile
, &lc
, &cc
, 1) != 0)
565 * Come here for printing the after-signal message.
566 * Duplicate messages won't be printed because
567 * the write is aborted if we get a SIGTTOU.
571 (void)fprintf(stderr
, tr(53,
572 "\n(Interrupt -- one more to kill letter)\n"));
574 printf(tr(54, "(continue)\n"));
575 (void)fflush(stdout
);
580 * No tilde escapes, interrupts not expected. Simply copy STDIN
582 if ((val
& val_INTERACT
) == 0 && tildeflag
<= 0 && ! tflag
) {
583 linebuf
= srealloc(linebuf
, linesize
= LINESIZE
);
584 while ((count
= fread(linebuf
, sizeof *linebuf
,
585 linesize
, stdin
)) > 0) {
586 if ((size_t)count
!= fwrite(linebuf
, sizeof *linebuf
,
596 * The interactive collect loop
600 count
= readline(stdin
, &linebuf
, &linesize
);
603 if ((val
& val_INTERACT
) &&
604 value("ignoreeof") != NULL
&& ++eofcount
< 25) {
606 "Use \".\" to terminate letter\n"));
611 if (tflag
&& count
== 0) {
613 if (makeheader(collf
, hp
) != OKAY
)
621 if (linebuf
[0] == '.' && linebuf
[1] == '\0' &&
622 (val
& val_INTERACT
) &&
623 (value("dot") != NULL
||
624 value("ignoreeof") != NULL
))
626 if (linebuf
[0] != escape
||
627 ((val
& val_INTERACT
) == 0 && tildeflag
== 0) ||
629 if (putline(collf
, linebuf
, count
) < 0)
638 * On double escape, just send the single one.
639 * Otherwise, it's an error.
642 if (putline(collf
, &linebuf
[1], count
- 1) < 0)
647 printf(tr(56, "Unknown tilde escape.\n"));
656 /* Shell escape, send the balance of line to sh -c */
661 /* Escape to command mode, but be nice! */
663 execute(&linebuf
[2], 1, count
- 2);
666 /* Simulate end of file on input */
669 /* Same as 'q', but no dead.letter saving */
676 * Force a quit of sending mail.
677 * Act like an interrupt happened.
684 /* Grab a bunch of headers */
686 grabh(hp
, GTO
|GSUBJECT
|GCC
|GBCC
,
687 value("bsdcompat") != NULL
&&
688 value("bsdorder") != NULL
);
689 while (hp
->h_to
== NULL
);
692 /* Grab extra headers */
694 grabh(hp
, GEXTRA
, 0);
695 while (check_from_and_sender(hp
->h_from
, hp
->h_sender
));
698 /* Add to the To list */
699 while ((hp
->h_to
= cat(hp
->h_to
, checkaddrs(
700 lextract(&linebuf
[2], GTO
|GFULL
))))
705 /* Set the Subject list */
707 while (whitechar(*cp
))
709 hp
->h_subject
= savestr(cp
);
712 /* Edit the attachment list */
713 if (linebuf
[2] != '\0')
714 hp
->h_attach
= append_attachments(hp
->h_attach
,
717 hp
->h_attach
= edit_attachments(hp
->h_attach
);
720 /* Add to the CC list */
721 hp
->h_cc
= cat(hp
->h_cc
, checkaddrs(
722 lextract(&linebuf
[2], GCC
|GFULL
)));
725 /* Add stuff to blind carbon copies list */
726 hp
->h_bcc
= cat(hp
->h_bcc
, checkaddrs(
727 lextract(&linebuf
[2], GBCC
|GFULL
)));
730 strncpy(linebuf
+ 2, getdeadletter(), linesize
- 2);
731 linebuf
[linesize
-1]='\0';
737 * Search for the file name,
738 * then open it and copy the contents to collf.
741 while (whitechar(*cp
))
744 printf(tr(57, "Interpolate what file?\n"));
748 insertcommand(collf
, cp
+ 1);
751 cp
= file_expand(cp
);
755 printf(tr(58, "%s: Directory\n"), cp
);
758 if ((fbuf
= Fopen(cp
, "r")) == NULL
) {
762 printf(tr(59, "\"%s\" "), cp
);
764 if (include_file(fbuf
, cp
, &lc
, &cc
, 0) != 0)
766 printf(tr(60, "%d/%d\n"), lc
, cc
);
769 /* Insert an environment variable into the file */
771 while (whitechar(*cp
))
773 if ((cp
= value(cp
)) == NULL
|| *cp
== '\0')
775 if (putesc(cp
, collf
) < 0)
777 if ((val
& val_INTERACT
) && putesc(cp
, stdout
) < 0)
782 /* Insert the contents of a signature variable */
783 if ((cp
= value(c
== 'a' ? "sign" : "Sign")) != NULL
&&
785 if (putesc(cp
, collf
) < 0)
787 if ((val
& val_INTERACT
) &&
788 putesc(cp
, stdout
) < 0)
793 /* Write the message on a file */
795 while (blankchar(*cp
))
797 if (*cp
== '\0' || (cp
= file_expand(cp
)) == NULL
) {
798 fprintf(stderr
, tr(61, "Write what file!?\n"));
802 if (exwrite(cp
, collf
, 1) < 0)
810 * Interpolate the named messages, if we
811 * are in receiving mail mode. Does the
812 * standard list processing garbage.
813 * If ~f is given, we don't shift over.
815 if (forward(linebuf
+ 2, collf
, c
) < 0)
820 * Print out the current state of the
821 * message without altering anything.
823 print_collf(collf
, hp
);
827 * Pipe message through command.
828 * Collect output as new message.
831 mespipe(&linebuf
[2]);
836 * Edit the current message.
837 * 'e' means to use EDITOR
838 * 'v' means to use VISUAL
841 mesedit(c
, value("editheaders") ? hp
: NULL
);
845 * Last the lengthy help string.
846 * (Very ugly, but take care for compiler supported
850 "-------------------- ~ ESCAPES ----------------------------\n"
851 "~~ Quote a single tilde\n"
852 "~@ [file ...] Edit attachment list\n"
853 "~b users Add users to \"blind\" cc list\n"
854 "~c users Add users to cc list\n"
855 "~d Read in dead.letter\n"
856 "~e Edit the message buffer\n"
857 "~f messages Read in messages without indenting lines\n"
858 "~F messages Same as ~f, but keep all header lines\n"
859 "~h Prompt for to list, subject, cc, and \"blind\" cc list\n");
861 "~r file Read a file into the message buffer\n"
862 "~p Print the message buffer\n"
863 "~q Abort message composition and save text to dead.letter\n"
864 "~m messages Read in messages with each line indented\n"
865 "~M messages Same as ~m, but keep all header lines\n"
866 "~s subject Set subject\n"
867 "~t users Add users to to list\n"
868 "~v Invoke display editor on message\n"
869 "~w file Write message onto file\n"
870 "~x Abort message composition and discard text written so far\n");
872 "~!command Invoke the shell\n"
873 "~:command Execute a regular command\n"
874 "-----------------------------------------------------------\n");
881 if ((cp
= value("NAIL_TAIL")) != NULL
) {
882 if (putesc(cp
, collf
) < 0)
884 if ((val
& val_INTERACT
) && putesc(cp
, stdout
) < 0)
892 sigaddset(&nset
, SIGINT
);
893 sigaddset(&nset
, SIGHUP
);
894 sigprocmask(SIG_BLOCK
, &nset
, (sigset_t
*)NULL
);
895 safe_signal(SIGINT
, saveint
);
896 safe_signal(SIGHUP
, savehup
);
897 safe_signal(SIGTSTP
, savetstp
);
898 safe_signal(SIGTTOU
, savettou
);
899 safe_signal(SIGTTIN
, savettin
);
900 sigprocmask(SIG_SETMASK
, &oset
, (sigset_t
*)NULL
);
903 if (tempMail
!= NULL
) {
915 * Write a file, ex-like if f set.
918 exwrite(char *name
, FILE *fp
, int f
)
926 printf("\"%s\" ", name
);
929 if ((of
= Fopen(name
, "a")) == NULL
) {
935 while ((c
= getc(fp
)) != EOF
) {
947 printf(catgets(catd
, CATSET
, 65, "%d/%ld\n"), lc
, cc
);
953 makeheader(FILE *fp
, struct header
*hp
)
959 if ((nf
= Ftemp(&tempEdit
, "Re", "w+", 0600, 1)) == NULL
) {
960 perror(catgets(catd
, CATSET
, 66, "temporary mail edit file"));
968 extract_header(fp
, hp
);
969 while ((c
= getc(fp
)) != EOF
)
975 if (check_from_and_sender(hp
->h_from
, hp
->h_sender
))
981 * Edit the message being collected on fp.
982 * On return, make the edit file the new temp file.
985 mesedit(int c
, struct header
*hp
)
987 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
988 FILE *nf
= run_editor(collf
, (off_t
)-1, c
, 0, hp
, NULL
, SEND_MBOX
,
996 fseek(nf
, 0L, SEEK_END
);
1001 safe_signal(SIGINT
, sigint
);
1005 * Pipe the message through the command.
1006 * Old message is on stdin of command;
1007 * New message collected from stdout.
1008 * Sh -c must return 0 to accept the new message.
1014 sighandler_type sigint
= safe_signal(SIGINT
, SIG_IGN
);
1018 if ((nf
= Ftemp(&tempEdit
, "Re", "w+", 0600, 1)) == NULL
) {
1019 perror(catgets(catd
, CATSET
, 66, "temporary mail edit file"));
1026 * stdin = current message.
1027 * stdout = new message.
1029 if ((shell
= value("SHELL")) == NULL
)
1031 if (run_command(shell
,
1032 0, fileno(collf
), fileno(nf
), "-c", cmd
, NULL
) < 0) {
1036 if (fsize(nf
) == 0) {
1037 fprintf(stderr
, catgets(catd
, CATSET
, 67,
1038 "No bytes from \"%s\" !?\n"), cmd
);
1045 fseek(nf
, 0L, SEEK_END
);
1049 safe_signal(SIGINT
, sigint
);
1053 * Interpolate the named messages into the current
1054 * message, preceding each line with a tab.
1055 * Return a count of the number of characters now in
1056 * the message, or -1 if an error is encountered writing
1057 * the message temporary. The flag argument is 'm' if we
1058 * should shift over and 'f' if not.
1061 forward(char *ms
, FILE *fp
, int f
)
1064 struct ignoretab
*ig
;
1066 enum sendaction action
;
1069 msgvec
= (int *)salloc((msgCount
+1) * sizeof *msgvec
);
1072 if (getmsglist(ms
, msgvec
, 0) < 0)
1075 *msgvec
= first(0, MMNORM
);
1077 printf(catgets(catd
, CATSET
, 68,
1078 "No appropriate messages\n"));
1083 if (f
== 'f' || f
== 'F')
1085 else if ((tabst
= value("indentprefix")) == NULL
)
1087 ig
= upperchar(f
) ? (struct ignoretab
*)NULL
: ignore
;
1088 action
= upperchar(f
) ? SEND_QUOTE_ALL
: SEND_QUOTE
;
1089 printf(catgets(catd
, CATSET
, 69, "Interpolating:"));
1090 for (; *msgvec
!= 0; msgvec
++) {
1091 struct message
*mp
= message
+ *msgvec
- 1;
1094 printf(" %d", *msgvec
);
1095 if (send(mp
, fp
, ig
, tabst
, action
, NULL
) < 0) {
1096 perror(catgets(catd
, CATSET
, 70,
1097 "temporary mail file"));
1106 * Print (continue) when continued after ^Z.
1112 sighandler_type old_action
= safe_signal(s
, SIG_DFL
);
1116 sigaddset(&nset
, s
);
1117 sigprocmask(SIG_UNBLOCK
, &nset
, (sigset_t
*)NULL
);
1119 sigprocmask(SIG_BLOCK
, &nset
, (sigset_t
*)NULL
);
1120 safe_signal(s
, old_action
);
1124 siglongjmp(colljmp
, 1);
1129 * On interrupt, come here to save the partial message in ~/dead.letter.
1130 * Then jump out of the collection loop.
1137 * the control flow is subtle, because we can be called from ~q.
1140 if (value("ignore") != NULL
) {
1147 siglongjmp(colljmp
, 1);
1151 if (value("save") != NULL
&& s
!= 0)
1152 savedeadletter(collf
);
1153 siglongjmp(collabort
, 1);
1162 savedeadletter(collf
);
1164 * Let's pretend nobody else wants to clean up,
1165 * a true statement at this time.
1171 savedeadletter(FILE *fp
)
1174 int c
, lines
= 0, bytes
= 0;
1179 cp
= getdeadletter();
1181 dbuf
= Fopen(cp
, "a");
1185 printf("\"%s\" ", cp
);
1186 while ((c
= getc(fp
)) != EOF
) {
1193 printf("%d/%d\n", lines
, bytes
);
1198 putesc(const char *s
, FILE *stream
)
1205 if (putc('\t', stream
) == EOF
)
1212 if (putc('\n', stream
) == EOF
)
1219 if (putc(s
[0], stream
) == EOF
)
1224 if (putc('\n', stream
) == EOF
)