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