Oh GACK! src-clean doesn't quite work that easily since cleandist rebuilds the
[unix-history] / usr.sbin / sendmail / aux / arpa.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1983 Eric P. Allman
3 * Copyright (c) 1988 Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifndef lint
36char copyright[] =
37"@(#) Copyright (c) 1988 Regents of the University of California.\n\
38 All rights reserved.\n";
39#endif /* not lint */
40
41#ifndef lint
42static char sccsid[] = "@(#)arpa.c 5.4 (Berkeley) 6/1/90";
43#endif /* not lint */
44
45# include <stdio.h>
46# include <ctype.h>
47# include <signal.h>
48# include <sysexits.h>
49# include <whoami.h>
50# include <useful.h>
51
52char Version[] = "@(#)Arpa-mailer version 5.4 of 6/1/90";
53
54# define void int
55
56/*
57** ARPA MAILER -- Queue ARPANET mail for eventual delivery
58**
59** The standard input is stuck away in the outgoing arpanet
60** mail queue for delivery by the true arpanet mailer.
61**
62** CUSTOMIZED FOR THE C/70
63**
64** Usage:
65** /usr/lib/mailers/arpa from host user
66**
67** Positional Parameters:
68** from -- the person sending the mail.
69** host -- the host to send the mail to.
70** user -- the user to send the mail to.
71**
72** Flags:
73** -T -- debug flag.
74**
75** Files:
76** /usr/spool/netmail/* -- the queue file.
77**
78** Return Codes:
79** 0 -- all messages successfully mailed.
80** 2 -- user or host unknown.
81** 3 -- service unavailable, probably temporary
82** file system condition.
83** 4 -- syntax error in address.
84**
85** Compilation Flags:
86** SPOOLDIR -- the spool directory
87**
88** Compilation Instructions:
89** cc -n -O -s arpa-mailer.c -o arpa-mailer -lX
90** chmod 755 arpa-mailer
91** mv arpa-mailer /usr/lib/mailers/arpa
92**
93** Author:
94** Eric Allman, UCB/INGRES (eric@berkeley)
95*/
96
97# ifdef C70
98# define SPOOLDIR "/usr/netmail"
99# else
100# define SPOOLDIR "/usr/spool/netmail"
101# endif
102
103
104
105
106char *From; /* person sending this mail */
107char *To; /* current "To:" person */
108int State; /* the current state (for exit codes) */
109# ifdef DEBUG
110bool Tflag; /* -T given */
111# endif DEBUG
112char FromHost[200]; /* string to prepend to addresses */
113\f/*
114** MAIN -- Main program for arpa mailer
115**
116** Processes arguments, and calls sendmail successively on
117** the To: list.
118**
119** Algorithm:
120** Scan for debug flag.
121** Catch interrupt signals.
122** Collect input file name and from person.
123** If more than one person in the to list, and
124** if the input file is not a real file,
125** collect input into a temp file.
126** For each person in the to list
127** Send to that person.
128**
129** Parameters:
130** argc
131** argv -- as usual
132**
133** Returns:
134** via exit
135**
136** Side Effects:
137** Mail gets sent.
138**
139** Author:
140** Eric Allman UCB/INGRES.
141*/
142
143main(argc, argv)
144 int argc;
145 char **argv;
146{
147 register int i;
148 register char *p;
149 register int ifd;
150 char buf[512];
151 extern int done();
152 extern char *locv();
153 register char *q;
154 char *lastmark;
155
156 State = 3;
157 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
158 (void) signal(SIGINT, done);
159
160 /* process flags */
161 argv[argc] = 0;
162# ifdef DEBUG
163 if (strcmp(argv[1], "-T") == 0)
164 {
165 Tflag++;
166 argv++;
167 argc--;
168 printf("%s\n", Version);
169 }
170# endif DEBUG
171
172 if (argc != 4)
173 {
174 rexit(EX_SOFTWARE);
175 }
176
177 /* decode parameters */
178 From = argv[1];
179 lastmark = &FromHost[-1];
180 for (p = From, q = FromHost; (*q = *p) != '\0'; p++, q++)
181 {
182 if (*p == ':')
183 *q = *p = '.';
184 if (*q == '.' || *q == '!' || *q == '@')
185 lastmark = q;
186 }
187 lastmark[1] = '\0';
188
189 /* start sending mail */
190 State = sendmail(argv[2], argv[3]);
191
192 /* all done, clean up */
193 done();
194}
195\f/*
196** DONE -- Finish up, remove temp files, etc.
197**
198** This does basic cleanup on interrupt, error, or
199** normal termination. It uses "State" to tell which
200** is happening.
201**
202** Parameters:
203** none
204**
205** Returns:
206** none
207**
208** Side Effects:
209** Exit(State).
210*/
211
212done()
213{
214 rexit(State);
215}
216
217/*
218** REXIT -- exit, reporting error code if -T given
219**
220** Parameters:
221** e -- error code to exit with; see sysexits.h
222**
223** Returns:
224** none
225**
226** Side Effects:
227** Exit(e).
228*/
229rexit(e)
230{
231
232# ifdef DEBUG
233 if (Tflag)
234 fprintf(stderr, "arpa-mail: return code %d\n", e);
235# endif
236 exit(e);
237}
238\f/*
239** SENDMAIL -- Queue up mail for the arpanet mailer.
240**
241** The mail is inserted with proper headers into the
242** arpanet queue directory.
243**
244** Algorithm:
245** decode "to" address
246** if error, exit.
247** create a spool file name.
248** output the header information to spool file,
249** separate names in To:, CC: fields with commas.
250** copy the mail to the spool file.
251**
252** Parameters:
253** host -- the host to send to.
254** user -- the user to send to.
255**
256** Returns:
257** none
258**
259** Side Effects:
260** the mail is copied into a file in the network
261** queue directory (/usr/spool/netmail).
262*/
263
264sendmail(host, user)
265 char *host;
266 char *user;
267{
268 char spoolfile[50]; /* gets the spool file name */
269 register int i;
270 register char *p;
271 static int callnum; /* for the final letter on spoolfile */
272 char buf[512];
273 register FILE *sfp; /* spool file */
274 register int c;
275 extern char *matchhdr();
276
277 /* verify that the host exists */
278 (void) strcpy(buf, "/dev/net/");
279 (void) strcat(buf, host);
280# ifndef C70
281#ifdef DEBUG
282 if (!Tflag)
283#endif DEBUG
284 if (host[0] == '\0' || access(buf, 0) < 0)
285 return (EX_NOHOST);
286# endif C70
287
288 /*
289 ** Create spool file name.
290 ** Format is "username000nnX", where username is
291 ** padded on the right with zeros and nn (the process
292 ** id) is padded on the left with zeros; X is a unique
293 ** sequence character.
294 */
295
296# ifdef DEBUG
297 if (Tflag)
298 (void) strcpy(spoolfile, "arpa.out");
299 else
300# endif DEBUG
301 (void) sprintf(spoolfile, "%s/arpamail%05d%c", SPOOLDIR, getpid(), 'a' + callnum++);
302
303 /* create spool file */
304 sfp = fopen(spoolfile, "w");
305 if (sfp == NULL)
306 {
307 spoolerr:
308 return (EX_OSERR);
309 }
310# ifdef DEBUG
311 if (!Tflag)
312# endif DEBUG
313 (void) chmod(spoolfile, 0400);
314
315 /*
316 ** Output mailer control lines.
317 ** These lines are as follows:
318 ** /dev/net/<hostname> {target host}
319 ** user-name {at target host}
320 ** /mnt/eric {pathname of sender; not used}
321 ** eric {name of user who is sending}
322 ** These are different (but close) on the C/70.
323 */
324
325# ifdef C70
326 fputs(host, sfp);
327 fputs(":", sfp);
328 fputs(user, sfp);
329 fputs(":", sfp);
330 fputs(From, sfp);
331 fputs(":\n", sfp);
332# else
333 fputs(buf, sfp);
334 fputs("\n", sfp);
335 fputs(user, sfp);
336 fputs("\n\n", sfp);
337 fputs(From, sfp);
338 fputs("\n", sfp);
339# endif
340
341 /*
342 ** Output the mail
343 ** Check the first line for the date. If not found,
344 ** assume the message is not in arpanet standard format
345 ** and output a "Date:" and "From:" header.
346 */
347
348 if (fgets(buf, sizeof buf, stdin) == NULL)
349 {
350 /* no message */
351 (void) unlink(spoolfile);
352 return (EX_OK);
353 }
354 if (strncmp("From ", buf, 5) == 0)
355 {
356 /* strip Unix "From" line */
357 /* should save the date here */
358 (void) fgets(buf, sizeof buf, stdin);
359 }
360 while (matchhdr(buf, "mail-from") != NULL ||
361 matchhdr(buf, "sender-path") != NULL ||
362 matchhdr(buf, "received") != NULL ||
363 matchhdr(buf, "via") != NULL)
364 {
365 fputs(buf, sfp);
366 (void) fgets(buf, sizeof buf, stdin);
367 }
368 if (matchhdr(buf, "date") == NULL)
369 putdate(sfp);
370 else
371 {
372 fputs(buf, sfp);
373 (void) fgets(buf, sizeof buf, stdin);
374 }
375 if (matchhdr(buf, "from") == NULL)
376 putfrom(sfp);
377 else
378 {
379 /* hack to support sendmail -- for a while */
380 if (index(buf, '@') == NULL)
381 putfrom(sfp);
382 else
383 fputs(buf, sfp);
384 (void) fgets(buf, sizeof buf, stdin);
385 }
386 if (!ishdr(buf))
387 {
388 if (buf[0] != '\n')
389 putc('\n', sfp);
390 goto hdrdone;
391 }
392
393 /*
394 ** At this point, we have a message with REAL headers.
395 ** We look at each head line and insert commas if it
396 ** is a To: or Cc: field.
397 */
398
399 do
400 {
401 if (!ishdr(buf))
402 break;
403 if (!matchhdr(buf, "to") && !matchhdr(buf, "cc"))
404 {
405 fputs(buf, sfp);
406 continue;
407 }
408 /* gotcha! */
409 fixaddr(buf, 1, sfp);
410 while (isspace(c = peekc(stdin)) && c != '\n')
411 {
412 (void) fgets(buf, BUFSIZ, stdin);
413 fixaddr(buf, 0, sfp);
414 }
415 } while (fgets(buf, BUFSIZ, stdin) != NULL);
416
417hdrdone:
418 /* output the rest of the header & the body of the letter */
419 do
420 {
421 fputs(buf, sfp);
422 if (ferror(sfp))
423 goto spoolerr;
424 } while (fgets(buf, sizeof buf, stdin) != NULL);
425
426 /* all done! */
427 (void) fclose(sfp);
428 return (EX_OK);
429}
430\f/*
431** FIXADDR -- Output header line with needed commas.
432**
433** Parameters:
434** buf -- header line
435** first -- true if this is not a continuation
436**
437** Returns:
438** none
439**
440** Side effects:
441** The contents of buf is copied onto the spool file with
442** with the right commas interlaced
443**
444** Called by:
445** sendmail
446*/
447
448fixaddr(buf, first, spf)
449 char buf[];
450 register FILE *spf;
451{
452 register char *cp;
453 register int c;
454 char word[BUFSIZ], word2[BUFSIZ];
455 char *gword();
456 static char wsep[] = ", ";
457
458 cp = buf;
459 if (first)
460 {
461 while (*cp != ':' && *cp)
462 putc(*cp++, spf);
463 if (*cp == ':')
464 {
465 fputs(": ", spf);
466 cp++;
467 }
468 }
469 else
470 while (*cp && isspace(*cp))
471 putc(*cp++, spf);
472 cp = gword(word, cp);
473 if (strlen(word) == 0)
474 {
475 putc('\n', spf);
476 goto test;
477 }
478 for (;;)
479 {
480 cp = gword(word2, cp);
481 if (strlen(word2) == 0)
482 {
483 putaddr(word, spf);
484 break;
485 }
486 if (strcmp(word2, "%") == 0)
487 word2[0] = '@';
488 if (strcmp(word2, "@") && strcmp(word2, "at"))
489 {
490 putaddr(word, spf);
491 fputs(wsep, spf);
492 (void) strcpy(word, word2);
493 continue;
494 }
495 fputs(word, spf);
496 if (word2[0] == '@')
497 putc('@', spf);
498 else
499 fputs(" at ", spf);
500 cp = gword(word, cp);
501 fputs(word, spf);
502 cp = gword(word, cp);
503 if (strlen(word))
504 fputs(wsep, spf);
505 }
506
507test:
508 c = peekc(stdin);
509 if (isspace(c) && c != '\n')
510 fputs(",\n", spf);
511 else
512 putc('\n', spf);
513}
514\f/*
515** PUTADDR -- output address onto file
516**
517** Putaddr prepends the network header onto the address
518** unless one already exists.
519**
520** Parameters:
521** name -- the name to output.
522** fp -- the file to put it on.
523**
524** Returns:
525** none.
526**
527** Side Effects:
528** name is put onto fp.
529*/
530
531putaddr(name, fp)
532 char *name;
533 FILE *fp;
534{
535 register char *p;
536
537 if (strlen(name) == 0)
538 return;
539 for (p = name; *p != '\0' && *p != ':' && *p != '.' && *p != '@' &&
540 *p != '!' && *p != '^'; p++)
541 continue;
542 if (*p == ':')
543 *p = '.';
544 else if (*p == '\0')
545 fputs(FromHost, fp);
546 fputs(name, fp);
547 if (*p != '@')
548 fputs("@Berkeley", fp);
549}
550\f/*
551** PEEKC -- peek at next character in input file
552**
553** Parameters:
554** fp -- stdio file buffer
555**
556** Returns:
557** the next character in the input or EOF
558**
559** Side effects:
560** None.
561*/
562peekc(fp)
563 register FILE *fp;
564{
565 register int c;
566
567 c = getc(fp);
568 (void) ungetc(c, fp);
569 return(c);
570}
571
572\f/*
573** GWORD -- get the next liberal word from a string
574**
575** Parameters:
576** buf -- place to put scanned word
577** p -- place to start looking for word
578**
579** Returns:
580** updated value of p or 0 if no more left after this
581**
582** Side effects:
583** buf gets the liberal word scanned.
584** buf will be length 0 if there is no more input,
585** or if p was passed as 0
586*/
587char *
588gword(buf, p)
589 char buf[];
590 register char *p;
591{
592 register char *sp, *dp;
593 int atfound = 0; /* weither or not a '@' found in the scan */
594
595 (void) strcpy(buf, "");
596 if (p == 0)
597 return(0);
598 sp = p;
599 while (*sp && (isspace(*sp) || *sp == ','))
600 sp++;
601 dp = buf;
602 if (*sp != '%' && *sp != '@')
603 {
604 while (*sp && !isspace(*sp) && *sp != ',' )
605 {
606 if ( *sp == '@' || *sp == '%' )
607 atfound++;
608 *dp++ = *sp++;
609 }
610 if ( atfound )
611 {
612 dp--;
613 while ( *dp != '@' && *dp != '%' )
614 dp--,sp--;
615 sp--;
616 }
617
618 }
619 else
620 *dp++ = *sp++;
621 *dp = 0;
622 if (*sp == 0)
623 return(0);
624 return(sp);
625}
626\f/*
627** ISHDR -- see if the passed line is a ARPA style header line
628**
629** Parameters:
630** buf -- header line
631**
632** Returns:
633** non-zero if the line is a header line, else zero
634**
635** Side effects:
636** none
637**
638** Called by:
639** sendmail
640*/
641ishdr(buf)
642 char buf[];
643{
644 register char *p;
645
646 p = buf;
647
648 /* check for continuation lines */
649 if (isspace(*p))
650 return (1);
651 else
652 {
653 while (*p != ':' && !isspace(*p))
654 p++;
655 while (isspace(*p))
656 p++;
657 if (*p != ':')
658 p = 0;
659 }
660 return(p != 0);
661}
662\f/*
663** PUTDATE -- Put the date field into the message.
664**
665** Parameters:
666** fp -- file to put it onto.
667**
668** Returns:
669** none
670**
671** Side Effects:
672** output onto fp.
673*/
674
675putdate(fp)
676 register FILE *fp;
677{
678 extern char *arpadate();
679
680 fputs("Date: ", fp);
681 fputs(arpadate(NULL), fp);
682 fputs("\n", fp);
683}
684\f/*
685** PUTFROM -- Put the from field into the message.
686**
687** Parameters:
688** fp -- file to put it onto.
689**
690** Returns:
691** none
692**
693** Side Effects:
694** output onto fp.
695*/
696
697putfrom(fp)
698 register FILE *fp;
699{
700
701 fputs("From: ", fp);
702 fputs(From, fp);
703 fputs("@Berkeley\n", fp);
704}