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