removed some functions in the C library
[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
470c33f3 19static char sccsid[] = "@(#)send.c 5.15 (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);
2f025913 289 if (senderr) {
3184019b 290 if (fsize(mtf) != 0) {
751b30cd
EW
291 (void) remove(deadletter);
292 (void) exwrite(deadletter, mtf, 1);
3184019b
KS
293 rewind(mtf);
294 }
295 }
3184019b 296 to = elide(to);
322c8626
EW
297 if (count(to) == 0)
298 goto out;
299 fixhead(hp, to);
300 if ((mtf = infix(hp, mtf)) == NULL) {
301 fprintf(stderr, ". . . message lost, sorry.\n");
302 return;
3184019b 303 }
3d6f01e5 304 namelist = unpack(cat(hp->h_smopts, to));
3184019b 305 if (debug) {
322c8626
EW
306 char **t;
307
3d6f01e5 308 printf("Sendmail arguments:");
3184019b
KS
309 for (t = namelist; *t != NOSTR; t++)
310 printf(" \"%s\"", *t);
311 printf("\n");
322c8626 312 goto out;
3184019b
KS
313 }
314 if ((cp = value("record")) != NOSTR)
751b30cd 315 (void) savemail(expand(cp), mtf);
3184019b 316 /*
322c8626
EW
317 * Fork, set up the temporary mail file as standard
318 * input for "mail", and exec with the user list we generated
319 * far above.
3184019b 320 */
3184019b
KS
321 pid = fork();
322 if (pid == -1) {
323 perror("fork");
751b30cd
EW
324 (void) remove(deadletter);
325 (void) exwrite(deadletter, mtf, 1);
3184019b
KS
326 goto out;
327 }
328 if (pid == 0) {
322c8626
EW
329 if (access(POSTAGE, 0) == 0) {
330 FILE *postage;
331
a1f76dd3 332 if ((postage = fopen(POSTAGE, "a")) != NULL) {
751b30cd 333 fprintf(postage, "%s %d %ld\n", myname,
a1f76dd3 334 count(to), fsize(mtf));
751b30cd 335 (void) fclose(postage);
a1f76dd3 336 }
322c8626
EW
337 }
338 prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
339 sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
340 fileno(mtf), -1);
341 if ((cp = value("sendmail")) != NOSTR)
342 cp = expand(cp);
343 else
344 cp = SENDMAIL;
345 execv(cp, namelist);
346 perror(cp);
3184019b
KS
347 exit(1);
348 }
322c8626
EW
349 if (value("verbose") != NOSTR)
350 (void) wait_child(pid);
351 else
352 free_child(pid);
3184019b 353out:
751b30cd 354 (void) fclose(mtf);
3184019b
KS
355}
356
357/*
358 * Fix the header by glopping all of the expanded names from
359 * the distribution list into the appropriate fields.
3184019b 360 */
3184019b
KS
361fixhead(hp, tolist)
362 struct header *hp;
363 struct name *tolist;
364{
3184019b
KS
365 register struct name *np;
366
3d6f01e5
EW
367 hp->h_to = NIL;
368 hp->h_cc = NIL;
369 hp->h_bcc = NIL;
370 for (np = tolist; np != NIL; np = np->n_flink)
371 if ((np->n_type & GMASK) == GTO)
372 hp->h_to =
373 cat(hp->h_to, nalloc(np->n_name, np->n_type));
374 else if ((np->n_type & GMASK) == GCC)
375 hp->h_cc =
376 cat(hp->h_cc, nalloc(np->n_name, np->n_type));
377 else if ((np->n_type & GMASK) == GBCC)
378 hp->h_bcc =
379 cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
3184019b
KS
380}
381
382/*
383 * Prepend a header in front of the collected stuff
384 * and return the new file.
385 */
3184019b
KS
386FILE *
387infix(hp, fi)
388 struct header *hp;
389 FILE *fi;
390{
391 extern char tempMail[];
392 register FILE *nfo, *nfi;
393 register int c;
394
395 if ((nfo = fopen(tempMail, "w")) == NULL) {
396 perror(tempMail);
397 return(fi);
398 }
399 if ((nfi = fopen(tempMail, "r")) == NULL) {
400 perror(tempMail);
751b30cd 401 (void) fclose(nfo);
3184019b
KS
402 return(fi);
403 }
751b30cd 404 (void) remove(tempMail);
3d6f01e5 405 (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA);
3184019b
KS
406 c = getc(fi);
407 while (c != EOF) {
751b30cd 408 (void) putc(c, nfo);
3184019b
KS
409 c = getc(fi);
410 }
411 if (ferror(fi)) {
412 perror("read");
322c8626 413 rewind(fi);
3184019b
KS
414 return(fi);
415 }
751b30cd 416 (void) fflush(nfo);
3184019b
KS
417 if (ferror(nfo)) {
418 perror(tempMail);
751b30cd
EW
419 (void) fclose(nfo);
420 (void) fclose(nfi);
322c8626 421 rewind(fi);
3184019b
KS
422 return(fi);
423 }
751b30cd
EW
424 (void) fclose(nfo);
425 (void) fclose(fi);
3184019b
KS
426 rewind(nfi);
427 return(nfi);
428}
429
430/*
431 * Dump the to, subject, cc header on the
432 * passed file buffer.
433 */
3184019b
KS
434puthead(hp, fo, w)
435 struct header *hp;
436 FILE *fo;
437{
438 register int gotcha;
439
440 gotcha = 0;
3d6f01e5 441 if (hp->h_to != NIL && w & GTO)
322c8626 442 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
3184019b
KS
443 if (hp->h_subject != NOSTR && w & GSUBJECT)
444 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
3d6f01e5 445 if (hp->h_cc != NIL && w & GCC)
322c8626 446 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
3d6f01e5 447 if (hp->h_bcc != NIL && w & GBCC)
322c8626 448 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
3184019b 449 if (gotcha && w & GNL)
751b30cd 450 (void) putc('\n', fo);
3184019b
KS
451 return(0);
452}
453
454/*
322c8626 455 * Format the given header line to not exceed 72 characters.
3184019b 456 */
3d6f01e5
EW
457fmt(str, np, fo, comma)
458 char *str;
459 register struct name *np;
460 FILE *fo;
461 int comma;
3184019b 462{
3d6f01e5 463 register col, len;
a1f76dd3 464
3d6f01e5 465 comma = comma ? 1 : 0;
a1f76dd3
CL
466 col = strlen(str);
467 if (col)
3d6f01e5 468 fputs(str, fo);
322c8626 469 for (; np != NIL; np = np->n_flink) {
3d6f01e5
EW
470 if (np->n_flink == NIL)
471 comma = 0;
472 len = strlen(np->n_name);
322c8626
EW
473 col++; /* for the space */
474 if (col + len + comma > 72 && col > 4) {
3d6f01e5
EW
475 fputs("\n ", fo);
476 col = 4;
322c8626 477 } else
3d6f01e5 478 putc(' ', fo);
322c8626
EW
479 fputs(np->n_name, fo);
480 if (comma)
481 putc(',', fo);
482 col += len + comma;
3184019b 483 }
3d6f01e5 484 putc('\n', fo);
3184019b
KS
485}
486
487/*
488 * Save the outgoing mail on the passed file.
489 */
490
828615a1
EW
491/*ARGSUSED*/
492savemail(name, fi)
3184019b 493 char name[];
828615a1 494 register FILE *fi;
3184019b
KS
495{
496 register FILE *fo;
828615a1
EW
497 char buf[BUFSIZ];
498 register i;
499 time_t now, time();
828615a1 500 char *ctime();
3184019b
KS
501
502 if ((fo = fopen(name, "a")) == NULL) {
503 perror(name);
828615a1 504 return (-1);
3184019b 505 }
751b30cd 506 (void) time(&now);
2f025913 507 fprintf(fo, "From %s %s", myname, ctime(&now));
828615a1 508 while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
751b30cd
EW
509 (void) fwrite(buf, 1, i, fo);
510 (void) putc('\n', fo);
511 (void) fflush(fo);
3184019b
KS
512 if (ferror(fo))
513 perror(name);
751b30cd 514 (void) fclose(fo);
322c8626 515 rewind(fi);
828615a1 516 return (0);
3184019b 517}