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