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