1 /* $NetBSD: collect.c,v 1.42 2007/10/29 23:20:38 christos Exp $ */
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94";
37 __RCSID("$NetBSD: collect.c,v 1.42 2007/10/29 23:20:38 christos Exp $");
42 * Mail -- a mail program
44 * Collect input from standard input, handling
62 * Read a message from standard input and return a read file to it
67 * The following hokiness with global variables is so that on
68 * receipt of an interrupt signal, the partial message can be salted
69 * away on dead.letter.
71 static FILE *collf
; /* File for saving away */
72 static int hadintr
; /* Have seen one SIGINT so far */
74 static jmp_buf abort_jmpbuf
; /* To end collection with error */
75 static jmp_buf reset_jmpbuf
; /* To get back to work */
76 static int reset_on_stop
; /* To do job control longjmp. */
79 * Write a file, ex-like if f set.
82 exwrite(const char name
[], FILE *fp
, int f
)
91 (void)printf("\"%s\" ", name
);
94 if (stat(name
, &junk
) >= 0 && S_ISREG(junk
.st_mode
)) {
96 (void)fprintf(stderr
, "%s: ", name
);
97 (void)fprintf(stderr
, "File exists\n");
100 if ((of
= Fopen(name
, "w")) == NULL
) {
106 while ((c
= getc(fp
)) != EOF
) {
118 (void)printf("%d/%ld\n", lc
, cc
);
119 (void)fflush(stdout
);
124 * Edit the message being collected on fp.
125 * On return, make the edit file the new temp file.
128 mesedit(FILE *fp
, int c
)
130 struct sigaction osa
;
135 (void)sig_ignore(SIGINT
, &osa
, &oset
);
136 nf
= run_editor(fp
, (off_t
)-1, c
, 0);
138 (void)fseek(nf
, 0L, 2);
142 (void)sig_restore(SIGINT
, &osa
, &oset
);
147 * Pipe the message through the command.
148 * Old message is on stdin of command;
149 * New message collected from stdout.
150 * Sh -c must return 0 to accept the new message.
153 mespipe(FILE *fp
, char cmd
[])
156 struct sigaction osa
;
158 const char *shellcmd
;
160 char tempname
[PATHSIZE
];
163 (void)sig_ignore(SIGINT
, &osa
, &oset
);
165 (void)snprintf(tempname
, sizeof(tempname
),
166 "%s/mail.ReXXXXXXXXXX", tmpdir
);
167 if ((fd
= mkstemp(tempname
)) == -1 ||
168 (nf
= Fdopen(fd
, "w+")) == NULL
) {
171 warn("%s", tempname
);
174 (void)unlink(tempname
);
176 * stdin = current message.
177 * stdout = new message.
179 if ((shellcmd
= value(ENAME_SHELL
)) == NULL
)
180 shellcmd
= _PATH_CSHELL
;
181 if (run_command(shellcmd
,
182 NULL
, fileno(fp
), fileno(nf
), "-c", cmd
, NULL
) < 0) {
186 if (fsize(nf
) == 0) {
187 (void)fprintf(stderr
, "No bytes from \"%s\" !?\n", cmd
);
194 (void)fseek(nf
, 0L, 2);
198 (void)sig_restore(SIGINT
, &osa
, &oset
);
203 * Interpolate the named messages into the current
204 * message, preceding each line with a tab.
205 * Return a count of the number of characters now in
206 * the message, or -1 if an error is encountered writing
207 * the message temporary. The flag argument is 'm' if we
208 * should shift over and 'f' if not.
211 interpolate(char ms
[], FILE *fp
, char *fn
, int f
)
214 struct ignoretab
*ig
;
217 struct mime_info
*mip
;
220 msgvec
= salloc((get_msgCount() + 1) * sizeof(*msgvec
));
223 if (getmsglist(ms
, msgvec
, 0) < 0)
226 *msgvec
= first(0, MMNORM
);
228 (void)printf("No appropriate messages\n");
233 if (f
== 'f' || f
== 'F')
235 else if ((tabst
= value(ENAME_INDENTPREFIX
)) == NULL
)
237 ig
= isupper(f
) ? NULL
: ignore
;
238 (void)printf("Interpolating:");
239 for (/*EMPTY*/; *msgvec
!= 0; msgvec
++) {
243 mp
= get_message(*msgvec
);
245 (void)printf(" %d", *msgvec
);
246 (void)fflush(stdout
); /* flush stdout and the above */
248 if (tabst
&& (fmtstr
= value(ENAME_INDENT_PREAMBLE
)) != NULL
)
249 fmsgprintf(collf
, fmtstr
, mp
);
252 if (value(ENAME_MIME_DECODE_MSG
)) {
253 if ((tabst
== NULL
&& value(ENAME_MIME_DECODE_INSERT
)) ||
254 (tabst
!= NULL
&& value(ENAME_MIME_DECODE_QUOTE
)))
255 mip
= mime_decode_open(mp
);
257 retval
= mime_sendmessage(mp
, fp
, ig
, tabst
, mip
);
258 mime_decode_close(mip
);
261 if (sendmessage(mp
, fp
, ig
, tabst
, NULL
) < 0)
267 if (tabst
&& (fmtstr
= value(ENAME_INDENT_POSTSCRIPT
)) != NULL
)
268 fmsgprintf(collf
, fmtstr
, mp
);
275 * Append the contents of the file to the end of the deadletter file.
278 savedeadletter(FILE *fp
)
287 cp
= getdeadletter();
289 dbuf
= Fopen(cp
, "a");
293 (void)printf("Saving message body to `%s'.\n", cp
);
294 while ((c
= getc(fp
)) != EOF
)
301 * On interrupt, come here to save the partial message in ~/dead.letter.
302 * Then jump out of the collection loop.
309 * the control flow is subtle, because we can be called from ~q.
312 if (value(ENAME_IGNORE
) != NULL
) {
314 (void)fflush(stdout
);
319 longjmp(reset_jmpbuf
, signo
);
322 if (value(ENAME_NOSAVE
) == NULL
)
323 savedeadletter(collf
);
324 longjmp(abort_jmpbuf
, signo
);
329 coll_hup(int signo __unused
)
333 savedeadletter(collf
);
335 * Let's pretend nobody else wants to clean up,
336 * a true statement at this time.
342 * Print (continue) when continued after ^Z.
351 longjmp(reset_jmpbuf
, signo
);
356 collect(struct header
*hp
, int printheaders
)
358 volatile sig_t old_sigint
;
359 volatile sig_t old_sighup
;
360 volatile sig_t old_sigtstp
;
361 volatile sig_t old_sigttin
;
362 volatile sig_t old_sigttou
;
366 char linebuf
[LINESIZE
];
368 char tempname
[PATHSIZE
];
369 char mailtempname
[PATHSIZE
];
372 int lastlong
, rc
; /* So we don't make 2 or more lines
373 out of a long input line. */
375 /* The following are declared volatile to avoid longjmp clobbering. */
376 char volatile getsub
;
379 (void)memset(mailtempname
, 0, sizeof(mailtempname
));
382 if (setjmp(abort_jmpbuf
) || setjmp(reset_jmpbuf
)) {
383 (void)rm(mailtempname
);
389 old_sigint
= sig_signal(SIGINT
, coll_int
);
390 old_sighup
= sig_signal(SIGHUP
, coll_hup
);
391 old_sigtstp
= sig_signal(SIGTSTP
, coll_stop
);
392 old_sigttin
= sig_signal(SIGTTIN
, coll_stop
);
393 old_sigttou
= sig_signal(SIGTTOU
, coll_stop
);
397 (void)snprintf(mailtempname
, sizeof(mailtempname
),
398 "%s/mail.RsXXXXXXXXXX", tmpdir
);
399 if ((fd
= mkstemp(mailtempname
)) == -1 ||
400 (collf
= Fdopen(fd
, "w+")) == NULL
) {
403 warn("%s", mailtempname
);
406 (void)rm(mailtempname
);
409 * If we are going to prompt for a subject,
410 * refrain from printing a newline after
411 * the headers (since some people mind).
413 t
= GTO
| GSUBJECT
| GCC
| GNL
| GSMOPTS
;
415 if (hp
->h_subject
== NULL
&& value(ENAME_INTERACTIVE
) != NULL
&&
416 (value(ENAME_ASK
) != NULL
|| value(ENAME_ASKSUB
) != NULL
)) {
421 (void)puthead(hp
, stdout
, t
);
422 (void)fflush(stdout
);
424 if ((cp
= value(ENAME_ESCAPE
)) != NULL
)
429 if (setjmp(reset_jmpbuf
) == 0) {
431 (void)grabh(hp
, GSUBJECT
);
434 * Come here for printing the after-signal message.
435 * Duplicate messages won't be printed because
436 * the write is aborted if we get a SIGTTOU.
440 (void)fflush(stdout
);
441 (void)fprintf(stderr
,
442 "\n(Interrupt -- one more to kill letter)\n");
444 (void)printf("(continue)\n");
445 (void)fflush(stdout
);
448 eofcount
= 0; /* reset after possible longjmp */
449 longline
= 0; /* reset after possible longjmp */
452 c
= readline(stdin
, linebuf
, LINESIZE
, reset_on_stop
);
458 if (value(ENAME_INTERACTIVE
) != NULL
&&
459 (p
= value(ENAME_IGNOREEOF
)) != NULL
&&
460 ++eofcount
< (*p
== 0 ? 25 : atoi(p
))) {
461 (void)printf("Use \".\" to terminate letter\n");
467 longline
= c
== LINESIZE
- 1;
470 if (linebuf
[0] == '.' && linebuf
[1] == '\0' &&
471 value(ENAME_INTERACTIVE
) != NULL
&& !lastlong
&&
472 (value(ENAME_DOT
) != NULL
|| value(ENAME_IGNOREEOF
) != NULL
))
474 if (linebuf
[0] != escape
|| value(ENAME_INTERACTIVE
) == NULL
||
476 if (putline(collf
, linebuf
, !longline
) < 0)
484 * On double escape, just send the single one.
485 * Otherwise, it's an error.
488 if (putline(collf
, &linebuf
[1], !longline
) < 0)
493 (void)printf("Unknown tilde escape.\n");
497 hp
->h_attach
= mime_attach_files(hp
->h_attach
, &linebuf
[2]);
508 * Shell escape, send the balance of the
511 (void)shell(&linebuf
[2]);
516 * Escape to command mode, but be nice!
518 (void)execute(&linebuf
[2], ec_composing
);
522 * Simulate end of file on input.
527 * Force a quit of sending mail.
528 * Act like an interrupt happened.
535 case 'x': /* exit, do not save in dead.letter */
540 * Grab a bunch of headers.
542 (void)grabh(hp
, GTO
| GSUBJECT
| GCC
| GBCC
| GSMOPTS
);
546 * Add to the To list.
548 hp
->h_to
= cat(hp
->h_to
, extract(&linebuf
[2], GTO
));
552 * Set the Subject list.
554 cp
= skip_WSP(&linebuf
[2]);
555 hp
->h_subject
= savestr(cp
);
559 * Add to the CC list.
561 hp
->h_cc
= cat(hp
->h_cc
, extract(&linebuf
[2], GCC
));
565 * Add stuff to blind carbon copies list.
567 hp
->h_bcc
= cat(hp
->h_bcc
, extract(&linebuf
[2], GBCC
));
573 * Insert named variable in message
578 cp
= skip_WSP(&linebuf
[2]);
590 if (*cp
&& (cp
= value(cp
)) != NULL
) {
591 (void)printf("%s\n", cp
);
592 if (putline(collf
, cp
, 1) < 0)
599 (void)strcpy(linebuf
+ 2, getdeadletter());
605 * Search for the file name,
606 * then open it and copy the contents to collf.
608 cp
= skip_WSP(&linebuf
[2]);
610 (void)printf("Interpolate what file?\n");
618 if (*cp
== '!') { /* insert stdout of command */
619 const char *shellcmd
;
623 if ((nullfd
= open("/dev/null", O_RDONLY
, 0)) == -1) {
628 (void)snprintf(tempname
, sizeof(tempname
),
629 "%s/mail.ReXXXXXXXXXX", tmpdir
);
630 if ((fd
= mkstemp(tempname
)) == -1 ||
631 (fbuf
= Fdopen(fd
, "w+")) == NULL
) {
634 warn("%s", tempname
);
637 (void)unlink(tempname
);
639 if ((shellcmd
= value(ENAME_SHELL
)) == NULL
)
640 shellcmd
= _PATH_CSHELL
;
642 rc2
= run_command(shellcmd
, NULL
, nullfd
, fileno(fbuf
), "-c", cp
+ 1, NULL
);
651 if (fsize(fbuf
) == 0) {
652 (void)fprintf(stderr
, "No bytes from command \"%s\"\n", cp
+ 1);
659 else if (isdir(cp
)) {
660 (void)printf("%s: Directory\n", cp
);
663 else if ((fbuf
= Fopen(cp
, "r")) == NULL
) {
667 (void)printf("\"%s\" ", cp
);
668 (void)fflush(stdout
);
671 while ((rc
= readline(fbuf
, linebuf
, LINESIZE
, 0)) >= 0) {
672 if (rc
!= LINESIZE
-1) lc
++;
673 if ((t
= putline(collf
, linebuf
,
674 rc
!= LINESIZE
-1)) < 0) {
681 (void)printf("%d/%d\n", lc
, cc
);
685 * Write the message on a file.
687 cp
= skip_WSP(&linebuf
[2]);
689 (void)fprintf(stderr
, "Write what file!?\n");
692 if ((cp
= expand(cp
)) == NULL
)
695 (void)exwrite(cp
, collf
, 1);
702 * Interpolate the named messages, if we
703 * are in receiving mail mode. Does the
704 * standard list processing garbage.
705 * If ~f is given, we don't shift over.
707 if (interpolate(linebuf
+ 2, collf
, mailtempname
, c
) < 0)
711 cathelp(_PATH_TILDE
);
715 * Print out the current state of the
716 * message without altering anything.
719 (void)printf("-------\nMessage contains:\n");
720 (void)puthead(hp
, stdout
,
721 GTO
| GSUBJECT
| GCC
| GBCC
| GSMOPTS
| GNL
);
722 while ((t
= getc(collf
)) != EOF
)
727 * Pipe message through command.
728 * Collect output as new message.
731 mespipe(collf
, &linebuf
[2]);
736 * Edit the current message.
737 * 'e' means to use EDITOR
738 * 'v' means to use VISUAL
757 (void)sig_signal(SIGINT
, old_sigint
);
758 (void)sig_signal(SIGHUP
, old_sighup
);
759 (void)sig_signal(SIGTSTP
, old_sigtstp
);
760 (void)sig_signal(SIGTTIN
, old_sigttin
);
761 (void)sig_signal(SIGTTOU
, old_sigttou
);