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