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