hash() could return negative values.
[unix-history] / usr / src / usr.bin / mail / send.c
CommitLineData
3184019b
KS
1#
2
3#include "rcv.h"
4#ifdef VMUNIX
5#include <wait.h>
6#endif
a6b3a6c5 7#include <ctype.h>
a1f76dd3 8#include <sys/stat.h>
3184019b
KS
9
10/*
11 * Mail -- a mail program
12 *
13 * Mail to others.
14 */
15
85e62533 16static char *SccsId = "@(#)send.c 2.11 %G%";
3184019b
KS
17
18/*
19 * Send message described by the passed pointer to the
20 * passed output buffer. Return -1 on error, but normally
57079810 21 * the number of lines written. Adjust the status: field
b870137d 22 * if need be. If doign is set, suppress ignored header fields.
3184019b 23 */
b870137d 24send(mailp, obuf, doign)
3184019b
KS
25 struct message *mailp;
26 FILE *obuf;
27{
28 register struct message *mp;
29 register int t;
0154300b 30 long c;
3184019b 31 FILE *ibuf;
b870137d
KS
32 char line[LINESIZE], field[BUFSIZ];
33 int lc, ishead, infld, fline, dostat;
34 char *cp, *cp2;
3184019b
KS
35
36 mp = mailp;
37 ibuf = setinput(mp);
0154300b 38 c = mp->m_size;
b870137d
KS
39 ishead = 1;
40 dostat = 1;
a6b3a6c5
KS
41 infld = 0;
42 fline = 1;
3184019b 43 lc = 0;
0154300b 44 while (c > 0L) {
57079810 45 fgets(line, LINESIZE, ibuf);
0154300b 46 c -= (long) strlen(line);
57079810
KS
47 lc++;
48 if (ishead) {
b870137d
KS
49 /*
50 * First line is the From line, so no headers
51 * there to worry about
52 */
a6b3a6c5
KS
53 if (fline) {
54 fline = 0;
55 goto writeit;
56 }
b870137d
KS
57 /*
58 * If line is blank, we've reached end of
59 * headers, so force out status: field
60 * and note that we are no longer in header
61 * fields
62 */
57079810 63 if (line[0] == '\n') {
b870137d
KS
64 if (dostat) {
65 statusput(mailp, obuf, doign);
66 dostat = 0;
67 }
57079810
KS
68 ishead = 0;
69 goto writeit;
70 }
b870137d 71 /*
df5134c8
BB
72 * If this line is a continuation (via space or tab)
73 * of a previous header field, just echo it
74 * (unless the field should be ignored).
b870137d 75 */
df5134c8
BB
76 if (infld && (isspace(line[0]) || line[0] == '\t')) {
77 if (doign && isign(field)) continue;
a6b3a6c5 78 goto writeit;
df5134c8 79 }
a6b3a6c5 80 infld = 0;
b870137d
KS
81 /*
82 * If we are no longer looking at real
83 * header lines, force out status:
84 * This happens in uucp style mail where
85 * there are no headers at all.
86 */
a6b3a6c5 87 if (!headerp(line)) {
b870137d
KS
88 if (dostat) {
89 statusput(mailp, obuf, doign);
90 dostat = 0;
91 }
a6b3a6c5
KS
92 putc('\n', obuf);
93 ishead = 0;
57079810 94 goto writeit;
a6b3a6c5
KS
95 }
96 infld++;
b870137d
KS
97 /*
98 * Pick up the header field.
99 * If it is an ignored field and
100 * we care about such things, skip it.
101 */
102 cp = line;
103 cp2 = field;
104 while (*cp && *cp != ':' && !isspace(*cp))
105 *cp2++ = *cp++;
106 *cp2 = 0;
107 if (doign && isign(field))
108 continue;
2d7a1b5d
KS
109 /*
110 * If the field is "status," go compute and print the
111 * real Status: field
112 */
113 if (icequal(field, "status")) {
114 if (dostat) {
115 statusput(mailp, obuf, doign);
116 dostat = 0;
117 }
118 continue;
119 }
57079810
KS
120 }
121writeit:
122 fputs(line, obuf);
3184019b
KS
123 if (ferror(obuf))
124 return(-1);
125 }
57079810
KS
126 if (ferror(obuf))
127 return(-1);
128 if (ishead && (mailp->m_flag & MSTATUS))
129 printf("failed to fix up status field\n");
3184019b
KS
130 return(lc);
131}
132
a6b3a6c5
KS
133/*
134 * Test if the passed line is a header line, RFC 733 style.
135 */
136headerp(line)
137 register char *line;
138{
139 register char *cp = line;
140
141 while (*cp && !isspace(*cp) && *cp != ':')
142 cp++;
143 while (*cp && isspace(*cp))
144 cp++;
145 return(*cp == ':');
146}
147
57079810
KS
148/*
149 * Output a reasonable looking status field.
b870137d 150 * But if "status" is ignored and doign, forget it.
57079810 151 */
b870137d 152statusput(mp, obuf, doign)
57079810
KS
153 register struct message *mp;
154 register FILE *obuf;
155{
156 char statout[3];
157
b870137d
KS
158 if (doign && isign("status"))
159 return;
57079810
KS
160 if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
161 return;
162 if (mp->m_flag & MREAD)
163 strcpy(statout, "R");
164 else
165 strcpy(statout, "");
166 if ((mp->m_flag & MNEW) == 0)
167 strcat(statout, "O");
168 fprintf(obuf, "Status: %s\n", statout);
169}
170
171
3184019b
KS
172/*
173 * Interface between the argument list and the mail1 routine
174 * which does all the dirty work.
175 */
176
177mail(people)
178 char **people;
179{
180 register char *cp2;
181 register int s;
182 char *buf, **ap;
183 struct header head;
184
185 for (s = 0, ap = people; *ap != (char *) -1; ap++)
186 s += strlen(*ap) + 1;
187 buf = salloc(s+1);
188 cp2 = buf;
189 for (ap = people; *ap != (char *) -1; ap++) {
190 cp2 = copy(*ap, cp2);
191 *cp2++ = ' ';
192 }
193 if (cp2 != buf)
194 cp2--;
195 *cp2 = '\0';
196 head.h_to = buf;
197 head.h_subject = NOSTR;
198 head.h_cc = NOSTR;
199 head.h_bcc = NOSTR;
200 head.h_seq = 0;
201 mail1(&head);
202 return(0);
203}
204
205
206/*
207 * Send mail to a bunch of user names. The interface is through
208 * the mail routine below.
209 */
210
211sendmail(str)
212 char *str;
213{
214 register char **ap;
215 char *bufp;
216 register int t;
217 struct header head;
218
219 if (blankline(str))
220 head.h_to = NOSTR;
221 else
222 head.h_to = str;
223 head.h_subject = NOSTR;
224 head.h_cc = NOSTR;
225 head.h_bcc = NOSTR;
226 head.h_seq = 0;
227 mail1(&head);
228 return(0);
229}
230
231/*
232 * Mail a message on standard input to the people indicated
233 * in the passed header. (Internal interface).
234 */
235
236mail1(hp)
237 struct header *hp;
238{
239 register char *cp;
240 int pid, i, s, p, gotcha;
6fa89d43 241 char **namelist, *deliver;
3184019b 242 struct name *to, *np;
a1f76dd3 243 struct stat sbuf;
3184019b
KS
244 FILE *mtf, *postage;
245 int remote = rflag != NOSTR || rmail;
246 char **t;
247
248 /*
249 * Collect user's mail from standard input.
250 * Get the result as mtf.
251 */
252
253 pid = -1;
254 if ((mtf = collect(hp)) == NULL)
255 return(-1);
256 hp->h_seq = 1;
257 if (hp->h_subject == NOSTR)
258 hp->h_subject = sflag;
3184019b
KS
259 if (intty && value("askcc") != NOSTR)
260 grabh(hp, GCC);
261 else if (intty) {
262 printf("EOT\n");
263 flush();
264 }
265
266 /*
267 * Now, take the user names from the combined
268 * to and cc lists and do all the alias
269 * processing.
270 */
271
272 senderr = 0;
273 to = usermap(cat(extract(hp->h_bcc, GBCC),
274 cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC))));
275 if (to == NIL) {
276 printf("No recipients specified\n");
277 goto topdog;
278 }
279
280 /*
281 * Look through the recipient list for names with /'s
282 * in them which we write to as files directly.
283 */
284
285 to = outof(to, mtf, hp);
286 rewind(mtf);
287 to = verify(to);
288 if (senderr && !remote) {
289topdog:
290
291 if (fsize(mtf) != 0) {
292 remove(deadletter);
293 exwrite(deadletter, mtf, 1);
294 rewind(mtf);
295 }
296 }
297 for (gotcha = 0, np = to; np != NIL; np = np->n_flink)
298 if ((np->n_type & GDEL) == 0) {
299 gotcha++;
300 break;
301 }
302 if (!gotcha)
303 goto out;
304 to = elide(to);
305 mechk(to);
306 if (count(to) > 1)
307 hp->h_seq++;
308 if (hp->h_seq > 0 && !remote) {
309 fixhead(hp, to);
310 if (fsize(mtf) == 0)
430e0622
CL
311 if (hp->h_subject == NOSTR)
312 printf("No message, no subject; hope that's ok\n");
313 else
3184019b
KS
314 printf("Null message body; hope that's ok\n");
315 if ((mtf = infix(hp, mtf)) == NULL) {
316 fprintf(stderr, ". . . message lost, sorry.\n");
317 return(-1);
318 }
319 }
320 namelist = unpack(to);
321 if (debug) {
322 printf("Recipients of message:\n");
323 for (t = namelist; *t != NOSTR; t++)
324 printf(" \"%s\"", *t);
325 printf("\n");
326 fflush(stdout);
327 return;
328 }
329 if ((cp = value("record")) != NOSTR)
330 savemail(expand(cp), hp, mtf);
331
332 /*
333 * Wait, to absorb a potential zombie, then
334 * fork, set up the temporary mail file as standard
335 * input for "mail" and exec with the user list we generated
336 * far above. Return the process id to caller in case he
337 * wants to await the completion of mail.
338 */
339
340#ifdef VMUNIX
69a45597
CS
341#ifdef pdp11
342 while (wait2(&s, WNOHANG) > 0)
343#endif
338c4a5d 344#if defined(vax) || defined(sun)
3184019b 345 while (wait3(&s, WNOHANG, 0) > 0)
69a45597 346#endif
3184019b
KS
347 ;
348#else
349 wait(&s);
350#endif
351 rewind(mtf);
352 pid = fork();
353 if (pid == -1) {
354 perror("fork");
355 remove(deadletter);
356 exwrite(deadletter, mtf, 1);
357 goto out;
358 }
359 if (pid == 0) {
a302be8d 360 sigchild();
3184019b
KS
361#ifdef SIGTSTP
362 if (remote == 0) {
67f02f55
KS
363 sigset(SIGTSTP, SIG_IGN);
364 sigset(SIGTTIN, SIG_IGN);
365 sigset(SIGTTOU, SIG_IGN);
3184019b
KS
366 }
367#endif
368 for (i = SIGHUP; i <= SIGQUIT; i++)
67f02f55 369 sigset(i, SIG_IGN);
a1f76dd3
CL
370 if (!stat(POSTAGE, &sbuf))
371 if ((postage = fopen(POSTAGE, "a")) != NULL) {
372 fprintf(postage, "%s %d %d\n", myname,
373 count(to), fsize(mtf));
374 fclose(postage);
375 }
3184019b
KS
376 s = fileno(mtf);
377 for (i = 3; i < 15; i++)
378 if (i != s)
379 close(i);
380 close(0);
381 dup(s);
382 close(s);
383#ifdef CC
384 submit(getpid());
385#endif CC
ac57be53 386#ifdef SENDMAIL
6fa89d43 387 if ((deliver = value("sendmail")) == NOSTR)
ac57be53 388 deliver = SENDMAIL;
6fa89d43 389 execv(deliver, namelist);
ac57be53 390#endif SENDMAIL
3184019b
KS
391 execv(MAIL, namelist);
392 perror(MAIL);
393 exit(1);
394 }
395
396out:
a1f76dd3 397 if (remote || (value("verbose") != NOSTR)) {
3184019b
KS
398 while ((p = wait(&s)) != pid && p != -1)
399 ;
400 if (s != 0)
401 senderr++;
402 pid = 0;
403 }
404 fclose(mtf);
405 return(pid);
406}
407
408/*
409 * Fix the header by glopping all of the expanded names from
410 * the distribution list into the appropriate fields.
411 * If there are any ARPA net recipients in the message,
412 * we must insert commas, alas.
413 */
414
415fixhead(hp, tolist)
416 struct header *hp;
417 struct name *tolist;
418{
419 register struct name *nlist;
420 register int f;
421 register struct name *np;
422
423 for (f = 0, np = tolist; np != NIL; np = np->n_flink)
424 if (any('@', np->n_name)) {
425 f |= GCOMMA;
426 break;
427 }
428
429 if (debug && f & GCOMMA)
430 fprintf(stderr, "Should be inserting commas in recip lists\n");
431 hp->h_to = detract(tolist, GTO|f);
432 hp->h_cc = detract(tolist, GCC|f);
433}
434
435/*
436 * Prepend a header in front of the collected stuff
437 * and return the new file.
438 */
439
440FILE *
441infix(hp, fi)
442 struct header *hp;
443 FILE *fi;
444{
445 extern char tempMail[];
446 register FILE *nfo, *nfi;
447 register int c;
448
1c5ad8c6 449 rewind(fi);
3184019b
KS
450 if ((nfo = fopen(tempMail, "w")) == NULL) {
451 perror(tempMail);
452 return(fi);
453 }
454 if ((nfi = fopen(tempMail, "r")) == NULL) {
455 perror(tempMail);
456 fclose(nfo);
457 return(fi);
458 }
459 remove(tempMail);
460 puthead(hp, nfo, GTO|GSUBJECT|GCC|GNL);
3184019b
KS
461 c = getc(fi);
462 while (c != EOF) {
463 putc(c, nfo);
464 c = getc(fi);
465 }
466 if (ferror(fi)) {
467 perror("read");
3184019b
KS
468 return(fi);
469 }
470 fflush(nfo);
471 if (ferror(nfo)) {
472 perror(tempMail);
473 fclose(nfo);
474 fclose(nfi);
475 return(fi);
476 }
477 fclose(nfo);
478 fclose(fi);
479 rewind(nfi);
480 return(nfi);
481}
482
483/*
484 * Dump the to, subject, cc header on the
485 * passed file buffer.
486 */
487
488puthead(hp, fo, w)
489 struct header *hp;
490 FILE *fo;
491{
492 register int gotcha;
493
494 gotcha = 0;
495 if (hp->h_to != NOSTR && w & GTO)
a1f76dd3 496 fmt("To: ", hp->h_to, fo), gotcha++;
3184019b
KS
497 if (hp->h_subject != NOSTR && w & GSUBJECT)
498 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
499 if (hp->h_cc != NOSTR && w & GCC)
a1f76dd3 500 fmt("Cc: ", hp->h_cc, fo), gotcha++;
3184019b 501 if (hp->h_bcc != NOSTR && w & GBCC)
a1f76dd3 502 fmt("Bcc: ", hp->h_bcc, fo), gotcha++;
3184019b
KS
503 if (gotcha && w & GNL)
504 putc('\n', fo);
505 return(0);
506}
507
508/*
509 * Format the given text to not exceed 72 characters.
510 */
511
a1f76dd3
CL
512fmt(str, txt, fo)
513 register char *str, *txt;
3184019b
KS
514 register FILE *fo;
515{
516 register int col;
a1f76dd3
CL
517 register char *bg, *bl, *pt, ch;
518
519 col = strlen(str);
520 if (col)
521 fprintf(fo, "%s", str);
522 pt = bg = txt;
523 bl = 0;
524 while (*bg) {
525 pt++;
526 if (++col >72) {
527 if (!bl) {
528 bl = bg;
529 while (*bl && !isspace(*bl))
530 bl++;
531 }
532 if (!*bl)
533 goto finish;
534 ch = *bl;
535 *bl = '\0';
536 fprintf(fo, "%s\n ", bg);
3184019b 537 col = 4;
a1f76dd3
CL
538 *bl = ch;
539 pt = bg = ++bl;
540 bl = 0;
541 }
542 if (!*pt) {
543finish:
544 fprintf(fo, "%s\n", bg);
545 return;
3184019b 546 }
a1f76dd3
CL
547 if (isspace(*pt))
548 bl = pt;
3184019b 549 }
3184019b
KS
550}
551
552/*
553 * Save the outgoing mail on the passed file.
554 */
555
556savemail(name, hp, fi)
557 char name[];
558 struct header *hp;
559 FILE *fi;
560{
561 register FILE *fo;
562 register int c;
563 long now;
564 char *n;
565
566 if ((fo = fopen(name, "a")) == NULL) {
567 perror(name);
568 return(-1);
569 }
570 time(&now);
571 n = rflag;
572 if (n == NOSTR)
573 n = myname;
574 fprintf(fo, "From %s %s", n, ctime(&now));
575 rewind(fi);
576 for (c = getc(fi); c != EOF; c = getc(fi))
577 putc(c, fo);
578 fprintf(fo, "\n");
579 fflush(fo);
580 if (ferror(fo))
581 perror(name);
582 fclose(fo);
583 return(0);
584}