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