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