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