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