]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/mail.local/mail.local.c
Merge commit '850ef5ae11d69ea3381bd310f564f025fc8caea3'
[FreeBSD/FreeBSD.git] / contrib / sendmail / mail.local / mail.local.c
1 /*
2  * Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1990, 1993, 1994
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  * $FreeBSD$
12  *
13  */
14
15 #include <sm/gen.h>
16
17 SM_IDSTR(copyright,
18 "@(#) Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers.\n\
19         All rights reserved.\n\
20      Copyright (c) 1990, 1993, 1994\n\
21         The Regents of the University of California.  All rights reserved.\n")
22
23 SM_IDSTR(id, "@(#)$Id: mail.local.c,v 8.257 2013-11-22 20:51:51 ca Exp $")
24
25 #include <stdlib.h>
26 #include <sm/sendmail.h>
27 #include <sm/errstring.h>
28 #include <sm/io.h>
29 #include <sm/limits.h>
30 #include <unistd.h>
31 #define LOCKFILE_PMODE 0
32 #include <sm/mbdb.h>
33 #include <sm/sysexits.h>
34
35 #ifndef HASHSPOOL
36 # define HASHSPOOL      0
37 #endif
38 #ifndef HASHSPOOLMD5
39 # define HASHSPOOLMD5   0
40 #endif
41
42 /*
43 **  This is not intended to work on System V derived systems
44 **  such as Solaris or HP-UX, since they use a totally different
45 **  approach to mailboxes (essentially, they have a set-group-ID program
46 **  rather than set-user-ID, and they rely on the ability to "give away"
47 **  files to do their work).  IT IS NOT A BUG that this doesn't
48 **  work on such architectures.
49 */
50
51
52 #include <stdio.h>
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <time.h>
58 #include <stdlib.h>
59 # include <sys/socket.h>
60 # include <sys/file.h>
61 # include <netinet/in.h>
62 # include <arpa/nameser.h>
63 # include <netdb.h>
64 # include <pwd.h>
65
66 #include <sm/string.h>
67 #include <syslog.h>
68 #include <ctype.h>
69
70 #include <sm/conf.h>
71 #include <sendmail/pathnames.h>
72
73 #if HASHSPOOL
74 # define HASH_NONE      0
75 # define HASH_USER      1
76 # if HASHSPOOLMD5
77 #  define HASH_MD5      2
78 #  include <openssl/md5.h>
79 # endif
80 #endif /* HASHSPOOL */
81
82 #if _FFR_SPOOL_PATH
83         /*
84         **  Override path to mail store at run time (using -p).
85         **  From: Eugene Grosbein of Svyaz Service JSC
86         **  See: http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/114195
87         **  NOTE: Update man page before adding this to a release.
88         */
89 #endif /* _FFR_SPOOL_PATH */
90
91
92 #ifndef LOCKTO_RM
93 # define LOCKTO_RM      300     /* timeout for stale lockfile removal */
94 #endif
95 #ifndef LOCKTO_GLOB
96 # define LOCKTO_GLOB    400     /* global timeout for lockfile creation */
97 #endif
98
99 /* define a realloc() which works for NULL pointers */
100 #define REALLOC(ptr, size)      (((ptr) == NULL) ? malloc(size) : realloc(ptr, size))
101
102 /*
103 **  If you don't have flock, you could try using lockf instead.
104 */
105
106 #ifdef LDA_USE_LOCKF
107 # define flock(a, b)    lockf(a, b, 0)
108 # ifdef LOCK_EX
109 #  undef LOCK_EX
110 # endif
111 # define LOCK_EX        F_LOCK
112 #endif /* LDA_USE_LOCKF */
113
114 #ifndef LOCK_EX
115 # include <sys/file.h>
116 #endif
117
118 /*
119 **  If you don't have setreuid, and you have saved uids, and you have
120 **  a seteuid() call that doesn't try to emulate using setuid(), then
121 **  you can try defining LDA_USE_SETEUID.
122 */
123
124 #ifdef LDA_USE_SETEUID
125 # define setreuid(r, e)         seteuid(e)
126 #endif
127
128 #ifdef LDA_CONTENTLENGTH
129 # define CONTENTLENGTH  1
130 #endif
131
132 #ifndef INADDRSZ
133 # define INADDRSZ       4               /* size of an IPv4 address in bytes */
134 #endif
135
136 #ifdef MAILLOCK
137 # include <maillock.h>
138 #endif
139
140 #ifndef MAILER_DAEMON
141 # define MAILER_DAEMON  "MAILER-DAEMON"
142 #endif
143
144 #ifdef CONTENTLENGTH
145 char    ContentHdr[40] = "Content-Length: ";
146 off_t   HeaderLength;
147 off_t   BodyLength;
148 #endif
149
150 bool    EightBitMime = true;            /* advertise 8BITMIME in LMTP */
151 #if USE_EAI
152 bool    EAI = true;                     /* advertise SMTPUTF8 in LMTP */
153 #endif
154 char    ErrBuf[10240];                  /* error buffer */
155 int     ExitVal = EX_OK;                /* sysexits.h error value. */
156 bool    nobiff = false;
157 bool    nofsync = false;
158 bool    HoldErrs = false;               /* Hold errors in ErrBuf */
159 bool    LMTPMode = false;
160 bool    BounceQuota = false;            /* permanent error when over quota */
161 bool    CloseMBDB = false;
162 char    *HomeMailFile = NULL;           /* store mail in homedir */
163
164 #if HASHSPOOL
165 int     HashType = HASH_NONE;
166 int     HashDepth = 0;
167 bool    StripRcptDomain = true;
168 #else
169 # define StripRcptDomain true
170 #endif
171 char    SpoolPath[MAXPATHLEN];
172
173 char    *parseaddr __P((char *, bool));
174 char    *process_recipient __P((char *));
175 void    dolmtp __P((void));
176 void    deliver __P((int, char *));
177 int     e_to_sys __P((int));
178 void    notifybiff __P((char *));
179 int     store __P((char *, bool *));
180 void    usage __P((void));
181 int     lockmbox __P((char *));
182 void    unlockmbox __P((void));
183 void    mailerr __P((const char *, const char *, ...));
184 void    flush_error __P((void));
185 #if HASHSPOOL
186 const char      *hashname __P((char *));
187 #endif
188
189
190 static void sm_exit __P((int));
191
192 static void
193 sm_exit(status)
194         int status;
195 {
196         if (CloseMBDB)
197         {
198                 sm_mbdb_terminate();
199                 CloseMBDB = false;      /* not really necessary, but ... */
200         }
201         exit(status);
202 }
203
204 int
205 main(argc, argv)
206         int argc;
207         char *argv[];
208 {
209         struct passwd *pw;
210         int ch, fd;
211         uid_t uid;
212         char *from;
213         char *mbdbname = "pw";
214         int err;
215         extern char *optarg;
216         extern int optind;
217
218
219         /* make sure we have some open file descriptors */
220         for (fd = 10; fd < 30; fd++)
221                 (void) close(fd);
222
223         /* use a reasonable umask */
224         (void) umask(0077);
225
226 #ifdef LOG_MAIL
227         openlog("mail.local", 0, LOG_MAIL);
228 #else
229         openlog("mail.local", 0);
230 #endif
231
232         from = NULL;
233
234         /* XXX can this be converted to a compile time check? */
235         if (sm_strlcpy(SpoolPath, _PATH_MAILDIR, sizeof(SpoolPath)) >=
236             sizeof(SpoolPath))
237         {
238                 mailerr("421", "Configuration error: _PATH_MAILDIR too large");
239                 sm_exit(EX_CONFIG);
240         }
241
242         /* HACK: add U to all options - this should be only for USE_EAI */
243 #if HASHSPOOL
244         while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lH:p:nsUV")) != -1)
245 #else /* HASHSPOOL */
246 # if _FFR_SPOOL_PATH
247         while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lp:sUV")) != -1)
248 # else
249         while ((ch = getopt(argc, argv, "7BbdD:f:h:r:lsUV")) != -1)
250 # endif
251 #endif /* HASHSPOOL */
252         {
253                 switch(ch)
254                 {
255                   case '7':             /* Do not advertise 8BITMIME */
256                         EightBitMime = false;
257                         break;
258
259                   case 'B':
260                         nobiff = true;
261                         break;
262
263                   case 'b':             /* bounce mail when over quota. */
264                         BounceQuota = true;
265                         break;
266
267                   case 'd':             /* Backward compatible. */
268                         break;
269
270                   case 'D':             /* mailbox database type */
271                         mbdbname = optarg;
272                         break;
273
274                   case 'f':
275                   case 'r':             /* Backward compatible. */
276                         if (from != NULL)
277                         {
278                                 mailerr(NULL, "Multiple -f options");
279                                 usage();
280                         }
281                         from = optarg;
282                         break;
283
284                   case 'h':
285                         if (optarg != NULL || *optarg != '\0')
286                                 HomeMailFile = optarg;
287                         else
288                         {
289                                 mailerr(NULL, "-h: missing filename");
290                                 usage();
291                         }
292                         break;
293
294                   case 'l':
295                         LMTPMode = true;
296                         break;
297
298                   case 's':
299                         nofsync++;
300                         break;
301
302 #if HASHSPOOL
303                   case 'H':
304                         if (optarg == NULL || *optarg == '\0')
305                         {
306                                 mailerr(NULL, "-H: missing hashinfo");
307                                 usage();
308                         }
309                         switch(optarg[0])
310                         {
311                           case 'u':
312                                 HashType = HASH_USER;
313                                 break;
314
315 # if HASHSPOOLMD5
316                           case 'm':
317                                 HashType = HASH_MD5;
318                                 break;
319 # endif
320
321                           default:
322                                 mailerr(NULL, "-H: unknown hash type");
323                                 usage();
324                         }
325                         if (optarg[1] == '\0')
326                         {
327                                 mailerr(NULL, "-H: invalid hash depth");
328                                 usage();
329                         }
330                         HashDepth = atoi(&optarg[1]);
331                         if ((HashDepth <= 0) || ((HashDepth * 2) >= MAXPATHLEN))
332                         {
333                                 mailerr(NULL, "-H: invalid hash depth");
334                                 usage();
335                         }
336                         break;
337
338                   case 'n':
339                         StripRcptDomain = false;
340                         break;
341 #endif /* HASHSPOOL */
342
343 #if HASHSPOOL || _FFR_SPOOL_PATH
344                   case 'p':
345                         if (optarg == NULL || *optarg == '\0')
346                         {
347                                 mailerr(NULL, "-p: missing spool path");
348                                 usage();
349                         }
350                         if (sm_strlcpy(SpoolPath, optarg, sizeof(SpoolPath)) >=
351                             sizeof(SpoolPath))
352                         {
353                                 mailerr(NULL, "-p: invalid spool path");
354                                 usage();
355                         }
356                         break;
357 #endif /* HASHSPOOL || _FFR_SPOOL_PATH */
358
359 #if USE_EAI
360                   case 'U':
361                         EAI = false;
362                         break;
363 #endif
364                   case 'V':
365                         fprintf(stderr, "compiled with\n");
366 #if MAIL_LOCAL_TEST
367                         fprintf(stderr, "MAIL_LOCAL_TEST\n");
368 #endif
369 #if USE_EAI
370                         /* test scripts should look for SMTPUTF8 */
371                         fprintf(stderr, "USE_EAI\n");
372 #endif
373                         break;
374
375                   case '?':
376                   default:
377                         usage();
378                 }
379         }
380         argc -= optind;
381         argv += optind;
382
383         /* initialize biff structures */
384         if (!nobiff)
385                 notifybiff(NULL);
386
387         err = sm_mbdb_initialize(mbdbname);
388         if (err != EX_OK)
389         {
390                 char *errcode = "521";
391
392                 if (err == EX_TEMPFAIL)
393                         errcode = "421";
394
395                 mailerr(errcode, "Can not open mailbox database %s: %s",
396                         mbdbname, sm_strexit(err));
397                 sm_exit(err);
398         }
399         CloseMBDB = true;
400
401         if (LMTPMode)
402         {
403                 if (argc > 0)
404                 {
405                         mailerr("421", "Users should not be specified in command line if LMTP required");
406                         sm_exit(EX_TEMPFAIL);
407                 }
408
409                 dolmtp();
410                 /* NOTREACHED */
411                 sm_exit(EX_OK);
412         }
413
414         /* Non-LMTP from here on out */
415         if (*argv == NULL)
416                 usage();
417
418         /*
419         **  If from not specified, use the name from getlogin() if the
420         **  uid matches, otherwise, use the name from the password file
421         **  corresponding to the uid.
422         */
423
424         uid = getuid();
425         if (from == NULL && ((from = getlogin()) == NULL ||
426                              (pw = getpwnam(from)) == NULL ||
427                              pw->pw_uid != uid))
428                 from = (pw = getpwuid(uid)) != NULL ? pw->pw_name : "???";
429
430         /*
431         **  There is no way to distinguish the error status of one delivery
432         **  from the rest of the deliveries.  So, if we failed hard on one
433         **  or more deliveries, but had no failures on any of the others, we
434         **  return a hard failure.  If we failed temporarily on one or more
435         **  deliveries, we return a temporary failure regardless of the other
436         **  failures.  This results in the delivery being reattempted later
437         **  at the expense of repeated failures and multiple deliveries.
438         */
439
440         HoldErrs = true;
441         fd = store(from, NULL);
442         HoldErrs = false;
443         if (fd < 0)
444         {
445                 flush_error();
446                 sm_exit(ExitVal);
447         }
448         for (; *argv != NULL; ++argv)
449                 deliver(fd, *argv);
450         sm_exit(ExitVal);
451         /* NOTREACHED */
452         return ExitVal;
453 }
454
455 char *
456 parseaddr(s, rcpt)
457         char *s;
458         bool rcpt;
459 {
460         char *p;
461         int l;
462
463         if (*s++ != '<')
464                 return NULL;
465
466         p = s;
467
468         /* at-domain-list */
469         while (*p == '@')
470         {
471                 p++;
472                 while (*p != ',' && *p != ':' && *p != '\0')
473                         p++;
474                 if (*p == '\0')
475                         return NULL;
476
477                 /* Skip over , or : */
478                 p++;
479         }
480
481         s = p;
482
483         /* local-part */
484         while (*p != '\0' && *p != '@' && *p != '>')
485         {
486                 if (*p == '\\')
487                 {
488                         if (*++p == '\0')
489                                 return NULL;
490                 }
491                 else if (*p == '\"')
492                 {
493                         p++;
494                         while (*p != '\0' && *p != '\"')
495                         {
496                                 if (*p == '\\')
497                                 {
498                                         if (*++p == '\0')
499                                                 return NULL;
500                                 }
501                                 p++;
502                         }
503                         if (*p == '\0' || *(p + 1) == '\0')
504                                 return NULL;
505                 }
506                 /* +detail ? */
507                 if (*p == '+' && rcpt)
508                         *p = '\0';
509                 p++;
510         }
511
512         /* @domain */
513         if (*p == '@')
514         {
515                 if (rcpt)
516                         *p++ = '\0';
517                 while (*p != '\0' && *p != '>')
518                         p++;
519         }
520
521         if (*p != '>')
522                 return NULL;
523         else
524                 *p = '\0';
525         p++;
526
527         if (*p != '\0' && *p != ' ')
528                 return NULL;
529
530         if (*s == '\0')
531                 s = MAILER_DAEMON;
532
533         l = strlen(s) + 1;
534         if (l < 0)
535                 return NULL;
536         p = malloc(l);
537         if (p == NULL)
538         {
539                 mailerr("421 4.3.0", "Memory exhausted");
540                 sm_exit(EX_TEMPFAIL);
541         }
542
543         (void) sm_strlcpy(p, s, l);
544         return p;
545 }
546
547 char *
548 process_recipient(addr)
549         char *addr;
550 {
551         SM_MBDB_T user;
552
553         switch (sm_mbdb_lookup(addr, &user))
554         {
555           case EX_OK:
556                 return NULL;
557
558           case EX_NOUSER:
559                 return "550 5.1.1 User unknown";
560
561           case EX_TEMPFAIL:
562                 return "451 4.3.0 User database failure; retry later";
563
564           default:
565                 return "550 5.3.0 User database failure";
566         }
567 }
568
569 #define RCPT_GROW       30
570
571 void
572 dolmtp()
573 {
574         char *return_path = NULL;
575         char **rcpt_addr = NULL;
576         int rcpt_num = 0;
577         int rcpt_alloc = 0;
578         bool gotlhlo = false;
579         char *err;
580         int msgfd;
581         char *p;
582         int i;
583         char myhostname[1024];
584         char buf[4096];
585
586         memset(myhostname, '\0', sizeof myhostname);
587         (void) gethostname(myhostname, sizeof myhostname - 1);
588         if (myhostname[0] == '\0')
589                 sm_strlcpy(myhostname, "localhost", sizeof myhostname);
590
591         printf("220 %s LMTP ready\r\n", myhostname);
592         for (;;)
593         {
594                 (void) fflush(stdout);
595                 if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
596                         sm_exit(EX_OK);
597                 p = buf + strlen(buf) - 1;
598                 if (p >= buf && *p == '\n')
599                         *p-- = '\0';
600                 if (p >= buf && *p == '\r')
601                         *p-- = '\0';
602
603                 switch (buf[0])
604                 {
605                   case 'd':
606                   case 'D':
607                         if (SM_STRCASEEQ(buf, "data"))
608                         {
609                                 bool inbody = false;
610
611                                 if (rcpt_num == 0)
612                                 {
613                                         mailerr("503 5.5.1", "No recipients");
614                                         continue;
615                                 }
616                                 HoldErrs = true;
617                                 msgfd = store(return_path, &inbody);
618                                 HoldErrs = false;
619                                 if (msgfd < 0 && !inbody)
620                                 {
621                                         flush_error();
622                                         continue;
623                                 }
624
625                                 for (i = 0; i < rcpt_num; i++)
626                                 {
627                                         if (msgfd < 0)
628                                         {
629                                                 /* print error for rcpt */
630                                                 flush_error();
631                                                 continue;
632                                         }
633                                         p = strchr(rcpt_addr[i], '+');
634                                         if (p != NULL)
635                                                 *p = '\0';
636                                         deliver(msgfd, rcpt_addr[i]);
637                                 }
638                                 if (msgfd >= 0)
639                                         (void) close(msgfd);
640                                 goto rset;
641                         }
642                         goto syntaxerr;
643                         /* NOTREACHED */
644                         break;
645
646                   case 'l':
647                   case 'L':
648                         if (sm_strncasecmp(buf, "lhlo ", 5) == 0)
649                         {
650                                 /* check for duplicate per RFC 1651 4.2 */
651                                 if (gotlhlo)
652                                 {
653                                         mailerr("503", "%s Duplicate LHLO",
654                                                myhostname);
655                                         continue;
656                                 }
657                                 gotlhlo = true;
658                                 printf("250-%s\r\n", myhostname);
659                                 if (EightBitMime)
660                                         printf("250-8BITMIME\r\n");
661 #if USE_EAI
662                                 if (EAI)
663                                         printf("250-SMTPUTF8\r\n");
664 #endif
665                                 printf("250-ENHANCEDSTATUSCODES\r\n");
666                                 printf("250 PIPELINING\r\n");
667                                 continue;
668                         }
669                         goto syntaxerr;
670                         /* NOTREACHED */
671                         break;
672
673                   case 'm':
674                   case 'M':
675                         if (sm_strncasecmp(buf, "mail ", 5) == 0)
676                         {
677                                 if (return_path != NULL)
678                                 {
679                                         mailerr("503 5.5.1",
680                                                 "Nested MAIL command");
681                                         continue;
682                                 }
683                                 if (sm_strncasecmp(buf + 5, "from:", 5) != 0 ||
684                                     ((return_path = parseaddr(buf + 10,
685                                                               false)) == NULL))
686                                 {
687                                         mailerr("501 5.5.4",
688                                                 "Syntax error in parameters");
689                                         continue;
690                                 }
691                                 printf("250 2.5.0 Ok\r\n");
692                                 continue;
693                         }
694                         goto syntaxerr;
695                         /* NOTREACHED */
696                         break;
697
698                   case 'n':
699                   case 'N':
700                         if (SM_STRCASEEQ(buf, "noop"))
701                         {
702                                 printf("250 2.0.0 Ok\r\n");
703                                 continue;
704                         }
705                         goto syntaxerr;
706                         /* NOTREACHED */
707                         break;
708
709                   case 'q':
710                   case 'Q':
711                         if (SM_STRCASEEQ(buf, "quit"))
712                         {
713                                 printf("221 2.0.0 Bye\r\n");
714                                 sm_exit(EX_OK);
715                         }
716                         goto syntaxerr;
717                         /* NOTREACHED */
718                         break;
719
720                   case 'r':
721                   case 'R':
722                         if (sm_strncasecmp(buf, "rcpt ", 5) == 0)
723                         {
724                                 if (return_path == NULL)
725                                 {
726                                         mailerr("503 5.5.1",
727                                                 "Need MAIL command");
728                                         continue;
729                                 }
730                                 if (rcpt_num >= rcpt_alloc)
731                                 {
732                                         rcpt_alloc += RCPT_GROW;
733                                         rcpt_addr = (char **)
734                                                 REALLOC((char *) rcpt_addr,
735                                                         rcpt_alloc *
736                                                         sizeof(char **));
737                                         if (rcpt_addr == NULL)
738                                         {
739                                                 mailerr("421 4.3.0",
740                                                         "Memory exhausted");
741                                                 sm_exit(EX_TEMPFAIL);
742                                         }
743                                 }
744                                 if (sm_strncasecmp(buf + 5, "to:", 3) != 0 ||
745                                     ((rcpt_addr[rcpt_num] = parseaddr(buf + 8,
746                                                                       StripRcptDomain)) == NULL))
747                                 {
748                                         mailerr("501 5.5.4",
749                                                 "Syntax error in parameters");
750                                         continue;
751                                 }
752                                 err = process_recipient(rcpt_addr[rcpt_num]);
753                                 if (err != NULL)
754                                 {
755                                         mailerr(NULL, "%s", err);
756                                         continue;
757                                 }
758                                 rcpt_num++;
759                                 printf("250 2.1.5 Ok\r\n");
760                                 continue;
761                         }
762                         else if (SM_STRCASEEQ(buf, "rset"))
763                         {
764                                 printf("250 2.0.0 Ok\r\n");
765
766 rset:
767                                 while (rcpt_num > 0)
768                                         free(rcpt_addr[--rcpt_num]);
769                                 if (return_path != NULL)
770                                         free(return_path);
771                                 return_path = NULL;
772                                 continue;
773                         }
774                         goto syntaxerr;
775                         /* NOTREACHED */
776                         break;
777
778                   case 'v':
779                   case 'V':
780                         if (sm_strncasecmp(buf, "vrfy ", 5) == 0)
781                         {
782                                 printf("252 2.3.3 Try RCPT to attempt delivery\r\n");
783                                 continue;
784                         }
785                         goto syntaxerr;
786                         /* NOTREACHED */
787                         break;
788
789                   default:
790   syntaxerr:
791                         mailerr("500 5.5.2", "Syntax error");
792                         continue;
793                         /* NOTREACHED */
794                         break;
795                 }
796         }
797 }
798
799 int
800 store(from, inbody)
801         char *from;
802         bool *inbody;
803 {
804         FILE *fp = NULL;
805         time_t tval;
806         bool eline;             /* previous line was empty */
807         bool fullline = true;   /* current line is terminated */
808         bool prevfl;            /* previous line was terminated */
809         char line[2048];
810         int fd;
811         char tmpbuf[sizeof _PATH_LOCTMP + 1];
812
813         if (inbody != NULL)
814                 *inbody = false;
815
816         (void) umask(0077);
817         (void) sm_strlcpy(tmpbuf, _PATH_LOCTMP, sizeof tmpbuf);
818         if ((fd = mkstemp(tmpbuf)) < 0 || (fp = fdopen(fd, "w+")) == NULL)
819         {
820                 if (fd >= 0)
821                         (void) close(fd);
822                 mailerr("451 4.3.0", "Unable to open temporary file");
823                 return -1;
824         }
825         (void) unlink(tmpbuf);
826
827         if (LMTPMode)
828         {
829                 printf("354 Go ahead\r\n");
830                 (void) fflush(stdout);
831         }
832         if (inbody != NULL)
833                 *inbody = true;
834
835         (void) time(&tval);
836         (void) fprintf(fp, "From %s %s", from, ctime(&tval));
837
838 #ifdef CONTENTLENGTH
839         HeaderLength = 0;
840         BodyLength = -1;
841 #endif
842
843         line[0] = '\0';
844         eline = true;
845         while (fgets(line, sizeof(line), stdin) != (char *) NULL)
846         {
847                 size_t line_len = 0;
848                 int peek;
849
850                 prevfl = fullline;      /* preserve state of previous line */
851                 while (line[line_len] != '\n' && line_len < sizeof(line) - 2)
852                         line_len++;
853                 line_len++;
854
855                 /* Check for dot-stuffing */
856                 if (prevfl && LMTPMode && line[0] == '.')
857                 {
858                         if (line[1] == '\n' ||
859                             (line[1] == '\r' && line[2] == '\n'))
860                                 goto lmtpdot;
861                         memcpy(line, line + 1, line_len);
862                         line_len--;
863                 }
864
865                 /* Check to see if we have the full line from fgets() */
866                 fullline = false;
867                 if (line_len > 0)
868                 {
869                         if (line[line_len - 1] == '\n')
870                         {
871                                 if (line_len >= 2 &&
872                                     line[line_len - 2] == '\r')
873                                 {
874                                         line[line_len - 2] = '\n';
875                                         line[line_len - 1] = '\0';
876                                         line_len--;
877                                 }
878                                 fullline = true;
879                         }
880                         else if (line[line_len - 1] == '\r')
881                         {
882                                 /* Did we just miss the CRLF? */
883                                 peek = fgetc(stdin);
884                                 if (peek == '\n')
885                                 {
886                                         line[line_len - 1] = '\n';
887                                         fullline = true;
888                                 }
889                                 else
890                                         (void) ungetc(peek, stdin);
891                         }
892                 }
893                 else
894                         fullline = true;
895
896 #ifdef CONTENTLENGTH
897                 if (prevfl && line[0] == '\n' && HeaderLength == 0)
898                 {
899                         eline = false;
900                         if (fp != NULL)
901                                 HeaderLength = ftell(fp);
902                         if (HeaderLength <= 0)
903                         {
904                                 /*
905                                 **  shouldn't happen, unless ftell() is
906                                 **  badly broken
907                                 */
908
909                                 HeaderLength = -1;
910                         }
911                 }
912 #else /* CONTENTLENGTH */
913                 if (prevfl && line[0] == '\n')
914                         eline = true;
915 #endif /* CONTENTLENGTH */
916                 else
917                 {
918                         if (eline && line[0] == 'F' &&
919                             fp != NULL &&
920                             !memcmp(line, "From ", 5))
921                                 (void) putc('>', fp);
922                         eline = false;
923 #ifdef CONTENTLENGTH
924                         /* discard existing "Content-Length:" headers */
925                         if (prevfl && HeaderLength == 0 &&
926                             (line[0] == 'C' || line[0] == 'c') &&
927                             sm_strncasecmp(line, ContentHdr, 15) == 0)
928                         {
929                                 /*
930                                 **  be paranoid: clear the line
931                                 **  so no "wrong matches" may occur later
932                                 */
933                                 line[0] = '\0';
934                                 continue;
935                         }
936 #endif /* CONTENTLENGTH */
937
938                 }
939                 if (fp != NULL)
940                 {
941                         (void) fwrite(line, sizeof(char), line_len, fp);
942                         if (ferror(fp))
943                         {
944                                 mailerr("451 4.3.0",
945                                         "Temporary file write error");
946                                 (void) fclose(fp);
947                                 fp = NULL;
948                                 continue;
949                         }
950                 }
951         }
952
953         /* check if an error occurred */
954         if (fp == NULL)
955                 return -1;
956
957         if (LMTPMode)
958         {
959                 /* Got a premature EOF -- toss message and exit */
960                 sm_exit(EX_OK);
961         }
962
963         /* If message not newline terminated, need an extra. */
964         if (fp != NULL && strchr(line, '\n') == NULL)
965                 (void) putc('\n', fp);
966
967   lmtpdot:
968
969 #ifdef CONTENTLENGTH
970         if (fp != NULL)
971                 BodyLength = ftell(fp);
972         if (HeaderLength == 0 && BodyLength > 0)        /* empty body */
973         {
974                 HeaderLength = BodyLength;
975                 BodyLength = 0;
976         }
977         else
978                 BodyLength = BodyLength - HeaderLength - 1 ;
979
980         if (HeaderLength > 0 && BodyLength >= 0)
981         {
982                 (void) sm_snprintf(line, sizeof line, "%lld\n",
983                                    (LONGLONG_T) BodyLength);
984                 (void) sm_strlcpy(&ContentHdr[16], line,
985                                   sizeof(ContentHdr) - 16);
986         }
987         else
988                 BodyLength = -1;        /* Something is wrong here */
989 #endif /* CONTENTLENGTH */
990
991         /* Output a newline; note, empty messages are allowed. */
992         if (fp != NULL)
993                 (void) putc('\n', fp);
994
995         if (fp == NULL || fflush(fp) == EOF || ferror(fp) != 0)
996         {
997                 mailerr("451 4.3.0", "Temporary file flush error");
998                 if (fp != NULL)
999                         (void) fclose(fp);
1000                 return -1;
1001         }
1002         return fd;
1003 }
1004
1005 void
1006 deliver(fd, name)
1007         int fd;
1008         char *name;
1009 {
1010         struct stat fsb;
1011         struct stat sb;
1012         char path[MAXPATHLEN];
1013         int mbfd = -1, nr = 0, nw, off;
1014         int exitval;
1015         char *p;
1016         char *errcode;
1017         off_t curoff, cursize;
1018 #ifdef CONTENTLENGTH
1019         off_t headerbytes;
1020         int readamount;
1021 #endif
1022         char biffmsg[100], buf[8 * 1024];
1023         SM_MBDB_T user;
1024
1025         /*
1026         **  Disallow delivery to unknown names -- special mailboxes can be
1027         **  handled in the sendmail aliases file.
1028         */
1029
1030         exitval = sm_mbdb_lookup(name, &user);
1031         switch (exitval)
1032         {
1033           case EX_OK:
1034                 break;
1035
1036           case EX_NOUSER:
1037                 exitval = EX_UNAVAILABLE;
1038                 mailerr("550 5.1.1", "%s: User unknown", name);
1039                 break;
1040
1041           case EX_TEMPFAIL:
1042                 mailerr("451 4.3.0", "%s: User database failure; retry later",
1043                         name);
1044                 break;
1045
1046           default:
1047                 exitval = EX_UNAVAILABLE;
1048                 mailerr("550 5.3.0", "%s: User database failure", name);
1049                 break;
1050         }
1051
1052         if (exitval != EX_OK)
1053         {
1054                 if (ExitVal != EX_TEMPFAIL)
1055                         ExitVal = exitval;
1056                 return;
1057         }
1058
1059         endpwent();
1060
1061         /*
1062         **  Keep name reasonably short to avoid buffer overruns.
1063         **      This isn't necessary on BSD because of the proper
1064         **      definition of snprintf(), but it can cause problems
1065         **      on other systems.
1066         **  Also, clear out any bogus characters.
1067         */
1068
1069 #if !HASHSPOOL
1070         if (strlen(name) > 40)
1071                 name[40] = '\0';
1072         for (p = name; *p != '\0'; p++)
1073         {
1074                 if (!isascii(*p))
1075                         *p &= 0x7f;
1076                 else if (!isprint(*p))
1077                         *p = '.';
1078         }
1079 #endif /* !HASHSPOOL */
1080
1081
1082         if (HomeMailFile == NULL)
1083         {
1084                 if (sm_strlcpyn(path, sizeof(path),
1085 #if HASHSPOOL
1086                                 4,
1087 #else
1088                                 3,
1089 #endif
1090                                 SpoolPath, "/",
1091 #if HASHSPOOL
1092                                 hashname(name),
1093 #endif
1094                                 name) >= sizeof(path))
1095                 {
1096                         exitval = EX_UNAVAILABLE;
1097                         mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
1098                         return;
1099                 }
1100         }
1101         else if (*user.mbdb_homedir == '\0')
1102         {
1103                 exitval = EX_UNAVAILABLE;
1104                 mailerr("550 5.1.1", "%s: User missing home directory", name);
1105                 return;
1106         }
1107         else if (sm_snprintf(path, sizeof(path), "%s/%s",
1108                              user.mbdb_homedir, HomeMailFile) >= sizeof(path))
1109         {
1110                 exitval = EX_UNAVAILABLE;
1111                 mailerr("550 5.1.1", "%s: Invalid mailbox path", name);
1112                 return;
1113         }
1114
1115
1116         /*
1117         **  If the mailbox is linked or a symlink, fail.  There's an obvious
1118         **  race here, that the file was replaced with a symbolic link after
1119         **  the lstat returned, but before the open.  We attempt to detect
1120         **  this by comparing the original stat information and information
1121         **  returned by an fstat of the file descriptor returned by the open.
1122         **
1123         **  NB: this is a symptom of a larger problem, that the mail spooling
1124         **  directory is writeable by the wrong users.  If that directory is
1125         **  writeable, system security is compromised for other reasons, and
1126         **  it cannot be fixed here.
1127         **
1128         **  If we created the mailbox, set the owner/group.  If that fails,
1129         **  just return.  Another process may have already opened it, so we
1130         **  can't unlink it.  Historically, binmail set the owner/group at
1131         **  each mail delivery.  We no longer do this, assuming that if the
1132         **  ownership or permissions were changed there was a reason.
1133         **
1134         **  XXX
1135         **  open(2) should support flock'ing the file.
1136         */
1137
1138 tryagain:
1139 #ifdef MAILLOCK
1140         p = name;
1141 #else
1142         p = path;
1143 #endif
1144         if ((off = lockmbox(p)) != 0)
1145         {
1146                 if (off == EX_TEMPFAIL || e_to_sys(off) == EX_TEMPFAIL)
1147                 {
1148                         ExitVal = EX_TEMPFAIL;
1149                         errcode = "451 4.3.0";
1150                 }
1151                 else
1152                         errcode = "551 5.3.0";
1153
1154                 mailerr(errcode, "lockmailbox %s failed; error code %d %s",
1155                         p, off, errno > 0 ? sm_errstring(errno) : "");
1156                 return;
1157         }
1158
1159         if (lstat(path, &sb) < 0)
1160         {
1161                 int save_errno;
1162                 int mode = S_IRUSR|S_IWUSR;
1163                 gid_t gid = user.mbdb_gid;
1164
1165 #ifdef MAILGID
1166                 (void) umask(0007);
1167                 gid = MAILGID;
1168                 mode |= S_IRGRP|S_IWGRP;
1169 #endif
1170
1171                 mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY,
1172                             mode);
1173                 save_errno = errno;
1174
1175                 if (lstat(path, &sb) < 0)
1176                 {
1177                         ExitVal = EX_CANTCREAT;
1178                         mailerr("550 5.2.0",
1179                                 "%s: lstat: file changed after open", path);
1180                         goto err1;
1181                 }
1182                 if (mbfd < 0)
1183                 {
1184                         if (save_errno == EEXIST)
1185                                 goto tryagain;
1186
1187                         /* open failed, don't try again */
1188                         mailerr("450 4.2.0", "Create %s: %s", path,
1189                                 sm_errstring(save_errno));
1190                         goto err0;
1191                 }
1192                 else if (fchown(mbfd, user.mbdb_uid, gid) < 0)
1193                 {
1194                         mailerr("451 4.3.0", "chown %u.%u: %s",
1195                                 user.mbdb_uid, gid, name);
1196                         goto err1;
1197                 }
1198                 else
1199                 {
1200                         /*
1201                         **  open() was successful, now close it so can
1202                         **  be opened as the right owner again.
1203                         **  Paranoia: reset mbdf since the file descriptor
1204                         **  is no longer valid; better safe than sorry.
1205                         */
1206
1207                         sb.st_uid = user.mbdb_uid;
1208                         (void) close(mbfd);
1209                         mbfd = -1;
1210                 }
1211         }
1212         else if (sb.st_nlink != 1)
1213         {
1214                 mailerr("550 5.2.0", "%s: too many links", path);
1215                 goto err0;
1216         }
1217         else if (!S_ISREG(sb.st_mode))
1218         {
1219                 mailerr("550 5.2.0", "%s: irregular file", path);
1220                 goto err0;
1221         }
1222         else if (sb.st_uid != user.mbdb_uid)
1223         {
1224                 ExitVal = EX_CANTCREAT;
1225                 mailerr("550 5.2.0", "%s: wrong ownership (%d)",
1226                         path, (int) sb.st_uid);
1227                 goto err0;
1228         }
1229
1230         /* change UID for quota checks */
1231         if (
1232 #if MAIL_LOCAL_TEST
1233             (HomeMailFile == NULL || user.mbdb_uid != getuid()) &&
1234 #endif
1235             setreuid(0, user.mbdb_uid) < 0)
1236         {
1237                 mailerr("450 4.2.0", "setreuid(0, %d): %s (r=%d, e=%d)",
1238                         (int) user.mbdb_uid, sm_errstring(errno),
1239                         (int) getuid(), (int) geteuid());
1240                 goto err1;
1241         }
1242 #ifdef DEBUG
1243         fprintf(stderr, "new euid = %d\n", (int) geteuid());
1244 #endif
1245         mbfd = open(path, O_APPEND|O_WRONLY, 0);
1246         if (mbfd < 0)
1247         {
1248                 mailerr("450 4.2.0", "Append %s: %s", path, sm_errstring(errno));
1249                 goto err0;
1250         }
1251         else if (fstat(mbfd, &fsb) < 0 ||
1252                  fsb.st_nlink != 1 ||
1253                  sb.st_nlink != 1 ||
1254                  !S_ISREG(fsb.st_mode) ||
1255                  sb.st_dev != fsb.st_dev ||
1256                  sb.st_ino != fsb.st_ino ||
1257 #if HAS_ST_GEN && 0             /* AFS returns random values for st_gen */
1258                  sb.st_gen != fsb.st_gen ||
1259 #endif
1260                  sb.st_uid != fsb.st_uid)
1261         {
1262                 ExitVal = EX_TEMPFAIL;
1263                 mailerr("550 5.2.0", "%s: fstat: file changed after open",
1264                         path);
1265                 goto err1;
1266         }
1267
1268 #if 0
1269         /*
1270         **  This code could be reused if we decide to add a
1271         **  per-user quota field to the sm_mbdb interface.
1272         */
1273
1274         /*
1275         **  Fail if the user has a quota specified, and delivery of this
1276         **  message would exceed that quota.  We bounce such failures using
1277         **  EX_UNAVAILABLE, unless there were internal problems, since
1278         **  storing immense messages for later retries can cause queueing
1279         **  issues.
1280         */
1281
1282         if (ui.quota > 0)
1283         {
1284                 struct stat dsb;
1285
1286                 if (fstat(fd, &dsb) < 0)
1287                 {
1288                         ExitVal = EX_TEMPFAIL;
1289                         mailerr("451 4.3.0",
1290                                 "%s: fstat: can't stat temporary storage: %s",
1291                                 ui.mailspool, sm_errstring(errno));
1292                         goto err1;
1293                 }
1294
1295                 if (dsb.st_size + sb.st_size + 1 > ui.quota)
1296                 {
1297                         ExitVal = EX_UNAVAILABLE;
1298                         mailerr("551 5.2.2",
1299                                 "%s: Mailbox full or quota exceeded",
1300                                 ui.mailspool);
1301                         goto err1;
1302                 }
1303         }
1304 #endif /* 0 */
1305
1306         /* Wait until we can get a lock on the file. */
1307         if (flock(mbfd, LOCK_EX) < 0)
1308         {
1309                 mailerr("450 4.2.0", "Lock %s: %s", path, sm_errstring(errno));
1310                 goto err1;
1311         }
1312
1313         /* Get the starting offset of the new message */
1314         curoff = lseek(mbfd, (off_t) 0, SEEK_END);
1315
1316         if (!nobiff)
1317         {
1318                 (void) sm_snprintf(biffmsg, sizeof(biffmsg), "%s@%lld\n",
1319                                    name, (LONGLONG_T) curoff);
1320         }
1321
1322         /* Copy the message into the file. */
1323         if (lseek(fd, (off_t) 0, SEEK_SET) == (off_t) -1)
1324         {
1325                 mailerr("450 4.2.0", "Temporary file seek error: %s",
1326                         sm_errstring(errno));
1327                 goto err1;
1328         }
1329 #ifdef DEBUG
1330         fprintf(stderr, "before writing: euid = %d\n", (int) geteuid());
1331 #endif
1332 #ifdef CONTENTLENGTH
1333         headerbytes = (BodyLength >= 0) ? HeaderLength : -1 ;
1334         for (;;)
1335         {
1336                 if (headerbytes == 0)
1337                 {
1338                         (void) sm_snprintf(buf, sizeof buf, "%s", ContentHdr);
1339                         nr = strlen(buf);
1340                         headerbytes = -1;
1341                         readamount = 0;
1342                 }
1343                 else if (headerbytes > sizeof(buf) || headerbytes < 0)
1344                         readamount = sizeof(buf);
1345                 else
1346                         readamount = headerbytes;
1347                 if (readamount != 0)
1348                         nr = read(fd, buf, readamount);
1349                 if (nr <= 0)
1350                         break;
1351                 if (headerbytes > 0)
1352                         headerbytes -= nr ;
1353
1354 #else /* CONTENTLENGTH */
1355         while ((nr = read(fd, buf, sizeof(buf))) > 0)
1356         {
1357 #endif /* CONTENTLENGTH */
1358                 for (off = 0; off < nr; off += nw)
1359                 {
1360                         if ((nw = write(mbfd, buf + off, nr - off)) < 0)
1361                         {
1362                                 errcode = "450 4.2.0";
1363 #ifdef EDQUOT
1364                                 if (errno == EDQUOT && BounceQuota)
1365                                         errcode = "552 5.2.2";
1366 #endif
1367                                 mailerr(errcode, "Write %s: %s",
1368                                         path, sm_errstring(errno));
1369                                 goto err3;
1370                         }
1371                 }
1372         }
1373         if (nr < 0)
1374         {
1375                 mailerr("450 4.2.0", "Temporary file read error: %s",
1376                         sm_errstring(errno));
1377                 goto err3;
1378         }
1379
1380         /* Flush to disk, don't wait for update. */
1381         if (!nofsync && fsync(mbfd) < 0)
1382         {
1383                 mailerr("450 4.2.0", "Sync %s: %s", path, sm_errstring(errno));
1384 err3:
1385 #ifdef DEBUG
1386                 fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1387 #endif
1388                 if (mbfd >= 0)
1389                         (void) ftruncate(mbfd, curoff);
1390 err1:           if (mbfd >= 0)
1391                         (void) close(mbfd);
1392 err0:
1393 #if MAIL_LOCAL_TEST
1394                 if (HomeMailFile == NULL || user.mbdb_uid != getuid())
1395 #endif
1396                 (void) setreuid(0, 0);
1397                 unlockmbox();
1398                 return;
1399         }
1400
1401         /*
1402         **  Save the current size so if the close() fails below
1403         **  we can make sure no other process has changed the mailbox
1404         **  between the failed close and the re-open()/re-lock().
1405         **  If something else has changed the size, we shouldn't
1406         **  try to truncate it as we may do more harm then good
1407         **  (e.g., truncate a later message delivery).
1408         */
1409
1410         if (fstat(mbfd, &sb) < 0)
1411                 cursize = 0;
1412         else
1413                 cursize = sb.st_size;
1414
1415
1416         /* Close and check -- NFS doesn't write until the close. */
1417         if (close(mbfd))
1418         {
1419                 errcode = "450 4.2.0";
1420 #ifdef EDQUOT
1421                 if (errno == EDQUOT && BounceQuota)
1422                         errcode = "552 5.2.2";
1423 #endif
1424                 mailerr(errcode, "Close %s: %s", path, sm_errstring(errno));
1425                 mbfd = open(path, O_WRONLY, 0);
1426                 if (mbfd < 0 ||
1427                     cursize == 0
1428                     || flock(mbfd, LOCK_EX) < 0 ||
1429                     fstat(mbfd, &sb) < 0 ||
1430                     sb.st_size != cursize ||
1431                     sb.st_nlink != 1 ||
1432                     !S_ISREG(sb.st_mode) ||
1433                     sb.st_dev != fsb.st_dev ||
1434                     sb.st_ino != fsb.st_ino ||
1435 #if HAS_ST_GEN && 0             /* AFS returns random values for st_gen */
1436                     sb.st_gen != fsb.st_gen ||
1437 #endif
1438                     sb.st_uid != fsb.st_uid
1439                    )
1440                 {
1441                         /* Don't use a bogus file */
1442                         if (mbfd >= 0)
1443                         {
1444                                 (void) close(mbfd);
1445                                 mbfd = -1;
1446                         }
1447                 }
1448
1449                 /* Attempt to truncate back to pre-write size */
1450                 goto err3;
1451         }
1452         else if (!nobiff)
1453                 notifybiff(biffmsg);
1454
1455         if (
1456 #if MAIL_LOCAL_TEST
1457             (HomeMailFile == NULL || user.mbdb_uid != getuid()) &&
1458 #endif
1459             setreuid(0, 0) < 0)
1460         {
1461                 mailerr("450 4.2.0", "setreuid(0, 0): %s",
1462                         sm_errstring(errno));
1463                 goto err0;
1464         }
1465 #ifdef DEBUG
1466         fprintf(stderr, "reset euid = %d\n", (int) geteuid());
1467 #endif
1468         unlockmbox();
1469         if (LMTPMode)
1470                 printf("250 2.1.5 %s Ok\r\n", name);
1471 }
1472
1473 /*
1474 **  user.lock files are necessary for compatibility with other
1475 **  systems, e.g., when the mail spool file is NFS exported.
1476 **  Alas, mailbox locking is more than just a local matter.
1477 **  EPA 11/94.
1478 */
1479
1480 bool    Locked = false;
1481
1482 #ifdef MAILLOCK
1483 int
1484 lockmbox(name)
1485         char *name;
1486 {
1487         int r = 0;
1488
1489         if (Locked)
1490                 return 0;
1491         if ((r = maillock(name, 15)) == L_SUCCESS)
1492         {
1493                 Locked = true;
1494                 return 0;
1495         }
1496         switch (r)
1497         {
1498           case L_TMPLOCK:       /* Can't create tmp file */
1499           case L_TMPWRITE:      /* Can't write pid into lockfile */
1500           case L_MAXTRYS:       /* Failed after retrycnt attempts */
1501                 errno = 0;
1502                 r = EX_TEMPFAIL;
1503                 break;
1504           case L_ERROR:         /* Check errno for reason */
1505                 r = errno;
1506                 break;
1507           default:              /* other permanent errors */
1508                 errno = 0;
1509                 r = EX_UNAVAILABLE;
1510                 break;
1511         }
1512         return r;
1513 }
1514
1515 void
1516 unlockmbox()
1517 {
1518         if (Locked)
1519                 mailunlock();
1520         Locked = false;
1521 }
1522 #else /* MAILLOCK */
1523
1524 char    LockName[MAXPATHLEN];
1525
1526 int
1527 lockmbox(path)
1528         char *path;
1529 {
1530         int statfailed = 0;
1531         time_t start;
1532
1533         if (Locked)
1534                 return 0;
1535         if (strlen(path) + 6 > sizeof LockName)
1536                 return EX_SOFTWARE;
1537         (void) sm_snprintf(LockName, sizeof LockName, "%s.lock", path);
1538         (void) time(&start);
1539         for (; ; sleep(5))
1540         {
1541                 int fd;
1542                 struct stat st;
1543                 time_t now;
1544
1545                 /* global timeout */
1546                 (void) time(&now);
1547                 if (now > start + LOCKTO_GLOB)
1548                 {
1549                         errno = 0;
1550                         return EX_TEMPFAIL;
1551                 }
1552                 fd = open(LockName, O_WRONLY|O_EXCL|O_CREAT, LOCKFILE_PMODE);
1553                 if (fd >= 0)
1554                 {
1555                         /* defeat lock checking programs which test pid */
1556                         (void) write(fd, "0", 2);
1557                         Locked = true;
1558                         (void) close(fd);
1559                         return 0;
1560                 }
1561                 if (stat(LockName, &st) < 0)
1562                 {
1563                         if (statfailed++ > 5)
1564                         {
1565                                 errno = 0;
1566                                 return EX_TEMPFAIL;
1567                         }
1568                         continue;
1569                 }
1570                 statfailed = 0;
1571                 (void) time(&now);
1572                 if (now < st.st_ctime + LOCKTO_RM)
1573                         continue;
1574
1575                 /* try to remove stale lockfile */
1576                 if (unlink(LockName) < 0)
1577                         return errno;
1578         }
1579 }
1580
1581 void
1582 unlockmbox()
1583 {
1584         if (!Locked)
1585                 return;
1586         (void) unlink(LockName);
1587         Locked = false;
1588 }
1589 #endif /* MAILLOCK */
1590
1591 void
1592 notifybiff(msg)
1593         char *msg;
1594 {
1595         static bool initialized = false;
1596         static int f = -1;
1597         struct hostent *hp;
1598         struct servent *sp;
1599         int len;
1600         static struct sockaddr_in addr;
1601
1602         if (!initialized)
1603         {
1604                 initialized = true;
1605
1606                 /* Be silent if biff service not available. */
1607                 if ((sp = getservbyname("biff", "udp")) == NULL ||
1608                     (hp = gethostbyname("localhost")) == NULL ||
1609                     hp->h_length != INADDRSZ)
1610                         return;
1611
1612                 addr.sin_family = hp->h_addrtype;
1613                 memcpy(&addr.sin_addr, hp->h_addr, INADDRSZ);
1614                 addr.sin_port = sp->s_port;
1615         }
1616
1617         /* No message, just return */
1618         if (msg == NULL)
1619                 return;
1620
1621         /* Couldn't initialize addr struct */
1622         if (addr.sin_family == AF_UNSPEC)
1623                 return;
1624
1625         if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1626                 return;
1627         len = strlen(msg) + 1;
1628         (void) sendto(f, msg, len, 0, (struct sockaddr *) &addr, sizeof(addr));
1629 }
1630
1631 void
1632 usage()
1633 {
1634         ExitVal = EX_USAGE;
1635         /* XXX add U to options for USE_EAI */
1636 #if _FFR_SPOOL_PATH
1637         mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] [-p path] user ...");
1638 #else
1639         mailerr(NULL, "usage: mail.local [-7] [-B] [-b] [-d] [-l] [-s] [-f from|-r from] [-h filename] user ...");
1640 #endif
1641         sm_exit(ExitVal);
1642 }
1643
1644 void
1645 /*VARARGS2*/
1646 #ifdef __STDC__
1647 mailerr(const char *hdr, const char *fmt, ...)
1648 #else /* __STDC__ */
1649 mailerr(hdr, fmt, va_alist)
1650         const char *hdr;
1651         const char *fmt;
1652         va_dcl
1653 #endif /* __STDC__ */
1654 {
1655         size_t len = 0;
1656         SM_VA_LOCAL_DECL
1657
1658         (void) e_to_sys(errno);
1659
1660         SM_VA_START(ap, fmt);
1661
1662         if (LMTPMode && hdr != NULL)
1663         {
1664                 sm_snprintf(ErrBuf, sizeof ErrBuf, "%s ", hdr);
1665                 len = strlen(ErrBuf);
1666         }
1667         (void) sm_vsnprintf(&ErrBuf[len], sizeof ErrBuf - len, fmt, ap);
1668         SM_VA_END(ap);
1669
1670         if (!HoldErrs)
1671                 flush_error();
1672
1673         /* Log the message to syslog. */
1674         if (!LMTPMode)
1675                 syslog(LOG_ERR, "%s", ErrBuf);
1676 }
1677
1678 void
1679 flush_error()
1680 {
1681         if (LMTPMode)
1682                 printf("%s\r\n", ErrBuf);
1683         else
1684         {
1685                 if (ExitVal != EX_USAGE)
1686                         (void) fprintf(stderr, "mail.local: ");
1687                 fprintf(stderr, "%s\n", ErrBuf);
1688         }
1689 }
1690
1691 #if HASHSPOOL
1692 const char *
1693 hashname(name)
1694         char *name;
1695 {
1696         static char p[MAXPATHLEN];
1697         int i;
1698         int len;
1699         char *str;
1700 # if HASHSPOOLMD5
1701         char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
1702         MD5_CTX ctx;
1703         unsigned char md5[18];
1704 #  if MAXPATHLEN <= 24
1705 #    error "MAXPATHLEN <= 24"
1706 #  endif
1707         char b64[24];
1708         MD5_LONG bits;
1709         int j;
1710 # endif /* HASHSPOOLMD5 */
1711
1712         if (HashType == HASH_NONE || HashDepth * 2 >= MAXPATHLEN)
1713         {
1714                 p[0] = '\0';
1715                 return p;
1716         }
1717
1718         switch(HashType)
1719         {
1720           case HASH_USER:
1721                 str = name;
1722                 break;
1723
1724 # if HASHSPOOLMD5
1725           case HASH_MD5:
1726                 MD5_Init(&ctx);
1727                 MD5_Update(&ctx, name, strlen(name));
1728                 MD5_Final(md5, &ctx);
1729                 md5[16] = 0;
1730                 md5[17] = 0;
1731
1732                 for (i = 0; i < 6; i++)
1733                 {
1734                         bits = (unsigned) md5[(3 * i)] << 16;
1735                         bits |= (unsigned) md5[(3 * i) + 1] << 8;
1736                         bits |= (unsigned) md5[(3 * i) + 2];
1737
1738                         for (j = 3; j >= 0; j--)
1739                         {
1740                                 b64[(4 * i) + j] = Base64[(bits & 0x3f)];
1741                                 bits >>= 6;
1742                         }
1743                 }
1744                 b64[22] = '\0';
1745                 str = b64;
1746                 break;
1747 # endif /* HASHSPOOLMD5 */
1748         }
1749
1750         len = strlen(str);
1751         for (i = 0; i < HashDepth; i++)
1752         {
1753                 if (i < len)
1754                         p[i * 2] = str[i];
1755                 else
1756                         p[i * 2] = '_';
1757                 p[(i * 2) + 1] = '/';
1758         }
1759         p[HashDepth * 2] = '\0';
1760         return p;
1761 }
1762 #endif /* HASHSPOOL */
1763
1764 /*
1765  * e_to_sys --
1766  *      Guess which errno's are temporary.  Gag me.
1767  */
1768
1769 int
1770 e_to_sys(num)
1771         int num;
1772 {
1773         /* Temporary failures override hard errors. */
1774         if (ExitVal == EX_TEMPFAIL)
1775                 return ExitVal;
1776
1777         switch (num)            /* Hopefully temporary errors. */
1778         {
1779 #ifdef EDQUOT
1780           case EDQUOT:          /* Disc quota exceeded */
1781                 if (BounceQuota)
1782                 {
1783                         ExitVal = EX_UNAVAILABLE;
1784                         break;
1785                 }
1786                 /* FALLTHROUGH */
1787 #endif /* EDQUOT */
1788 #ifdef EAGAIN
1789           case EAGAIN:          /* Resource temporarily unavailable */
1790 #endif
1791 #ifdef EBUSY
1792           case EBUSY:           /* Device busy */
1793 #endif
1794 #ifdef EPROCLIM
1795           case EPROCLIM:        /* Too many processes */
1796 #endif
1797 #ifdef EUSERS
1798           case EUSERS:          /* Too many users */
1799 #endif
1800 #ifdef ECONNABORTED
1801           case ECONNABORTED:    /* Software caused connection abort */
1802 #endif
1803 #ifdef ECONNREFUSED
1804           case ECONNREFUSED:    /* Connection refused */
1805 #endif
1806 #ifdef ECONNRESET
1807           case ECONNRESET:      /* Connection reset by peer */
1808 #endif
1809 #ifdef EDEADLK
1810           case EDEADLK:         /* Resource deadlock avoided */
1811 #endif
1812 #ifdef EFBIG
1813           case EFBIG:           /* File too large */
1814 #endif
1815 #ifdef EHOSTDOWN
1816           case EHOSTDOWN:       /* Host is down */
1817 #endif
1818 #ifdef EHOSTUNREACH
1819           case EHOSTUNREACH:    /* No route to host */
1820 #endif
1821 #ifdef EMFILE
1822           case EMFILE:          /* Too many open files */
1823 #endif
1824 #ifdef ENETDOWN
1825           case ENETDOWN:        /* Network is down */
1826 #endif
1827 #ifdef ENETRESET
1828           case ENETRESET:       /* Network dropped connection on reset */
1829 #endif
1830 #ifdef ENETUNREACH
1831           case ENETUNREACH:     /* Network is unreachable */
1832 #endif
1833 #ifdef ENFILE
1834           case ENFILE:          /* Too many open files in system */
1835 #endif
1836 #ifdef ENOBUFS
1837           case ENOBUFS:         /* No buffer space available */
1838 #endif
1839 #ifdef ENOMEM
1840           case ENOMEM:          /* Cannot allocate memory */
1841 #endif
1842 #ifdef ENOSPC
1843           case ENOSPC:          /* No space left on device */
1844 #endif
1845 #ifdef EROFS
1846           case EROFS:           /* Read-only file system */
1847 #endif
1848 #ifdef ESTALE
1849           case ESTALE:          /* Stale NFS file handle */
1850 #endif
1851 #ifdef ETIMEDOUT
1852           case ETIMEDOUT:       /* Connection timed out */
1853 #endif
1854 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
1855           case EWOULDBLOCK:     /* Operation would block. */
1856 #endif
1857                 ExitVal = EX_TEMPFAIL;
1858                 break;
1859
1860           default:
1861                 ExitVal = EX_UNAVAILABLE;
1862                 break;
1863         }
1864         return ExitVal;
1865 }
1866
1867 #if defined(ultrix) || defined(_CRAY)
1868 /*
1869  * Copyright (c) 1987, 1993
1870  *      The Regents of the University of California.  All rights reserved.
1871  *
1872  * Redistribution and use in source and binary forms, with or without
1873  * modification, are permitted provided that the following conditions
1874  * are met:
1875  * 1. Redistributions of source code must retain the above copyright
1876  *    notice, this list of conditions and the following disclaimer.
1877  * 2. Redistributions in binary form must reproduce the above copyright
1878  *    notice, this list of conditions and the following disclaimer in the
1879  *    documentation and/or other materials provided with the distribution.
1880  * 3. All advertising materials mentioning features or use of this software
1881  *    must display the following acknowledgement:
1882  *      This product includes software developed by the University of
1883  *      California, Berkeley and its contributors.
1884  * 4. Neither the name of the University nor the names of its contributors
1885  *    may be used to endorse or promote products derived from this software
1886  *    without specific prior written permission.
1887  *
1888  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1889  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1890  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1891  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1892  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1893  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1894  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1895  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1896  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1897  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1898  * SUCH DAMAGE.
1899  */
1900
1901 # if defined(LIBC_SCCS) && !defined(lint)
1902 static char sccsid[] = "@(#)mktemp.c    8.1 (Berkeley) 6/4/93";
1903 # endif
1904
1905 # include <sys/types.h>
1906 # include <sys/stat.h>
1907 # include <fcntl.h>
1908 # include <errno.h>
1909 # include <stdio.h>
1910 # include <ctype.h>
1911
1912 static int _gettemp();
1913
1914 mkstemp(path)
1915         char *path;
1916 {
1917         int fd;
1918
1919         return (_gettemp(path, &fd) ? fd : -1);
1920 }
1921
1922 static
1923 _gettemp(path, doopen)
1924         char *path;
1925         register int *doopen;
1926 {
1927         extern int errno;
1928         register char *start, *trv;
1929         struct stat sbuf;
1930         unsigned int pid;
1931
1932         pid = getpid();
1933         for (trv = path; *trv; ++trv);          /* extra X's get set to 0's */
1934         while (*--trv == 'X')
1935         {
1936                 *trv = (pid % 10) + '0';
1937                 pid /= 10;
1938         }
1939
1940         /*
1941          * check the target directory; if you have six X's and it
1942          * doesn't exist this runs for a *very* long time.
1943          */
1944         for (start = trv + 1;; --trv)
1945         {
1946                 if (trv <= path)
1947                         break;
1948                 if (*trv == '/')
1949                 {
1950                         *trv = '\0';
1951                         if (stat(path, &sbuf) < 0)
1952                                 return(0);
1953                         if (!S_ISDIR(sbuf.st_mode))
1954                         {
1955                                 errno = ENOTDIR;
1956                                 return(0);
1957                         }
1958                         *trv = '/';
1959                         break;
1960                 }
1961         }
1962
1963         for (;;)
1964         {
1965                 if (doopen)
1966                 {
1967                         if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR,
1968                                             0600)) >= 0)
1969                                 return(1);
1970                         if (errno != EEXIST)
1971                                 return(0);
1972                 }
1973                 else if (stat(path, &sbuf) < 0)
1974                         return(errno == ENOENT ? 1 : 0);
1975
1976                 /* tricky little algorithm for backward compatibility */
1977                 for (trv = start;;)
1978                 {
1979                         if (!*trv)
1980                                 return(0);
1981                         if (*trv == 'z')
1982                                 *trv++ = 'a';
1983                         else
1984                         {
1985                                 if (isascii(*trv) && isdigit(*trv))
1986                                         *trv = 'a';
1987                                 else
1988                                         ++*trv;
1989                                 break;
1990                         }
1991                 }
1992         }
1993         /* NOTREACHED */
1994 }
1995 #endif /* defined(ultrix) || defined(_CRAY) */