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