sendmail -> paths.h
[unix-history] / usr / src / usr.bin / mail / send.c
CommitLineData
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
435e8dff 19static char sccsid[] = "@(#)send.c 5.18 (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 37send(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 186statusput(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 208mail(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
228sendmail(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 246mail1(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) {
435e8dff 323 if (access(_PATH_MAIL_LOG, 0) == 0) {
322c8626
EW
324 FILE *postage;
325
435e8dff 326 if ((postage = fopen(_PATH_MAIL_LOG, "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
435e8dff 338 cp = _PATH_SENDMAIL;
322c8626
EW
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 347out:
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
355fixhead(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
380FILE *
381infix(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
428puthead(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
451fmt(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*/
486savemail(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}