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