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