BSD 4_1_snap release
[unix-history] / usr / src / cmd / delivermail / arpa.c
CommitLineData
31cef89c
BJ
1# include <stdio.h>
2# include <ctype.h>
3# include <signal.h>
4# include <sysexits.h>
5# include "useful.h"
6
4b9ccde7
C
7static char SccsId[] = "@(#)arpa.c 2.2 1/11/81";
8char Version[] = "@(#)Arpa-mailer version 2.2 of 1/11/81";
31cef89c
BJ
9
10
11/*
12** ARPA MAILER -- Queue ARPANET mail for eventual delivery
13**
14** The standard input is stuck away in the outgoing arpanet
15** mail queue for delivery by the true arpanet mailer.
16**
17** Usage:
18** /usr/lib/mailers/arpa from host user
19**
20** Positional Parameters:
21** from -- the person sending the mail.
22** host -- the host to send the mail to.
23** user -- the user to send the mail to.
24**
25** Flags:
26** -T -- debug flag.
27**
28** Files:
29** /usr/spool/netmail/* -- the queue file.
30**
31** Return Codes:
32** 0 -- all messages successfully mailed.
33** 2 -- user or host unknown.
34** 3 -- service unavailable, probably temporary
35** file system condition.
36** 4 -- syntax error in address.
37**
38** Compilation Flags:
39** SPOOLDIR -- the spool directory
40**
41** Compilation Instructions:
42** cc -n -O -s arpa-mailer.c -o arpa-mailer -lX
43** chmod 755 arpa-mailer
44** mv arpa-mailer /usr/lib/mailers/arpa
45**
46** Author:
47** Eric Allman, UCB/INGRES (eric@berkeley)
48*/
49
50# define SPOOLDIR "/usr/spool/netmail"
51
52
53
54
55char *From; /* person sending this mail */
56char *To; /* current "To:" person */
57int State; /* the current state (for exit codes) */
58# ifdef DEBUG
59bool Tflag; /* -T given */
60# endif DEBUG
61char FromHost[200]; /* string to prepend to addresses */
62\f/*
63** MAIN -- Main program for arpa mailer
64**
65** Processes arguments, and calls sendmail successively on
66** the To: list.
67**
68** Algorithm:
69** Scan for debug flag.
70** Catch interrupt signals.
71** Collect input file name and from person.
72** If more than one person in the to list, and
73** if the input file is not a real file,
74** collect input into a temp file.
75** For each person in the to list
76** Send to that person.
77**
78** Parameters:
79** argc
80** argv -- as usual
81**
82** Returns:
83** via exit
84**
85** Side Effects:
86** Mail gets sent.
87**
88** Called By:
89** /etc/delivermail
90**
91** Author:
92** Eric Allman UCB/INGRES.
93*/
94
95main(argc, argv)
96 int argc;
97 char **argv;
98{
99 register int i;
100 register char *p;
101 register int ifd;
102 char buf[512];
103 extern int finis();
104 extern char *locv();
105 register char *q;
106 char *lastmark;
107
108 State = 3;
109 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
110 signal(SIGINT, finis);
111
112 /* process flags */
113 argv[argc] = 0;
114# ifdef DEBUG
115 if (strcmp(argv[1], "-T") == 0)
116 {
117 Tflag++;
118 argv++;
119 argc--;
120 printf("%s\n", Version);
121 }
122# endif DEBUG
123
124 if (argc != 4)
125 {
126 rexit (EX_SOFTWARE);
127 }
128
129 /* decode parameters */
130 From = argv[1];
131 lastmark = &FromHost[-1];
132 for (p = From, q = FromHost; (*q = *p) != '\0'; p++, q++)
133 {
134 if (*p == ':')
135 *q = *p = '.';
136 if (*q == '.' || *q == '!' || *q == '@')
137 lastmark = q;
138 }
139 lastmark[1] = '\0';
140
141 /* start sending mail */
142 State = sendmail(argv[2], argv[3]);
143
144 /* all done, clean up */
145 finis();
146}
147\f/*
148** FINIS -- Finish up, remove temp files, etc.
149**
150** This does basic cleanup on interrupt, error, or
151** normal termination. It uses "State" to tell which
152** is happening.
153**
154** Parameters:
155** none
156**
157** Returns:
158** none
159**
160** Side Effects:
161** Exit(State).
162**
163** Called By:
164** interrupt signal.
165** main
166*/
167
168finis()
169{
170 rexit(State);
171}
172
173/*
174** REXIT -- exit, reporting error code if -T given
175**
176** Parameters:
177** e -- error code to exit with; see sysexits.h
178**
179** Returns:
180** none
181**
182** Side Effects:
183** Exit(e).
184**
185** Called By:
186** main
187** finis
188** sendmail
189*/
190rexit(e)
191{
192
193# ifdef DEBUG
194 if (Tflag)
195 fprintf(stderr, "arpa-mail: return code %d\n", e);
196# endif
197 exit(e);
198}
199\f/*
200** SENDMAIL -- Queue up mail for the arpanet mailer.
201**
202** The mail is inserted with proper headers into the
203** arpanet queue directory.
204**
205** Algorithm:
206** decode "to" address
207** if error, exit.
208** create a spool file name.
209** output the header information to spool file,
210** separate names in To:, CC: fields with commas.
211** copy the mail to the spool file.
212**
213** Parameters:
214** host -- the host to send to.
215** user -- the user to send to.
216**
217** Returns:
218** none
219**
220** Side Effects:
221** the mail is copied into a file in the network
222** queue directory (/usr/spool/netmail).
223**
224** Called By:
225** main
226*/
227
228sendmail(host, user)
229 char *host;
230 char *user;
231{
232 char spoolfile[50]; /* gets the spool file name */
233 register int i;
234 register char *p;
235 static int callnum; /* for the final letter on spoolfile */
236 char buf[512];
237 register FILE *sfp; /* spool file */
238 register int c;
239 extern char *matchhdr();
240
241 /* verify that the host exists */
242#ifndef TESTING
243 strcpy(buf, "/dev/net/");
244 strcat(buf, host);
245 if (host[0] == '\0' || access(buf, 0) < 0)
246 return (EX_NOHOST);
247#endif TESTING
248
249 /*
250 ** Create spool file name.
251 ** Format is "username000nnX", where username is
252 ** padded on the right with zeros and nn (the process
253 ** id) is padded on the left with zeros; X is a unique
254 ** sequence character.
255 */
256
257# ifdef DEBUG
258 if (Tflag)
259 strcpy(spoolfile, "test.out");
260# endif DEBUG
261 else
262 sprintf(spoolfile, "%s/arpamail%05d%c", SPOOLDIR, getpid(), 'a' + callnum++);
263
264 /* create spool file */
265 sfp = fopen(spoolfile, "w");
266 if (sfp == NULL)
267 {
268 spoolerr:
269 return (EX_OSERR);
270 }
271# ifdef DEBUG
272 if (!Tflag)
273# endif DEBUG
274 chmod(spoolfile, 0400);
275
276 /*
277 ** Output mailer control lines.
278 ** These lines are as follows:
279 ** /dev/net/<hostname> {target host}
280 ** user-name {at target host}
281 ** /mnt/eric {pathname of sender; not used}
282 ** eric {name of user who is sending}
283 */
284
285 fputs(buf, sfp);
286 fputs("\n", sfp);
287 fputs(user, sfp);
288 fputs("\n\n", sfp);
289 fputs(From, sfp);
290 fputs("\n", sfp);
291
292 /*
293 ** Output the mail
294 ** Check the first line for the date. If not found,
295 ** assume the message is not in arpanet standard format
296 ** and output a "Date:" and "From:" header.
297 */
298
299 if (fgets(buf, sizeof buf, stdin) == NULL)
300 {
301 /* no message */
302 unlink(spoolfile);
303 return (EX_OK);
304 }
4b9ccde7
C
305 if (strncmp("From ", buf, 5) == 0)
306 {
307 /* strip Unix "From" line */
308 /* should save the date here */
309 fgets(buf, sizeof buf, stdin);
310 }
31cef89c
BJ
311 if (matchhdr(buf, "date") == NULL)
312 putdate(sfp);
313 if (!ishdr(buf))
314 {
315 putc('\n', sfp);
316 goto hdrdone;
317 }
318
319 /*
320 ** At this point, we have a message with REAL headers.
321 ** We look at each head line and insert commas if it
322 ** is a To: or Cc: field.
323 */
324
325 do
326 {
327 if (!ishdr(buf))
328 break;
329 if (!matchhdr(buf, "to") && !matchhdr(buf, "cc"))
330 {
331 fputs(buf, sfp);
332 continue;
333 }
334 /* gotcha! */
335 rewrite(buf, 1, sfp);
336 while (isspace(c = peekc(stdin)) && c != '\n')
337 {
338 fgets(buf, BUFSIZ, stdin);
339 rewrite(buf, 0, sfp);
340 }
341 } while (fgets(buf, BUFSIZ, stdin) != NULL);
342
343hdrdone:
344 /* output the rest of the header & the body of the letter */
345 do
346 {
347 fputs(buf, sfp);
348 if (ferror(sfp))
349 goto spoolerr;
350 } while (fgets(buf, sizeof buf, stdin) != NULL);
351
352 /* all done! */
353 fclose(sfp);
354 return (EX_OK);
355}
356\f/*
357** REWRITE -- Output header line with needed commas.
358**
359** Parameters:
360** buf -- header line
361** first -- true if this is not a continuation
362**
363** Returns:
364** none
365**
366** Side effects:
367** The contents of buf is copied onto the spool file with
368** with the right commas interlaced
369**
370** Called by:
371** sendmail
372*/
373
374rewrite(buf, first, spf)
375 char buf[];
376 register FILE *spf;
377{
378 register char *cp;
379 register int c;
380 char word[BUFSIZ], word2[BUFSIZ];
381 char *gword();
382 static char wsep[] = ", ";
383
384 cp = buf;
385 if (first)
386 {
387 while (*cp != ':' && *cp)
388 putc(*cp++, spf);
389 if (*cp == ':')
390 {
391 fputs(": ", spf);
392 cp++;
393 }
394 }
395 else
396 while (*cp && isspace(*cp))
397 putc(*cp++, spf);
398 cp = gword(word, cp);
399 if (strlen(word) == 0)
400 {
401 putc('\n', spf);
402 goto test;
403 }
404 for (;;)
405 {
406 cp = gword(word2, cp);
407 if (strlen(word2) == 0)
408 {
409 putaddr(word, spf);
410 break;
411 }
412 if (strcmp(word2, "%") == 0)
413 word2[0] = '@';
414 if (strcmp(word2, "@") && strcmp(word2, "at"))
415 {
416 putaddr(word, spf);
417 fputs(wsep, spf);
418 strcpy(word, word2);
419 continue;
420 }
421 fputs(word, spf);
422 if (word2[0] == '@')
423 putc('@', spf);
424 else
425 fputs(" at ", spf);
426 cp = gword(word, cp);
427 fputs(word, spf);
428 cp = gword(word, cp);
429 if (strlen(word))
430 fputs(wsep, spf);
431 }
432
433test:
434 c = peekc(stdin);
435 if (isspace(c) && c != '\n')
436 fputs(",\n", spf);
437 else
438 putc('\n', spf);
439}
440\f/*
441** PUTADDR -- output address onto file
442**
443** Putaddr prepends the network header onto the address
444** unless one already exists.
445**
446** Parameters:
447** name -- the name to output.
448** fp -- the file to put it on.
449**
450** Returns:
451** none.
452**
453** Side Effects:
454** name is put onto fp.
455*/
456
457putaddr(name, fp)
458 char *name;
459 FILE *fp;
460{
461 register char *p;
462
463 if (strlen(name) == 0)
464 return;
465 for (p = name; *p != '\0' && *p != ':' && *p != '.' && *p != '@' &&
466 *p != '!' && *p != '^'; p++)
467 continue;
468 if (*p == ':')
469 *p = '.';
470 else if (*p == '\0')
471 fputs(FromHost, fp);
472 fputs(name, fp);
473 if (*p != '@')
474 fputs("@Berkeley", fp);
475}
476\f/*
477** PEEKC -- peek at next character in input file
478**
479** Parameters:
480** fp -- stdio file buffer
481**
482** Returns:
483** the next character in the input or EOF
484**
485** Side effects:
486** None.
487**
488** Called by:
489** sendmail
490** rewrite
491*/
492peekc(fp)
493 register FILE *fp;
494{
495 register int c;
496
497 c = getc(fp);
498 ungetc(c, fp);
499 return(c);
500}
501
502\f/*
503** GWORD -- get the next liberal word from a string
504**
505** Parameters:
506** buf -- place to put scanned word
507** p -- place to start looking for word
508**
509** Returns:
510** updated value of p or 0 if no more left after this
511**
512** Side effects:
513** buf gets the liberal word scanned.
514** buf will be length 0 if there is no more input,
515** or if p was passed as 0
516**
517** Called by:
518** rewrite
519*/
520char *
521gword(buf, p)
522 char buf[];
523 register char *p;
524{
525 register char *sp, *dp;
526
527 strcpy(buf, "");
528 if (p == 0)
529 return(0);
530 sp = p;
531 while (*sp && (isspace(*sp) || *sp == ','))
532 sp++;
533 dp = buf;
534 if (*sp != '%' && *sp != '@')
535 {
536 while (*sp && !isspace(*sp) && *sp != ',' && *sp != '%' && *sp != '@')
537 *dp++ = *sp++;
538 }
539 else
540 *dp++ = *sp++;
541 *dp = 0;
542 if (*sp == 0)
543 return(0);
544 return(sp);
545}
546\f/*
547** ISHDR -- see if the passed line is a ARPA style header line
548**
549** Parameters:
550** buf -- header line
551**
552** Returns:
553** non-zero if the line is a header line, else zero
554**
555** Side effects:
556** none
557**
558** Called by:
559** sendmail
560*/
561ishdr(buf)
562 char buf[];
563{
564 register char *p;
565
566 p = buf;
567 if (isspace(*p))
568 p = 0;
569 else
570 {
571 while (*p != ':' && !isspace(*p))
572 p++;
573 while (isspace(*p))
574 p++;
575 if (*p != ':')
576 p = 0;
577 }
578 return(p != 0);
579}
580\f/*
581** PUTDATE -- Put the date & from field into the message.
582**
583** Parameters:
584** fp -- file to put them onto.
585**
586** Returns:
587** none
588**
589** Side Effects:
590** output onto fp.
591**
592** Called By:
593** sendmail
594*/
595
596putdate(fp)
597 register FILE *fp;
598{
599 register char *p;
600
601 fputs("Date: ", fp);
602 fputs(arpadate(), fp);
603
604 /* output from field */
605 fputs("\nFrom: ", fp);
606 fputs(From, fp);
607 fputs(" at Berkeley\n", fp);
608}