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