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