]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/mail/send.c
This commit was generated by cvs2svn to compensate for changes in r76726,
[FreeBSD/FreeBSD.git] / usr.bin / mail / send.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)send.c      8.1 (Berkeley) 6/6/93";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41
42 #include "rcv.h"
43 #include "extern.h"
44
45 /*
46  * Mail -- a mail program
47  *
48  * Mail to others.
49  */
50
51 /*
52  * Send message described by the passed pointer to the
53  * passed output buffer.  Return -1 on error.
54  * Adjust the status: field if need be.
55  * If doign is given, suppress ignored header fields.
56  * prefix is a string to prepend to each output line.
57  */
58 int
59 sendmessage(mp, obuf, doign, prefix)
60         register struct message *mp;
61         FILE *obuf;
62         struct ignoretab *doign;
63         char *prefix;
64 {
65         long count;
66         register FILE *ibuf;
67         char line[LINESIZE];
68         int ishead, infld, ignoring, dostat, firstline;
69         register char *cp, *cp2;
70         register int c;
71         int length;
72         int prefixlen;
73
74         /*
75          * Compute the prefix string, without trailing whitespace
76          */
77         if (prefix != NOSTR) {
78                 cp2 = 0;
79                 for (cp = prefix; *cp; cp++)
80                         if (*cp != ' ' && *cp != '\t')
81                                 cp2 = cp;
82                 prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
83         }
84         ibuf = setinput(mp);
85         count = mp->m_size;
86         ishead = 1;
87         dostat = doign == 0 || !isign("status", doign);
88         infld = 0;
89         firstline = 1;
90         /*
91          * Process headers first
92          */
93         while (count > 0 && ishead) {
94                 if (fgets(line, sizeof(line), ibuf) == NULL)
95                         break;
96                 count -= length = strlen(line);
97                 if (firstline) {
98                         /*
99                          * First line is the From line, so no headers
100                          * there to worry about
101                          */
102                         firstline = 0;
103                         ignoring = doign == ignoreall;
104                 } else if (line[0] == '\n') {
105                         /*
106                          * If line is blank, we've reached end of
107                          * headers, so force out status: field
108                          * and note that we are no longer in header
109                          * fields
110                          */
111                         if (dostat) {
112                                 statusput(mp, obuf, prefix);
113                                 dostat = 0;
114                         }
115                         ishead = 0;
116                         ignoring = doign == ignoreall;
117                 } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
118                         /*
119                          * If this line is a continuation (via space or tab)
120                          * of a previous header field, just echo it
121                          * (unless the field should be ignored).
122                          * In other words, nothing to do.
123                          */
124                 } else {
125                         /*
126                          * Pick up the header field if we have one.
127                          */
128                         for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
129                                 ;
130                         cp2 = --cp;
131                         while (isspace(*cp++))
132                                 ;
133                         if (cp[-1] != ':') {
134                                 /*
135                                  * Not a header line, force out status:
136                                  * This happens in uucp style mail where
137                                  * there are no headers at all.
138                                  */
139                                 if (dostat) {
140                                         statusput(mp, obuf, prefix);
141                                         dostat = 0;
142                                 }
143                                 if (doign != ignoreall)
144                                         /* add blank line */
145                                         (void) putc('\n', obuf);
146                                 ishead = 0;
147                                 ignoring = 0;
148                         } else {
149                                 /*
150                                  * If it is an ignored field and
151                                  * we care about such things, skip it.
152                                  */
153                                 *cp2 = 0;       /* temporarily null terminate */
154                                 if (doign && isign(line, doign))
155                                         ignoring = 1;
156                                 else if ((line[0] == 's' || line[0] == 'S') &&
157                                          strcasecmp(line, "status") == 0) {
158                                         /*
159                                          * If the field is "status," go compute
160                                          * and print the real Status: field
161                                          */
162                                         if (dostat) {
163                                                 statusput(mp, obuf, prefix);
164                                                 dostat = 0;
165                                         }
166                                         ignoring = 1;
167                                 } else {
168                                         ignoring = 0;
169                                         *cp2 = c;       /* restore */
170                                 }
171                                 infld = 1;
172                         }
173                 }
174                 if (!ignoring) {
175                         /*
176                          * Strip trailing whitespace from prefix
177                          * if line is blank.
178                          */
179                         if (prefix != NOSTR) {
180                                 if (length > 1)
181                                         fputs(prefix, obuf);
182                                 else
183                                         (void) fwrite(prefix, sizeof *prefix,
184                                                         prefixlen, obuf);
185                         }
186                         (void) fwrite(line, sizeof *line, length, obuf);
187                         if (ferror(obuf))
188                                 return -1;
189                 }
190         }
191         /*
192          * Copy out message body
193          */
194         if (doign == ignoreall)
195                 count--;                /* skip final blank line */
196         if (prefix != NOSTR)
197                 while (count > 0) {
198                         if (fgets(line, sizeof(line), ibuf) == NULL) {
199                                 c = 0;
200                                 break;
201                         }
202                         count -= c = strlen(line);
203                         /*
204                          * Strip trailing whitespace from prefix
205                          * if line is blank.
206                          */
207                         if (c > 1)
208                                 fputs(prefix, obuf);
209                         else
210                                 (void) fwrite(prefix, sizeof *prefix,
211                                                 prefixlen, obuf);
212                         (void) fwrite(line, sizeof *line, c, obuf);
213                         if (ferror(obuf))
214                                 return -1;
215                 }
216         else
217                 while (count > 0) {
218                         c = count < LINESIZE ? count : LINESIZE;
219                         if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
220                                 break;
221                         count -= c;
222                         if (fwrite(line, sizeof *line, c, obuf) != c)
223                                 return -1;
224                 }
225         if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
226                 /* no final blank line */
227                 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
228                         return -1;
229         return 0;
230 }
231
232 /*
233  * Output a reasonable looking status field.
234  */
235 void
236 statusput(mp, obuf, prefix)
237         register struct message *mp;
238         FILE *obuf;
239         char *prefix;
240 {
241         char statout[3];
242         register char *cp = statout;
243
244         if (mp->m_flag & MREAD)
245                 *cp++ = 'R';
246         if ((mp->m_flag & MNEW) == 0)
247                 *cp++ = 'O';
248         *cp = 0;
249         if (statout[0])
250                 fprintf(obuf, "%sStatus: %s\n",
251                         prefix == NOSTR ? "" : prefix, statout);
252 }
253
254 /*
255  * Interface between the argument list and the mail1 routine
256  * which does all the dirty work.
257  */
258 int
259 mail(to, cc, bcc, smopts, subject, replyto)
260         struct name *to, *cc, *bcc, *smopts;
261         char *subject, *replyto;
262 {
263         struct header head;
264
265         head.h_to = to;
266         head.h_subject = subject;
267         head.h_cc = cc;
268         head.h_bcc = bcc;
269         head.h_smopts = smopts;
270         head.h_replyto = replyto;
271         head.h_inreplyto = NOSTR;
272         mail1(&head, 0);
273         return(0);
274 }
275
276
277 /*
278  * Send mail to a bunch of user names.  The interface is through
279  * the mail routine below.
280  */
281 int
282 sendmail(str)
283         char *str;
284 {
285         struct header head;
286
287         head.h_to = extract(str, GTO);
288         head.h_subject = NOSTR;
289         head.h_cc = NIL;
290         head.h_bcc = NIL;
291         head.h_smopts = NIL;
292         if ((head.h_replyto = getenv("REPLYTO")) == NULL)
293                 head.h_replyto = NOSTR;
294         head.h_inreplyto = NOSTR;
295         mail1(&head, 0);
296         return(0);
297 }
298
299 /*
300  * Mail a message on standard input to the people indicated
301  * in the passed header.  (Internal interface).
302  */
303 void
304 mail1(hp, printheaders)
305         struct header *hp;
306         int printheaders;
307 {
308         char *cp;
309         int pid;
310         char **namelist;
311         struct name *to;
312         FILE *mtf;
313
314         /*
315          * Collect user's mail from standard input.
316          * Get the result as mtf.
317          */
318         if ((mtf = collect(hp, printheaders)) == NULL)
319                 return;
320         if (value("interactive") != NOSTR) {
321                 if (value("askcc") != NOSTR)
322                         grabh(hp, GCC);
323                 else {
324                         printf("EOT\n");
325                         (void) fflush(stdout);
326                 }
327         }
328         if (fsize(mtf) == 0) {
329                 if (hp->h_subject == NOSTR)
330                         printf("No message, no subject; hope that's ok\n");
331                 else
332                         printf("Null message body; hope that's ok\n");
333         }
334         /*
335          * Now, take the user names from the combined
336          * to and cc lists and do all the alias
337          * processing.
338          */
339         senderr = 0;
340         to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
341         if (to == NIL) {
342                 printf("No recipients specified\n");
343                 senderr++;
344         }
345         /*
346          * Look through the recipient list for names with /'s
347          * in them which we write to as files directly.
348          */
349         to = outof(to, mtf, hp);
350         if (senderr)
351                 savedeadletter(mtf);
352         to = elide(to);
353         if (count(to) == 0)
354                 goto out;
355         fixhead(hp, to);
356         if ((mtf = infix(hp, mtf)) == NULL) {
357                 fprintf(stderr, ". . . message lost, sorry.\n");
358                 return;
359         }
360         namelist = unpack(cat(hp->h_smopts, to));
361         if (debug) {
362                 char **t;
363
364                 printf("Sendmail arguments:");
365                 for (t = namelist; *t != NOSTR; t++)
366                         printf(" \"%s\"", *t);
367                 printf("\n");
368                 goto out;
369         }
370         if ((cp = value("record")) != NOSTR)
371                 (void) savemail(expand(cp), mtf);
372         /*
373          * Fork, set up the temporary mail file as standard
374          * input for "mail", and exec with the user list we generated
375          * far above.
376          */
377         pid = fork();
378         if (pid == -1) {
379                 warn("fork");
380                 savedeadletter(mtf);
381                 goto out;
382         }
383         if (pid == 0) {
384                 prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
385                         sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
386                         fileno(mtf), -1);
387                 if ((cp = value("sendmail")) != NOSTR)
388                         cp = expand(cp);
389                 else
390                         cp = _PATH_SENDMAIL;
391                 execv(cp, namelist);
392                 warn("%s", cp);
393                 _exit(1);
394         }
395         if (value("verbose") != NOSTR)
396                 (void) wait_child(pid);
397         else
398                 free_child(pid);
399 out:
400         (void) Fclose(mtf);
401 }
402
403 /*
404  * Fix the header by glopping all of the expanded names from
405  * the distribution list into the appropriate fields.
406  */
407 void
408 fixhead(hp, tolist)
409         struct header *hp;
410         struct name *tolist;
411 {
412         register struct name *np;
413
414         hp->h_to = NIL;
415         hp->h_cc = NIL;
416         hp->h_bcc = NIL;
417         for (np = tolist; np != NIL; np = np->n_flink)
418                 if ((np->n_type & GMASK) == GTO)
419                         hp->h_to =
420                                 cat(hp->h_to, nalloc(np->n_name, np->n_type));
421                 else if ((np->n_type & GMASK) == GCC)
422                         hp->h_cc =
423                                 cat(hp->h_cc, nalloc(np->n_name, np->n_type));
424                 else if ((np->n_type & GMASK) == GBCC)
425                         hp->h_bcc =
426                                 cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
427 }
428
429 /*
430  * Prepend a header in front of the collected stuff
431  * and return the new file.
432  */
433 FILE *
434 infix(hp, fi)
435         struct header *hp;
436         FILE *fi;
437 {
438         register FILE *nfo, *nfi;
439         register int c;
440         int fd;
441         char tempname[PATHSIZE];
442
443         snprintf(tempname, sizeof(tempname), "%s/mail.RsXXXXXXXXXX", tmpdir);
444         if ((fd = mkstemp(tempname)) == -1 ||
445             (nfo = Fdopen(fd, "w")) == NULL) {
446                 warn("%s", tempname);
447                 return(fi);
448         }
449         if ((nfi = Fopen(tempname, "r")) == NULL) {
450                 warn("%s", tempname);
451                 (void) Fclose(nfo);
452                 (void) rm(tempname);
453                 return(fi);
454         }
455         (void) rm(tempname);
456         (void) puthead(hp, nfo,
457                        GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA);
458         c = getc(fi);
459         while (c != EOF) {
460                 (void) putc(c, nfo);
461                 c = getc(fi);
462         }
463         if (ferror(fi)) {
464                 warnx("read");
465                 rewind(fi);
466                 return(fi);
467         }
468         (void) fflush(nfo);
469         if (ferror(nfo)) {
470                 warn("%s", tempname);
471                 (void) Fclose(nfo);
472                 (void) Fclose(nfi);
473                 rewind(fi);
474                 return(fi);
475         }
476         (void) Fclose(nfo);
477         (void) Fclose(fi);
478         rewind(nfi);
479         return(nfi);
480 }
481
482 /*
483  * Dump the to, subject, cc header on the
484  * passed file buffer.
485  */
486 int
487 puthead(hp, fo, w)
488         struct header *hp;
489         FILE *fo;
490         int w;
491 {
492         register int gotcha;
493
494         gotcha = 0;
495         if (hp->h_to != NIL && w & GTO)
496                 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
497         if (hp->h_subject != NOSTR && w & GSUBJECT)
498                 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
499         if (hp->h_cc != NIL && w & GCC)
500                 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
501         if (hp->h_bcc != NIL && w & GBCC)
502                 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
503         if (hp->h_replyto != NOSTR && w & GREPLYTO)
504                 fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++;
505         if (hp->h_inreplyto != NOSTR && w & GINREPLYTO)
506                 fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++;
507         if (gotcha && w & GNL)
508                 (void) putc('\n', fo);
509         return(0);
510 }
511
512 /*
513  * Format the given header line to not exceed 72 characters.
514  */
515 void
516 fmt(str, np, fo, comma)
517         char *str;
518         register struct name *np;
519         FILE *fo;
520         int comma;
521 {
522         register col, len;
523
524         comma = comma ? 1 : 0;
525         col = strlen(str);
526         if (col)
527                 fputs(str, fo);
528         for (; np != NIL; np = np->n_flink) {
529                 if (np->n_flink == NIL)
530                         comma = 0;
531                 len = strlen(np->n_name);
532                 col++;          /* for the space */
533                 if (col + len + comma > 72 && col > 4) {
534                         fputs("\n    ", fo);
535                         col = 4;
536                 } else
537                         putc(' ', fo);
538                 fputs(np->n_name, fo);
539                 if (comma)
540                         putc(',', fo);
541                 col += len + comma;
542         }
543         putc('\n', fo);
544 }
545
546 /*
547  * Save the outgoing mail on the passed file.
548  */
549
550 /*ARGSUSED*/
551 int
552 savemail(name, fi)
553         char name[];
554         register FILE *fi;
555 {
556         register FILE *fo;
557         char buf[BUFSIZ];
558         register i;
559         time_t now, time();
560         char *ctime();
561
562         if ((fo = Fopen(name, "a")) == NULL) {
563                 warn("%s", name);
564                 return (-1);
565         }
566         (void) time(&now);
567         fprintf(fo, "From %s %s", myname, ctime(&now));
568         while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
569                 (void) fwrite(buf, 1, i, fo);
570         (void) putc('\n', fo);
571         (void) fflush(fo);
572         if (ferror(fo))
573                 warn("%s", name);
574         (void) Fclose(fo);
575         rewind(fi);
576         return (0);
577 }