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