176b7bf71SPeter Wemm /* 2320f00e7SGregory Neil Shapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers. 33299c2f1SGregory Neil Shapiro * All rights reserved. 4c2aa98e2SPeter Wemm * Copyright (c) 1990, 1993, 1994 5c2aa98e2SPeter Wemm * The Regents of the University of California. All rights reserved. 6c2aa98e2SPeter Wemm * 7c2aa98e2SPeter Wemm * By using this file, you agree to the terms and conditions set 8c2aa98e2SPeter Wemm * forth in the LICENSE file which can be found at the top level of 9c2aa98e2SPeter Wemm * the sendmail distribution. 10c2aa98e2SPeter Wemm * 113edfa581SGregory Neil Shapiro * $FreeBSD$ 123edfa581SGregory Neil Shapiro * 13c2aa98e2SPeter Wemm */ 14c2aa98e2SPeter Wemm 1512ed1c7cSGregory Neil Shapiro #include <sm/gen.h> 1612ed1c7cSGregory Neil Shapiro 1712ed1c7cSGregory Neil Shapiro SM_IDSTR(copyright, 18b4662009SGregory Neil Shapiro "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 193299c2f1SGregory Neil Shapiro All rights reserved.\n\ 203299c2f1SGregory Neil Shapiro Copyright (c) 1990, 1993, 1994\n\ 2112ed1c7cSGregory Neil Shapiro The Regents of the University of California. All rights reserved.\n") 22c2aa98e2SPeter Wemm 232ef40764SGregory Neil Shapiro SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.239.2.2 2002/09/24 02:09:09 ca Exp $") 2412ed1c7cSGregory Neil Shapiro 2512ed1c7cSGregory Neil Shapiro #include <stdlib.h> 2612ed1c7cSGregory Neil Shapiro #include <sm/errstring.h> 2712ed1c7cSGregory Neil Shapiro #include <sm/io.h> 2812ed1c7cSGregory Neil Shapiro #include <sm/limits.h> 2912ed1c7cSGregory Neil Shapiro # include <unistd.h> 3012ed1c7cSGregory Neil Shapiro # ifdef EX_OK 3112ed1c7cSGregory Neil Shapiro # undef EX_OK /* unistd.h may have another use for this */ 3212ed1c7cSGregory Neil Shapiro # endif /* EX_OK */ 3312ed1c7cSGregory Neil Shapiro #include <sm/mbdb.h> 3412ed1c7cSGregory Neil Shapiro #include <sm/sysexits.h> 353299c2f1SGregory Neil Shapiro 36c2aa98e2SPeter Wemm /* 373299c2f1SGregory Neil Shapiro ** This is not intended to work on System V derived systems 383299c2f1SGregory Neil Shapiro ** such as Solaris or HP-UX, since they use a totally different 3912ed1c7cSGregory Neil Shapiro ** approach to mailboxes (essentially, they have a set-group-ID program 4012ed1c7cSGregory Neil Shapiro ** rather than set-user-ID, and they rely on the ability to "give away" 413299c2f1SGregory Neil Shapiro ** files to do their work). IT IS NOT A BUG that this doesn't 423299c2f1SGregory Neil Shapiro ** work on such architectures. 43c2aa98e2SPeter Wemm */ 44c2aa98e2SPeter Wemm 453299c2f1SGregory Neil Shapiro 4612ed1c7cSGregory Neil Shapiro #include <stdio.h> 4712ed1c7cSGregory Neil Shapiro #include <errno.h> 4812ed1c7cSGregory Neil Shapiro #include <fcntl.h> 4912ed1c7cSGregory Neil Shapiro #include <sys/types.h> 5012ed1c7cSGregory Neil Shapiro #include <sys/stat.h> 5112ed1c7cSGregory Neil Shapiro #include <time.h> 5212ed1c7cSGregory Neil Shapiro #include <stdlib.h> 5312ed1c7cSGregory Neil Shapiro # include <sys/socket.h> 5412ed1c7cSGregory Neil Shapiro # include <sys/file.h> 5512ed1c7cSGregory Neil Shapiro # include <netinet/in.h> 5612ed1c7cSGregory Neil Shapiro # include <arpa/nameser.h> 5712ed1c7cSGregory Neil Shapiro # include <netdb.h> 5812ed1c7cSGregory Neil Shapiro # include <pwd.h> 5912ed1c7cSGregory Neil Shapiro 6012ed1c7cSGregory Neil Shapiro #include <sm/string.h> 6112ed1c7cSGregory Neil Shapiro #include <syslog.h> 6212ed1c7cSGregory Neil Shapiro #include <ctype.h> 6312ed1c7cSGregory Neil Shapiro 6412ed1c7cSGregory Neil Shapiro #include <sm/conf.h> 6512ed1c7cSGregory Neil Shapiro #include <sendmail/pathnames.h> 6612ed1c7cSGregory Neil Shapiro 6712ed1c7cSGregory Neil Shapiro 68d995d2baSGregory Neil Shapiro /* additional mode for open() */ 69d995d2baSGregory Neil Shapiro # define EXTRA_MODE 0 70d995d2baSGregory Neil Shapiro 713299c2f1SGregory Neil Shapiro 723299c2f1SGregory Neil Shapiro #ifndef LOCKTO_RM 733299c2f1SGregory Neil Shapiro # define LOCKTO_RM 300 /* timeout for stale lockfile removal */ 74d995d2baSGregory Neil Shapiro #endif /* ! LOCKTO_RM */ 753299c2f1SGregory Neil Shapiro #ifndef LOCKTO_GLOB 763299c2f1SGregory Neil Shapiro # define LOCKTO_GLOB 400 /* global timeout for lockfile creation */ 77d995d2baSGregory Neil Shapiro #endif /* ! LOCKTO_GLOB */ 783299c2f1SGregory Neil Shapiro 793299c2f1SGregory Neil Shapiro /* define a realloc() which works for NULL pointers */ 803299c2f1SGregory Neil Shapiro #define REALLOC(ptr, size) (((ptr) == NULL) ? malloc(size) : realloc(ptr, size)) 81c2aa98e2SPeter Wemm 82c2aa98e2SPeter Wemm /* 8312ed1c7cSGregory Neil Shapiro ** If you don't have flock, you could try using lockf instead. 84c2aa98e2SPeter Wemm */ 85c2aa98e2SPeter Wemm 8612ed1c7cSGregory Neil Shapiro #ifdef LDA_USE_LOCKF 87c2aa98e2SPeter Wemm # define flock(a, b) lockf(a, b, 0) 883299c2f1SGregory Neil Shapiro # ifdef LOCK_EX 893299c2f1SGregory Neil Shapiro # undef LOCK_EX 903299c2f1SGregory Neil Shapiro # endif /* LOCK_EX */ 91c2aa98e2SPeter Wemm # define LOCK_EX F_LOCK 9212ed1c7cSGregory Neil Shapiro #endif /* LDA_USE_LOCKF */ 93c2aa98e2SPeter Wemm 94c2aa98e2SPeter Wemm #ifndef LOCK_EX 95c2aa98e2SPeter Wemm # include <sys/file.h> 963299c2f1SGregory Neil Shapiro #endif /* ! LOCK_EX */ 97c2aa98e2SPeter Wemm 98c2aa98e2SPeter Wemm /* 993299c2f1SGregory Neil Shapiro ** If you don't have setreuid, and you have saved uids, and you have 1003299c2f1SGregory Neil Shapiro ** a seteuid() call that doesn't try to emulate using setuid(), then 10112ed1c7cSGregory Neil Shapiro ** you can try defining LDA_USE_SETEUID. 102c2aa98e2SPeter Wemm */ 103b4662009SGregory Neil Shapiro 10412ed1c7cSGregory Neil Shapiro #ifdef LDA_USE_SETEUID 105c2aa98e2SPeter Wemm # define setreuid(r, e) seteuid(e) 10612ed1c7cSGregory Neil Shapiro #endif /* LDA_USE_SETEUID */ 107c2aa98e2SPeter Wemm 10812ed1c7cSGregory Neil Shapiro #ifdef LDA_CONTENTLENGTH 10912ed1c7cSGregory Neil Shapiro # define CONTENTLENGTH 1 11012ed1c7cSGregory Neil Shapiro #endif /* LDA_CONTENTLENGTH */ 111d995d2baSGregory Neil Shapiro 1123299c2f1SGregory Neil Shapiro #ifndef INADDRSZ 1133299c2f1SGregory Neil Shapiro # define INADDRSZ 4 /* size of an IPv4 address in bytes */ 1143299c2f1SGregory Neil Shapiro #endif /* ! INADDRSZ */ 115c2aa98e2SPeter Wemm 11612ed1c7cSGregory Neil Shapiro #ifdef MAILLOCK 11712ed1c7cSGregory Neil Shapiro # include <maillock.h> 11812ed1c7cSGregory Neil Shapiro #endif /* MAILLOCK */ 11912ed1c7cSGregory Neil Shapiro 12076b7bf71SPeter Wemm #ifndef MAILER_DAEMON 12176b7bf71SPeter Wemm # define MAILER_DAEMON "MAILER-DAEMON" 1223299c2f1SGregory Neil Shapiro #endif /* ! MAILER_DAEMON */ 12376b7bf71SPeter Wemm 1243299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 1253299c2f1SGregory Neil Shapiro char ContentHdr[40] = "Content-Length: "; 1263299c2f1SGregory Neil Shapiro off_t HeaderLength; 1273299c2f1SGregory Neil Shapiro off_t BodyLength; 1283299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 129c2aa98e2SPeter Wemm 13012ed1c7cSGregory Neil Shapiro bool EightBitMime = true; /* advertise 8BITMIME in LMTP */ 131b4662009SGregory Neil Shapiro char ErrBuf[10240]; /* error buffer */ 1323299c2f1SGregory Neil Shapiro int ExitVal = EX_OK; /* sysexits.h error value. */ 13312ed1c7cSGregory Neil Shapiro bool nobiff = false; 13412ed1c7cSGregory Neil Shapiro bool nofsync = false; 13512ed1c7cSGregory Neil Shapiro bool HoldErrs = false; /* Hold errors in ErrBuf */ 13612ed1c7cSGregory Neil Shapiro bool LMTPMode = false; 13712ed1c7cSGregory Neil Shapiro bool BounceQuota = false; /* permanent error when over quota */ 13812ed1c7cSGregory Neil Shapiro char *HomeMailFile = NULL; /* store mail in homedir */ 1393299c2f1SGregory Neil Shapiro 140b4662009SGregory Neil Shapiro void deliver __P((int, char *)); 1413299c2f1SGregory Neil Shapiro int e_to_sys __P((int)); 142c2aa98e2SPeter Wemm void notifybiff __P((char *)); 14312ed1c7cSGregory Neil Shapiro int store __P((char *, bool *)); 144c2aa98e2SPeter Wemm void usage __P((void)); 1453299c2f1SGregory Neil Shapiro int lockmbox __P((char *)); 146c2aa98e2SPeter Wemm void unlockmbox __P((void)); 147c2aa98e2SPeter Wemm void mailerr __P((const char *, const char *, ...)); 148b4662009SGregory Neil Shapiro void flush_error __P((void)); 1493299c2f1SGregory Neil Shapiro 150c2aa98e2SPeter Wemm 151c2aa98e2SPeter Wemm int 152c2aa98e2SPeter Wemm main(argc, argv) 153c2aa98e2SPeter Wemm int argc; 154c2aa98e2SPeter Wemm char *argv[]; 155c2aa98e2SPeter Wemm { 156c2aa98e2SPeter Wemm struct passwd *pw; 1573299c2f1SGregory Neil Shapiro int ch, fd; 158c2aa98e2SPeter Wemm uid_t uid; 159c2aa98e2SPeter Wemm char *from; 16012ed1c7cSGregory Neil Shapiro char *mbdbname = "pw"; 16112ed1c7cSGregory Neil Shapiro int err; 162c2aa98e2SPeter Wemm extern char *optarg; 163c2aa98e2SPeter Wemm extern int optind; 1643299c2f1SGregory Neil Shapiro 165c2aa98e2SPeter Wemm 166c2aa98e2SPeter Wemm /* make sure we have some open file descriptors */ 167c2aa98e2SPeter Wemm for (fd = 10; fd < 30; fd++) 168c2aa98e2SPeter Wemm (void) close(fd); 169c2aa98e2SPeter Wemm 170c2aa98e2SPeter Wemm /* use a reasonable umask */ 171c2aa98e2SPeter Wemm (void) umask(0077); 172c2aa98e2SPeter Wemm 173c2aa98e2SPeter Wemm # ifdef LOG_MAIL 174c2aa98e2SPeter Wemm openlog("mail.local", 0, LOG_MAIL); 1753299c2f1SGregory Neil Shapiro # else /* LOG_MAIL */ 176c2aa98e2SPeter Wemm openlog("mail.local", 0); 1773299c2f1SGregory Neil Shapiro # endif /* LOG_MAIL */ 178c2aa98e2SPeter Wemm 179c2aa98e2SPeter Wemm from = NULL; 18012ed1c7cSGregory Neil Shapiro while ((ch = getopt(argc, argv, "7BbdD:f:h:r:ls")) != -1) 1813299c2f1SGregory Neil Shapiro { 1823299c2f1SGregory Neil Shapiro switch(ch) 1833299c2f1SGregory Neil Shapiro { 1843299c2f1SGregory Neil Shapiro case '7': /* Do not advertise 8BITMIME */ 18512ed1c7cSGregory Neil Shapiro EightBitMime = false; 186d615a192SPeter Wemm break; 1873299c2f1SGregory Neil Shapiro 1883299c2f1SGregory Neil Shapiro case 'B': 18939e37e72SGregory Neil Shapiro nobiff = true; 1903299c2f1SGregory Neil Shapiro break; 1913299c2f1SGregory Neil Shapiro 1923299c2f1SGregory Neil Shapiro case 'b': /* bounce mail when over quota. */ 19312ed1c7cSGregory Neil Shapiro BounceQuota = true; 1943299c2f1SGregory Neil Shapiro break; 1953299c2f1SGregory Neil Shapiro 196c2aa98e2SPeter Wemm case 'd': /* Backward compatible. */ 197c2aa98e2SPeter Wemm break; 1983299c2f1SGregory Neil Shapiro 19912ed1c7cSGregory Neil Shapiro case 'D': /* mailbox database type */ 20012ed1c7cSGregory Neil Shapiro mbdbname = optarg; 20112ed1c7cSGregory Neil Shapiro break; 20212ed1c7cSGregory Neil Shapiro 203c2aa98e2SPeter Wemm case 'f': 204c2aa98e2SPeter Wemm case 'r': /* Backward compatible. */ 2053299c2f1SGregory Neil Shapiro if (from != NULL) 2063299c2f1SGregory Neil Shapiro { 207b4662009SGregory Neil Shapiro mailerr(NULL, "Multiple -f options"); 208c2aa98e2SPeter Wemm usage(); 209c2aa98e2SPeter Wemm } 210c2aa98e2SPeter Wemm from = optarg; 211c2aa98e2SPeter Wemm break; 2123299c2f1SGregory Neil Shapiro 21312ed1c7cSGregory Neil Shapiro case 'h': 21412ed1c7cSGregory Neil Shapiro if (optarg != NULL || *optarg != '\0') 21512ed1c7cSGregory Neil Shapiro HomeMailFile = optarg; 21612ed1c7cSGregory Neil Shapiro else 21712ed1c7cSGregory Neil Shapiro { 21812ed1c7cSGregory Neil Shapiro mailerr(NULL, "-h: missing filename"); 21912ed1c7cSGregory Neil Shapiro usage(); 22012ed1c7cSGregory Neil Shapiro } 22112ed1c7cSGregory Neil Shapiro break; 22212ed1c7cSGregory Neil Shapiro 223c2aa98e2SPeter Wemm case 'l': 22412ed1c7cSGregory Neil Shapiro LMTPMode = true; 225c2aa98e2SPeter Wemm break; 2263299c2f1SGregory Neil Shapiro 227d615a192SPeter Wemm case 's': 22805b73c60SPeter Wemm nofsync++; 229d615a192SPeter Wemm break; 2303299c2f1SGregory Neil Shapiro 231c2aa98e2SPeter Wemm case '?': 232c2aa98e2SPeter Wemm default: 233c2aa98e2SPeter Wemm usage(); 234c2aa98e2SPeter Wemm } 2353299c2f1SGregory Neil Shapiro } 236c2aa98e2SPeter Wemm argc -= optind; 237c2aa98e2SPeter Wemm argv += optind; 238c2aa98e2SPeter Wemm 2393299c2f1SGregory Neil Shapiro /* initialize biff structures */ 2403299c2f1SGregory Neil Shapiro if (!nobiff) 2413299c2f1SGregory Neil Shapiro notifybiff(NULL); 242c2aa98e2SPeter Wemm 24312ed1c7cSGregory Neil Shapiro err = sm_mbdb_initialize(mbdbname); 24412ed1c7cSGregory Neil Shapiro if (err != EX_OK) 24512ed1c7cSGregory Neil Shapiro { 24612ed1c7cSGregory Neil Shapiro char *errcode = "521"; 24712ed1c7cSGregory Neil Shapiro 24812ed1c7cSGregory Neil Shapiro if (err == EX_TEMPFAIL) 24912ed1c7cSGregory Neil Shapiro errcode = "421"; 25012ed1c7cSGregory Neil Shapiro 25112ed1c7cSGregory Neil Shapiro mailerr(errcode, "Can not open mailbox database %s: %s", 25212ed1c7cSGregory Neil Shapiro mbdbname, sm_strexit(err)); 25312ed1c7cSGregory Neil Shapiro exit(err); 25412ed1c7cSGregory Neil Shapiro } 25512ed1c7cSGregory Neil Shapiro 2563299c2f1SGregory Neil Shapiro if (LMTPMode) 257b4662009SGregory Neil Shapiro { 258b4662009SGregory Neil Shapiro extern void dolmtp __P((void)); 2593299c2f1SGregory Neil Shapiro 260b4662009SGregory Neil Shapiro if (argc > 0) 261b4662009SGregory Neil Shapiro { 262b4662009SGregory Neil Shapiro mailerr("421", "Users should not be specified in command line if LMTP required"); 263b4662009SGregory Neil Shapiro exit(EX_TEMPFAIL); 264b4662009SGregory Neil Shapiro } 265b4662009SGregory Neil Shapiro 266b4662009SGregory Neil Shapiro dolmtp(); 267b4662009SGregory Neil Shapiro /* NOTREACHED */ 268b4662009SGregory Neil Shapiro exit(EX_OK); 269b4662009SGregory Neil Shapiro } 270b4662009SGregory Neil Shapiro 271b4662009SGregory Neil Shapiro /* Non-LMTP from here on out */ 2723299c2f1SGregory Neil Shapiro if (*argv == '\0') 273c2aa98e2SPeter Wemm usage(); 274c2aa98e2SPeter Wemm 275c2aa98e2SPeter Wemm /* 2763299c2f1SGregory Neil Shapiro ** If from not specified, use the name from getlogin() if the 2773299c2f1SGregory Neil Shapiro ** uid matches, otherwise, use the name from the password file 2783299c2f1SGregory Neil Shapiro ** corresponding to the uid. 279c2aa98e2SPeter Wemm */ 280b4662009SGregory Neil Shapiro 281c2aa98e2SPeter Wemm uid = getuid(); 2823299c2f1SGregory Neil Shapiro if (from == NULL && ((from = getlogin()) == NULL || 2833299c2f1SGregory Neil Shapiro (pw = getpwnam(from)) == NULL || 2843299c2f1SGregory Neil Shapiro pw->pw_uid != uid)) 2853299c2f1SGregory Neil Shapiro from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???"; 286c2aa98e2SPeter Wemm 287c2aa98e2SPeter Wemm /* 2883299c2f1SGregory Neil Shapiro ** There is no way to distinguish the error status of one delivery 2893299c2f1SGregory Neil Shapiro ** from the rest of the deliveries. So, if we failed hard on one 2903299c2f1SGregory Neil Shapiro ** or more deliveries, but had no failures on any of the others, we 2913299c2f1SGregory Neil Shapiro ** return a hard failure. If we failed temporarily on one or more 2923299c2f1SGregory Neil Shapiro ** deliveries, we return a temporary failure regardless of the other 2933299c2f1SGregory Neil Shapiro ** failures. This results in the delivery being reattempted later 2943299c2f1SGregory Neil Shapiro ** at the expense of repeated failures and multiple deliveries. 295c2aa98e2SPeter Wemm */ 296b4662009SGregory Neil Shapiro 29712ed1c7cSGregory Neil Shapiro HoldErrs = true; 29812ed1c7cSGregory Neil Shapiro fd = store(from, NULL); 29912ed1c7cSGregory Neil Shapiro HoldErrs = false; 300b4662009SGregory Neil Shapiro if (fd < 0) 301b4662009SGregory Neil Shapiro { 302b4662009SGregory Neil Shapiro flush_error(); 303b4662009SGregory Neil Shapiro exit(ExitVal); 304b4662009SGregory Neil Shapiro } 305b4662009SGregory Neil Shapiro for (; *argv != NULL; ++argv) 306b4662009SGregory Neil Shapiro deliver(fd, *argv); 3073299c2f1SGregory Neil Shapiro exit(ExitVal); 3083299c2f1SGregory Neil Shapiro /* NOTREACHED */ 3093299c2f1SGregory Neil Shapiro return ExitVal; 310c2aa98e2SPeter Wemm } 311c2aa98e2SPeter Wemm 312c2aa98e2SPeter Wemm char * 3133299c2f1SGregory Neil Shapiro parseaddr(s, rcpt) 314c2aa98e2SPeter Wemm char *s; 3153299c2f1SGregory Neil Shapiro bool rcpt; 316c2aa98e2SPeter Wemm { 317c2aa98e2SPeter Wemm char *p; 3183299c2f1SGregory Neil Shapiro int l; 319c2aa98e2SPeter Wemm 320c2aa98e2SPeter Wemm if (*s++ != '<') 321c2aa98e2SPeter Wemm return NULL; 322c2aa98e2SPeter Wemm 323c2aa98e2SPeter Wemm p = s; 324c2aa98e2SPeter Wemm 325c2aa98e2SPeter Wemm /* at-domain-list */ 3263299c2f1SGregory Neil Shapiro while (*p == '@') 3273299c2f1SGregory Neil Shapiro { 328c2aa98e2SPeter Wemm p++; 3293299c2f1SGregory Neil Shapiro while (*p != ',' && *p != ':' && *p != '\0') 330c2aa98e2SPeter Wemm p++; 3313299c2f1SGregory Neil Shapiro if (*p == '\0') 332c2aa98e2SPeter Wemm return NULL; 3333299c2f1SGregory Neil Shapiro 3343299c2f1SGregory Neil Shapiro /* Skip over , or : */ 335c2aa98e2SPeter Wemm p++; 336c2aa98e2SPeter Wemm } 337c2aa98e2SPeter Wemm 33876b7bf71SPeter Wemm s = p; 33976b7bf71SPeter Wemm 340c2aa98e2SPeter Wemm /* local-part */ 3413299c2f1SGregory Neil Shapiro while (*p != '\0' && *p != '@' && *p != '>') 3423299c2f1SGregory Neil Shapiro { 3433299c2f1SGregory Neil Shapiro if (*p == '\\') 3443299c2f1SGregory Neil Shapiro { 3453299c2f1SGregory Neil Shapiro if (*++p == '\0') 3463299c2f1SGregory Neil Shapiro return NULL; 3473299c2f1SGregory Neil Shapiro } 3483299c2f1SGregory Neil Shapiro else if (*p == '\"') 3493299c2f1SGregory Neil Shapiro { 350c2aa98e2SPeter Wemm p++; 3513299c2f1SGregory Neil Shapiro while (*p != '\0' && *p != '\"') 3523299c2f1SGregory Neil Shapiro { 3533299c2f1SGregory Neil Shapiro if (*p == '\\') 3543299c2f1SGregory Neil Shapiro { 3553299c2f1SGregory Neil Shapiro if (*++p == '\0') 356c2aa98e2SPeter Wemm return NULL; 357c2aa98e2SPeter Wemm } 358c2aa98e2SPeter Wemm p++; 359c2aa98e2SPeter Wemm } 3603299c2f1SGregory Neil Shapiro if (*p == '\0' || *(p + 1) == '\0') 361c2aa98e2SPeter Wemm return NULL; 362c2aa98e2SPeter Wemm } 3633299c2f1SGregory Neil Shapiro /* +detail ? */ 3643299c2f1SGregory Neil Shapiro if (*p == '+' && rcpt) 3653299c2f1SGregory Neil Shapiro *p = '\0'; 366c2aa98e2SPeter Wemm p++; 367c2aa98e2SPeter Wemm } 368c2aa98e2SPeter Wemm 369c2aa98e2SPeter Wemm /* @domain */ 3703299c2f1SGregory Neil Shapiro if (*p == '@') 37176b7bf71SPeter Wemm { 3723299c2f1SGregory Neil Shapiro if (rcpt) 3733299c2f1SGregory Neil Shapiro *p++ = '\0'; 3743299c2f1SGregory Neil Shapiro while (*p != '\0' && *p != '>') 3753299c2f1SGregory Neil Shapiro p++; 37676b7bf71SPeter Wemm } 377c2aa98e2SPeter Wemm 3783299c2f1SGregory Neil Shapiro if (*p != '>') 3793299c2f1SGregory Neil Shapiro return NULL; 3803299c2f1SGregory Neil Shapiro else 3813299c2f1SGregory Neil Shapiro *p = '\0'; 3823299c2f1SGregory Neil Shapiro p++; 3833299c2f1SGregory Neil Shapiro 3843299c2f1SGregory Neil Shapiro if (*p != '\0' && *p != ' ') 3853299c2f1SGregory Neil Shapiro return NULL; 3863299c2f1SGregory Neil Shapiro 3873299c2f1SGregory Neil Shapiro if (*s == '\0') 3883299c2f1SGregory Neil Shapiro s = MAILER_DAEMON; 3893299c2f1SGregory Neil Shapiro 3903299c2f1SGregory Neil Shapiro l = strlen(s) + 1; 39112ed1c7cSGregory Neil Shapiro if (l < 0) 39212ed1c7cSGregory Neil Shapiro return NULL; 3933299c2f1SGregory Neil Shapiro p = malloc(l); 3943299c2f1SGregory Neil Shapiro if (p == NULL) 3953299c2f1SGregory Neil Shapiro { 396b4662009SGregory Neil Shapiro mailerr("421 4.3.0", "Memory exhausted"); 397c2aa98e2SPeter Wemm exit(EX_TEMPFAIL); 398c2aa98e2SPeter Wemm } 399c2aa98e2SPeter Wemm 40012ed1c7cSGregory Neil Shapiro (void) sm_strlcpy(p, s, l); 401c2aa98e2SPeter Wemm return p; 402c2aa98e2SPeter Wemm } 403c2aa98e2SPeter Wemm 404c2aa98e2SPeter Wemm char * 405c2aa98e2SPeter Wemm process_recipient(addr) 406c2aa98e2SPeter Wemm char *addr; 407c2aa98e2SPeter Wemm { 40812ed1c7cSGregory Neil Shapiro SM_MBDB_T user; 40912ed1c7cSGregory Neil Shapiro 41012ed1c7cSGregory Neil Shapiro switch (sm_mbdb_lookup(addr, &user)) 41112ed1c7cSGregory Neil Shapiro { 41212ed1c7cSGregory Neil Shapiro case EX_OK: 413c2aa98e2SPeter Wemm return NULL; 41412ed1c7cSGregory Neil Shapiro 41512ed1c7cSGregory Neil Shapiro case EX_NOUSER: 41612ed1c7cSGregory Neil Shapiro return "550 5.1.1 User unknown"; 41712ed1c7cSGregory Neil Shapiro 41812ed1c7cSGregory Neil Shapiro case EX_TEMPFAIL: 41912ed1c7cSGregory Neil Shapiro return "451 4.3.0 User database failure; retry later"; 42012ed1c7cSGregory Neil Shapiro 42112ed1c7cSGregory Neil Shapiro default: 42212ed1c7cSGregory Neil Shapiro return "550 5.3.0 User database failure"; 42312ed1c7cSGregory Neil Shapiro } 424c2aa98e2SPeter Wemm } 425c2aa98e2SPeter Wemm 426c2aa98e2SPeter Wemm #define RCPT_GROW 30 427c2aa98e2SPeter Wemm 428c2aa98e2SPeter Wemm void 429b4662009SGregory Neil Shapiro dolmtp() 430c2aa98e2SPeter Wemm { 431c2aa98e2SPeter Wemm char *return_path = NULL; 432c2aa98e2SPeter Wemm char **rcpt_addr = NULL; 433c2aa98e2SPeter Wemm int rcpt_num = 0; 434c2aa98e2SPeter Wemm int rcpt_alloc = 0; 43512ed1c7cSGregory Neil Shapiro bool gotlhlo = false; 436c2aa98e2SPeter Wemm char *err; 437c2aa98e2SPeter Wemm int msgfd; 438c2aa98e2SPeter Wemm char *p; 439c2aa98e2SPeter Wemm int i; 4403299c2f1SGregory Neil Shapiro char myhostname[1024]; 4413299c2f1SGregory Neil Shapiro char buf[4096]; 442c2aa98e2SPeter Wemm 443b4662009SGregory Neil Shapiro memset(myhostname, '\0', sizeof myhostname); 4443299c2f1SGregory Neil Shapiro (void) gethostname(myhostname, sizeof myhostname - 1); 445b4662009SGregory Neil Shapiro if (myhostname[0] == '\0') 44612ed1c7cSGregory Neil Shapiro sm_strlcpy(myhostname, "localhost", sizeof myhostname); 447c2aa98e2SPeter Wemm 448c2aa98e2SPeter Wemm printf("220 %s LMTP ready\r\n", myhostname); 4493299c2f1SGregory Neil Shapiro for (;;) 4503299c2f1SGregory Neil Shapiro { 4513299c2f1SGregory Neil Shapiro (void) fflush(stdout); 4523299c2f1SGregory Neil Shapiro if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) 453c2aa98e2SPeter Wemm exit(EX_OK); 454c2aa98e2SPeter Wemm p = buf + strlen(buf) - 1; 455c2aa98e2SPeter Wemm if (p >= buf && *p == '\n') 456c2aa98e2SPeter Wemm *p-- = '\0'; 457c2aa98e2SPeter Wemm if (p >= buf && *p == '\r') 458c2aa98e2SPeter Wemm *p-- = '\0'; 459c2aa98e2SPeter Wemm 4603299c2f1SGregory Neil Shapiro switch (buf[0]) 4613299c2f1SGregory Neil Shapiro { 462c2aa98e2SPeter Wemm case 'd': 463c2aa98e2SPeter Wemm case 'D': 46412ed1c7cSGregory Neil Shapiro if (sm_strcasecmp(buf, "data") == 0) 4653299c2f1SGregory Neil Shapiro { 46612ed1c7cSGregory Neil Shapiro bool inbody = false; 467b4662009SGregory Neil Shapiro 4683299c2f1SGregory Neil Shapiro if (rcpt_num == 0) 4693299c2f1SGregory Neil Shapiro { 470b4662009SGregory Neil Shapiro mailerr("503 5.5.1", "No recipients"); 471c2aa98e2SPeter Wemm continue; 472c2aa98e2SPeter Wemm } 47312ed1c7cSGregory Neil Shapiro HoldErrs = true; 47412ed1c7cSGregory Neil Shapiro msgfd = store(return_path, &inbody); 47512ed1c7cSGregory Neil Shapiro HoldErrs = false; 476b4662009SGregory Neil Shapiro if (msgfd < 0 && !inbody) 477b4662009SGregory Neil Shapiro { 478b4662009SGregory Neil Shapiro flush_error(); 479c2aa98e2SPeter Wemm continue; 480b4662009SGregory Neil Shapiro } 481c2aa98e2SPeter Wemm 4823299c2f1SGregory Neil Shapiro for (i = 0; i < rcpt_num; i++) 4833299c2f1SGregory Neil Shapiro { 484b4662009SGregory Neil Shapiro if (msgfd < 0) 485b4662009SGregory Neil Shapiro { 486b4662009SGregory Neil Shapiro /* print error for rcpt */ 487b4662009SGregory Neil Shapiro flush_error(); 488b4662009SGregory Neil Shapiro continue; 489b4662009SGregory Neil Shapiro } 490c2aa98e2SPeter Wemm p = strchr(rcpt_addr[i], '+'); 491c2aa98e2SPeter Wemm if (p != NULL) 492b4662009SGregory Neil Shapiro *p = '\0'; 493b4662009SGregory Neil Shapiro deliver(msgfd, rcpt_addr[i]); 494c2aa98e2SPeter Wemm } 495b4662009SGregory Neil Shapiro if (msgfd >= 0) 4963299c2f1SGregory Neil Shapiro (void) close(msgfd); 497c2aa98e2SPeter Wemm goto rset; 498c2aa98e2SPeter Wemm } 499c2aa98e2SPeter Wemm goto syntaxerr; 5003299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5013299c2f1SGregory Neil Shapiro break; 502c2aa98e2SPeter Wemm 503c2aa98e2SPeter Wemm case 'l': 504c2aa98e2SPeter Wemm case 'L': 50512ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf, "lhlo ", 5) == 0) 5063299c2f1SGregory Neil Shapiro { 5073299c2f1SGregory Neil Shapiro /* check for duplicate per RFC 1651 4.2 */ 5083299c2f1SGregory Neil Shapiro if (gotlhlo) 5093299c2f1SGregory Neil Shapiro { 510b4662009SGregory Neil Shapiro mailerr("503", "%s Duplicate LHLO", 511c2aa98e2SPeter Wemm myhostname); 512c2aa98e2SPeter Wemm continue; 513c2aa98e2SPeter Wemm } 51412ed1c7cSGregory Neil Shapiro gotlhlo = true; 5153299c2f1SGregory Neil Shapiro printf("250-%s\r\n", myhostname); 5163299c2f1SGregory Neil Shapiro if (EightBitMime) 5173299c2f1SGregory Neil Shapiro printf("250-8BITMIME\r\n"); 5183299c2f1SGregory Neil Shapiro printf("250-ENHANCEDSTATUSCODES\r\n"); 5193299c2f1SGregory Neil Shapiro printf("250 PIPELINING\r\n"); 5203299c2f1SGregory Neil Shapiro continue; 5213299c2f1SGregory Neil Shapiro } 522c2aa98e2SPeter Wemm goto syntaxerr; 5233299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5243299c2f1SGregory Neil Shapiro break; 525c2aa98e2SPeter Wemm 526c2aa98e2SPeter Wemm case 'm': 527c2aa98e2SPeter Wemm case 'M': 52812ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf, "mail ", 5) == 0) 5293299c2f1SGregory Neil Shapiro { 5303299c2f1SGregory Neil Shapiro if (return_path != NULL) 5313299c2f1SGregory Neil Shapiro { 532b4662009SGregory Neil Shapiro mailerr("503 5.5.1", 533b4662009SGregory Neil Shapiro "Nested MAIL command"); 534c2aa98e2SPeter Wemm continue; 535c2aa98e2SPeter Wemm } 53612ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf + 5, "from:", 5) != 0 || 5373299c2f1SGregory Neil Shapiro ((return_path = parseaddr(buf + 10, 53812ed1c7cSGregory Neil Shapiro false)) == NULL)) 5393299c2f1SGregory Neil Shapiro { 540b4662009SGregory Neil Shapiro mailerr("501 5.5.4", 541b4662009SGregory Neil Shapiro "Syntax error in parameters"); 542c2aa98e2SPeter Wemm continue; 543c2aa98e2SPeter Wemm } 544b4662009SGregory Neil Shapiro printf("250 2.5.0 Ok\r\n"); 545c2aa98e2SPeter Wemm continue; 546c2aa98e2SPeter Wemm } 547c2aa98e2SPeter Wemm goto syntaxerr; 5483299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5493299c2f1SGregory Neil Shapiro break; 550c2aa98e2SPeter Wemm 551c2aa98e2SPeter Wemm case 'n': 552c2aa98e2SPeter Wemm case 'N': 55312ed1c7cSGregory Neil Shapiro if (sm_strcasecmp(buf, "noop") == 0) 5543299c2f1SGregory Neil Shapiro { 555b4662009SGregory Neil Shapiro printf("250 2.0.0 Ok\r\n"); 556c2aa98e2SPeter Wemm continue; 557c2aa98e2SPeter Wemm } 558c2aa98e2SPeter Wemm goto syntaxerr; 5593299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5603299c2f1SGregory Neil Shapiro break; 561c2aa98e2SPeter Wemm 562c2aa98e2SPeter Wemm case 'q': 563c2aa98e2SPeter Wemm case 'Q': 56412ed1c7cSGregory Neil Shapiro if (sm_strcasecmp(buf, "quit") == 0) 5653299c2f1SGregory Neil Shapiro { 566b4662009SGregory Neil Shapiro printf("221 2.0.0 Bye\r\n"); 567c2aa98e2SPeter Wemm exit(EX_OK); 568c2aa98e2SPeter Wemm } 569c2aa98e2SPeter Wemm goto syntaxerr; 5703299c2f1SGregory Neil Shapiro /* NOTREACHED */ 5713299c2f1SGregory Neil Shapiro break; 572c2aa98e2SPeter Wemm 573c2aa98e2SPeter Wemm case 'r': 574c2aa98e2SPeter Wemm case 'R': 57512ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf, "rcpt ", 5) == 0) 5763299c2f1SGregory Neil Shapiro { 5773299c2f1SGregory Neil Shapiro if (return_path == NULL) 5783299c2f1SGregory Neil Shapiro { 579b4662009SGregory Neil Shapiro mailerr("503 5.5.1", 580b4662009SGregory Neil Shapiro "Need MAIL command"); 581c2aa98e2SPeter Wemm continue; 582c2aa98e2SPeter Wemm } 5833299c2f1SGregory Neil Shapiro if (rcpt_num >= rcpt_alloc) 5843299c2f1SGregory Neil Shapiro { 585c2aa98e2SPeter Wemm rcpt_alloc += RCPT_GROW; 586c2aa98e2SPeter Wemm rcpt_addr = (char **) 5873299c2f1SGregory Neil Shapiro REALLOC((char *) rcpt_addr, 5883299c2f1SGregory Neil Shapiro rcpt_alloc * 5893299c2f1SGregory Neil Shapiro sizeof(char **)); 5903299c2f1SGregory Neil Shapiro if (rcpt_addr == NULL) 5913299c2f1SGregory Neil Shapiro { 592b4662009SGregory Neil Shapiro mailerr("421 4.3.0", 593b4662009SGregory Neil Shapiro "Memory exhausted"); 594c2aa98e2SPeter Wemm exit(EX_TEMPFAIL); 595c2aa98e2SPeter Wemm } 596c2aa98e2SPeter Wemm } 59712ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf + 5, "to:", 3) != 0 || 5983299c2f1SGregory Neil Shapiro ((rcpt_addr[rcpt_num] = parseaddr(buf + 8, 59912ed1c7cSGregory Neil Shapiro true)) == NULL)) 6003299c2f1SGregory Neil Shapiro { 601b4662009SGregory Neil Shapiro mailerr("501 5.5.4", 602b4662009SGregory Neil Shapiro "Syntax error in parameters"); 603c2aa98e2SPeter Wemm continue; 604c2aa98e2SPeter Wemm } 605b4662009SGregory Neil Shapiro err = process_recipient(rcpt_addr[rcpt_num]); 606b4662009SGregory Neil Shapiro if (err != NULL) 6073299c2f1SGregory Neil Shapiro { 608b4662009SGregory Neil Shapiro mailerr(NULL, "%s", err); 609c2aa98e2SPeter Wemm continue; 610c2aa98e2SPeter Wemm } 611c2aa98e2SPeter Wemm rcpt_num++; 612b4662009SGregory Neil Shapiro printf("250 2.1.5 Ok\r\n"); 613c2aa98e2SPeter Wemm continue; 614c2aa98e2SPeter Wemm } 61512ed1c7cSGregory Neil Shapiro else if (sm_strcasecmp(buf, "rset") == 0) 6163299c2f1SGregory Neil Shapiro { 617b4662009SGregory Neil Shapiro printf("250 2.0.0 Ok\r\n"); 618c2aa98e2SPeter Wemm 619c2aa98e2SPeter Wemm rset: 620c46d91b7SGregory Neil Shapiro while (rcpt_num > 0) 621c2aa98e2SPeter Wemm free(rcpt_addr[--rcpt_num]); 622c2aa98e2SPeter Wemm if (return_path != NULL) 623c2aa98e2SPeter Wemm free(return_path); 624c2aa98e2SPeter Wemm return_path = NULL; 625c2aa98e2SPeter Wemm continue; 626c2aa98e2SPeter Wemm } 627c2aa98e2SPeter Wemm goto syntaxerr; 6283299c2f1SGregory Neil Shapiro /* NOTREACHED */ 6293299c2f1SGregory Neil Shapiro break; 630c2aa98e2SPeter Wemm 631c2aa98e2SPeter Wemm case 'v': 632c2aa98e2SPeter Wemm case 'V': 63312ed1c7cSGregory Neil Shapiro if (sm_strncasecmp(buf, "vrfy ", 5) == 0) 6343299c2f1SGregory Neil Shapiro { 635b4662009SGregory Neil Shapiro printf("252 2.3.3 Try RCPT to attempt delivery\r\n"); 636c2aa98e2SPeter Wemm continue; 637c2aa98e2SPeter Wemm } 638c2aa98e2SPeter Wemm goto syntaxerr; 6393299c2f1SGregory Neil Shapiro /* NOTREACHED */ 6403299c2f1SGregory Neil Shapiro break; 641c2aa98e2SPeter Wemm 642c2aa98e2SPeter Wemm default: 643c2aa98e2SPeter Wemm syntaxerr: 644b4662009SGregory Neil Shapiro mailerr("500 5.5.2", "Syntax error"); 645c2aa98e2SPeter Wemm continue; 6463299c2f1SGregory Neil Shapiro /* NOTREACHED */ 6473299c2f1SGregory Neil Shapiro break; 648c2aa98e2SPeter Wemm } 649c2aa98e2SPeter Wemm } 650c2aa98e2SPeter Wemm } 651c2aa98e2SPeter Wemm 652c2aa98e2SPeter Wemm int 65312ed1c7cSGregory Neil Shapiro store(from, inbody) 654c2aa98e2SPeter Wemm char *from; 655b4662009SGregory Neil Shapiro bool *inbody; 656c2aa98e2SPeter Wemm { 65776b7bf71SPeter Wemm FILE *fp = NULL; 658c2aa98e2SPeter Wemm time_t tval; 65912ed1c7cSGregory Neil Shapiro bool eline; /* previous line was empty */ 66012ed1c7cSGregory Neil Shapiro bool fullline = true; /* current line is terminated */ 6613299c2f1SGregory Neil Shapiro bool prevfl; /* previous line was terminated */ 662c2aa98e2SPeter Wemm char line[2048]; 6633299c2f1SGregory Neil Shapiro int fd; 664c2aa98e2SPeter Wemm char tmpbuf[sizeof _PATH_LOCTMP + 1]; 665c2aa98e2SPeter Wemm 666b4662009SGregory Neil Shapiro if (inbody != NULL) 66712ed1c7cSGregory Neil Shapiro *inbody = false; 668b4662009SGregory Neil Shapiro 6693299c2f1SGregory Neil Shapiro (void) umask(0077); 67012ed1c7cSGregory Neil Shapiro (void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf); 671b4662009SGregory Neil Shapiro if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL) 6723299c2f1SGregory Neil Shapiro { 6732ef40764SGregory Neil Shapiro if (fd >= 0) 6742ef40764SGregory Neil Shapiro (void) close(fd); 675b4662009SGregory Neil Shapiro mailerr("451 4.3.0", "Unable to open temporary file"); 676c2aa98e2SPeter Wemm return -1; 6773299c2f1SGregory Neil Shapiro } 678c2aa98e2SPeter Wemm (void) unlink(tmpbuf); 679c2aa98e2SPeter Wemm 6803299c2f1SGregory Neil Shapiro if (LMTPMode) 6813299c2f1SGregory Neil Shapiro { 682b4662009SGregory Neil Shapiro printf("354 Go ahead\r\n"); 6833299c2f1SGregory Neil Shapiro (void) fflush(stdout); 684c2aa98e2SPeter Wemm } 685b4662009SGregory Neil Shapiro if (inbody != NULL) 68612ed1c7cSGregory Neil Shapiro *inbody = true; 687c2aa98e2SPeter Wemm 688c2aa98e2SPeter Wemm (void) time(&tval); 689c2aa98e2SPeter Wemm (void) fprintf(fp, "From %s %s", from, ctime(&tval)); 690c2aa98e2SPeter Wemm 6913299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 6923299c2f1SGregory Neil Shapiro HeaderLength = 0; 6933299c2f1SGregory Neil Shapiro BodyLength = -1; 6943299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 6953299c2f1SGregory Neil Shapiro 696c2aa98e2SPeter Wemm line[0] = '\0'; 69712ed1c7cSGregory Neil Shapiro eline = true; 6983299c2f1SGregory Neil Shapiro while (fgets(line, sizeof(line), stdin) != (char *) NULL) 6993299c2f1SGregory Neil Shapiro { 7003299c2f1SGregory Neil Shapiro size_t line_len = 0; 7013299c2f1SGregory Neil Shapiro int peek; 70276b7bf71SPeter Wemm 7033299c2f1SGregory Neil Shapiro prevfl = fullline; /* preserve state of previous line */ 7043299c2f1SGregory Neil Shapiro while (line[line_len] != '\n' && line_len < sizeof(line) - 2) 7053299c2f1SGregory Neil Shapiro line_len++; 7063299c2f1SGregory Neil Shapiro line_len++; 70776b7bf71SPeter Wemm 7083299c2f1SGregory Neil Shapiro /* Check for dot-stuffing */ 709b4662009SGregory Neil Shapiro if (prevfl && LMTPMode && line[0] == '.') 7103299c2f1SGregory Neil Shapiro { 7113299c2f1SGregory Neil Shapiro if (line[1] == '\n' || 7123299c2f1SGregory Neil Shapiro (line[1] == '\r' && line[2] == '\n')) 713c2aa98e2SPeter Wemm goto lmtpdot; 7143299c2f1SGregory Neil Shapiro memcpy(line, line + 1, line_len); 7153299c2f1SGregory Neil Shapiro line_len--; 716c2aa98e2SPeter Wemm } 7173299c2f1SGregory Neil Shapiro 7183299c2f1SGregory Neil Shapiro /* Check to see if we have the full line from fgets() */ 71912ed1c7cSGregory Neil Shapiro fullline = false; 7203299c2f1SGregory Neil Shapiro if (line_len > 0) 7213299c2f1SGregory Neil Shapiro { 7223299c2f1SGregory Neil Shapiro if (line[line_len - 1] == '\n') 7233299c2f1SGregory Neil Shapiro { 7243299c2f1SGregory Neil Shapiro if (line_len >= 2 && 7253299c2f1SGregory Neil Shapiro line[line_len - 2] == '\r') 7263299c2f1SGregory Neil Shapiro { 7273299c2f1SGregory Neil Shapiro line[line_len - 2] = '\n'; 7283299c2f1SGregory Neil Shapiro line[line_len - 1] = '\0'; 7293299c2f1SGregory Neil Shapiro line_len--; 7303299c2f1SGregory Neil Shapiro } 73112ed1c7cSGregory Neil Shapiro fullline = true; 7323299c2f1SGregory Neil Shapiro } 7333299c2f1SGregory Neil Shapiro else if (line[line_len - 1] == '\r') 7343299c2f1SGregory Neil Shapiro { 7353299c2f1SGregory Neil Shapiro /* Did we just miss the CRLF? */ 7363299c2f1SGregory Neil Shapiro peek = fgetc(stdin); 7373299c2f1SGregory Neil Shapiro if (peek == '\n') 7383299c2f1SGregory Neil Shapiro { 7393299c2f1SGregory Neil Shapiro line[line_len - 1] = '\n'; 74012ed1c7cSGregory Neil Shapiro fullline = true; 7413299c2f1SGregory Neil Shapiro } 7423299c2f1SGregory Neil Shapiro else 7433299c2f1SGregory Neil Shapiro (void) ungetc(peek, stdin); 7443299c2f1SGregory Neil Shapiro } 7453299c2f1SGregory Neil Shapiro } 7463299c2f1SGregory Neil Shapiro else 74712ed1c7cSGregory Neil Shapiro fullline = true; 7483299c2f1SGregory Neil Shapiro 7493299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 7503299c2f1SGregory Neil Shapiro if (prevfl && line[0] == '\n' && HeaderLength == 0) 7513299c2f1SGregory Neil Shapiro { 75212ed1c7cSGregory Neil Shapiro eline = false; 753b4662009SGregory Neil Shapiro if (fp != NULL) 7543299c2f1SGregory Neil Shapiro HeaderLength = ftell(fp); 7553299c2f1SGregory Neil Shapiro if (HeaderLength <= 0) 7563299c2f1SGregory Neil Shapiro { 7573299c2f1SGregory Neil Shapiro /* 7583299c2f1SGregory Neil Shapiro ** shouldn't happen, unless ftell() is 7593299c2f1SGregory Neil Shapiro ** badly broken 7603299c2f1SGregory Neil Shapiro */ 7613299c2f1SGregory Neil Shapiro 7623299c2f1SGregory Neil Shapiro HeaderLength = -1; 7633299c2f1SGregory Neil Shapiro } 7643299c2f1SGregory Neil Shapiro } 7653299c2f1SGregory Neil Shapiro #else /* CONTENTLENGTH */ 7663299c2f1SGregory Neil Shapiro if (prevfl && line[0] == '\n') 76712ed1c7cSGregory Neil Shapiro eline = true; 7683299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 7693299c2f1SGregory Neil Shapiro else 7703299c2f1SGregory Neil Shapiro { 771c2aa98e2SPeter Wemm if (eline && line[0] == 'F' && 772b4662009SGregory Neil Shapiro fp != NULL && 773c2aa98e2SPeter Wemm !memcmp(line, "From ", 5)) 774c2aa98e2SPeter Wemm (void) putc('>', fp); 77512ed1c7cSGregory Neil Shapiro eline = false; 7763299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 7773299c2f1SGregory Neil Shapiro /* discard existing "Content-Length:" headers */ 7783299c2f1SGregory Neil Shapiro if (prevfl && HeaderLength == 0 && 7793299c2f1SGregory Neil Shapiro (line[0] == 'C' || line[0] == 'c') && 78012ed1c7cSGregory Neil Shapiro sm_strncasecmp(line, ContentHdr, 15) == 0) 7813299c2f1SGregory Neil Shapiro { 7823299c2f1SGregory Neil Shapiro /* 7833299c2f1SGregory Neil Shapiro ** be paranoid: clear the line 7843299c2f1SGregory Neil Shapiro ** so no "wrong matches" may occur later 7853299c2f1SGregory Neil Shapiro */ 7863299c2f1SGregory Neil Shapiro line[0] = '\0'; 7873299c2f1SGregory Neil Shapiro continue; 788c2aa98e2SPeter Wemm } 7893299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 7903299c2f1SGregory Neil Shapiro 7913299c2f1SGregory Neil Shapiro } 792b4662009SGregory Neil Shapiro if (fp != NULL) 793b4662009SGregory Neil Shapiro { 7943299c2f1SGregory Neil Shapiro (void) fwrite(line, sizeof(char), line_len, fp); 7953299c2f1SGregory Neil Shapiro if (ferror(fp)) 7963299c2f1SGregory Neil Shapiro { 79776b7bf71SPeter Wemm mailerr("451 4.3.0", 798b4662009SGregory Neil Shapiro "Temporary file write error"); 7993299c2f1SGregory Neil Shapiro (void) fclose(fp); 800b4662009SGregory Neil Shapiro fp = NULL; 801b4662009SGregory Neil Shapiro continue; 802c2aa98e2SPeter Wemm } 803c2aa98e2SPeter Wemm } 804c2aa98e2SPeter Wemm } 805c2aa98e2SPeter Wemm 806b4662009SGregory Neil Shapiro /* check if an error occurred */ 807b4662009SGregory Neil Shapiro if (fp == NULL) 808b4662009SGregory Neil Shapiro return -1; 809b4662009SGregory Neil Shapiro 810b4662009SGregory Neil Shapiro if (LMTPMode) 8113299c2f1SGregory Neil Shapiro { 812c2aa98e2SPeter Wemm /* Got a premature EOF -- toss message and exit */ 813c2aa98e2SPeter Wemm exit(EX_OK); 814c2aa98e2SPeter Wemm } 815c2aa98e2SPeter Wemm 816c2aa98e2SPeter Wemm /* If message not newline terminated, need an extra. */ 817b4662009SGregory Neil Shapiro if (fp != NULL && strchr(line, '\n') == NULL) 818c2aa98e2SPeter Wemm (void) putc('\n', fp); 819c2aa98e2SPeter Wemm 820c2aa98e2SPeter Wemm lmtpdot: 821c2aa98e2SPeter Wemm 8223299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 823b4662009SGregory Neil Shapiro if (fp != NULL) 8243299c2f1SGregory Neil Shapiro BodyLength = ftell(fp); 8253299c2f1SGregory Neil Shapiro if (HeaderLength == 0 && BodyLength > 0) /* empty body */ 8263299c2f1SGregory Neil Shapiro { 8273299c2f1SGregory Neil Shapiro HeaderLength = BodyLength; 8283299c2f1SGregory Neil Shapiro BodyLength = 0; 8293299c2f1SGregory Neil Shapiro } 8303299c2f1SGregory Neil Shapiro else 8313299c2f1SGregory Neil Shapiro BodyLength = BodyLength - HeaderLength - 1 ; 8323299c2f1SGregory Neil Shapiro 8333299c2f1SGregory Neil Shapiro if (HeaderLength > 0 && BodyLength >= 0) 8343299c2f1SGregory Neil Shapiro { 83512ed1c7cSGregory Neil Shapiro (void) sm_snprintf(line, sizeof line, "%lld\n", 83612ed1c7cSGregory Neil Shapiro (LONGLONG_T) BodyLength); 83712ed1c7cSGregory Neil Shapiro (void) sm_strlcpy(&ContentHdr[16], line, 83812ed1c7cSGregory Neil Shapiro sizeof(ContentHdr) - 16); 8393299c2f1SGregory Neil Shapiro } 8403299c2f1SGregory Neil Shapiro else 8413299c2f1SGregory Neil Shapiro BodyLength = -1; /* Something is wrong here */ 8423299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 8433299c2f1SGregory Neil Shapiro 844c2aa98e2SPeter Wemm /* Output a newline; note, empty messages are allowed. */ 845b4662009SGregory Neil Shapiro if (fp != NULL) 846c2aa98e2SPeter Wemm (void) putc('\n', fp); 847c2aa98e2SPeter Wemm 848b4662009SGregory Neil Shapiro if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0) 8493299c2f1SGregory Neil Shapiro { 850b4662009SGregory Neil Shapiro mailerr("451 4.3.0", "Temporary file write error"); 851b4662009SGregory Neil Shapiro if (fp != NULL) 8523299c2f1SGregory Neil Shapiro (void) fclose(fp); 853c2aa98e2SPeter Wemm return -1; 8543299c2f1SGregory Neil Shapiro } 8553299c2f1SGregory Neil Shapiro return fd; 856c2aa98e2SPeter Wemm } 857c2aa98e2SPeter Wemm 858c2aa98e2SPeter Wemm void 859b4662009SGregory Neil Shapiro deliver(fd, name) 860c2aa98e2SPeter Wemm int fd; 861c2aa98e2SPeter Wemm char *name; 862c2aa98e2SPeter Wemm { 8633299c2f1SGregory Neil Shapiro struct stat fsb; 8643299c2f1SGregory Neil Shapiro struct stat sb; 8653299c2f1SGregory Neil Shapiro char path[MAXPATHLEN]; 866c46d91b7SGregory Neil Shapiro int mbfd = -1, nr = 0, nw, off; 86712ed1c7cSGregory Neil Shapiro int exitval; 868c2aa98e2SPeter Wemm char *p; 869b4662009SGregory Neil Shapiro char *errcode; 870c2aa98e2SPeter Wemm off_t curoff; 8713299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 8723299c2f1SGregory Neil Shapiro off_t headerbytes; 8733299c2f1SGregory Neil Shapiro int readamount; 8743299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 8753299c2f1SGregory Neil Shapiro char biffmsg[100], buf[8 * 1024]; 87612ed1c7cSGregory Neil Shapiro SM_MBDB_T user; 8773299c2f1SGregory Neil Shapiro 878c2aa98e2SPeter Wemm /* 8793299c2f1SGregory Neil Shapiro ** Disallow delivery to unknown names -- special mailboxes can be 8803299c2f1SGregory Neil Shapiro ** handled in the sendmail aliases file. 881c2aa98e2SPeter Wemm */ 882b4662009SGregory Neil Shapiro 88312ed1c7cSGregory Neil Shapiro exitval = sm_mbdb_lookup(name, &user); 88412ed1c7cSGregory Neil Shapiro switch (exitval) 8853299c2f1SGregory Neil Shapiro { 88612ed1c7cSGregory Neil Shapiro case EX_OK: 88712ed1c7cSGregory Neil Shapiro break; 88812ed1c7cSGregory Neil Shapiro 88912ed1c7cSGregory Neil Shapiro case EX_NOUSER: 89012ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 89112ed1c7cSGregory Neil Shapiro mailerr("550 5.1.1", "%s: User unknown", name); 89212ed1c7cSGregory Neil Shapiro break; 89312ed1c7cSGregory Neil Shapiro 89412ed1c7cSGregory Neil Shapiro case EX_TEMPFAIL: 89512ed1c7cSGregory Neil Shapiro mailerr("451 4.3.0", "%s: User database failure; retry later", 89612ed1c7cSGregory Neil Shapiro name); 89712ed1c7cSGregory Neil Shapiro break; 89812ed1c7cSGregory Neil Shapiro 89912ed1c7cSGregory Neil Shapiro default: 90012ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 90112ed1c7cSGregory Neil Shapiro mailerr("550 5.3.0", "%s: User database failure", name); 90212ed1c7cSGregory Neil Shapiro break; 903c2aa98e2SPeter Wemm } 90412ed1c7cSGregory Neil Shapiro 90512ed1c7cSGregory Neil Shapiro if (exitval != EX_OK) 90612ed1c7cSGregory Neil Shapiro { 90712ed1c7cSGregory Neil Shapiro if (ExitVal != EX_TEMPFAIL) 90812ed1c7cSGregory Neil Shapiro ExitVal = exitval; 909c2aa98e2SPeter Wemm return; 910c2aa98e2SPeter Wemm } 91112ed1c7cSGregory Neil Shapiro 912c2aa98e2SPeter Wemm endpwent(); 913c2aa98e2SPeter Wemm 914c2aa98e2SPeter Wemm /* 9153299c2f1SGregory Neil Shapiro ** Keep name reasonably short to avoid buffer overruns. 9163299c2f1SGregory Neil Shapiro ** This isn't necessary on BSD because of the proper 9173299c2f1SGregory Neil Shapiro ** definition of snprintf(), but it can cause problems 9183299c2f1SGregory Neil Shapiro ** on other systems. 9193299c2f1SGregory Neil Shapiro ** Also, clear out any bogus characters. 920c2aa98e2SPeter Wemm */ 921c2aa98e2SPeter Wemm 922c2aa98e2SPeter Wemm if (strlen(name) > 40) 923c2aa98e2SPeter Wemm name[40] = '\0'; 924c2aa98e2SPeter Wemm for (p = name; *p != '\0'; p++) 925c2aa98e2SPeter Wemm { 926c2aa98e2SPeter Wemm if (!isascii(*p)) 927c2aa98e2SPeter Wemm *p &= 0x7f; 928c2aa98e2SPeter Wemm else if (!isprint(*p)) 929c2aa98e2SPeter Wemm *p = '.'; 930c2aa98e2SPeter Wemm } 931c2aa98e2SPeter Wemm 9323299c2f1SGregory Neil Shapiro 93312ed1c7cSGregory Neil Shapiro if (HomeMailFile == NULL) 93412ed1c7cSGregory Neil Shapiro { 93512ed1c7cSGregory Neil Shapiro if (sm_snprintf(path, sizeof(path), "%s/%s", 93612ed1c7cSGregory Neil Shapiro _PATH_MAILDIR, name) >= sizeof(path)) 93712ed1c7cSGregory Neil Shapiro { 93812ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 93912ed1c7cSGregory Neil Shapiro mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 94012ed1c7cSGregory Neil Shapiro return; 94112ed1c7cSGregory Neil Shapiro } 94212ed1c7cSGregory Neil Shapiro } 94312ed1c7cSGregory Neil Shapiro else if (*user.mbdb_homedir == '\0') 94412ed1c7cSGregory Neil Shapiro { 94512ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 94612ed1c7cSGregory Neil Shapiro mailerr("550 5.1.1", "%s: User missing home directory", name); 94712ed1c7cSGregory Neil Shapiro return; 94812ed1c7cSGregory Neil Shapiro } 94912ed1c7cSGregory Neil Shapiro else if (sm_snprintf(path, sizeof(path), "%s/%s", 95012ed1c7cSGregory Neil Shapiro user.mbdb_homedir, HomeMailFile) >= sizeof(path)) 95112ed1c7cSGregory Neil Shapiro { 95212ed1c7cSGregory Neil Shapiro exitval = EX_UNAVAILABLE; 95312ed1c7cSGregory Neil Shapiro mailerr("550 5.1.1", "%s: Invalid mailbox path", name); 95412ed1c7cSGregory Neil Shapiro return; 95512ed1c7cSGregory Neil Shapiro } 956c2aa98e2SPeter Wemm 9573299c2f1SGregory Neil Shapiro 958c2aa98e2SPeter Wemm /* 9593299c2f1SGregory Neil Shapiro ** If the mailbox is linked or a symlink, fail. There's an obvious 9603299c2f1SGregory Neil Shapiro ** race here, that the file was replaced with a symbolic link after 9613299c2f1SGregory Neil Shapiro ** the lstat returned, but before the open. We attempt to detect 9623299c2f1SGregory Neil Shapiro ** this by comparing the original stat information and information 9633299c2f1SGregory Neil Shapiro ** returned by an fstat of the file descriptor returned by the open. 9643299c2f1SGregory Neil Shapiro ** 9653299c2f1SGregory Neil Shapiro ** NB: this is a symptom of a larger problem, that the mail spooling 9663299c2f1SGregory Neil Shapiro ** directory is writeable by the wrong users. If that directory is 9673299c2f1SGregory Neil Shapiro ** writeable, system security is compromised for other reasons, and 9683299c2f1SGregory Neil Shapiro ** it cannot be fixed here. 9693299c2f1SGregory Neil Shapiro ** 9703299c2f1SGregory Neil Shapiro ** If we created the mailbox, set the owner/group. If that fails, 9713299c2f1SGregory Neil Shapiro ** just return. Another process may have already opened it, so we 9723299c2f1SGregory Neil Shapiro ** can't unlink it. Historically, binmail set the owner/group at 9733299c2f1SGregory Neil Shapiro ** each mail delivery. We no longer do this, assuming that if the 9743299c2f1SGregory Neil Shapiro ** ownership or permissions were changed there was a reason. 9753299c2f1SGregory Neil Shapiro ** 9763299c2f1SGregory Neil Shapiro ** XXX 9773299c2f1SGregory Neil Shapiro ** open(2) should support flock'ing the file. 978c2aa98e2SPeter Wemm */ 9793299c2f1SGregory Neil Shapiro 980c2aa98e2SPeter Wemm tryagain: 9813299c2f1SGregory Neil Shapiro #ifdef MAILLOCK 9823299c2f1SGregory Neil Shapiro p = name; 9833299c2f1SGregory Neil Shapiro #else /* MAILLOCK */ 9843299c2f1SGregory Neil Shapiro p = path; 9853299c2f1SGregory Neil Shapiro #endif /* MAILLOCK */ 9863299c2f1SGregory Neil Shapiro if ((off = lockmbox(p)) != 0) 9873299c2f1SGregory Neil Shapiro { 9883299c2f1SGregory Neil Shapiro if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL) 9893299c2f1SGregory Neil Shapiro { 9903299c2f1SGregory Neil Shapiro ExitVal = EX_TEMPFAIL; 991b4662009SGregory Neil Shapiro errcode = "451 4.3.0"; 9923299c2f1SGregory Neil Shapiro } 9933299c2f1SGregory Neil Shapiro else 994b4662009SGregory Neil Shapiro errcode = "551 5.3.0"; 995b4662009SGregory Neil Shapiro 996b4662009SGregory Neil Shapiro mailerr(errcode, "lockmailbox %s failed; error code %d %s", 99712ed1c7cSGregory Neil Shapiro p, off, errno > 0 ? sm_errstring(errno) : ""); 9983299c2f1SGregory Neil Shapiro return; 9993299c2f1SGregory Neil Shapiro } 10003299c2f1SGregory Neil Shapiro 1001c2aa98e2SPeter Wemm if (lstat(path, &sb) < 0) 1002c2aa98e2SPeter Wemm { 10033299c2f1SGregory Neil Shapiro int save_errno; 10043299c2f1SGregory Neil Shapiro int mode = S_IRUSR|S_IWUSR; 100512ed1c7cSGregory Neil Shapiro gid_t gid = user.mbdb_gid; 10063299c2f1SGregory Neil Shapiro 10073299c2f1SGregory Neil Shapiro #ifdef MAILGID 10083299c2f1SGregory Neil Shapiro (void) umask(0007); 10093299c2f1SGregory Neil Shapiro gid = MAILGID; 10103299c2f1SGregory Neil Shapiro mode |= S_IRGRP|S_IWGRP; 10113299c2f1SGregory Neil Shapiro #endif /* MAILGID */ 10123299c2f1SGregory Neil Shapiro 1013d995d2baSGregory Neil Shapiro mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|EXTRA_MODE, 1014d995d2baSGregory Neil Shapiro mode); 10153299c2f1SGregory Neil Shapiro save_errno = errno; 10163299c2f1SGregory Neil Shapiro 10173299c2f1SGregory Neil Shapiro if (lstat(path, &sb) < 0) 10183299c2f1SGregory Neil Shapiro { 10193299c2f1SGregory Neil Shapiro ExitVal = EX_CANTCREAT; 102076b7bf71SPeter Wemm mailerr("550 5.2.0", 102176b7bf71SPeter Wemm "%s: lstat: file changed after open", path); 1022c2aa98e2SPeter Wemm goto err1; 1023c2aa98e2SPeter Wemm } 1024b4662009SGregory Neil Shapiro if (mbfd < 0) 10253299c2f1SGregory Neil Shapiro { 10263299c2f1SGregory Neil Shapiro if (save_errno == EEXIST) 1027c2aa98e2SPeter Wemm goto tryagain; 1028d995d2baSGregory Neil Shapiro 1029d995d2baSGregory Neil Shapiro /* open failed, don't try again */ 1030d995d2baSGregory Neil Shapiro mailerr("450 4.2.0", "%s: %s", path, 103112ed1c7cSGregory Neil Shapiro sm_errstring(save_errno)); 1032d995d2baSGregory Neil Shapiro goto err0; 10333299c2f1SGregory Neil Shapiro } 103412ed1c7cSGregory Neil Shapiro else if (fchown(mbfd, user.mbdb_uid, gid) < 0) 10353299c2f1SGregory Neil Shapiro { 1036c2aa98e2SPeter Wemm mailerr("451 4.3.0", "chown %u.%u: %s", 103712ed1c7cSGregory Neil Shapiro user.mbdb_uid, gid, name); 1038c2aa98e2SPeter Wemm goto err1; 1039c2aa98e2SPeter Wemm } 1040d995d2baSGregory Neil Shapiro else 1041d995d2baSGregory Neil Shapiro { 1042d995d2baSGregory Neil Shapiro /* 1043d995d2baSGregory Neil Shapiro ** open() was successful, now close it so can 1044d995d2baSGregory Neil Shapiro ** be opened as the right owner again. 1045d995d2baSGregory Neil Shapiro ** Paranoia: reset mbdf since the file descriptor 1046d995d2baSGregory Neil Shapiro ** is no longer valid; better safe than sorry. 1047d995d2baSGregory Neil Shapiro */ 1048d995d2baSGregory Neil Shapiro 104912ed1c7cSGregory Neil Shapiro sb.st_uid = user.mbdb_uid; 1050d995d2baSGregory Neil Shapiro (void) close(mbfd); 1051d995d2baSGregory Neil Shapiro mbfd = -1; 1052d995d2baSGregory Neil Shapiro } 10533299c2f1SGregory Neil Shapiro } 10543299c2f1SGregory Neil Shapiro else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) 10553299c2f1SGregory Neil Shapiro { 1056c2aa98e2SPeter Wemm mailerr("550 5.2.0", "%s: irregular file", path); 1057c2aa98e2SPeter Wemm goto err0; 10583299c2f1SGregory Neil Shapiro } 105912ed1c7cSGregory Neil Shapiro else if (sb.st_uid != user.mbdb_uid) 10603299c2f1SGregory Neil Shapiro { 10613299c2f1SGregory Neil Shapiro ExitVal = EX_CANTCREAT; 1062c2aa98e2SPeter Wemm mailerr("550 5.2.0", "%s: wrong ownership (%d)", 106312ed1c7cSGregory Neil Shapiro path, (int) sb.st_uid); 1064c2aa98e2SPeter Wemm goto err0; 1065c2aa98e2SPeter Wemm } 1066c2aa98e2SPeter Wemm 1067d995d2baSGregory Neil Shapiro /* change UID for quota checks */ 106812ed1c7cSGregory Neil Shapiro if (setreuid(0, user.mbdb_uid) < 0) 1069d995d2baSGregory Neil Shapiro { 1070d995d2baSGregory Neil Shapiro mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)", 107112ed1c7cSGregory Neil Shapiro (int) user.mbdb_uid, sm_errstring(errno), 107212ed1c7cSGregory Neil Shapiro (int) getuid(), (int) geteuid()); 1073d995d2baSGregory Neil Shapiro goto err1; 1074d995d2baSGregory Neil Shapiro } 1075d995d2baSGregory Neil Shapiro #ifdef DEBUG 107612ed1c7cSGregory Neil Shapiro fprintf(stderr, "new euid = %d\n", (int) geteuid()); 1077d995d2baSGregory Neil Shapiro #endif /* DEBUG */ 1078d995d2baSGregory Neil Shapiro mbfd = open(path, O_APPEND|O_WRONLY|EXTRA_MODE, 0); 1079d995d2baSGregory Neil Shapiro if (mbfd < 0) 10803299c2f1SGregory Neil Shapiro { 108112ed1c7cSGregory Neil Shapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1082c2aa98e2SPeter Wemm goto err0; 10833299c2f1SGregory Neil Shapiro } 10843299c2f1SGregory Neil Shapiro else if (fstat(mbfd, &fsb) < 0 || 1085c2aa98e2SPeter Wemm fsb.st_nlink != 1 || 1086c2aa98e2SPeter Wemm sb.st_nlink != 1 || 1087c2aa98e2SPeter Wemm !S_ISREG(fsb.st_mode) || 1088c2aa98e2SPeter Wemm sb.st_dev != fsb.st_dev || 1089c2aa98e2SPeter Wemm sb.st_ino != fsb.st_ino || 1090c2aa98e2SPeter Wemm # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 1091c2aa98e2SPeter Wemm sb.st_gen != fsb.st_gen || 10923299c2f1SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */ 10933299c2f1SGregory Neil Shapiro sb.st_uid != fsb.st_uid) 10943299c2f1SGregory Neil Shapiro { 10953299c2f1SGregory Neil Shapiro ExitVal = EX_TEMPFAIL; 109676b7bf71SPeter Wemm mailerr("550 5.2.0", "%s: fstat: file changed after open", 109776b7bf71SPeter Wemm path); 1098c2aa98e2SPeter Wemm goto err1; 1099c2aa98e2SPeter Wemm } 1100c2aa98e2SPeter Wemm 110112ed1c7cSGregory Neil Shapiro #if 0 110212ed1c7cSGregory Neil Shapiro /* 110312ed1c7cSGregory Neil Shapiro ** This code could be reused if we decide to add a 110412ed1c7cSGregory Neil Shapiro ** per-user quota field to the sm_mbdb interface. 110512ed1c7cSGregory Neil Shapiro */ 110612ed1c7cSGregory Neil Shapiro 110712ed1c7cSGregory Neil Shapiro /* 110812ed1c7cSGregory Neil Shapiro ** Fail if the user has a quota specified, and delivery of this 110912ed1c7cSGregory Neil Shapiro ** message would exceed that quota. We bounce such failures using 111012ed1c7cSGregory Neil Shapiro ** EX_UNAVAILABLE, unless there were internal problems, since 111112ed1c7cSGregory Neil Shapiro ** storing immense messages for later retries can cause queueing 111212ed1c7cSGregory Neil Shapiro ** issues. 111312ed1c7cSGregory Neil Shapiro */ 111412ed1c7cSGregory Neil Shapiro 111512ed1c7cSGregory Neil Shapiro if (ui.quota > 0) 111612ed1c7cSGregory Neil Shapiro { 111712ed1c7cSGregory Neil Shapiro struct stat dsb; 111812ed1c7cSGregory Neil Shapiro 111912ed1c7cSGregory Neil Shapiro if (fstat(fd, &dsb) < 0) 112012ed1c7cSGregory Neil Shapiro { 112112ed1c7cSGregory Neil Shapiro ExitVal = EX_TEMPFAIL; 112212ed1c7cSGregory Neil Shapiro mailerr("451 4.3.0", 112312ed1c7cSGregory Neil Shapiro "%s: fstat: can't stat temporary storage: %s", 112412ed1c7cSGregory Neil Shapiro ui.mailspool, sm_errstring(errno)); 112512ed1c7cSGregory Neil Shapiro goto err1; 112612ed1c7cSGregory Neil Shapiro } 112712ed1c7cSGregory Neil Shapiro 112812ed1c7cSGregory Neil Shapiro if (dsb.st_size + sb.st_size + 1 > ui.quota) 112912ed1c7cSGregory Neil Shapiro { 113012ed1c7cSGregory Neil Shapiro ExitVal = EX_UNAVAILABLE; 113112ed1c7cSGregory Neil Shapiro mailerr("551 5.2.2", 113212ed1c7cSGregory Neil Shapiro "%s: Mailbox full or quota exceeded", 113312ed1c7cSGregory Neil Shapiro ui.mailspool); 113412ed1c7cSGregory Neil Shapiro goto err1; 113512ed1c7cSGregory Neil Shapiro } 113612ed1c7cSGregory Neil Shapiro } 113712ed1c7cSGregory Neil Shapiro #endif /* 0 */ 11383299c2f1SGregory Neil Shapiro 1139c2aa98e2SPeter Wemm /* Wait until we can get a lock on the file. */ 11403299c2f1SGregory Neil Shapiro if (flock(mbfd, LOCK_EX) < 0) 11413299c2f1SGregory Neil Shapiro { 114212ed1c7cSGregory Neil Shapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1143c2aa98e2SPeter Wemm goto err1; 1144c2aa98e2SPeter Wemm } 1145c2aa98e2SPeter Wemm 1146c2aa98e2SPeter Wemm /* Get the starting offset of the new message for biff. */ 1147c2aa98e2SPeter Wemm curoff = lseek(mbfd, (off_t) 0, SEEK_END); 1148518536daSGregory Neil Shapiro 1149518536daSGregory Neil Shapiro if (!nobiff) 1150518536daSGregory Neil Shapiro { 115112ed1c7cSGregory Neil Shapiro (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n", 115212ed1c7cSGregory Neil Shapiro name, (LONGLONG_T) curoff); 1153d615a192SPeter Wemm } 1154c2aa98e2SPeter Wemm 1155c2aa98e2SPeter Wemm /* Copy the message into the file. */ 11563299c2f1SGregory Neil Shapiro if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1) 11573299c2f1SGregory Neil Shapiro { 1158b4662009SGregory Neil Shapiro mailerr("450 4.2.0", "Temporary file: %s", 115912ed1c7cSGregory Neil Shapiro sm_errstring(errno)); 1160c2aa98e2SPeter Wemm goto err1; 1161c2aa98e2SPeter Wemm } 1162c2aa98e2SPeter Wemm #ifdef DEBUG 116312ed1c7cSGregory Neil Shapiro fprintf(stderr, "before writing: euid = %d\n", (int) geteuid()); 11643299c2f1SGregory Neil Shapiro #endif /* DEBUG */ 11653299c2f1SGregory Neil Shapiro #ifdef CONTENTLENGTH 11663299c2f1SGregory Neil Shapiro headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ; 11673299c2f1SGregory Neil Shapiro for (;;) 11683299c2f1SGregory Neil Shapiro { 11693299c2f1SGregory Neil Shapiro if (headerbytes == 0) 11703299c2f1SGregory Neil Shapiro { 117112ed1c7cSGregory Neil Shapiro (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr); 11723299c2f1SGregory Neil Shapiro nr = strlen(buf); 11733299c2f1SGregory Neil Shapiro headerbytes = -1; 11743299c2f1SGregory Neil Shapiro readamount = 0; 11753299c2f1SGregory Neil Shapiro } 11763299c2f1SGregory Neil Shapiro else if (headerbytes > sizeof(buf) || headerbytes < 0) 11773299c2f1SGregory Neil Shapiro readamount = sizeof(buf); 11783299c2f1SGregory Neil Shapiro else 11793299c2f1SGregory Neil Shapiro readamount = headerbytes; 11803299c2f1SGregory Neil Shapiro if (readamount != 0) 11813299c2f1SGregory Neil Shapiro nr = read(fd, buf, readamount); 11823299c2f1SGregory Neil Shapiro if (nr <= 0) 11833299c2f1SGregory Neil Shapiro break; 11843299c2f1SGregory Neil Shapiro if (headerbytes > 0) 11853299c2f1SGregory Neil Shapiro headerbytes -= nr ; 11863299c2f1SGregory Neil Shapiro 11873299c2f1SGregory Neil Shapiro #else /* CONTENTLENGTH */ 1188c2aa98e2SPeter Wemm while ((nr = read(fd, buf, sizeof(buf))) > 0) 11893299c2f1SGregory Neil Shapiro { 11903299c2f1SGregory Neil Shapiro #endif /* CONTENTLENGTH */ 1191c2aa98e2SPeter Wemm for (off = 0; off < nr; off += nw) 11923299c2f1SGregory Neil Shapiro { 11933299c2f1SGregory Neil Shapiro if ((nw = write(mbfd, buf + off, nr - off)) < 0) 11943299c2f1SGregory Neil Shapiro { 1195b4662009SGregory Neil Shapiro errcode = "450 4.2.0"; 11963299c2f1SGregory Neil Shapiro #ifdef EDQUOT 1197b4662009SGregory Neil Shapiro if (errno == EDQUOT && BounceQuota) 1198b4662009SGregory Neil Shapiro errcode = "552 5.2.2"; 11993299c2f1SGregory Neil Shapiro #endif /* EDQUOT */ 1200b4662009SGregory Neil Shapiro mailerr(errcode, "%s: %s", 120112ed1c7cSGregory Neil Shapiro path, sm_errstring(errno)); 1202c2aa98e2SPeter Wemm goto err3; 1203c2aa98e2SPeter Wemm } 12043299c2f1SGregory Neil Shapiro } 12053299c2f1SGregory Neil Shapiro } 12063299c2f1SGregory Neil Shapiro if (nr < 0) 12073299c2f1SGregory Neil Shapiro { 1208b4662009SGregory Neil Shapiro mailerr("450 4.2.0", "Temporary file: %s", 120912ed1c7cSGregory Neil Shapiro sm_errstring(errno)); 1210c2aa98e2SPeter Wemm goto err3; 1211c2aa98e2SPeter Wemm } 1212c2aa98e2SPeter Wemm 1213c2aa98e2SPeter Wemm /* Flush to disk, don't wait for update. */ 12143299c2f1SGregory Neil Shapiro if (!nofsync && fsync(mbfd) < 0) 12153299c2f1SGregory Neil Shapiro { 121612ed1c7cSGregory Neil Shapiro mailerr("450 4.2.0", "%s: %s", path, sm_errstring(errno)); 1217c2aa98e2SPeter Wemm err3: 1218b4662009SGregory Neil Shapiro (void) setreuid(0, 0); 1219c2aa98e2SPeter Wemm #ifdef DEBUG 122012ed1c7cSGregory Neil Shapiro fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 12213299c2f1SGregory Neil Shapiro #endif /* DEBUG */ 12222ef40764SGregory Neil Shapiro if (mbfd >= 0) 1223c2aa98e2SPeter Wemm (void) ftruncate(mbfd, curoff); 1224d995d2baSGregory Neil Shapiro err1: if (mbfd >= 0) 1225d995d2baSGregory Neil Shapiro (void) close(mbfd); 1226c2aa98e2SPeter Wemm err0: unlockmbox(); 1227c2aa98e2SPeter Wemm return; 1228c2aa98e2SPeter Wemm } 1229c2aa98e2SPeter Wemm 1230c2aa98e2SPeter Wemm /* Close and check -- NFS doesn't write until the close. */ 12313299c2f1SGregory Neil Shapiro if (close(mbfd)) 12323299c2f1SGregory Neil Shapiro { 1233b4662009SGregory Neil Shapiro errcode = "450 4.2.0"; 12343299c2f1SGregory Neil Shapiro #ifdef EDQUOT 1235b4662009SGregory Neil Shapiro if (errno == EDQUOT && BounceQuota) 1236b4662009SGregory Neil Shapiro errcode = "552 5.2.2"; 12373299c2f1SGregory Neil Shapiro #endif /* EDQUOT */ 123812ed1c7cSGregory Neil Shapiro mailerr(errcode, "%s: %s", path, sm_errstring(errno)); 12392ef40764SGregory Neil Shapiro mbfd = open(path, O_WRONLY|EXTRA_MODE, 0); 12402ef40764SGregory Neil Shapiro if (mbfd < 0 12412ef40764SGregory Neil Shapiro || fstat(mbfd, &sb) < 0 || 12422ef40764SGregory Neil Shapiro sb.st_nlink != 1 || 12432ef40764SGregory Neil Shapiro !S_ISREG(sb.st_mode) || 12442ef40764SGregory Neil Shapiro sb.st_dev != fsb.st_dev || 12452ef40764SGregory Neil Shapiro sb.st_ino != fsb.st_ino || 12462ef40764SGregory Neil Shapiro # if HAS_ST_GEN && 0 /* AFS returns random values for st_gen */ 12472ef40764SGregory Neil Shapiro sb.st_gen != fsb.st_gen || 12482ef40764SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */ 12492ef40764SGregory Neil Shapiro sb.st_uid != fsb.st_uid 12502ef40764SGregory Neil Shapiro ) 12512ef40764SGregory Neil Shapiro { 12522ef40764SGregory Neil Shapiro /* Don't use a bogus file */ 12532ef40764SGregory Neil Shapiro if (mbfd >= 0) 12542ef40764SGregory Neil Shapiro { 12552ef40764SGregory Neil Shapiro (void) close(mbfd); 12562ef40764SGregory Neil Shapiro mbfd = -1; 12572ef40764SGregory Neil Shapiro } 12582ef40764SGregory Neil Shapiro } 12592ef40764SGregory Neil Shapiro 12602ef40764SGregory Neil Shapiro /* Attempt to truncate back to pre-write size */ 12612ef40764SGregory Neil Shapiro goto err3; 12623299c2f1SGregory Neil Shapiro } 12633299c2f1SGregory Neil Shapiro else if (!nobiff) 1264c2aa98e2SPeter Wemm notifybiff(biffmsg); 1265c2aa98e2SPeter Wemm 12663299c2f1SGregory Neil Shapiro if (setreuid(0, 0) < 0) 12673299c2f1SGregory Neil Shapiro { 1268c2aa98e2SPeter Wemm mailerr("450 4.2.0", "setreuid(0, 0): %s", 126912ed1c7cSGregory Neil Shapiro sm_errstring(errno)); 1270c2aa98e2SPeter Wemm goto err0; 1271c2aa98e2SPeter Wemm } 1272c2aa98e2SPeter Wemm #ifdef DEBUG 127312ed1c7cSGregory Neil Shapiro fprintf(stderr, "reset euid = %d\n", (int) geteuid()); 12743299c2f1SGregory Neil Shapiro #endif /* DEBUG */ 1275c2aa98e2SPeter Wemm unlockmbox(); 12763299c2f1SGregory Neil Shapiro if (LMTPMode) 1277b4662009SGregory Neil Shapiro printf("250 2.1.5 %s Ok\r\n", name); 1278c2aa98e2SPeter Wemm } 1279c2aa98e2SPeter Wemm 1280c2aa98e2SPeter Wemm /* 12813299c2f1SGregory Neil Shapiro ** user.lock files are necessary for compatibility with other 12823299c2f1SGregory Neil Shapiro ** systems, e.g., when the mail spool file is NFS exported. 12833299c2f1SGregory Neil Shapiro ** Alas, mailbox locking is more than just a local matter. 12843299c2f1SGregory Neil Shapiro ** EPA 11/94. 1285c2aa98e2SPeter Wemm */ 1286c2aa98e2SPeter Wemm 128712ed1c7cSGregory Neil Shapiro bool Locked = false; 12883299c2f1SGregory Neil Shapiro 12893299c2f1SGregory Neil Shapiro #ifdef MAILLOCK 12903299c2f1SGregory Neil Shapiro int 12913299c2f1SGregory Neil Shapiro lockmbox(name) 12923299c2f1SGregory Neil Shapiro char *name; 12933299c2f1SGregory Neil Shapiro { 1294d995d2baSGregory Neil Shapiro int r = 0; 12953299c2f1SGregory Neil Shapiro 12963299c2f1SGregory Neil Shapiro if (Locked) 12973299c2f1SGregory Neil Shapiro return 0; 12983299c2f1SGregory Neil Shapiro if ((r = maillock(name, 15)) == L_SUCCESS) 12993299c2f1SGregory Neil Shapiro { 130012ed1c7cSGregory Neil Shapiro Locked = true; 13013299c2f1SGregory Neil Shapiro return 0; 13023299c2f1SGregory Neil Shapiro } 13033299c2f1SGregory Neil Shapiro switch (r) 13043299c2f1SGregory Neil Shapiro { 13053299c2f1SGregory Neil Shapiro case L_TMPLOCK: /* Can't create tmp file */ 13063299c2f1SGregory Neil Shapiro case L_TMPWRITE: /* Can't write pid into lockfile */ 13073299c2f1SGregory Neil Shapiro case L_MAXTRYS: /* Failed after retrycnt attempts */ 13083299c2f1SGregory Neil Shapiro errno = 0; 13093299c2f1SGregory Neil Shapiro r = EX_TEMPFAIL; 13103299c2f1SGregory Neil Shapiro break; 13113299c2f1SGregory Neil Shapiro case L_ERROR: /* Check errno for reason */ 13123299c2f1SGregory Neil Shapiro r = errno; 13133299c2f1SGregory Neil Shapiro break; 13143299c2f1SGregory Neil Shapiro default: /* other permanent errors */ 13153299c2f1SGregory Neil Shapiro errno = 0; 13163299c2f1SGregory Neil Shapiro r = EX_UNAVAILABLE; 13173299c2f1SGregory Neil Shapiro break; 13183299c2f1SGregory Neil Shapiro } 13193299c2f1SGregory Neil Shapiro return r; 13203299c2f1SGregory Neil Shapiro } 1321c2aa98e2SPeter Wemm 1322c2aa98e2SPeter Wemm void 13233299c2f1SGregory Neil Shapiro unlockmbox() 13243299c2f1SGregory Neil Shapiro { 13253299c2f1SGregory Neil Shapiro if (Locked) 13263299c2f1SGregory Neil Shapiro mailunlock(); 132712ed1c7cSGregory Neil Shapiro Locked = false; 13283299c2f1SGregory Neil Shapiro } 13293299c2f1SGregory Neil Shapiro #else /* MAILLOCK */ 13303299c2f1SGregory Neil Shapiro 13313299c2f1SGregory Neil Shapiro char LockName[MAXPATHLEN]; 13323299c2f1SGregory Neil Shapiro 13333299c2f1SGregory Neil Shapiro int 1334c2aa98e2SPeter Wemm lockmbox(path) 1335c2aa98e2SPeter Wemm char *path; 1336c2aa98e2SPeter Wemm { 1337c2aa98e2SPeter Wemm int statfailed = 0; 13383299c2f1SGregory Neil Shapiro time_t start; 1339c2aa98e2SPeter Wemm 13403299c2f1SGregory Neil Shapiro if (Locked) 13413299c2f1SGregory Neil Shapiro return 0; 13423299c2f1SGregory Neil Shapiro if (strlen(path) + 6 > sizeof LockName) 13433299c2f1SGregory Neil Shapiro return EX_SOFTWARE; 134412ed1c7cSGregory Neil Shapiro (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path); 13453299c2f1SGregory Neil Shapiro (void) time(&start); 13463299c2f1SGregory Neil Shapiro for (; ; sleep(5)) 13473299c2f1SGregory Neil Shapiro { 1348c2aa98e2SPeter Wemm int fd; 1349c2aa98e2SPeter Wemm struct stat st; 1350c2aa98e2SPeter Wemm time_t now; 1351c2aa98e2SPeter Wemm 13523299c2f1SGregory Neil Shapiro /* global timeout */ 13533299c2f1SGregory Neil Shapiro (void) time(&now); 13543299c2f1SGregory Neil Shapiro if (now > start + LOCKTO_GLOB) 13553299c2f1SGregory Neil Shapiro { 13563299c2f1SGregory Neil Shapiro errno = 0; 13573299c2f1SGregory Neil Shapiro return EX_TEMPFAIL; 1358c2aa98e2SPeter Wemm } 13593299c2f1SGregory Neil Shapiro fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, 0); 13603299c2f1SGregory Neil Shapiro if (fd >= 0) 13613299c2f1SGregory Neil Shapiro { 13623299c2f1SGregory Neil Shapiro /* defeat lock checking programs which test pid */ 13633299c2f1SGregory Neil Shapiro (void) write(fd, "0", 2); 136412ed1c7cSGregory Neil Shapiro Locked = true; 13653299c2f1SGregory Neil Shapiro (void) close(fd); 13663299c2f1SGregory Neil Shapiro return 0; 13673299c2f1SGregory Neil Shapiro } 13683299c2f1SGregory Neil Shapiro if (stat(LockName, &st) < 0) 13693299c2f1SGregory Neil Shapiro { 1370c2aa98e2SPeter Wemm if (statfailed++ > 5) 13713299c2f1SGregory Neil Shapiro { 13723299c2f1SGregory Neil Shapiro errno = 0; 13733299c2f1SGregory Neil Shapiro return EX_TEMPFAIL; 13743299c2f1SGregory Neil Shapiro } 1375c2aa98e2SPeter Wemm continue; 1376c2aa98e2SPeter Wemm } 1377c2aa98e2SPeter Wemm statfailed = 0; 13783299c2f1SGregory Neil Shapiro (void) time(&now); 13793299c2f1SGregory Neil Shapiro if (now < st.st_ctime + LOCKTO_RM) 1380c2aa98e2SPeter Wemm continue; 13813299c2f1SGregory Neil Shapiro 13823299c2f1SGregory Neil Shapiro /* try to remove stale lockfile */ 13833299c2f1SGregory Neil Shapiro if (unlink(LockName) < 0) 13843299c2f1SGregory Neil Shapiro return errno; 1385c2aa98e2SPeter Wemm } 1386c2aa98e2SPeter Wemm } 1387c2aa98e2SPeter Wemm 1388c2aa98e2SPeter Wemm void 1389c2aa98e2SPeter Wemm unlockmbox() 1390c2aa98e2SPeter Wemm { 13913299c2f1SGregory Neil Shapiro if (!Locked) 1392c2aa98e2SPeter Wemm return; 13933299c2f1SGregory Neil Shapiro (void) unlink(LockName); 139412ed1c7cSGregory Neil Shapiro Locked = false; 1395c2aa98e2SPeter Wemm } 13963299c2f1SGregory Neil Shapiro #endif /* MAILLOCK */ 1397c2aa98e2SPeter Wemm 1398c2aa98e2SPeter Wemm void 1399c2aa98e2SPeter Wemm notifybiff(msg) 1400c2aa98e2SPeter Wemm char *msg; 1401c2aa98e2SPeter Wemm { 140212ed1c7cSGregory Neil Shapiro static bool initialized = false; 1403c2aa98e2SPeter Wemm static int f = -1; 1404c2aa98e2SPeter Wemm struct hostent *hp; 1405c2aa98e2SPeter Wemm struct servent *sp; 1406c2aa98e2SPeter Wemm int len; 14073299c2f1SGregory Neil Shapiro static struct sockaddr_in addr; 1408c2aa98e2SPeter Wemm 14093299c2f1SGregory Neil Shapiro if (!initialized) 14103299c2f1SGregory Neil Shapiro { 141112ed1c7cSGregory Neil Shapiro initialized = true; 14123299c2f1SGregory Neil Shapiro 1413c2aa98e2SPeter Wemm /* Be silent if biff service not available. */ 14143299c2f1SGregory Neil Shapiro if ((sp = getservbyname("biff", "udp")) == NULL || 14153299c2f1SGregory Neil Shapiro (hp = gethostbyname("localhost")) == NULL || 14163299c2f1SGregory Neil Shapiro hp->h_length != INADDRSZ) 1417c2aa98e2SPeter Wemm return; 14183299c2f1SGregory Neil Shapiro 1419c2aa98e2SPeter Wemm addr.sin_family = hp->h_addrtype; 14203299c2f1SGregory Neil Shapiro memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ); 1421c2aa98e2SPeter Wemm addr.sin_port = sp->s_port; 1422c2aa98e2SPeter Wemm } 14233299c2f1SGregory Neil Shapiro 14243299c2f1SGregory Neil Shapiro /* No message, just return */ 14253299c2f1SGregory Neil Shapiro if (msg == NULL) 1426c2aa98e2SPeter Wemm return; 14273299c2f1SGregory Neil Shapiro 14283299c2f1SGregory Neil Shapiro /* Couldn't initialize addr struct */ 14293299c2f1SGregory Neil Shapiro if (addr.sin_family == AF_UNSPEC) 14303299c2f1SGregory Neil Shapiro return; 14313299c2f1SGregory Neil Shapiro 1432b4662009SGregory Neil Shapiro if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 14333299c2f1SGregory Neil Shapiro return; 1434c2aa98e2SPeter Wemm len = strlen(msg) + 1; 143576b7bf71SPeter Wemm (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr)); 1436c2aa98e2SPeter Wemm } 1437c2aa98e2SPeter Wemm 1438c2aa98e2SPeter Wemm void 1439c2aa98e2SPeter Wemm usage() 1440c2aa98e2SPeter Wemm { 14413299c2f1SGregory Neil Shapiro ExitVal = EX_USAGE; 144212ed1c7cSGregory Neil Shapiro mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ..."); 14433299c2f1SGregory Neil Shapiro exit(ExitVal); 1444c2aa98e2SPeter Wemm } 1445c2aa98e2SPeter Wemm 1446c2aa98e2SPeter Wemm void 144712ed1c7cSGregory Neil Shapiro /*VARARGS2*/ 1448c2aa98e2SPeter Wemm #ifdef __STDC__ 1449c2aa98e2SPeter Wemm mailerr(const char *hdr, const char *fmt, ...) 14503299c2f1SGregory Neil Shapiro #else /* __STDC__ */ 1451c2aa98e2SPeter Wemm mailerr(hdr, fmt, va_alist) 1452c2aa98e2SPeter Wemm const char *hdr; 1453c2aa98e2SPeter Wemm const char *fmt; 1454c2aa98e2SPeter Wemm va_dcl 14553299c2f1SGregory Neil Shapiro #endif /* __STDC__ */ 1456c2aa98e2SPeter Wemm { 1457b4662009SGregory Neil Shapiro size_t len = 0; 145812ed1c7cSGregory Neil Shapiro SM_VA_LOCAL_DECL 1459c2aa98e2SPeter Wemm 1460b4662009SGregory Neil Shapiro (void) e_to_sys(errno); 1461b4662009SGregory Neil Shapiro 146212ed1c7cSGregory Neil Shapiro SM_VA_START(ap, fmt); 1463b4662009SGregory Neil Shapiro 146412ed1c7cSGregory Neil Shapiro if (LMTPMode && hdr != NULL) 1465c2aa98e2SPeter Wemm { 146612ed1c7cSGregory Neil Shapiro sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr); 1467b4662009SGregory Neil Shapiro len = strlen(ErrBuf); 1468c2aa98e2SPeter Wemm } 146912ed1c7cSGregory Neil Shapiro (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap); 147012ed1c7cSGregory Neil Shapiro SM_VA_END(ap); 1471b4662009SGregory Neil Shapiro 1472b4662009SGregory Neil Shapiro if (!HoldErrs) 1473b4662009SGregory Neil Shapiro flush_error(); 1474b4662009SGregory Neil Shapiro 1475b4662009SGregory Neil Shapiro /* Log the message to syslog. */ 1476b4662009SGregory Neil Shapiro if (!LMTPMode) 1477b4662009SGregory Neil Shapiro syslog(LOG_ERR, "%s", ErrBuf); 1478b4662009SGregory Neil Shapiro } 1479c2aa98e2SPeter Wemm 1480c2aa98e2SPeter Wemm void 1481b4662009SGregory Neil Shapiro flush_error() 1482c2aa98e2SPeter Wemm { 1483b4662009SGregory Neil Shapiro if (LMTPMode) 1484b4662009SGregory Neil Shapiro printf("%s\r\n", ErrBuf); 1485b4662009SGregory Neil Shapiro else 1486b4662009SGregory Neil Shapiro { 14873299c2f1SGregory Neil Shapiro if (ExitVal != EX_USAGE) 1488c2aa98e2SPeter Wemm (void) fprintf(stderr, "mail.local: "); 1489b4662009SGregory Neil Shapiro fprintf(stderr, "%s\n", ErrBuf); 1490c2aa98e2SPeter Wemm } 1491c2aa98e2SPeter Wemm } 1492c2aa98e2SPeter Wemm 1493c2aa98e2SPeter Wemm /* 1494c2aa98e2SPeter Wemm * e_to_sys -- 1495c2aa98e2SPeter Wemm * Guess which errno's are temporary. Gag me. 1496c2aa98e2SPeter Wemm */ 1497b4662009SGregory Neil Shapiro 14983299c2f1SGregory Neil Shapiro int 1499c2aa98e2SPeter Wemm e_to_sys(num) 1500c2aa98e2SPeter Wemm int num; 1501c2aa98e2SPeter Wemm { 1502c2aa98e2SPeter Wemm /* Temporary failures override hard errors. */ 15033299c2f1SGregory Neil Shapiro if (ExitVal == EX_TEMPFAIL) 15043299c2f1SGregory Neil Shapiro return ExitVal; 1505c2aa98e2SPeter Wemm 15063299c2f1SGregory Neil Shapiro switch (num) /* Hopefully temporary errors. */ 15073299c2f1SGregory Neil Shapiro { 1508c2aa98e2SPeter Wemm #ifdef EDQUOT 1509c2aa98e2SPeter Wemm case EDQUOT: /* Disc quota exceeded */ 1510b4662009SGregory Neil Shapiro if (BounceQuota) 15113299c2f1SGregory Neil Shapiro { 15123299c2f1SGregory Neil Shapiro ExitVal = EX_UNAVAILABLE; 15133299c2f1SGregory Neil Shapiro break; 15143299c2f1SGregory Neil Shapiro } 15153299c2f1SGregory Neil Shapiro /* FALLTHROUGH */ 15163299c2f1SGregory Neil Shapiro #endif /* EDQUOT */ 15173299c2f1SGregory Neil Shapiro #ifdef EAGAIN 15183299c2f1SGregory Neil Shapiro case EAGAIN: /* Resource temporarily unavailable */ 15193299c2f1SGregory Neil Shapiro #endif /* EAGAIN */ 1520c2aa98e2SPeter Wemm #ifdef EBUSY 1521c2aa98e2SPeter Wemm case EBUSY: /* Device busy */ 15223299c2f1SGregory Neil Shapiro #endif /* EBUSY */ 1523c2aa98e2SPeter Wemm #ifdef EPROCLIM 1524c2aa98e2SPeter Wemm case EPROCLIM: /* Too many processes */ 15253299c2f1SGregory Neil Shapiro #endif /* EPROCLIM */ 1526c2aa98e2SPeter Wemm #ifdef EUSERS 1527c2aa98e2SPeter Wemm case EUSERS: /* Too many users */ 15283299c2f1SGregory Neil Shapiro #endif /* EUSERS */ 1529c2aa98e2SPeter Wemm #ifdef ECONNABORTED 1530c2aa98e2SPeter Wemm case ECONNABORTED: /* Software caused connection abort */ 15313299c2f1SGregory Neil Shapiro #endif /* ECONNABORTED */ 1532c2aa98e2SPeter Wemm #ifdef ECONNREFUSED 1533c2aa98e2SPeter Wemm case ECONNREFUSED: /* Connection refused */ 15343299c2f1SGregory Neil Shapiro #endif /* ECONNREFUSED */ 1535c2aa98e2SPeter Wemm #ifdef ECONNRESET 1536c2aa98e2SPeter Wemm case ECONNRESET: /* Connection reset by peer */ 15373299c2f1SGregory Neil Shapiro #endif /* ECONNRESET */ 1538c2aa98e2SPeter Wemm #ifdef EDEADLK 1539c2aa98e2SPeter Wemm case EDEADLK: /* Resource deadlock avoided */ 15403299c2f1SGregory Neil Shapiro #endif /* EDEADLK */ 1541c2aa98e2SPeter Wemm #ifdef EFBIG 1542c2aa98e2SPeter Wemm case EFBIG: /* File too large */ 15433299c2f1SGregory Neil Shapiro #endif /* EFBIG */ 1544c2aa98e2SPeter Wemm #ifdef EHOSTDOWN 1545c2aa98e2SPeter Wemm case EHOSTDOWN: /* Host is down */ 15463299c2f1SGregory Neil Shapiro #endif /* EHOSTDOWN */ 1547c2aa98e2SPeter Wemm #ifdef EHOSTUNREACH 1548c2aa98e2SPeter Wemm case EHOSTUNREACH: /* No route to host */ 15493299c2f1SGregory Neil Shapiro #endif /* EHOSTUNREACH */ 1550c2aa98e2SPeter Wemm #ifdef EMFILE 1551c2aa98e2SPeter Wemm case EMFILE: /* Too many open files */ 15523299c2f1SGregory Neil Shapiro #endif /* EMFILE */ 1553c2aa98e2SPeter Wemm #ifdef ENETDOWN 1554c2aa98e2SPeter Wemm case ENETDOWN: /* Network is down */ 15553299c2f1SGregory Neil Shapiro #endif /* ENETDOWN */ 1556c2aa98e2SPeter Wemm #ifdef ENETRESET 1557c2aa98e2SPeter Wemm case ENETRESET: /* Network dropped connection on reset */ 15583299c2f1SGregory Neil Shapiro #endif /* ENETRESET */ 1559c2aa98e2SPeter Wemm #ifdef ENETUNREACH 1560c2aa98e2SPeter Wemm case ENETUNREACH: /* Network is unreachable */ 15613299c2f1SGregory Neil Shapiro #endif /* ENETUNREACH */ 1562c2aa98e2SPeter Wemm #ifdef ENFILE 1563c2aa98e2SPeter Wemm case ENFILE: /* Too many open files in system */ 15643299c2f1SGregory Neil Shapiro #endif /* ENFILE */ 1565c2aa98e2SPeter Wemm #ifdef ENOBUFS 1566c2aa98e2SPeter Wemm case ENOBUFS: /* No buffer space available */ 15673299c2f1SGregory Neil Shapiro #endif /* ENOBUFS */ 1568c2aa98e2SPeter Wemm #ifdef ENOMEM 1569c2aa98e2SPeter Wemm case ENOMEM: /* Cannot allocate memory */ 15703299c2f1SGregory Neil Shapiro #endif /* ENOMEM */ 1571c2aa98e2SPeter Wemm #ifdef ENOSPC 1572c2aa98e2SPeter Wemm case ENOSPC: /* No space left on device */ 15733299c2f1SGregory Neil Shapiro #endif /* ENOSPC */ 1574c2aa98e2SPeter Wemm #ifdef EROFS 1575c2aa98e2SPeter Wemm case EROFS: /* Read-only file system */ 15763299c2f1SGregory Neil Shapiro #endif /* EROFS */ 1577c2aa98e2SPeter Wemm #ifdef ESTALE 1578c2aa98e2SPeter Wemm case ESTALE: /* Stale NFS file handle */ 15793299c2f1SGregory Neil Shapiro #endif /* ESTALE */ 1580c2aa98e2SPeter Wemm #ifdef ETIMEDOUT 1581c2aa98e2SPeter Wemm case ETIMEDOUT: /* Connection timed out */ 15823299c2f1SGregory Neil Shapiro #endif /* ETIMEDOUT */ 1583c2aa98e2SPeter Wemm #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK 1584c2aa98e2SPeter Wemm case EWOULDBLOCK: /* Operation would block. */ 15853299c2f1SGregory Neil Shapiro #endif /* defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK */ 15863299c2f1SGregory Neil Shapiro ExitVal = EX_TEMPFAIL; 1587c2aa98e2SPeter Wemm break; 15883299c2f1SGregory Neil Shapiro 1589c2aa98e2SPeter Wemm default: 15903299c2f1SGregory Neil Shapiro ExitVal = EX_UNAVAILABLE; 1591c2aa98e2SPeter Wemm break; 1592c2aa98e2SPeter Wemm } 15933299c2f1SGregory Neil Shapiro return ExitVal; 1594c2aa98e2SPeter Wemm } 1595c2aa98e2SPeter Wemm 1596c2aa98e2SPeter Wemm #if defined(ultrix) || defined(_CRAY) 1597c2aa98e2SPeter Wemm /* 1598c2aa98e2SPeter Wemm * Copyright (c) 1987, 1993 1599c2aa98e2SPeter Wemm * The Regents of the University of California. All rights reserved. 1600c2aa98e2SPeter Wemm * 1601c2aa98e2SPeter Wemm * Redistribution and use in source and binary forms, with or without 1602c2aa98e2SPeter Wemm * modification, are permitted provided that the following conditions 1603c2aa98e2SPeter Wemm * are met: 1604c2aa98e2SPeter Wemm * 1. Redistributions of source code must retain the above copyright 1605c2aa98e2SPeter Wemm * notice, this list of conditions and the following disclaimer. 1606c2aa98e2SPeter Wemm * 2. Redistributions in binary form must reproduce the above copyright 1607c2aa98e2SPeter Wemm * notice, this list of conditions and the following disclaimer in the 1608c2aa98e2SPeter Wemm * documentation and/or other materials provided with the distribution. 1609c2aa98e2SPeter Wemm * 3. All advertising materials mentioning features or use of this software 1610c2aa98e2SPeter Wemm * must display the following acknowledgement: 1611c2aa98e2SPeter Wemm * This product includes software developed by the University of 1612c2aa98e2SPeter Wemm * California, Berkeley and its contributors. 1613c2aa98e2SPeter Wemm * 4. Neither the name of the University nor the names of its contributors 1614c2aa98e2SPeter Wemm * may be used to endorse or promote products derived from this software 1615c2aa98e2SPeter Wemm * without specific prior written permission. 1616c2aa98e2SPeter Wemm * 1617c2aa98e2SPeter Wemm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1618c2aa98e2SPeter Wemm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1619c2aa98e2SPeter Wemm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1620c2aa98e2SPeter Wemm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 1621c2aa98e2SPeter Wemm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1622c2aa98e2SPeter Wemm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1623c2aa98e2SPeter Wemm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 1624c2aa98e2SPeter Wemm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 1625c2aa98e2SPeter Wemm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 1626c2aa98e2SPeter Wemm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1627c2aa98e2SPeter Wemm * SUCH DAMAGE. 1628c2aa98e2SPeter Wemm */ 1629c2aa98e2SPeter Wemm 1630c2aa98e2SPeter Wemm # if defined(LIBC_SCCS) && !defined(lint) 1631c2aa98e2SPeter Wemm static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93"; 16323299c2f1SGregory Neil Shapiro # endif /* defined(LIBC_SCCS) && !defined(lint) */ 1633c2aa98e2SPeter Wemm 1634c2aa98e2SPeter Wemm # include <sys/types.h> 1635c2aa98e2SPeter Wemm # include <sys/stat.h> 1636c2aa98e2SPeter Wemm # include <fcntl.h> 1637c2aa98e2SPeter Wemm # include <errno.h> 1638c2aa98e2SPeter Wemm # include <stdio.h> 1639c2aa98e2SPeter Wemm # include <ctype.h> 1640c2aa98e2SPeter Wemm 1641c2aa98e2SPeter Wemm static int _gettemp(); 1642c2aa98e2SPeter Wemm 1643c2aa98e2SPeter Wemm mkstemp(path) 1644c2aa98e2SPeter Wemm char *path; 1645c2aa98e2SPeter Wemm { 1646c2aa98e2SPeter Wemm int fd; 1647c2aa98e2SPeter Wemm 1648c2aa98e2SPeter Wemm return (_gettemp(path, &fd) ? fd : -1); 1649c2aa98e2SPeter Wemm } 1650c2aa98e2SPeter Wemm 1651c2aa98e2SPeter Wemm static 1652c2aa98e2SPeter Wemm _gettemp(path, doopen) 1653c2aa98e2SPeter Wemm char *path; 1654c2aa98e2SPeter Wemm register int *doopen; 1655c2aa98e2SPeter Wemm { 1656c2aa98e2SPeter Wemm extern int errno; 1657c2aa98e2SPeter Wemm register char *start, *trv; 1658c2aa98e2SPeter Wemm struct stat sbuf; 165912ed1c7cSGregory Neil Shapiro unsigned int pid; 1660c2aa98e2SPeter Wemm 1661c2aa98e2SPeter Wemm pid = getpid(); 1662c2aa98e2SPeter Wemm for (trv = path; *trv; ++trv); /* extra X's get set to 0's */ 16633299c2f1SGregory Neil Shapiro while (*--trv == 'X') 16643299c2f1SGregory Neil Shapiro { 1665c2aa98e2SPeter Wemm *trv = (pid % 10) + '0'; 1666c2aa98e2SPeter Wemm pid /= 10; 1667c2aa98e2SPeter Wemm } 1668c2aa98e2SPeter Wemm 1669c2aa98e2SPeter Wemm /* 1670c2aa98e2SPeter Wemm * check the target directory; if you have six X's and it 1671c2aa98e2SPeter Wemm * doesn't exist this runs for a *very* long time. 1672c2aa98e2SPeter Wemm */ 16733299c2f1SGregory Neil Shapiro for (start = trv + 1;; --trv) 16743299c2f1SGregory Neil Shapiro { 1675c2aa98e2SPeter Wemm if (trv <= path) 1676c2aa98e2SPeter Wemm break; 16773299c2f1SGregory Neil Shapiro if (*trv == '/') 16783299c2f1SGregory Neil Shapiro { 1679c2aa98e2SPeter Wemm *trv = '\0'; 1680c2aa98e2SPeter Wemm if (stat(path, &sbuf) < 0) 1681c2aa98e2SPeter Wemm return(0); 16823299c2f1SGregory Neil Shapiro if (!S_ISDIR(sbuf.st_mode)) 16833299c2f1SGregory Neil Shapiro { 1684c2aa98e2SPeter Wemm errno = ENOTDIR; 1685c2aa98e2SPeter Wemm return(0); 1686c2aa98e2SPeter Wemm } 1687c2aa98e2SPeter Wemm *trv = '/'; 1688c2aa98e2SPeter Wemm break; 1689c2aa98e2SPeter Wemm } 1690c2aa98e2SPeter Wemm } 1691c2aa98e2SPeter Wemm 16923299c2f1SGregory Neil Shapiro for (;;) 16933299c2f1SGregory Neil Shapiro { 16943299c2f1SGregory Neil Shapiro if (doopen) 16953299c2f1SGregory Neil Shapiro { 1696d995d2baSGregory Neil Shapiro if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR, 1697d995d2baSGregory Neil Shapiro 0600)) >= 0) 1698c2aa98e2SPeter Wemm return(1); 1699c2aa98e2SPeter Wemm if (errno != EEXIST) 1700c2aa98e2SPeter Wemm return(0); 1701c2aa98e2SPeter Wemm } 1702c2aa98e2SPeter Wemm else if (stat(path, &sbuf) < 0) 1703c2aa98e2SPeter Wemm return(errno == ENOENT ? 1 : 0); 1704c2aa98e2SPeter Wemm 1705c2aa98e2SPeter Wemm /* tricky little algorithm for backward compatibility */ 17063299c2f1SGregory Neil Shapiro for (trv = start;;) 17073299c2f1SGregory Neil Shapiro { 1708c2aa98e2SPeter Wemm if (!*trv) 1709c2aa98e2SPeter Wemm return(0); 1710c2aa98e2SPeter Wemm if (*trv == 'z') 1711c2aa98e2SPeter Wemm *trv++ = 'a'; 17123299c2f1SGregory Neil Shapiro else 17133299c2f1SGregory Neil Shapiro { 1714c2aa98e2SPeter Wemm if (isascii(*trv) && isdigit(*trv)) 1715c2aa98e2SPeter Wemm *trv = 'a'; 1716c2aa98e2SPeter Wemm else 1717c2aa98e2SPeter Wemm ++*trv; 1718c2aa98e2SPeter Wemm break; 1719c2aa98e2SPeter Wemm } 1720c2aa98e2SPeter Wemm } 1721c2aa98e2SPeter Wemm } 1722c2aa98e2SPeter Wemm /* NOTREACHED */ 1723c2aa98e2SPeter Wemm } 17243299c2f1SGregory Neil Shapiro #endif /* defined(ultrix) || defined(_CRAY) */ 1725