3 static char Id[] = "$Id: sendmail.c,v 1.9 1992/11/24 18:37:01 jromine Exp $";
8 * Contributed by Scott Erickson <erickson@ics.uci.edu>
10 /* Include files glommed from post.c */
13 #include "../h/addrsbr.h"
14 #include "../h/aliasbr.h"
15 #include "../h/dropsbr.h"
16 #include "../zotnet/tws.h"
22 #include <sys/types.h>
24 #include "../mts/mmdf/util.h"
25 #include "../mts/mmdf/mmdf.h"
27 #include "../zotnet/mts.h"
30 #include <sys/ioctl.h>
35 #include "../mts/sendmail/smail.h"
43 char *SMTPSRVR = "smtpsrvr";
45 char msgfname[50]; /* name of message file */
46 char *FullName; /* sender's full name */
47 char *from; /* sender's mail address */
53 int status; /* return value from procedures */
54 static int childid; /* id from smtp child process */
56 long lclock = 0L; /* the time we started (more or less) */
59 FILE *fp; /* file pointer for message file */
60 extern FILE *tmpfile();
62 static struct swit switches[] = {
147 #if !defined(POSIX) && !defined(_POSIX_SOURCE)
148 extern char *mktemp();
151 static void removemsg();
152 static int isheader(), sendfile();
160 char **argp = argv + 1;
163 setlocale(LC_ALL, "");
165 invo_name = r1bindex (argv[0], '/');
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);
175 FullName = getfullname();
176 from = adrsprintf(NULLCP,NULLCP);
177 (void) time (&lclock);
179 while ( (cp = *argp) && *cp == '-' ) {
181 switch (smatch ( ++cp, switches )) {
182 case ARPASW: /* smtp on stdin */
183 case SMTPSW: /* smtp on stdin */
185 exit(98); /* should never happen */
187 case DELIVSW: /* just send mail */
190 case ADRVRFSW: /* verify mode */
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 */
201 adios (NULLCP, "More than one \"from\" person");
208 case EXTHDRSW: /* read recipients from message */
212 case VERBSW: /* give blow-by-blow description */
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 */
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 */
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 */
264 adios (NULLCP, "non-numeric argument to %s", argp[-2]);
265 continue; /* Ignore */
269 (void) setuid(getuid());
271 if (verify && extract)
272 adios (NULLCP, "mode not supported on header components");
274 if (*argp == NULL && !extract)
275 adios (NULLCP, "usage: /usr/lib/sendmail [flags] addr...");
277 strcpy (msgfname, "/tmp/sendmhXXXXXX");
278 if ( mktemp(msgfname) == NULL )
279 adios (msgfname, "can't create msg file ");
281 if ( (fp = fopen(msgfname,"w") ) == NULL ) {
282 adios (msgfname, "error opening ");
298 static void removemsg()
300 if ( unlink(msgfname) != 0 )
308 int gotdate, gotfrom, gotsender, gotto;
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".
315 /* If we're doing a verify, just create a "To:" header. */
317 gotdate = gotfrom = gotto = gotsender = dodist = 0;
318 while (fgets (line, BUFSIZ, stdin) != NULL) {
319 if (line[0] == '\n') /* end of header */
321 if ( !isheader(line) )
324 /* if any of the following headers are present, then we
327 if ( !gotdate && uprf(line, "date") )
328 gotdate = dodist = 1;
330 else if ( !gotto && (uprf(line, "to") || uprf(line, "cc")) )
333 else if ( uprf(line, "message-id") )
336 else if ( !gotsender && uprf(line, "sender") )
337 gotsender = dodist = 1;
339 else if ( uprf ( line, "resent-" ) ) {
341 (void) fputs("Prev-", fp);
344 /* See if we are re-writing the from line */
345 if ( uprf(line, "from") ) {
350 (void) fputs(line,fp);
353 (void) fputs(line,fp);
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
361 (void) fprintf (fp, "%sTo: %s", (dodist ? "Resent-" : "" ), *argp++);
363 (void) fprintf ( fp, ",\n\t%s", *argp++ );
364 (void) fputs("\n",fp);
366 /* If we're doing a dist, we must have a "Date:" and "From:" field.
370 (void) fprintf (fp, "Date: %s\n", dtime (&lclock));
374 #ifdef MMDFI /* sigh */
376 (void) fprintf (fp, "Sender: %s\n", from);
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 */
383 if ( rewritefrom && uprf(line, "from"))
386 (void) fputs(line,fp);
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
395 if ( line[0] != '\n' ) /* i.e. a "body" line */
396 (void) fputc('\n', fp);
397 (void) fputs(line, fp);
400 static int isheader(s)
405 /* If the first character is a space, assume a continuation of a header */
409 /* If there's no ':', it's not a header */
410 if ( (cp = index(s,':')) == NULL )
413 /* If there's a space between BOL and ':', it's not a header */
422 /* This procedure does the verify and returns the status */
424 char *command, buf[BUFSIZ], *bp;
425 FILE *verfp, *popen();
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)))
436 (void) strcpy(command,postproc);
437 (void) strcat(command," -whom -check ");
439 (void) strcat(command, "-verbose " );
440 (void) strcat(command, msgfname);
442 /* open up the pipe */
443 if ( (verfp = popen(command,"r")) == NULL )
446 while ( fgets(buf, BUFSIZ, verfp) != NULL )
449 * so we need to strip the extra post headers.
456 (void) fputs(bp,stdout);
459 /* return the error status of post */
460 return( pclose(verfp) >> 8 );
463 static int sendfile()
465 char *command, buf[BUFSIZ];
466 FILE *verfp, *popen();
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)))
477 (void) strcpy(command,postproc);
478 (void) strcat(command," ");
480 (void) strcat(command, "-verbose " );
482 (void) strcat(command, "-dist " );
483 (void) strcat(command, msgfname);
485 /* open up the pipe */
486 if ( (verfp = popen(command,"r")) == NULL )
489 while ( fgets(buf, BUFSIZ, verfp) != NULL )
490 (void) fputs(buf,stdout);
492 /* return the error status of post */
493 return( pclose(verfp) >> 8 );
500 (void) sprintf(line, "From: %s <%s>\n", FullName, from);
502 (void) sprintf(line, "From: %s\n", from);
503 (void) fputs(line, fp);
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");
516 adios (NULLCP, "Problem reading body");
518 if ( fclose(fp) != 0 )
519 adios (NULLCP, "problem ending submission");
527 char buf[BUFSIZ], response[BUFSIZ];
529 if ((sd = client(NULLCP, "tcp", "smtp", 0, response)) == NOTOK)
530 adios (NULLCP, "cannot open smtp client process");
532 (void) signal(SIGCHLD, silentdie);
534 switch ((childid = fork())) {
536 adios (NULLCP, "unable to fork smtp process");
538 case OK: /* i.e. child */
542 default: /* i.e. parent */
546 while ( (len = read(0, buf, BUFSIZ)) > 0)
547 (void) write (1, buf, len);
550 (void) kill(childid, SIGHUP);
561 (void) unlink(msgfname);
564 (void) fprintf(stderr, "sendmail: dying from signal %d\n", sig);
570 TYPESIG silentdie(sig)
573 pidwait (childid, OK);