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:
7: #ifndef lint
8: static char *sccsid = "@(#)send.c 5.2 (Berkeley) 6/21/85";
9: #endif not lint
10:
11: #include "rcv.h"
12: #ifdef VMUNIX
13: #include <sys/wait.h>
14: #endif
15: #include <ctype.h>
16: #include <sys/stat.h>
17:
18: /*
19: * Mail -- a mail program
20: *
21: * Mail to others.
22: */
23:
24: /*
25: * Send message described by the passed pointer to the
26: * passed output buffer. Return -1 on error, but normally
27: * the number of lines written. Adjust the status: field
28: * if need be. If doign is set, suppress ignored header fields.
29: */
30: send(mailp, obuf, doign)
31: struct message *mailp;
32: FILE *obuf;
33: {
34: register struct message *mp;
35: register int t;
36: long c;
37: FILE *ibuf;
38: char line[LINESIZE], field[BUFSIZ];
39: int lc, ishead, infld, fline, dostat;
40: char *cp, *cp2;
41:
42: mp = mailp;
43: ibuf = setinput(mp);
44: c = mp->m_size;
45: ishead = 1;
46: dostat = 1;
47: infld = 0;
48: fline = 1;
49: lc = 0;
50: while (c > 0L) {
51: fgets(line, LINESIZE, ibuf);
52: c -= (long) strlen(line);
53: lc++;
54: if (ishead) {
55: /*
56: * First line is the From line, so no headers
57: * there to worry about
58: */
59: if (fline) {
60: fline = 0;
61: goto writeit;
62: }
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: */
69: if (line[0] == '\n') {
70: if (dostat) {
71: statusput(mailp, obuf, doign);
72: dostat = 0;
73: }
74: ishead = 0;
75: goto writeit;
76: }
77: /*
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).
81: */
82: if (infld && (isspace(line[0]) || line[0] == '\t')) {
83: if (doign && isign(field)) continue;
84: goto writeit;
85: }
86: infld = 0;
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: */
93: if (!headerp(line)) {
94: if (dostat) {
95: statusput(mailp, obuf, doign);
96: dostat = 0;
97: }
98: putc('\n', obuf);
99: ishead = 0;
100: goto writeit;
101: }
102: infld++;
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;
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: }
126: }
127: writeit:
128: fputs(line, obuf);
129: if (ferror(obuf))
130: return(-1);
131: }
132: if (ferror(obuf))
133: return(-1);
134: if (ishead && (mailp->m_flag & MSTATUS))
135: printf("failed to fix up status field\n");
136: return(lc);
137: }
138:
139: /*
140: * Test if the passed line is a header line, RFC 733 style.
141: */
142: (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:
154: /*
155: * Output a reasonable looking status field.
156: * But if "status" is ignored and doign, forget it.
157: */
158: statusput(mp, obuf, doign)
159: register struct message *mp;
160: register FILE *obuf;
161: {
162: char statout[3];
163:
164: if (doign && isign("status"))
165: return;
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:
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;
247: char **namelist, *deliver;
248: struct name *to, *np;
249: struct stat sbuf;
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;
265: if (intty && value("askcc") != NOSTR)
266: grabh(hp, GCC);
267: else if (intty) {
268: printf("EOT\n");
269: fflush(stdout);
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)
317: if (hp->h_subject == NOSTR)
318: printf("No message, no subject; hope that's ok\n");
319: else
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
347: #ifdef pdp11
348: while (wait2(&s, WNOHANG) > 0)
349: #endif
350: #if defined(vax) || defined(sun)
351: while (wait3(&s, WNOHANG, 0) > 0)
352: #endif
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) {
366: sigchild();
367: #ifdef SIGTSTP
368: if (remote == 0) {
369: sigset(SIGTSTP, SIG_IGN);
370: sigset(SIGTTIN, SIG_IGN);
371: sigset(SIGTTOU, SIG_IGN);
372: }
373: #endif
374: for (i = SIGHUP; i <= SIGQUIT; i++)
375: sigset(i, SIG_IGN);
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: }
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
392: #ifdef SENDMAIL
393: if ((deliver = value("sendmail")) == NOSTR)
394: deliver = SENDMAIL;
395: execv(deliver, namelist);
396: #endif SENDMAIL
397: execv(MAIL, namelist);
398: perror(MAIL);
399: exit(1);
400: }
401:
402: out:
403: if (remote || (value("verbose") != NOSTR)) {
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:
455: rewind(fi);
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);
467: c = getc(fi);
468: while (c != EOF) {
469: putc(c, nfo);
470: c = getc(fi);
471: }
472: if (ferror(fi)) {
473: perror("read");
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)
502: fmt("To: ", hp->h_to, fo), gotcha++;
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)
506: fmt("Cc: ", hp->h_cc, fo), gotcha++;
507: if (hp->h_bcc != NOSTR && w & GBCC)
508: fmt("Bcc: ", hp->h_bcc, fo), gotcha++;
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:
518: fmt(str, txt, fo)
519: register char *str, *txt;
520: register FILE *fo;
521: {
522: register int col;
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);
543: col = 4;
544: *bl = ch;
545: pt = bg = ++bl;
546: bl = 0;
547: }
548: if (!*pt) {
549: finish:
550: fprintf(fo, "%s\n", bg);
551: return;
552: }
553: if (isspace(*pt))
554: bl = pt;
555: }
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: }