send.c revision 1.17 1 /* $NetBSD: send.c,v 1.17 2002/03/04 03:16:10 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.17 2002/03/04 03:16:10 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 *tempMail;
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 perror("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 perror(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;
441
442 if ((nfo = Fopen(tempMail, "w")) == NULL) {
443 perror(tempMail);
444 return(fi);
445 }
446 if ((nfi = Fopen(tempMail, "r")) == NULL) {
447 perror(tempMail);
448 (void) Fclose(nfo);
449 return(fi);
450 }
451 (void) rm(tempMail);
452 (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
453 c = getc(fi);
454 while (c != EOF) {
455 (void) putc(c, nfo);
456 c = getc(fi);
457 }
458 if (ferror(fi)) {
459 perror("read");
460 rewind(fi);
461 return(fi);
462 }
463 (void) fflush(nfo);
464 if (ferror(nfo)) {
465 perror(tempMail);
466 (void) Fclose(nfo);
467 (void) Fclose(nfi);
468 rewind(fi);
469 return(fi);
470 }
471 (void) Fclose(nfo);
472 (void) Fclose(fi);
473 rewind(nfi);
474 return(nfi);
475 }
476
477 /*
478 * Dump the to, subject, cc header on the
479 * passed file buffer.
480 */
481 int
482 puthead(struct header *hp, FILE *fo, int w)
483 {
484 int gotcha;
485
486 gotcha = 0;
487 if (hp->h_to != NULL && w & GTO)
488 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
489 if (hp->h_subject != NULL && w & GSUBJECT)
490 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
491 if (hp->h_cc != NULL && w & GCC)
492 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
493 if (hp->h_bcc != NULL && w & GBCC)
494 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
495 if (gotcha && w & GNL)
496 (void) putc('\n', fo);
497 return(0);
498 }
499
500 /*
501 * Format the given header line to not exceed 72 characters.
502 */
503 void
504 fmt(char *str, struct name *np, FILE *fo, int comma)
505 {
506 int col, len;
507
508 comma = comma ? 1 : 0;
509 col = strlen(str);
510 if (col)
511 fputs(str, fo);
512 for (; np != NULL; np = np->n_flink) {
513 if (np->n_flink == NULL)
514 comma = 0;
515 len = strlen(np->n_name);
516 col++; /* for the space */
517 if (col + len + comma > 72 && col > 4) {
518 fputs("\n ", fo);
519 col = 4;
520 } else
521 putc(' ', fo);
522 fputs(np->n_name, fo);
523 if (comma)
524 putc(',', fo);
525 col += len + comma;
526 }
527 putc('\n', fo);
528 }
529
530 /*
531 * Save the outgoing mail on the passed file.
532 */
533
534 /*ARGSUSED*/
535 int
536 savemail(char name[], FILE *fi)
537 {
538 FILE *fo;
539 char buf[BUFSIZ];
540 int i;
541 time_t now;
542
543 if ((fo = Fopen(name, "a")) == NULL) {
544 perror(name);
545 return (-1);
546 }
547 (void) time(&now);
548 fprintf(fo, "From %s %s", myname, ctime(&now));
549 while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
550 (void) fwrite(buf, 1, i, fo);
551 (void) putc('\n', fo);
552 (void) fflush(fo);
553 if (ferror(fo))
554 perror(name);
555 (void) Fclose(fo);
556 rewind(fi);
557 return (0);
558 }
559