Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / uip / sendmail.c
1 /* sendmail.c - */
2 #ifndef lint
3 static char Id[] = "$Id: sendmail.c,v 1.9 1992/11/24 18:37:01 jromine Exp $";
4 #endif
5 /*
6  **  A Sendmail fake.
7  *
8  * Contributed by Scott Erickson <erickson@ics.uci.edu>
9  */
10 /* Include files glommed from post.c */
11
12 #include "../h/mh.h"
13 #include "../h/addrsbr.h"
14 #include "../h/aliasbr.h"
15 #include "../h/dropsbr.h"
16 #include "../zotnet/tws.h"
17 #ifndef MMDFMTS
18 #include <ctype.h>
19 #include <errno.h>
20 #include <setjmp.h>
21 #include <stdio.h>
22 #include <sys/types.h>
23 #else   MMDFMTS
24 #include "../mts/mmdf/util.h"
25 #include "../mts/mmdf/mmdf.h"
26 #endif  MMDFMTS
27 #include "../zotnet/mts.h"
28 #ifdef  MHMTS
29 #ifndef V7
30 #include <sys/ioctl.h>
31 #endif  not V7
32 #include <sys/stat.h>
33 #endif  MHMTS
34 #ifdef  SENDMTS
35 #include "../mts/sendmail/smail.h"
36 #undef  MF
37 #endif  SENDMTS
38 #include <signal.h>
39 #ifdef LOCALE
40 #include        <locale.h>
41 #endif
42
43 char    *SMTPSRVR = "smtpsrvr";
44
45 char    msgfname[50];   /* name of message file */
46 char    *FullName;      /* sender's full name */
47 char    *from;          /* sender's mail address */
48 int     verbose;
49 int     verify;
50 int     extract;
51 int     dodist;
52 int     rewritefrom;
53 int     status;         /* return value from procedures */
54 static int childid;     /* id from smtp child process */
55 TYPESIG die();
56 long    lclock = 0L;    /* the time we started (more or less) */
57
58
59 FILE *fp;               /* file pointer for message file */
60 extern FILE *tmpfile();
61
62 static struct swit switches[] = {
63 #define ARPASW    0
64     "ba", -2,
65 #define DAEMONSW  1
66     "bd", -2,
67 #define INITALSW  2
68     "bi", -2,
69 #define DELIVSW   3
70     "bm", -2,
71 #define QSUMSW    4
72     "bp", -2,
73 #define SMTPSW    5
74     "bs", -2,
75 #define ADRTSTSW  6
76     "bt", -2,
77 #define ADRVRFSW  7
78     "bv", -2,
79 #define CFGFRZSW  8
80     "bz", -2,
81 #define ALTCFGSW  9
82     "C", -1,
83 #define DBGVALSW 10
84     "d", -1,
85 #define FULLSW   11
86     "F", -1,
87 #define FROMSW   12
88     "f", -1,
89 #define HOPCNTSW 13
90     "h", -1,
91 #define MSGIDSW  14
92     "M", -1,
93 #define NOALISW  15
94     "n", -1,
95 #define QTIMESW  16
96     "q", -1,
97 #define OBSFRMSW 17
98     "r", -1,
99 #define EXTHDRSW 18
100     "t", -1,
101 #define VERBSW   19
102     "v", -1,
103 #define ALTALISW 20
104     "oA", -2,
105 #define NOCONSW   21
106     "oc", -2,
107 #define DLVMODSW 22
108     "od", -2,
109 #define NEWALISW 23
110     "oD", -2,
111 #define ERRMODSW 24
112     "oe", -2,
113 #define TMPMODSW 25
114     "oF", -2,
115 #define UFROMSW  26
116     "of", -2,
117 #define GIDSW    27
118     "og", -2,
119 #define HLPFILSW 28
120     "oH", -2,
121 #define NODOTSW  29
122     "oi", -2,
123 #define LOGLEVSW 30
124     "oL", -2,
125 #define MEOKSW   31
126     "om", -2,
127 #define OLDHDRSW 32
128     "oo", -2,
129 #define QDIRSW   33
130     "oQ", -2,
131 #define RTMOUTSW 34
132     "or", -2,
133 #define SFILESW  35
134     "oS", -2,
135 #define QMSGSW   36
136     "os", -2,
137 #define MTMOUTSW 37
138     "oT", -2,
139 #define TZSW     38
140     "ot", -2,
141 #define UIDSW    39
142     "ou", -2,
143     
144     NULL, 0
145     };
146
147 #if !defined(POSIX) && !defined(_POSIX_SOURCE)
148 extern char *mktemp();
149 #endif
150
151 static void removemsg();
152 static int  isheader(), sendfile();
153
154 /*ARGSUSED*/
155 main (argc, argv)
156 int    argc;
157 char **argv;
158 {
159     register char *cp;
160     char **argp = argv + 1;
161     
162 #ifdef LOCALE
163         setlocale(LC_ALL, "");
164 #endif
165     invo_name = r1bindex (argv[0], '/');
166     mts_init(argv[0]);
167     
168     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
169         (void) signal(SIGINT, die);
170     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
171         (void) signal(SIGHUP, die);
172     (void) signal(SIGTERM, die);
173     (void) signal(SIGPIPE, die);
174     
175     FullName = getfullname();
176     from = adrsprintf(NULLCP,NULLCP);
177     (void) time (&lclock);
178     
179     while ( (cp = *argp) &&  *cp == '-' ) {
180         argp++;
181         switch (smatch ( ++cp, switches )) {
182         case ARPASW:    /* smtp on stdin */
183         case SMTPSW:    /* smtp on stdin */
184             smtp();
185             exit(98);   /* should never happen */
186             
187         case DELIVSW:   /* just send mail */
188             continue;
189             
190         case ADRVRFSW:  /* verify mode */
191             verify = 1;
192             continue;
193             
194         case FROMSW:    /* from address */
195         case OBSFRMSW:  /* obsolete -f flag */
196             if (*(++cp) == '\0' &&
197                 (!(cp = *argp++) || *cp == '-'))
198                 adios (NULLCP, "missing argument to %s", argp[-2]);
199             /* At this point, cp points to the from name */
200             if (rewritefrom) {
201                 adios (NULLCP, "More than one \"from\" person");
202                 continue;
203             }
204             from = cp;
205             rewritefrom = 1;
206             continue;
207             
208         case EXTHDRSW:  /* read recipients from message */
209             extract = 1;
210             continue;
211             
212         case VERBSW:    /* give blow-by-blow description */
213             verbose = 1;
214             continue;
215             
216             /* These switches have no args. */
217         case QMSGSW:    /* always queue the message */
218         case DAEMONSW:  /* run as a daemon & wait for SMTP */
219         case INITALSW:  /* initialize the alias database */
220         case QSUMSW:    /* print summary of mail queue */
221         case ADRTSTSW:  /* test the addresses to debug config file */
222         case CFGFRZSW:  /* create the configuration freeze file */
223         case NOALISW:   /* do not do aliasing */
224         case NOCONSW:   /* do not initiate immediate host connection */
225         case NEWALISW:  /* run newaliases to rebuild db */
226         case UFROMSW:   /* save UNIX-style From lines at front of msg*/
227         case NODOTSW:   /* dots on line are not msg terminators */
228         case MEOKSW:    /* ok to send to me if I'm in an alias */
229         case OLDHDRSW:  /* msg may have old-style headers */
230             continue;
231             
232             /* These switches have string args. */
233         case ALTALISW:  /* use alternate alias file */
234         case ALTCFGSW:  /* use alternate configuration file */
235         case DBGVALSW:  /* set the debug value */
236         case FULLSW:    /* set full name */
237         case MSGIDSW:   /* try to deliver queued msg with msg-id */
238         case QTIMESW:   /* interval between queue passes */
239         case DLVMODSW:  /* set the delivery mode */
240         case ERRMODSW:  /* set the error mode */
241         case TMPMODSW:  /* the mode to use when creating tmp files */
242         case HLPFILSW:  /* the SMTP help file */
243         case QDIRSW:    /* directory into which to queue messages */
244         case RTMOUTSW:  /* timeout on reads */
245         case SFILESW:   /* save statistics in this file */
246         case MTMOUTSW:  /* timeout on messages in the queue */
247         case TZSW:      /* set the name of the timezone */
248             if (*(++cp) == '\0' &&
249                 (!(cp = *argp++) || *cp == '-'))
250                 adios (NULLCP, "missing argument to %s", argp[-2]);
251             /* At this point, cp points to the argument */
252             continue;   /* Ignore */
253             
254             /* These switches have numeric args. */
255         case HOPCNTSW:  /* hop count */
256         case GIDSW:     /* gid when calling mailers */
257         case LOGLEVSW:  /* the log level */
258         case UIDSW:     /* uid when calling mailers */
259             if (*(++cp) == '\0' &&
260                 (!(cp = *argp++) || *cp == '-'))
261                 adios (NULLCP, "missing argument to %s", argp[-2]);
262             /* At this point, cp points to the numeric arg */
263             if (!isdigit(*cp))
264                 adios (NULLCP, "non-numeric argument to %s", argp[-2]);
265             continue;   /* Ignore */
266         }
267     }
268     
269     (void) setuid(getuid());
270     
271     if (verify && extract)
272         adios (NULLCP, "mode not supported on header components");
273     
274     if (*argp == NULL && !extract)
275         adios (NULLCP, "usage: /usr/lib/sendmail [flags] addr...");
276     
277     strcpy (msgfname, "/tmp/sendmhXXXXXX");
278     if ( mktemp(msgfname) == NULL )
279         adios (msgfname, "can't create msg file ");
280     
281     if ( (fp = fopen(msgfname,"w") ) == NULL ) {
282         adios (msgfname, "error opening ");
283     }
284     
285     doheader(argp);
286     if ( verify ) {
287         (void) fclose(fp);
288         status = doverify();
289         removemsg();
290         exit ( status ) ;
291     }
292     dobody();
293     status = sendfile();
294     removemsg();
295     exit ( status );
296 }
297
298 static void removemsg()
299 {
300   if ( unlink(msgfname) != 0 )
301       perror("unlink");
302 }
303
304 doheader(argp)
305 char **argp;
306 {
307     char        line[BUFSIZ];
308     int         gotdate, gotfrom, gotsender, gotto;
309     
310     /* if we're not extracting the headers from the message, then we
311      * need to check to see if we need to do a "send" or a "dist".
312      */
313     
314     if ( !extract ) {
315         /* If we're doing a verify, just create a "To:" header. */
316         if ( ! verify ) {
317             gotdate = gotfrom = gotto = gotsender = dodist = 0;
318             while (fgets (line, BUFSIZ, stdin) != NULL) {
319                 if (line[0] == '\n')            /* end of header */
320                     break;
321                 if ( !isheader(line) )
322                     break;
323
324                 /* if any of the following headers are present, then we
325                  * want to do a dist.
326                  */
327                 if ( !gotdate && uprf(line, "date") )
328                     gotdate = dodist = 1;
329
330                 else if ( !gotto && (uprf(line, "to") || uprf(line, "cc")) )
331                     gotto = dodist = 1;
332
333                 else if ( uprf(line, "message-id") )
334                     dodist = 1;
335
336                 else if ( !gotsender && uprf(line, "sender") )
337                     gotsender = dodist = 1;
338                 
339                 else if ( uprf ( line, "resent-" ) ) {
340                     dodist = 1;
341                     (void) fputs("Prev-", fp);
342                 }
343                 
344                 /* See if we are re-writing the from line */
345                 if ( uprf(line, "from") ) {
346                     gotfrom = 1;
347                     if ( rewritefrom )
348                         dofrom();
349                     else
350                         (void) fputs(line,fp);
351                 }
352                 else
353                     (void) fputs(line,fp);
354             }
355         }
356         /* Now, generate a "to" line.  The first line is easy.
357          * Write the rest of the lines with a newline/tab so that we
358          * don't accidentally write a line that's too long to be parsed
359          * by post.
360          */
361         (void) fprintf (fp, "%sTo: %s", (dodist ? "Resent-" : "" ), *argp++);
362         while ( *argp )
363             (void) fprintf ( fp, ",\n\t%s", *argp++ );
364         (void) fputs("\n",fp);
365
366         /* If we're doing a dist, we must have a "Date:" and "From:" field.
367          */
368         if ( dodist ) {
369             if ( !gotdate )
370                 (void) fprintf (fp, "Date: %s\n", dtime (&lclock));
371             if ( !gotfrom )
372                 dofrom();
373         }
374 #ifdef  MMDFI                   /* sigh */
375         if ( !gotsender )
376             (void) fprintf (fp, "Sender: %s\n", from);
377 #endif  MMDFI
378     } else {    /* we're verifying, so just pass everything through */
379         while (fgets (line, BUFSIZ, stdin) != NULL) {
380             if (line[0] == '\n')                /* end of header */
381                 break;
382             
383             if ( rewritefrom && uprf(line, "from"))
384                 dofrom();
385             else
386                 (void) fputs(line,fp);
387         }
388     }
389     /* At this point, line is either a newline (end of header) or the
390      * first line of the body (poorly formatted message).  If line
391      * contains a line of body from a poorly formatted message, then
392      * print a newline to separate the header correctly, then print
393      * the body line.
394      */
395     if ( line[0] != '\n' )      /* i.e. a "body" line */
396         (void) fputc('\n', fp);
397     (void) fputs(line, fp);
398 }
399
400 static int isheader(s)
401 char *s;
402 {
403     register char *cp;
404
405     /* If the first character is a space, assume a continuation of a header */
406     if ( isspace(*s) )
407         return 1;
408
409     /* If there's no ':', it's not a header */
410     if ( (cp = index(s,':')) == NULL )
411         return 0;
412
413     /* If there's a space between BOL and ':', it's not a header */
414     while ( s < cp ) {
415         if ( isspace(*s) )
416             return 0;
417         s++;
418     }
419     return 1;
420 }
421
422 /* This procedure does the verify and returns the status */
423 doverify() {
424     char *command, buf[BUFSIZ], *bp;
425     FILE *verfp, *popen();
426     
427     /* set up the command line for post */
428     if ( (command = (char *)malloc((strlen(postproc) +
429                                     strlen(" -whom -check -verbose ") +
430                                     strlen(msgfname) + 1 )*sizeof(char)))
431         == NULL ) {
432         perror("malloc");
433         return NOTOK;
434     }
435     
436     (void) strcpy(command,postproc);
437     (void) strcat(command," -whom -check ");
438     if ( verbose )
439         (void) strcat(command, "-verbose " );
440     (void) strcat(command, msgfname);
441     
442     /* open up the pipe */
443     if ( (verfp = popen(command,"r")) == NULL )
444         return NOTOK;
445     
446     while ( fgets(buf, BUFSIZ, verfp) != NULL )
447         /* sendmail returns:
448          *   address:  result
449          * so we need to strip the extra post headers.
450          */
451         if ( verbose ) {
452             bp = buf;
453             while (isspace(*bp))
454                 bp++;
455             if ( *bp != '-' )
456                 (void) fputs(bp,stdout);
457         }
458     
459     /* return the error status of post */
460     return( pclose(verfp) >> 8 );
461 }
462
463 static int sendfile()
464 {
465     char *command, buf[BUFSIZ];
466     FILE *verfp, *popen();
467     
468     /* set up the command line for post */
469     if ( (command = (char *)malloc((strlen(postproc) +
470                                     strlen(" -dist -verbose ") +
471                                     strlen(msgfname) + 1 )*sizeof(char)))
472         == NULL ) {
473         perror("malloc");
474         return NOTOK;
475     }
476     
477     (void) strcpy(command,postproc);
478     (void) strcat(command," ");
479     if ( verbose )
480         (void) strcat(command, "-verbose " );
481     if ( dodist )
482         (void) strcat(command, "-dist " );
483     (void) strcat(command, msgfname);
484     
485     /* open up the pipe */
486     if ( (verfp = popen(command,"r")) == NULL )
487         return NOTOK;
488     
489     while ( fgets(buf, BUFSIZ, verfp) != NULL )
490         (void) fputs(buf,stdout);
491
492     /* return the error status of post */
493     return( pclose(verfp) >> 8 );
494 }
495
496 dofrom() {
497     char        line[128];
498     
499     if (FullName)
500         (void) sprintf(line, "From: %s <%s>\n", FullName, from);
501     else
502         (void) sprintf(line, "From: %s\n", from);
503     (void) fputs(line, fp);
504 }
505
506 dobody() {
507     register int    i;
508     char    buffer[BUFSIZ];
509     
510     while (!feof (stdin) && !ferror (stdin) &&
511            (i = fread (buffer, sizeof (char), sizeof (buffer), stdin)) > 0)
512         if (fwrite (buffer, sizeof (char), i , fp) != i )
513             adios (NULLCP, "Problem writing body");
514     
515     if (ferror (stdin))
516         adios (NULLCP, "Problem reading body");
517     
518     if ( fclose(fp) != 0 )
519         adios (NULLCP, "problem ending submission");
520 }
521
522 TYPESIG silentdie();
523
524 smtp()
525 {
526     int sd,len;
527     char buf[BUFSIZ], response[BUFSIZ];
528
529     if ((sd = client(NULLCP, "tcp", "smtp", 0, response)) == NOTOK)
530         adios (NULLCP, "cannot open smtp client process");
531
532     (void) signal(SIGCHLD, silentdie);
533
534     switch ((childid = fork())) {
535         case NOTOK:
536             adios (NULLCP, "unable to fork smtp process");
537         
538         case OK:        /* i.e. child */
539             (void) dup2(sd,0);
540             break;
541
542         default:        /* i.e. parent */
543             (void) dup2(sd,1);
544             break;
545     }
546     while ( (len = read(0, buf, BUFSIZ)) > 0)
547         (void) write (1, buf, len);
548
549     if (childid)
550         (void) kill(childid, SIGHUP);
551
552     exit(9);
553 }
554
555 /* ARGSUSED */
556 TYPESIG die(sig)
557 int sig;
558 {
559     if (fp) {
560         (void) fclose(fp);
561         (void) unlink(msgfname);
562     }
563     if (sig != SIGHUP)
564         (void) fprintf(stderr, "sendmail: dying from signal %d\n", sig);
565     exit(99);
566 }
567
568 /* ARGSUSED */
569
570 TYPESIG silentdie(sig)
571 int sig;
572 {
573     pidwait (childid, OK);
574     exit(0);
575 }