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