]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/vacation/vacation.c
Import of sendmail version 8.11.1 into vendor branch SENDMAIL with
[FreeBSD/FreeBSD.git] / contrib / sendmail / vacation / vacation.c
1 /*
2  * Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  * Copyright (c) 1983, 1987, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1983 Eric P. Allman.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  */
13
14 #ifndef lint
15 static char copyright[] =
16 "@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
17         All rights reserved.\n\
18      Copyright (c) 1983, 1987, 1993\n\
19         The Regents of the University of California.  All rights reserved.\n\
20      Copyright (c) 1983 Eric P. Allman.  All rights reserved.\n";
21 #endif /* ! lint */
22
23 #ifndef lint
24 static char id[] = "@(#)$Id: vacation.c,v 8.68.4.7 2000/09/05 21:48:45 gshapiro Exp $";
25 #endif /* ! lint */
26
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <syslog.h>
30 #include <time.h>
31 #include <unistd.h>
32 #ifdef EX_OK
33 # undef EX_OK           /* unistd.h may have another use for this */
34 #endif /* EX_OK */
35 #include <sysexits.h>
36
37 #include "sendmail/sendmail.h"
38 #include "libsmdb/smdb.h"
39
40 #if defined(__hpux) && !defined(HPUX11)
41 # undef syslog          /* Undo hard_syslog conf.h change */
42 #endif /* defined(__hpux) && !defined(HPUX11) */
43
44 #ifndef _PATH_SENDMAIL
45 # define _PATH_SENDMAIL "/usr/lib/sendmail"
46 #endif /* ! _PATH_SENDMAIL */
47
48 #define ONLY_ONCE       ((time_t) 0)    /* send at most one reply */
49 #define INTERVAL_UNDEF  ((time_t) (-1)) /* no value given */
50
51 uid_t   RealUid;
52 gid_t   RealGid;
53 char    *RealUserName;
54 uid_t   RunAsUid;
55 uid_t   RunAsGid;
56 char    *RunAsUserName;
57 int     Verbose = 2;
58 bool    DontInitGroups = FALSE;
59 uid_t   TrustedUid = 0;
60 BITMAP256 DontBlameSendmail;
61
62 /*
63 **  VACATION -- return a message to the sender when on vacation.
64 **
65 **      This program is invoked as a message receiver.  It returns a
66 **      message specified by the user to whomever sent the mail, taking
67 **      care not to return a message too often to prevent "I am on
68 **      vacation" loops.
69 */
70
71 #define VDB     ".vacation"             /* vacation database */
72 #define VMSG    ".vacation.msg"         /* vacation message */
73 #define SECSPERDAY      (60 * 60 * 24)
74 #define DAYSPERWEEK     7
75
76 #ifndef TRUE
77 # define TRUE   1
78 # define FALSE  0
79 #endif /* ! TRUE */
80
81 #ifndef __P
82 # ifdef __STDC__
83 #  define __P(protos)   protos
84 # else /* __STDC__ */
85 #  define __P(protos)   ()
86 #  define const
87 # endif /* __STDC__ */
88 #endif /* ! __P */
89
90 typedef struct alias
91 {
92         char *name;
93         struct alias *next;
94 } ALIAS;
95
96 ALIAS *Names = NULL;
97
98 SMDB_DATABASE *Db;
99
100 char From[MAXLINE];
101
102 #if _FFR_DEBUG
103 void (*msglog)(int, const char *, ...) = &syslog;
104 static void debuglog __P((int, const char *, ...));
105 #else /* _FFR_DEBUG */
106 # define msglog         syslog
107 #endif /* _FFR_DEBUG */
108
109 static void eatmsg __P((void));
110
111 /* exit after reading input */
112 #define EXITIT(excode)  { \
113                                 eatmsg(); \
114                                 exit(excode); \
115                         }
116 int
117 main(argc, argv)
118         int argc;
119         char **argv;
120 {
121         bool iflag, emptysender, exclude;
122 #if _FFR_LISTDB
123         bool lflag = FALSE;
124 #endif /* _FFR_LISTDB */
125         int mfail = 0, ufail = 0;
126         int ch;
127         int result;
128         long sff;
129         time_t interval;
130         struct passwd *pw;
131         ALIAS *cur;
132         char *dbfilename = VDB;
133         char *msgfilename = VMSG;
134         char *name;
135         SMDB_USER_INFO user_info;
136         static char rnamebuf[MAXNAME];
137         extern int optind, opterr;
138         extern char *optarg;
139         extern void usage __P((void));
140         extern void setinterval __P((time_t));
141         extern void readheaders __P((void));
142         extern bool recent __P((void));
143         extern void setreply __P((char *, time_t));
144         extern void sendmessage __P((char *, char *, bool));
145         extern void xclude __P((FILE *));
146 #if _FFR_LISTDB
147 #define EXITM(excode)   { \
148                                 if (!iflag && !lflag) \
149                                         eatmsg(); \
150                                 exit(excode); \
151                         }
152 #else /* _FFR_LISTDB */
153 #define EXITM(excode)   { \
154                                 if (!iflag) \
155                                         eatmsg(); \
156                                 exit(excode); \
157                         }
158 #endif /* _FFR_LISTDB */
159
160         /* Vars needed to link with smutil */
161         clrbitmap(DontBlameSendmail);
162         RunAsUid = RealUid = getuid();
163         RunAsGid = RealGid = getgid();
164         pw = getpwuid(RealUid);
165         if (pw != NULL)
166         {
167                 if (strlen(pw->pw_name) > MAXNAME - 1)
168                         pw->pw_name[MAXNAME] = '\0';
169                 snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
170         }
171         else
172                 snprintf(rnamebuf, sizeof rnamebuf,
173                          "Unknown UID %d", (int) RealUid);
174         RunAsUserName = RealUserName = rnamebuf;
175
176 #ifdef LOG_MAIL
177         openlog("vacation", LOG_PID, LOG_MAIL);
178 #else /* LOG_MAIL */
179         openlog("vacation", LOG_PID);
180 #endif /* LOG_MAIL */
181
182         opterr = 0;
183         iflag = FALSE;
184         emptysender = FALSE;
185         exclude = FALSE;
186         interval = INTERVAL_UNDEF;
187         *From = '\0';
188
189 #if _FFR_DEBUG && _FFR_LISTDB
190 # define OPTIONS                "a:df:Iilm:r:s:t:xz"
191 #else /* _FFR_DEBUG && _FFR_LISTDB */
192 # if _FFR_DEBUG
193 #  define OPTIONS               "a:df:Iim:r:s:t:xz"
194 # else /* _FFR_DEBUG */
195 #  if _FFR_LISTDB
196 #   define OPTIONS              "a:f:Iilm:r:s:t:xz"
197 #  else /* _FFR_LISTDB */
198 #   define OPTIONS              "a:f:Iim:r:s:t:xz"
199 #  endif /* _FFR_LISTDB */
200 # endif /* _FFR_DEBUG */
201 #endif /* _FFR_DEBUG && _FFR_LISTDB */
202
203         while (mfail == 0 && ufail == 0 &&
204                (ch = getopt(argc, argv, OPTIONS)) != -1)
205         {
206                 switch((char)ch)
207                 {
208                   case 'a':                     /* alias */
209                         cur = (ALIAS *)malloc((u_int)sizeof(ALIAS));
210                         if (cur == NULL)
211                         {
212                                 mfail++;
213                                 break;
214                         }
215                         cur->name = optarg;
216                         cur->next = Names;
217                         Names = cur;
218                         break;
219
220 #if _FFR_DEBUG
221                 case 'd':                       /* debug mode */
222                         msglog = &debuglog;
223                         break;
224 #endif /* _FFR_DEBUG */
225
226
227                   case 'f':             /* alternate database */
228                         dbfilename = optarg;
229                         break;
230
231                   case 'I':                     /* backward compatible */
232                   case 'i':                     /* init the database */
233                         iflag = TRUE;
234                         break;
235
236 #if _FFR_LISTDB
237                   case 'l':
238                         lflag = TRUE;           /* list the database */
239                         break;
240 #endif /* _FFR_LISTDB */
241
242                   case 'm':             /* alternate message file */
243                         msgfilename = optarg;
244                         break;
245
246                   case 'r':
247                         if (isascii(*optarg) && isdigit(*optarg))
248                         {
249                                 interval = atol(optarg) * SECSPERDAY;
250                                 if (interval < 0)
251                                         ufail++;
252                         }
253                         else
254                                 interval = ONLY_ONCE;
255                         break;
256
257                   case 's':             /* alternate sender name */
258                         (void) strlcpy(From, optarg, sizeof From);
259                         break;
260
261                   case 't':             /* SunOS: -t1d (default expire) */
262                         break;
263
264                   case 'x':
265                         exclude = TRUE;
266                         break;
267
268                   case 'z':
269                         emptysender = TRUE;
270                         break;
271
272                   case '?':
273                   default:
274                         ufail++;
275                         break;
276                 }
277         }
278         argc -= optind;
279         argv += optind;
280
281         if (mfail != 0)
282         {
283                 msglog(LOG_NOTICE,
284                        "vacation: can't allocate memory for alias.\n");
285                 EXITM(EX_TEMPFAIL);
286         }
287         if (ufail != 0)
288                 usage();
289
290         if (argc != 1)
291         {
292                 if (!iflag &&
293 #if _FFR_LISTDB
294                     !lflag &&
295 #endif /* _FFR_LISTDB */
296                     !exclude)
297                         usage();
298                 if ((pw = getpwuid(getuid())) == NULL)
299                 {
300                         msglog(LOG_ERR,
301                                "vacation: no such user uid %u.\n", getuid());
302                         EXITM(EX_NOUSER);
303                 }
304         }
305 #if _FFR_BLACKBOX
306         name = *argv;
307 #else /* _FFR_BLACKBOX */
308         else if ((pw = getpwnam(*argv)) == NULL)
309         {
310                 msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
311                 EXITM(EX_NOUSER);
312         }
313         name = pw->pw_name;
314         if (chdir(pw->pw_dir) != 0)
315         {
316                 msglog(LOG_NOTICE,
317                        "vacation: no such directory %s.\n", pw->pw_dir);
318                 EXITM(EX_NOINPUT);
319         }
320 #endif /* _FFR_BLACKBOX */
321         user_info.smdbu_id = pw->pw_uid;
322         user_info.smdbu_group_id = pw->pw_gid;
323         (void) strlcpy(user_info.smdbu_name, pw->pw_name,
324                        SMDB_MAX_USER_NAME_LEN);
325
326         sff = SFF_CREAT;
327 #if _FFR_BLACKBOX
328         if (getegid() != getgid())
329                 RunAsGid = user_info.smdbu_group_id = getegid();
330
331         sff |= SFF_NOPATHCHECK|SFF_OPENASROOT;
332 #endif /* _FFR_BLACKBOX */
333
334         result = smdb_open_database(&Db, dbfilename,
335                                     O_CREAT|O_RDWR | (iflag ? O_TRUNC : 0),
336                                     S_IRUSR|S_IWUSR, sff,
337                                     SMDB_TYPE_DEFAULT, &user_info, NULL);
338         if (result != SMDBE_OK)
339         {
340                 msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
341                        errstring(result));
342                 EXITM(EX_DATAERR);
343         }
344
345 #if _FFR_LISTDB
346         if (lflag)
347         {
348                 static void listdb __P((void));
349
350                 listdb();
351                 (void)Db->smdb_close(Db);
352                 exit(EX_OK);
353         }
354 #endif /* _FFR_LISTDB */
355
356         if (interval != INTERVAL_UNDEF)
357                 setinterval(interval);
358
359         if (iflag)
360         {
361                 result = Db->smdb_close(Db);
362                 if (!exclude)
363                         exit(EX_OK);
364         }
365
366         if (exclude)
367         {
368                 xclude(stdin);
369                 result = Db->smdb_close(Db);
370                 EXITM(EX_OK);
371         }
372
373         if ((cur = (ALIAS *)malloc((u_int)sizeof(ALIAS))) == NULL)
374         {
375                 msglog(LOG_NOTICE,
376                        "vacation: can't allocate memory for username.\n");
377                 EXITM(EX_OSERR);
378         }
379         cur->name = name;
380         cur->next = Names;
381         Names = cur;
382
383         readheaders();
384         if (!recent())
385         {
386                 time_t now;
387
388                 (void) time(&now);
389                 setreply(From, now);
390                 result = Db->smdb_close(Db);
391                 sendmessage(name, msgfilename, emptysender);
392         }
393         else
394                 result = Db->smdb_close(Db);
395         exit(EX_OK);
396         /* NOTREACHED */
397         return EX_OK;
398 }
399
400 /*
401 ** EATMSG -- read stdin till EOF
402 **
403 **      Parameters:
404 **              none.
405 **
406 **      Returns:
407 **              nothing.
408 **
409 */
410 static void
411 eatmsg()
412 {
413         /*
414         **  read the rest of the e-mail and ignore it to avoid problems
415         **  with EPIPE in sendmail
416         */
417         while (getc(stdin) != EOF)
418                 continue;
419 }
420
421 /*
422 ** READHEADERS -- read mail headers
423 **
424 **      Parameters:
425 **              none.
426 **
427 **      Returns:
428 **              nothing.
429 **
430 **      Side Effects:
431 **              may exit().
432 **
433 */
434 void
435 readheaders()
436 {
437         bool tome, cont;
438         register char *p;
439         register ALIAS *cur;
440         char buf[MAXLINE];
441         extern bool junkmail __P((char *));
442         extern bool nsearch __P((char *, char *));
443
444         cont = tome = FALSE;
445         while (fgets(buf, sizeof(buf), stdin) && *buf != '\n')
446         {
447                 switch(*buf)
448                 {
449                   case 'F':             /* "From " */
450                         cont = FALSE;
451                         if (strncmp(buf, "From ", 5) == 0)
452                         {
453                                 bool quoted = FALSE;
454
455                                 p = buf + 5;
456                                 while (*p != '\0')
457                                 {
458                                         /* escaped character */
459                                         if (*p == '\\')
460                                         {
461                                                 p++;
462                                                 if (*p == '\0')
463                                                 {
464                                                         msglog(LOG_NOTICE,
465                                                                "vacation: badly formatted \"From \" line.\n");
466                                                         EXITIT(EX_DATAERR);
467                                                 }
468                                         }
469                                         else if (*p == '"')
470                                                 quoted = !quoted;
471                                         else if (*p == '\r' || *p == '\n')
472                                                 break;
473                                         else if (*p == ' ' && !quoted)
474                                                 break;
475                                         p++;
476                                 }
477                                 if (quoted)
478                                 {
479                                         msglog(LOG_NOTICE,
480                                                "vacation: badly formatted \"From \" line.\n");
481                                         EXITIT(EX_DATAERR);
482                                 }
483                                 *p = '\0';
484
485                                 /* ok since both strings have MAXLINE length */
486                                 if (*From == '\0')
487                                         (void)strlcpy(From, buf + 5,
488                                                       sizeof From);
489                                 if ((p = strchr(buf + 5, '\n')) != NULL)
490                                         *p = '\0';
491                                 if (junkmail(buf + 5))
492                                         EXITIT(EX_OK);
493                         }
494                         break;
495
496                   case 'P':             /* "Precedence:" */
497                   case 'p':
498                         cont = FALSE;
499                         if (strlen(buf) <= 10 ||
500                             strncasecmp(buf, "Precedence", 10) != 0 ||
501                             (buf[10] != ':' && buf[10] != ' ' &&
502                              buf[10] != '\t'))
503                                 break;
504                         if ((p = strchr(buf, ':')) == NULL)
505                                 break;
506                         while (*++p != '\0' && isascii(*p) && isspace(*p));
507                         if (*p == '\0')
508                                 break;
509                         if (strncasecmp(p, "junk", 4) == 0 ||
510                             strncasecmp(p, "bulk", 4) == 0 ||
511                             strncasecmp(p, "list", 4) == 0)
512                                 EXITIT(EX_OK);
513                         break;
514
515                   case 'C':             /* "Cc:" */
516                   case 'c':
517                         if (strncasecmp(buf, "Cc:", 3) != 0)
518                                 break;
519                         cont = TRUE;
520                         goto findme;
521
522                   case 'T':             /* "To:" */
523                   case 't':
524                         if (strncasecmp(buf, "To:", 3) != 0)
525                                 break;
526                         cont = TRUE;
527                         goto findme;
528
529                   default:
530                         if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
531                         {
532                                 cont = FALSE;
533                                 break;
534                         }
535 findme:
536                         for (cur = Names;
537                              !tome && cur != NULL;
538                              cur = cur->next)
539                                 tome = nsearch(cur->name, buf);
540                 }
541         }
542         if (!tome)
543                 EXITIT(EX_OK);
544         if (*From == '\0')
545         {
546                 msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
547                 EXITIT(EX_DATAERR);
548         }
549 }
550
551 /*
552 ** NSEARCH --
553 **      do a nice, slow, search of a string for a substring.
554 **
555 **      Parameters:
556 **              name -- name to search.
557 **              str -- string in which to search.
558 **
559 **      Returns:
560 **              is name a substring of str?
561 **
562 */
563 bool
564 nsearch(name, str)
565         register char *name, *str;
566 {
567         register size_t len;
568         register char *s;
569
570         len = strlen(name);
571
572         for (s = str; *s != '\0'; ++s)
573         {
574                 /*
575                 **  Check to make sure that the string matches and
576                 **  the previous character is not an alphanumeric and
577                 **  the next character after the match is not an alphanumeric.
578                 **
579                 **  This prevents matching "eric" to "derick" while still
580                 **  matching "eric" to "<eric+detail>".
581                 */
582
583                 if (tolower(*s) == tolower(*name) &&
584                     strncasecmp(name, s, len) == 0 &&
585                     (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
586                     (!isascii(*(s + len)) || !isalnum(*(s + len))))
587                         return TRUE;
588         }
589         return FALSE;
590 }
591
592 /*
593 ** JUNKMAIL --
594 **      read the header and return if automagic/junk/bulk/list mail
595 **
596 **      Parameters:
597 **              from -- sender address.
598 **
599 **      Returns:
600 **              is this some automated/junk/bulk/list mail?
601 **
602 */
603 bool
604 junkmail(from)
605         char *from;
606 {
607         register size_t len;
608         register char *p;
609         register struct ignore *cur;
610         static struct ignore
611         {
612                 char    *name;
613                 size_t  len;
614         } ignore[] =
615         {
616                 { "-request",           8       },
617                 { "postmaster",         10      },
618                 { "uucp",               4       },
619                 { "mailer-daemon",      13      },
620                 { "mailer",             6       },
621                 { "-relay",             6       },
622                 { NULL,                 0       }
623         };
624
625         /*
626          * This is mildly amusing, and I'm not positive it's right; trying
627          * to find the "real" name of the sender, assuming that addresses
628          * will be some variant of:
629          *
630          * From site!site!SENDER%site.domain%site.domain@site.domain
631          */
632         if ((p = strchr(from, '%')) == NULL &&
633             (p = strchr(from, '@')) == NULL)
634         {
635                 if ((p = strrchr(from, '!')) != NULL)
636                         ++p;
637                 else
638                         p = from;
639                 for (; *p; ++p)
640                         continue;
641         }
642         len = p - from;
643         for (cur = ignore; cur->name != NULL; ++cur)
644         {
645                 if (len >= cur->len &&
646                     strncasecmp(cur->name, p - cur->len, cur->len) == 0)
647                         return TRUE;
648         }
649         return FALSE;
650 }
651
652 #define VIT     "__VACATION__INTERVAL__TIMER__"
653
654 /*
655 ** RECENT --
656 **      find out if user has gotten a vacation message recently.
657 **
658 **      Parameters:
659 **              none.
660 **
661 **      Returns:
662 **              TRUE iff user has gotten a vacation message recently.
663 **
664 */
665 bool
666 recent()
667 {
668         SMDB_DBENT key, data;
669         time_t then, next;
670         bool trydomain = FALSE;
671         int st;
672         char *domain;
673
674         memset(&key, '\0', sizeof key);
675         memset(&data, '\0', sizeof data);
676
677         /* get interval time */
678         key.data.data = VIT;
679         key.data.size = sizeof(VIT);
680
681         st = Db->smdb_get(Db, &key, &data, 0);
682         if (st != SMDBE_OK)
683                 next = SECSPERDAY * DAYSPERWEEK;
684         else
685                 memmove(&next, data.data.data, sizeof(next));
686
687         memset(&data, '\0', sizeof data);
688
689         /* get record for this address */
690         key.data.data = From;
691         key.data.size = strlen(From);
692
693         do
694         {
695                 st = Db->smdb_get(Db, &key, &data, 0);
696                 if (st == SMDBE_OK)
697                 {
698                         memmove(&then, data.data.data, sizeof(then));
699                         if (next == ONLY_ONCE || then == ONLY_ONCE ||
700                             then + next > time(NULL))
701                                 return TRUE;
702                 }
703                 if ((trydomain = !trydomain) &&
704                     (domain = strchr(From, '@')) != NULL)
705                 {
706                         key.data.data = domain;
707                         key.data.size = strlen(domain);
708                 }
709         } while (trydomain);
710         return FALSE;
711 }
712
713 /*
714 ** SETINTERVAL --
715 **      store the reply interval
716 **
717 **      Parameters:
718 **              interval -- time interval for replies.
719 **
720 **      Returns:
721 **              nothing.
722 **
723 **      Side Effects:
724 **              stores the reply interval in database.
725 */
726 void
727 setinterval(interval)
728         time_t interval;
729 {
730         SMDB_DBENT key, data;
731
732         memset(&key, '\0', sizeof key);
733         memset(&data, '\0', sizeof data);
734
735         key.data.data = VIT;
736         key.data.size = sizeof(VIT);
737         data.data.data = (char*) &interval;
738         data.data.size = sizeof(interval);
739         (void)(Db->smdb_put)(Db, &key, &data, 0);
740 }
741
742 /*
743 ** SETREPLY --
744 **      store that this user knows about the vacation.
745 **
746 **      Parameters:
747 **              from -- sender address.
748 **              when -- last reply time.
749 **
750 **      Returns:
751 **              nothing.
752 **
753 **      Side Effects:
754 **              stores user/time in database.
755 */
756 void
757 setreply(from, when)
758         char *from;
759         time_t when;
760 {
761         SMDB_DBENT key, data;
762
763         memset(&key, '\0', sizeof key);
764         memset(&data, '\0', sizeof data);
765
766         key.data.data = from;
767         key.data.size = strlen(from);
768         data.data.data = (char*) &when;
769         data.data.size = sizeof(when);
770         (void)(Db->smdb_put)(Db, &key, &data, 0);
771 }
772
773 /*
774 ** XCLUDE --
775 **      add users to vacation db so they don't get a reply.
776 **
777 **      Parameters:
778 **              f -- file pointer with list of address to exclude
779 **
780 **      Returns:
781 **              nothing.
782 **
783 **      Side Effects:
784 **              stores users in database.
785 */
786 void
787 xclude(f)
788         FILE *f;
789 {
790         char buf[MAXLINE], *p;
791
792         if (f == NULL)
793                 return;
794         while (fgets(buf, sizeof buf, f))
795         {
796                 if ((p = strchr(buf, '\n')) != NULL)
797                         *p = '\0';
798                 setreply(buf, ONLY_ONCE);
799         }
800 }
801
802 /*
803 ** SENDMESSAGE --
804 **      exec sendmail to send the vacation file to sender
805 **
806 **      Parameters:
807 **              myname -- user name.
808 **              msgfn -- name of file with vacation message.
809 **              emptysender -- use <> as sender address?
810 **
811 **      Returns:
812 **              nothing.
813 **
814 **      Side Effects:
815 **              sends vacation reply.
816 */
817 void
818 sendmessage(myname, msgfn, emptysender)
819         char *myname;
820         char *msgfn;
821         bool emptysender;
822 {
823         FILE *mfp, *sfp;
824         int i;
825         int pvect[2];
826         char buf[MAXLINE];
827
828         mfp = fopen(msgfn, "r");
829         if (mfp == NULL)
830         {
831                 if (msgfn[0] == '/')
832                         msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
833                 else
834                         msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
835                                myname, msgfn);
836                 exit(EX_NOINPUT);
837         }
838         if (pipe(pvect) < 0)
839         {
840                 msglog(LOG_ERR, "vacation: pipe: %s", errstring(errno));
841                 exit(EX_OSERR);
842         }
843         i = fork();
844         if (i < 0)
845         {
846                 msglog(LOG_ERR, "vacation: fork: %s", errstring(errno));
847                 exit(EX_OSERR);
848         }
849         if (i == 0)
850         {
851                 (void) dup2(pvect[0], 0);
852                 (void) close(pvect[0]);
853                 (void) close(pvect[1]);
854                 (void) fclose(mfp);
855                 if (emptysender)
856                         myname = "<>";
857                 (void) execl(_PATH_SENDMAIL, "sendmail", "-oi",
858                              "-f", myname, "--", From, NULL);
859                 msglog(LOG_ERR, "vacation: can't exec %s: %s",
860                         _PATH_SENDMAIL, errstring(errno));
861                 exit(EX_UNAVAILABLE);
862         }
863         /* check return status of the following calls? XXX */
864         (void) close(pvect[0]);
865         if ((sfp = fdopen(pvect[1], "w")) != NULL)
866         {
867                 (void) fprintf(sfp, "To: %s\n", From);
868                 (void) fprintf(sfp, "Auto-Submitted: auto-generated\n");
869                 while (fgets(buf, sizeof buf, mfp))
870                         (void) fputs(buf, sfp);
871                 (void) fclose(mfp);
872                 (void) fclose(sfp);
873         }
874         else
875         {
876                 (void) fclose(mfp);
877                 msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
878                 exit(EX_UNAVAILABLE);
879         }
880 }
881
882 void
883 usage()
884 {
885         msglog(LOG_NOTICE, "uid %u: usage: vacation [-i] [-a alias]%s [-f db]%s [-m msg] [-r interval] [-s sender] [-t time] [-x] [-z] login\n",
886                getuid(),
887 #if _FFR_DEBUG
888                " [-d]",
889 #else /* _FFR_DEBUG */
890                "",
891 #endif /* _FFR_DEBUG */
892 #if _FFR_LISTDB
893                " [-l]"
894 #else /* _FFR_LISTDB */
895                ""
896 #endif /* _FFR_LISTDB */
897                );
898         exit(EX_USAGE);
899 }
900
901 #if _FFR_LISTDB
902 /*
903 ** LISTDB -- list the contents of the vacation database
904 **
905 **      Parameters:
906 **              none.
907 **
908 **      Returns:
909 **              nothing.
910 */
911
912 static void
913 listdb()
914 {
915         int result;
916         time_t t;
917         SMDB_CURSOR *cursor = NULL;
918         SMDB_DBENT db_key, db_value;
919
920         memset(&db_key, '\0', sizeof db_key);
921         memset(&db_value, '\0', sizeof db_value);
922
923         result = Db->smdb_cursor(Db, &cursor, 0);
924         if (result != SMDBE_OK)
925         {
926                 fprintf(stderr, "vacation: set cursor: %s\n",
927                         errstring(result));
928                 return;
929         }
930
931         while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
932                                            SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
933         {
934                 /* skip magic VIT entry */
935                 if ((int)db_key.data.size -1 == strlen(VIT) &&
936                     strncmp((char *)db_key.data.data, VIT,
937                             (int)db_key.data.size - 1) == 0)
938                         continue;
939
940                 /* skip bogus values */
941                 if (db_value.data.size != sizeof t)
942                 {
943                         fprintf(stderr, "vacation: %.*s invalid time stamp\n",
944                                 (int) db_key.data.size,
945                                 (char *) db_key.data.data);
946                         continue;
947                 }
948
949                 memcpy(&t, db_value.data.data, sizeof t);
950
951                 if (db_key.data.size > 40)
952                         db_key.data.size = 40;
953
954                 printf("%-40.*s %-10s",
955                        (int) db_key.data.size, (char *) db_key.data.data,
956                        ctime(&t));
957
958                 memset(&db_key, '\0', sizeof db_key);
959                 memset(&db_value, '\0', sizeof db_value);
960         }
961
962         if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
963         {
964                 fprintf(stderr, "vacation: get value at cursor: %s\n",
965                         errstring(result));
966                 if (cursor != NULL)
967                 {
968                         (void) cursor->smdbc_close(cursor);
969                         cursor = NULL;
970                 }
971                 return;
972         }
973         (void) cursor->smdbc_close(cursor);
974         cursor = NULL;
975 }
976 #endif /* _FFR_LISTDB */
977
978 #if _FFR_DEBUG
979 /*
980 ** DEBUGLOG -- write message to standard error
981 **
982 **      Append a message to the standard error for the convenience of
983 **      end-users debugging without access to the syslog messages.
984 **
985 **      Parameters:
986 **              i -- syslog log level
987 **              fmt -- string format
988 **
989 **      Returns:
990 **              nothing.
991 */
992
993 /*VARARGS2*/
994 static void
995 #ifdef __STDC__
996 debuglog(int i, const char *fmt, ...)
997 #else /* __STDC__ */
998 debuglog(i, fmt, va_alist)
999         int i;
1000         const char *fmt;
1001         va_dcl
1002 #endif /* __STDC__ */
1003
1004 {
1005         VA_LOCAL_DECL
1006
1007         VA_START(fmt);
1008         vfprintf(stderr, fmt, ap);
1009         VA_END;
1010 }
1011 #endif /* _FFR_DEBUG */
1012
1013 /*VARARGS1*/
1014 void
1015 #ifdef __STDC__
1016 message(const char *msg, ...)
1017 #else /* __STDC__ */
1018 message(msg, va_alist)
1019         const char *msg;
1020         va_dcl
1021 #endif /* __STDC__ */
1022 {
1023         const char *m;
1024         VA_LOCAL_DECL
1025
1026         m = msg;
1027         if (isascii(m[0]) && isdigit(m[0]) &&
1028             isascii(m[1]) && isdigit(m[1]) &&
1029             isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
1030                 m += 4;
1031         VA_START(msg);
1032         (void) vfprintf(stderr, m, ap);
1033         VA_END;
1034         (void) fprintf(stderr, "\n");
1035 }
1036
1037 /*VARARGS1*/
1038 void
1039 #ifdef __STDC__
1040 syserr(const char *msg, ...)
1041 #else /* __STDC__ */
1042 syserr(msg, va_alist)
1043         const char *msg;
1044         va_dcl
1045 #endif /* __STDC__ */
1046 {
1047         const char *m;
1048         VA_LOCAL_DECL
1049
1050         m = msg;
1051         if (isascii(m[0]) && isdigit(m[0]) &&
1052             isascii(m[1]) && isdigit(m[1]) &&
1053             isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
1054                 m += 4;
1055         VA_START(msg);
1056         (void) vfprintf(stderr, m, ap);
1057         VA_END;
1058         (void) fprintf(stderr, "\n");
1059 }
1060
1061 void
1062 dumpfd(fd, printclosed, logit)
1063         int fd;
1064         bool printclosed;
1065         bool logit;
1066 {
1067         return;
1068 }