Commit | Line | Data |
---|---|---|
761330fe DF |
1 | /* |
2 | * Copyright (c) 1980 Regents of the University of California. | |
0c5f72fb KB |
3 | * All rights reserved. |
4 | * | |
5 | * Redistribution and use in source and binary forms are permitted | |
6 | * provided that this notice is preserved and that due credit is given | |
7 | * to the University of California at Berkeley. The name of the University | |
8 | * may not be used to endorse or promote products derived from this | |
9 | * software without specific prior written permission. This software | |
10 | * is provided ``as is'' without express or implied warranty. | |
761330fe DF |
11 | */ |
12 | ||
0c5f72fb | 13 | #ifdef notdef |
686f6134 | 14 | static char sccsid[] = "@(#)send.c 5.9 (Berkeley) %G%"; |
0c5f72fb | 15 | #endif /* notdef */ |
3184019b KS |
16 | |
17 | #include "rcv.h" | |
97917bd0 | 18 | #include <sys/wait.h> |
a1f76dd3 | 19 | #include <sys/stat.h> |
3184019b KS |
20 | |
21 | /* | |
22 | * Mail -- a mail program | |
23 | * | |
24 | * Mail to others. | |
25 | */ | |
26 | ||
3184019b KS |
27 | /* |
28 | * Send message described by the passed pointer to the | |
29 | * passed output buffer. Return -1 on error, but normally | |
57079810 | 30 | * the number of lines written. Adjust the status: field |
887efe38 | 31 | * if need be. If doign is given, suppress ignored header fields. |
3184019b | 32 | */ |
828615a1 EW |
33 | send(mp, obuf, doign) |
34 | register struct message *mp; | |
3184019b | 35 | FILE *obuf; |
887efe38 | 36 | struct ignoretab *doign; |
3184019b | 37 | { |
828615a1 EW |
38 | long count; |
39 | register FILE *ibuf; | |
40 | char line[LINESIZE]; | |
41 | int lc, ishead, infld, ignoring, dostat; | |
42 | register char *cp, *cp2; | |
43 | register int c; | |
44 | int length; | |
45 | ||
3184019b | 46 | ibuf = setinput(mp); |
828615a1 | 47 | count = mp->m_size; |
b870137d | 48 | ishead = 1; |
887efe38 | 49 | dostat = doign == 0 || !isign("status", doign); |
a6b3a6c5 | 50 | infld = 0; |
3184019b | 51 | lc = 0; |
828615a1 EW |
52 | /* |
53 | * Process headers first | |
54 | */ | |
55 | while (count > 0 && ishead) { | |
56 | if (fgets(line, LINESIZE, ibuf) == NULL) | |
57 | break; | |
58 | count -= length = strlen(line); | |
59 | if (lc == 0) { | |
b870137d KS |
60 | /* |
61 | * First line is the From line, so no headers | |
62 | * there to worry about | |
63 | */ | |
828615a1 EW |
64 | ignoring = 0; |
65 | } else if (line[0] == '\n') { | |
b870137d KS |
66 | /* |
67 | * If line is blank, we've reached end of | |
68 | * headers, so force out status: field | |
69 | * and note that we are no longer in header | |
70 | * fields | |
71 | */ | |
828615a1 EW |
72 | if (dostat) { |
73 | statusput(mp, obuf); | |
74 | dostat = 0; | |
57079810 | 75 | } |
828615a1 EW |
76 | ishead = 0; |
77 | ignoring = 0; | |
78 | } else if (infld && (line[0] == ' ' || line[0] == '\t')) { | |
b870137d | 79 | /* |
df5134c8 BB |
80 | * If this line is a continuation (via space or tab) |
81 | * of a previous header field, just echo it | |
82 | * (unless the field should be ignored). | |
828615a1 | 83 | * In other words, nothing to do. |
b870137d | 84 | */ |
828615a1 | 85 | } else { |
b870137d | 86 | /* |
828615a1 | 87 | * Pick up the header field if we have one. |
b870137d | 88 | */ |
828615a1 EW |
89 | for (cp = line; (c = *cp++) && c != ':' && !isspace(c);) |
90 | ; | |
91 | cp2 = --cp; | |
92 | while (isspace(*cp++)) | |
93 | ; | |
94 | if (cp[-1] != ':') { | |
95 | /* | |
96 | * Not a header line, force out status: | |
97 | * This happens in uucp style mail where | |
98 | * there are no headers at all. | |
99 | */ | |
b870137d | 100 | if (dostat) { |
828615a1 | 101 | statusput(mp, obuf); |
b870137d KS |
102 | dostat = 0; |
103 | } | |
751b30cd | 104 | (void) putc('\n', obuf); /* add blank line */ |
828615a1 | 105 | lc++; |
a6b3a6c5 | 106 | ishead = 0; |
828615a1 EW |
107 | ignoring = 0; |
108 | } else { | |
109 | /* | |
110 | * If it is an ignored field and | |
111 | * we care about such things, skip it. | |
112 | */ | |
113 | *cp2 = 0; /* temporarily null terminate */ | |
887efe38 | 114 | if (doign && isign(line, doign)) |
828615a1 EW |
115 | ignoring = 1; |
116 | else if ((line[0] == 's' || line[0] == 'S') && | |
117 | icequal(line, "status")) { | |
118 | /* | |
119 | * If the field is "status," go compute | |
120 | * and print the real Status: field | |
121 | */ | |
122 | if (dostat) { | |
123 | statusput(mp, obuf); | |
124 | dostat = 0; | |
125 | } | |
126 | ignoring = 1; | |
127 | } else { | |
128 | ignoring = 0; | |
129 | *cp2 = c; /* restore */ | |
2d7a1b5d | 130 | } |
828615a1 | 131 | infld = 1; |
2d7a1b5d | 132 | } |
57079810 | 133 | } |
828615a1 | 134 | if (!ignoring) { |
751b30cd | 135 | (void) fwrite(line, sizeof *line, length, obuf); |
828615a1 EW |
136 | if (ferror(obuf)) |
137 | return -1; | |
138 | lc++; | |
139 | } | |
3184019b | 140 | } |
828615a1 EW |
141 | /* |
142 | * Copy out message body | |
143 | */ | |
144 | while (count > 0) { | |
145 | cp = line; | |
146 | c = count < LINESIZE ? count : LINESIZE; | |
147 | if ((c = fread(cp, sizeof *cp, c, ibuf)) <= 0) | |
148 | break; | |
149 | if (fwrite(cp, sizeof *cp, c, obuf) != c) | |
150 | return -1; | |
151 | count -= c; | |
152 | while (--c >= 0) | |
153 | if (*cp++ == '\n') | |
154 | lc++; | |
155 | } | |
156 | if (ishead && (mp->m_flag & MSTATUS)) | |
57079810 | 157 | printf("failed to fix up status field\n"); |
828615a1 | 158 | return (lc); |
a6b3a6c5 KS |
159 | } |
160 | ||
57079810 KS |
161 | /* |
162 | * Output a reasonable looking status field. | |
b870137d | 163 | * But if "status" is ignored and doign, forget it. |
57079810 | 164 | */ |
828615a1 | 165 | statusput(mp, obuf) |
57079810 | 166 | register struct message *mp; |
828615a1 | 167 | FILE *obuf; |
57079810 KS |
168 | { |
169 | char statout[3]; | |
828615a1 | 170 | register char *cp = statout; |
57079810 | 171 | |
57079810 | 172 | if (mp->m_flag & MREAD) |
828615a1 | 173 | *cp++ = 'R'; |
57079810 | 174 | if ((mp->m_flag & MNEW) == 0) |
828615a1 EW |
175 | *cp++ = 'O'; |
176 | *cp = 0; | |
177 | if (statout[0]) | |
178 | fprintf(obuf, "Status: %s\n", statout); | |
57079810 KS |
179 | } |
180 | ||
3184019b KS |
181 | /* |
182 | * Interface between the argument list and the mail1 routine | |
183 | * which does all the dirty work. | |
184 | */ | |
185 | ||
790344bc EW |
186 | mail(to, cc, bcc, smopts) |
187 | struct name *to, *cc, *bcc, *smopts; | |
3184019b | 188 | { |
3184019b KS |
189 | struct header head; |
190 | ||
790344bc | 191 | head.h_to = detract(to, 0); |
3184019b | 192 | head.h_subject = NOSTR; |
790344bc EW |
193 | head.h_cc = detract(cc, 0); |
194 | head.h_bcc = detract(bcc, 0); | |
195 | head.h_smopts = detract(smopts, 0); | |
3184019b | 196 | head.h_seq = 0; |
751b30cd | 197 | (void) mail1(&head); |
3184019b KS |
198 | return(0); |
199 | } | |
200 | ||
201 | ||
202 | /* | |
203 | * Send mail to a bunch of user names. The interface is through | |
204 | * the mail routine below. | |
205 | */ | |
206 | ||
207 | sendmail(str) | |
208 | char *str; | |
209 | { | |
3184019b KS |
210 | struct header head; |
211 | ||
212 | if (blankline(str)) | |
213 | head.h_to = NOSTR; | |
214 | else | |
215 | head.h_to = str; | |
216 | head.h_subject = NOSTR; | |
217 | head.h_cc = NOSTR; | |
218 | head.h_bcc = NOSTR; | |
790344bc | 219 | head.h_smopts = NOSTR; |
3184019b | 220 | head.h_seq = 0; |
751b30cd | 221 | (void) mail1(&head); |
3184019b KS |
222 | return(0); |
223 | } | |
224 | ||
225 | /* | |
226 | * Mail a message on standard input to the people indicated | |
227 | * in the passed header. (Internal interface). | |
228 | */ | |
229 | ||
230 | mail1(hp) | |
231 | struct header *hp; | |
232 | { | |
233 | register char *cp; | |
828615a1 EW |
234 | int pid, i, p, gotcha; |
235 | union wait s; | |
6fa89d43 | 236 | char **namelist, *deliver; |
3184019b | 237 | struct name *to, *np; |
a1f76dd3 | 238 | struct stat sbuf; |
3184019b KS |
239 | FILE *mtf, *postage; |
240 | int remote = rflag != NOSTR || rmail; | |
241 | char **t; | |
242 | ||
243 | /* | |
244 | * Collect user's mail from standard input. | |
245 | * Get the result as mtf. | |
246 | */ | |
247 | ||
248 | pid = -1; | |
790344bc EW |
249 | if (hp->h_subject == NOSTR) |
250 | hp->h_subject = sflag; | |
3184019b KS |
251 | if ((mtf = collect(hp)) == NULL) |
252 | return(-1); | |
253 | hp->h_seq = 1; | |
686f6134 EW |
254 | if (value("interactive") != NOSTR) |
255 | if (value("askcc") != NOSTR) | |
256 | grabh(hp, GCC); | |
257 | else { | |
258 | printf("EOT\n"); | |
259 | (void) fflush(stdout); | |
260 | } | |
3184019b KS |
261 | |
262 | /* | |
263 | * Now, take the user names from the combined | |
264 | * to and cc lists and do all the alias | |
265 | * processing. | |
266 | */ | |
267 | ||
268 | senderr = 0; | |
269 | to = usermap(cat(extract(hp->h_bcc, GBCC), | |
790344bc | 270 | cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC)))); |
3184019b KS |
271 | if (to == NIL) { |
272 | printf("No recipients specified\n"); | |
273 | goto topdog; | |
274 | } | |
275 | ||
276 | /* | |
277 | * Look through the recipient list for names with /'s | |
278 | * in them which we write to as files directly. | |
279 | */ | |
280 | ||
281 | to = outof(to, mtf, hp); | |
282 | rewind(mtf); | |
3184019b KS |
283 | if (senderr && !remote) { |
284 | topdog: | |
285 | ||
286 | if (fsize(mtf) != 0) { | |
751b30cd EW |
287 | (void) remove(deadletter); |
288 | (void) exwrite(deadletter, mtf, 1); | |
3184019b KS |
289 | rewind(mtf); |
290 | } | |
291 | } | |
292 | for (gotcha = 0, np = to; np != NIL; np = np->n_flink) | |
293 | if ((np->n_type & GDEL) == 0) { | |
294 | gotcha++; | |
295 | break; | |
296 | } | |
297 | if (!gotcha) | |
298 | goto out; | |
299 | to = elide(to); | |
300 | mechk(to); | |
301 | if (count(to) > 1) | |
302 | hp->h_seq++; | |
303 | if (hp->h_seq > 0 && !remote) { | |
304 | fixhead(hp, to); | |
305 | if (fsize(mtf) == 0) | |
430e0622 CL |
306 | if (hp->h_subject == NOSTR) |
307 | printf("No message, no subject; hope that's ok\n"); | |
308 | else | |
3184019b KS |
309 | printf("Null message body; hope that's ok\n"); |
310 | if ((mtf = infix(hp, mtf)) == NULL) { | |
311 | fprintf(stderr, ". . . message lost, sorry.\n"); | |
312 | return(-1); | |
313 | } | |
314 | } | |
790344bc | 315 | namelist = unpack(cat(extract(hp->h_smopts, 0), to)); |
3184019b KS |
316 | if (debug) { |
317 | printf("Recipients of message:\n"); | |
318 | for (t = namelist; *t != NOSTR; t++) | |
319 | printf(" \"%s\"", *t); | |
320 | printf("\n"); | |
751b30cd | 321 | (void) fflush(stdout); |
828615a1 | 322 | return 0; |
3184019b KS |
323 | } |
324 | if ((cp = value("record")) != NOSTR) | |
751b30cd | 325 | (void) savemail(expand(cp), mtf); |
3184019b KS |
326 | |
327 | /* | |
328 | * Wait, to absorb a potential zombie, then | |
329 | * fork, set up the temporary mail file as standard | |
330 | * input for "mail" and exec with the user list we generated | |
331 | * far above. Return the process id to caller in case he | |
332 | * wants to await the completion of mail. | |
333 | */ | |
334 | ||
828615a1 | 335 | while (wait3(&s, WNOHANG, (struct timeval *) 0) > 0) |
3184019b | 336 | ; |
3184019b KS |
337 | rewind(mtf); |
338 | pid = fork(); | |
339 | if (pid == -1) { | |
340 | perror("fork"); | |
751b30cd EW |
341 | (void) remove(deadletter); |
342 | (void) exwrite(deadletter, mtf, 1); | |
3184019b KS |
343 | goto out; |
344 | } | |
345 | if (pid == 0) { | |
346 | #ifdef SIGTSTP | |
347 | if (remote == 0) { | |
751b30cd EW |
348 | (void) signal(SIGTSTP, SIG_IGN); |
349 | (void) signal(SIGTTIN, SIG_IGN); | |
350 | (void) signal(SIGTTOU, SIG_IGN); | |
3184019b KS |
351 | } |
352 | #endif | |
751b30cd EW |
353 | (void) signal(SIGHUP, SIG_IGN); |
354 | (void) signal(SIGINT, SIG_IGN); | |
355 | (void) signal(SIGQUIT, SIG_IGN); | |
a1f76dd3 CL |
356 | if (!stat(POSTAGE, &sbuf)) |
357 | if ((postage = fopen(POSTAGE, "a")) != NULL) { | |
751b30cd | 358 | fprintf(postage, "%s %d %ld\n", myname, |
a1f76dd3 | 359 | count(to), fsize(mtf)); |
751b30cd | 360 | (void) fclose(postage); |
a1f76dd3 | 361 | } |
751b30cd EW |
362 | (void) close(0); |
363 | (void) dup(fileno(mtf)); | |
828615a1 | 364 | for (i = getdtablesize(); --i > 2;) |
751b30cd | 365 | (void) close(i); |
6fa89d43 | 366 | if ((deliver = value("sendmail")) == NOSTR) |
ac57be53 | 367 | deliver = SENDMAIL; |
6fa89d43 | 368 | execv(deliver, namelist); |
9c226cb6 | 369 | perror(deliver); |
3184019b KS |
370 | exit(1); |
371 | } | |
372 | ||
373 | out: | |
a1f76dd3 | 374 | if (remote || (value("verbose") != NOSTR)) { |
3184019b KS |
375 | while ((p = wait(&s)) != pid && p != -1) |
376 | ; | |
828615a1 | 377 | if (s.w_status != 0) |
3184019b KS |
378 | senderr++; |
379 | pid = 0; | |
380 | } | |
751b30cd | 381 | (void) fclose(mtf); |
3184019b KS |
382 | return(pid); |
383 | } | |
384 | ||
385 | /* | |
386 | * Fix the header by glopping all of the expanded names from | |
387 | * the distribution list into the appropriate fields. | |
388 | * If there are any ARPA net recipients in the message, | |
389 | * we must insert commas, alas. | |
390 | */ | |
391 | ||
392 | fixhead(hp, tolist) | |
393 | struct header *hp; | |
394 | struct name *tolist; | |
395 | { | |
3184019b KS |
396 | register int f; |
397 | register struct name *np; | |
398 | ||
399 | for (f = 0, np = tolist; np != NIL; np = np->n_flink) | |
400 | if (any('@', np->n_name)) { | |
401 | f |= GCOMMA; | |
402 | break; | |
403 | } | |
404 | ||
405 | if (debug && f & GCOMMA) | |
406 | fprintf(stderr, "Should be inserting commas in recip lists\n"); | |
407 | hp->h_to = detract(tolist, GTO|f); | |
408 | hp->h_cc = detract(tolist, GCC|f); | |
790344bc | 409 | hp->h_bcc = detract(tolist, GBCC|f); |
3184019b KS |
410 | } |
411 | ||
412 | /* | |
413 | * Prepend a header in front of the collected stuff | |
414 | * and return the new file. | |
415 | */ | |
416 | ||
417 | FILE * | |
418 | infix(hp, fi) | |
419 | struct header *hp; | |
420 | FILE *fi; | |
421 | { | |
422 | extern char tempMail[]; | |
423 | register FILE *nfo, *nfi; | |
424 | register int c; | |
425 | ||
1c5ad8c6 | 426 | rewind(fi); |
3184019b KS |
427 | if ((nfo = fopen(tempMail, "w")) == NULL) { |
428 | perror(tempMail); | |
429 | return(fi); | |
430 | } | |
431 | if ((nfi = fopen(tempMail, "r")) == NULL) { | |
432 | perror(tempMail); | |
751b30cd | 433 | (void) fclose(nfo); |
3184019b KS |
434 | return(fi); |
435 | } | |
751b30cd | 436 | (void) remove(tempMail); |
790344bc | 437 | (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL); |
3184019b KS |
438 | c = getc(fi); |
439 | while (c != EOF) { | |
751b30cd | 440 | (void) putc(c, nfo); |
3184019b KS |
441 | c = getc(fi); |
442 | } | |
443 | if (ferror(fi)) { | |
444 | perror("read"); | |
3184019b KS |
445 | return(fi); |
446 | } | |
751b30cd | 447 | (void) fflush(nfo); |
3184019b KS |
448 | if (ferror(nfo)) { |
449 | perror(tempMail); | |
751b30cd EW |
450 | (void) fclose(nfo); |
451 | (void) fclose(nfi); | |
3184019b KS |
452 | return(fi); |
453 | } | |
751b30cd EW |
454 | (void) fclose(nfo); |
455 | (void) fclose(fi); | |
3184019b KS |
456 | rewind(nfi); |
457 | return(nfi); | |
458 | } | |
459 | ||
460 | /* | |
461 | * Dump the to, subject, cc header on the | |
462 | * passed file buffer. | |
463 | */ | |
464 | ||
465 | puthead(hp, fo, w) | |
466 | struct header *hp; | |
467 | FILE *fo; | |
468 | { | |
469 | register int gotcha; | |
470 | ||
471 | gotcha = 0; | |
472 | if (hp->h_to != NOSTR && w & GTO) | |
a1f76dd3 | 473 | fmt("To: ", hp->h_to, fo), gotcha++; |
3184019b KS |
474 | if (hp->h_subject != NOSTR && w & GSUBJECT) |
475 | fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; | |
476 | if (hp->h_cc != NOSTR && w & GCC) | |
a1f76dd3 | 477 | fmt("Cc: ", hp->h_cc, fo), gotcha++; |
3184019b | 478 | if (hp->h_bcc != NOSTR && w & GBCC) |
a1f76dd3 | 479 | fmt("Bcc: ", hp->h_bcc, fo), gotcha++; |
3184019b | 480 | if (gotcha && w & GNL) |
751b30cd | 481 | (void) putc('\n', fo); |
3184019b KS |
482 | return(0); |
483 | } | |
484 | ||
485 | /* | |
486 | * Format the given text to not exceed 72 characters. | |
487 | */ | |
488 | ||
a1f76dd3 CL |
489 | fmt(str, txt, fo) |
490 | register char *str, *txt; | |
3184019b KS |
491 | register FILE *fo; |
492 | { | |
493 | register int col; | |
a1f76dd3 CL |
494 | register char *bg, *bl, *pt, ch; |
495 | ||
496 | col = strlen(str); | |
497 | if (col) | |
498 | fprintf(fo, "%s", str); | |
499 | pt = bg = txt; | |
500 | bl = 0; | |
501 | while (*bg) { | |
502 | pt++; | |
828615a1 | 503 | if (++col > 72) { |
a1f76dd3 CL |
504 | if (!bl) { |
505 | bl = bg; | |
506 | while (*bl && !isspace(*bl)) | |
507 | bl++; | |
508 | } | |
509 | if (!*bl) | |
510 | goto finish; | |
511 | ch = *bl; | |
512 | *bl = '\0'; | |
513 | fprintf(fo, "%s\n ", bg); | |
3184019b | 514 | col = 4; |
a1f76dd3 CL |
515 | *bl = ch; |
516 | pt = bg = ++bl; | |
517 | bl = 0; | |
518 | } | |
519 | if (!*pt) { | |
520 | finish: | |
521 | fprintf(fo, "%s\n", bg); | |
522 | return; | |
3184019b | 523 | } |
a1f76dd3 CL |
524 | if (isspace(*pt)) |
525 | bl = pt; | |
3184019b | 526 | } |
3184019b KS |
527 | } |
528 | ||
529 | /* | |
530 | * Save the outgoing mail on the passed file. | |
531 | */ | |
532 | ||
828615a1 EW |
533 | /*ARGSUSED*/ |
534 | savemail(name, fi) | |
3184019b | 535 | char name[]; |
828615a1 | 536 | register FILE *fi; |
3184019b KS |
537 | { |
538 | register FILE *fo; | |
828615a1 EW |
539 | char buf[BUFSIZ]; |
540 | register i; | |
541 | time_t now, time(); | |
3184019b | 542 | char *n; |
828615a1 | 543 | char *ctime(); |
3184019b KS |
544 | |
545 | if ((fo = fopen(name, "a")) == NULL) { | |
546 | perror(name); | |
828615a1 | 547 | return (-1); |
3184019b | 548 | } |
751b30cd | 549 | (void) time(&now); |
828615a1 | 550 | if ((n = rflag) == NOSTR) |
3184019b KS |
551 | n = myname; |
552 | fprintf(fo, "From %s %s", n, ctime(&now)); | |
553 | rewind(fi); | |
828615a1 | 554 | while ((i = fread(buf, 1, sizeof buf, fi)) > 0) |
751b30cd EW |
555 | (void) fwrite(buf, 1, i, fo); |
556 | (void) putc('\n', fo); | |
557 | (void) fflush(fo); | |
3184019b KS |
558 | if (ferror(fo)) |
559 | perror(name); | |
751b30cd | 560 | (void) fclose(fo); |
828615a1 | 561 | return (0); |
3184019b | 562 | } |