sendmail: Update vendor branch to v8.14.4
[dragonfly.git] / contrib / sendmail-8.14 / vacation / vacation.c
blob2ead0b86cbee1c236e8f2eeadc5d466fac585454
1 /*
2 * Copyright (c) 1999-2002, 2009 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.
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.
14 #include <sm/gen.h>
16 SM_IDSTR(copyright,
17 "@(#) Copyright (c) 1999-2002, 2009 Sendmail, Inc. and its suppliers.\n\
18 All rights reserved.\n\
19 Copyright (c) 1983, 1987, 1993\n\
20 The Regents of the University of California. All rights reserved.\n\
21 Copyright (c) 1983 Eric P. Allman. All rights reserved.\n")
23 SM_IDSTR(id, "@(#)$Id: vacation.c,v 8.146 2009/08/07 21:28:39 ca Exp $")
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <syslog.h>
29 #include <time.h>
30 #include <unistd.h>
31 #ifdef EX_OK
32 # undef EX_OK /* unistd.h may have another use for this */
33 #endif /* EX_OK */
34 #include <sm/sysexits.h>
36 #include <sm/cf.h>
37 #include <sm/mbdb.h>
38 #include "sendmail/sendmail.h"
39 #include <sendmail/pathnames.h>
40 #include "libsmdb/smdb.h"
42 #define ONLY_ONCE ((time_t) 0) /* send at most one reply */
43 #define INTERVAL_UNDEF ((time_t) (-1)) /* no value given */
45 uid_t RealUid;
46 gid_t RealGid;
47 char *RealUserName;
48 uid_t RunAsUid;
49 gid_t RunAsGid;
50 char *RunAsUserName;
51 int Verbose = 2;
52 bool DontInitGroups = false;
53 uid_t TrustedUid = 0;
54 BITMAP256 DontBlameSendmail;
56 static int readheaders __P((bool));
57 static bool junkmail __P((char *));
58 static bool nsearch __P((char *, char *));
59 static void usage __P((void));
60 static void setinterval __P((time_t));
61 static bool recent __P((void));
62 static void setreply __P((char *, time_t));
63 static void sendmessage __P((char *, char *, char *));
64 static void xclude __P((SM_FILE_T *));
67 ** VACATION -- return a message to the sender when on vacation.
69 ** This program is invoked as a message receiver. It returns a
70 ** message specified by the user to whomever sent the mail, taking
71 ** care not to return a message too often to prevent "I am on
72 ** vacation" loops.
75 #define VDB ".vacation" /* vacation database */
76 #define VMSG ".vacation.msg" /* vacation message */
77 #define SECSPERDAY (60 * 60 * 24)
78 #define DAYSPERWEEK 7
80 typedef struct alias
82 char *name;
83 struct alias *next;
84 } ALIAS;
86 ALIAS *Names = NULL;
88 SMDB_DATABASE *Db;
90 char From[MAXLINE];
91 bool CloseMBDB = false;
93 #if defined(__hpux) || defined(__osf__)
94 # ifndef SM_CONF_SYSLOG_INT
95 # define SM_CONF_SYSLOG_INT 1
96 # endif /* SM_CONF_SYSLOG_INT */
97 #endif /* defined(__hpux) || defined(__osf__) */
99 #if SM_CONF_SYSLOG_INT
100 # define SYSLOG_RET_T int
101 # define SYSLOG_RET return 0
102 #else /* SM_CONF_SYSLOG_INT */
103 # define SYSLOG_RET_T void
104 # define SYSLOG_RET
105 #endif /* SM_CONF_SYSLOG_INT */
107 typedef SYSLOG_RET_T SYSLOG_T __P((int, const char *, ...));
108 SYSLOG_T *msglog = syslog;
109 static SYSLOG_RET_T debuglog __P((int, const char *, ...));
110 static void eatmsg __P((void));
111 static void listdb __P((void));
113 /* exit after reading input */
114 #define EXITIT(excode) \
116 eatmsg(); \
117 if (CloseMBDB) \
119 sm_mbdb_terminate(); \
120 CloseMBDB = false; \
122 return excode; \
125 #define EXITM(excode) \
127 if (!initdb && !list) \
128 eatmsg(); \
129 if (CloseMBDB) \
131 sm_mbdb_terminate(); \
132 CloseMBDB = false; \
134 exit(excode); \
138 main(argc, argv)
139 int argc;
140 char **argv;
142 bool alwaysrespond = false;
143 bool initdb, exclude;
144 bool runasuser = false;
145 bool list = false;
146 int mfail = 0, ufail = 0;
147 int ch;
148 int result;
149 long sff;
150 time_t interval;
151 struct passwd *pw;
152 ALIAS *cur;
153 char *dbfilename = NULL;
154 char *msgfilename = NULL;
155 char *cfpath = NULL;
156 char *name = NULL;
157 char *returnaddr = NULL;
158 SMDB_USER_INFO user_info;
159 static char rnamebuf[MAXNAME];
160 extern int optind, opterr;
161 extern char *optarg;
163 /* Vars needed to link with smutil */
164 clrbitmap(DontBlameSendmail);
165 RunAsUid = RealUid = getuid();
166 RunAsGid = RealGid = getgid();
167 pw = getpwuid(RealUid);
168 if (pw != NULL)
170 if (strlen(pw->pw_name) > MAXNAME - 1)
171 pw->pw_name[MAXNAME] = '\0';
172 sm_snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
174 else
175 sm_snprintf(rnamebuf, sizeof rnamebuf,
176 "Unknown UID %d", (int) RealUid);
177 RunAsUserName = RealUserName = rnamebuf;
179 # ifdef LOG_MAIL
180 openlog("vacation", LOG_PID, LOG_MAIL);
181 # else /* LOG_MAIL */
182 openlog("vacation", LOG_PID);
183 # endif /* LOG_MAIL */
185 opterr = 0;
186 initdb = false;
187 exclude = false;
188 interval = INTERVAL_UNDEF;
189 *From = '\0';
192 #define OPTIONS "a:C:df:Iijlm:R:r:s:t:Uxz"
194 while (mfail == 0 && ufail == 0 &&
195 (ch = getopt(argc, argv, OPTIONS)) != -1)
197 switch((char)ch)
199 case 'a': /* alias */
200 cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS));
201 if (cur == NULL)
203 mfail++;
204 break;
206 cur->name = optarg;
207 cur->next = Names;
208 Names = cur;
209 break;
211 case 'C':
212 cfpath = optarg;
213 break;
215 case 'd': /* debug mode */
216 msglog = debuglog;
217 break;
219 case 'f': /* alternate database */
220 dbfilename = optarg;
221 break;
223 case 'I': /* backward compatible */
224 case 'i': /* init the database */
225 initdb = true;
226 break;
228 case 'j':
229 alwaysrespond = true;
230 break;
232 case 'l':
233 list = true; /* list the database */
234 break;
236 case 'm': /* alternate message file */
237 msgfilename = optarg;
238 break;
240 case 'R':
241 returnaddr = optarg;
242 break;
244 case 'r':
245 if (isascii(*optarg) && isdigit(*optarg))
247 interval = atol(optarg) * SECSPERDAY;
248 if (interval < 0)
249 ufail++;
251 else
252 interval = ONLY_ONCE;
253 break;
255 case 's': /* alternate sender name */
256 (void) sm_strlcpy(From, optarg, sizeof From);
257 break;
259 case 't': /* SunOS: -t1d (default expire) */
260 break;
262 case 'U': /* run as single user mode */
263 runasuser = true;
264 break;
266 case 'x':
267 exclude = true;
268 break;
270 case 'z':
271 returnaddr = "<>";
272 break;
274 case '?':
275 default:
276 ufail++;
277 break;
280 argc -= optind;
281 argv += optind;
283 if (mfail != 0)
285 msglog(LOG_NOTICE,
286 "vacation: can't allocate memory for alias.\n");
287 EXITM(EX_TEMPFAIL);
289 if (ufail != 0)
290 usage();
292 if (argc != 1)
294 if (!initdb && !list && !exclude)
295 usage();
296 if ((pw = getpwuid(getuid())) == NULL)
298 msglog(LOG_ERR,
299 "vacation: no such user uid %u.\n", getuid());
300 EXITM(EX_NOUSER);
302 name = strdup(pw->pw_name);
303 user_info.smdbu_id = pw->pw_uid;
304 user_info.smdbu_group_id = pw->pw_gid;
305 (void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
306 SMDB_MAX_USER_NAME_LEN);
307 if (chdir(pw->pw_dir) != 0)
309 msglog(LOG_NOTICE,
310 "vacation: no such directory %s.\n",
311 pw->pw_dir);
312 EXITM(EX_NOINPUT);
315 else if (runasuser)
317 name = strdup(*argv);
318 if (dbfilename == NULL || msgfilename == NULL)
320 msglog(LOG_NOTICE,
321 "vacation: -U requires setting both -f and -m\n");
322 EXITM(EX_NOINPUT);
324 user_info.smdbu_id = pw->pw_uid;
325 user_info.smdbu_group_id = pw->pw_gid;
326 (void) sm_strlcpy(user_info.smdbu_name, pw->pw_name,
327 SMDB_MAX_USER_NAME_LEN);
329 else
331 int err;
332 SM_CF_OPT_T mbdbname;
333 SM_MBDB_T user;
335 cfpath = getcfname(0, 0, SM_GET_SENDMAIL_CF, cfpath);
336 mbdbname.opt_name = "MailboxDatabase";
337 mbdbname.opt_val = "pw";
338 (void) sm_cf_getopt(cfpath, 1, &mbdbname);
339 err = sm_mbdb_initialize(mbdbname.opt_val);
340 if (err != EX_OK)
342 msglog(LOG_ERR,
343 "vacation: can't open mailbox database: %s.\n",
344 sm_strexit(err));
345 EXITM(err);
347 CloseMBDB = true;
348 err = sm_mbdb_lookup(*argv, &user);
349 if (err == EX_NOUSER)
351 msglog(LOG_ERR, "vacation: no such user %s.\n", *argv);
352 EXITM(EX_NOUSER);
354 if (err != EX_OK)
356 msglog(LOG_ERR,
357 "vacation: can't read mailbox database: %s.\n",
358 sm_strexit(err));
359 EXITM(err);
361 name = strdup(user.mbdb_name);
362 if (chdir(user.mbdb_homedir) != 0)
364 msglog(LOG_NOTICE,
365 "vacation: no such directory %s.\n",
366 user.mbdb_homedir);
367 EXITM(EX_NOINPUT);
369 user_info.smdbu_id = user.mbdb_uid;
370 user_info.smdbu_group_id = user.mbdb_gid;
371 (void) sm_strlcpy(user_info.smdbu_name, user.mbdb_name,
372 SMDB_MAX_USER_NAME_LEN);
374 if (name == NULL)
376 msglog(LOG_ERR,
377 "vacation: can't allocate memory for username.\n");
378 EXITM(EX_OSERR);
381 if (dbfilename == NULL)
382 dbfilename = VDB;
383 if (msgfilename == NULL)
384 msgfilename = VMSG;
386 sff = SFF_CREAT;
387 if (getegid() != getgid())
389 /* Allow a set-group-ID vacation binary */
390 RunAsGid = user_info.smdbu_group_id = getegid();
391 sff |= SFF_OPENASROOT;
393 if (getuid() == 0)
395 /* Allow root to initialize user's vacation databases */
396 sff |= SFF_OPENASROOT|SFF_ROOTOK;
398 /* ... safely */
399 sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY;
403 result = smdb_open_database(&Db, dbfilename,
404 O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0),
405 S_IRUSR|S_IWUSR, sff,
406 SMDB_TYPE_DEFAULT, &user_info, NULL);
407 if (result != SMDBE_OK)
409 msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
410 sm_errstring(result));
411 EXITM(EX_DATAERR);
414 if (list)
416 listdb();
417 (void) Db->smdb_close(Db);
418 exit(EX_OK);
421 if (interval != INTERVAL_UNDEF)
422 setinterval(interval);
424 if (initdb && !exclude)
426 (void) Db->smdb_close(Db);
427 exit(EX_OK);
430 if (exclude)
432 xclude(smioin);
433 (void) Db->smdb_close(Db);
434 EXITM(EX_OK);
437 if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
439 msglog(LOG_NOTICE,
440 "vacation: can't allocate memory for username.\n");
441 (void) Db->smdb_close(Db);
442 EXITM(EX_OSERR);
444 cur->name = name;
445 cur->next = Names;
446 Names = cur;
448 result = readheaders(alwaysrespond);
449 if (result == EX_OK && !recent())
451 time_t now;
453 (void) time(&now);
454 setreply(From, now);
455 (void) Db->smdb_close(Db);
456 sendmessage(name, msgfilename, returnaddr);
458 else
459 (void) Db->smdb_close(Db);
460 if (result == EX_NOUSER)
461 result = EX_OK;
462 exit(result);
466 ** EATMSG -- read stdin till EOF
468 ** Parameters:
469 ** none.
471 ** Returns:
472 ** nothing.
476 static void
477 eatmsg()
480 ** read the rest of the e-mail and ignore it to avoid problems
481 ** with EPIPE in sendmail
483 while (getc(stdin) != EOF)
484 continue;
488 ** READHEADERS -- read mail headers
490 ** Parameters:
491 ** alwaysrespond -- respond regardless of whether msg is to me
493 ** Returns:
494 ** a exit code: NOUSER if no reply, OK if reply, * if error
496 ** Side Effects:
497 ** may exit().
501 static int
502 readheaders(alwaysrespond)
503 bool alwaysrespond;
505 bool tome, cont;
506 register char *p;
507 register ALIAS *cur;
508 char buf[MAXLINE];
510 cont = false;
511 tome = alwaysrespond;
512 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) &&
513 *buf != '\n')
515 switch(*buf)
517 case 'F': /* "From " */
518 cont = false;
519 if (strncmp(buf, "From ", 5) == 0)
521 bool quoted = false;
523 p = buf + 5;
524 while (*p != '\0')
526 /* escaped character */
527 if (*p == '\\')
529 p++;
530 if (*p == '\0')
532 msglog(LOG_NOTICE,
533 "vacation: badly formatted \"From \" line.\n");
534 EXITIT(EX_DATAERR);
537 else if (*p == '"')
538 quoted = !quoted;
539 else if (*p == '\r' || *p == '\n')
540 break;
541 else if (*p == ' ' && !quoted)
542 break;
543 p++;
545 if (quoted)
547 msglog(LOG_NOTICE,
548 "vacation: badly formatted \"From \" line.\n");
549 EXITIT(EX_DATAERR);
551 *p = '\0';
553 /* ok since both strings have MAXLINE length */
554 if (*From == '\0')
555 (void) sm_strlcpy(From, buf + 5,
556 sizeof From);
557 if ((p = strchr(buf + 5, '\n')) != NULL)
558 *p = '\0';
559 if (junkmail(buf + 5))
560 EXITIT(EX_NOUSER);
562 break;
564 case 'P': /* "Precedence:" */
565 case 'p':
566 cont = false;
567 if (strlen(buf) <= 10 ||
568 strncasecmp(buf, "Precedence", 10) != 0 ||
569 (buf[10] != ':' && buf[10] != ' ' &&
570 buf[10] != '\t'))
571 break;
572 if ((p = strchr(buf, ':')) == NULL)
573 break;
574 while (*++p != '\0' && isascii(*p) && isspace(*p));
575 if (*p == '\0')
576 break;
577 if (strncasecmp(p, "junk", 4) == 0 ||
578 strncasecmp(p, "bulk", 4) == 0 ||
579 strncasecmp(p, "list", 4) == 0)
580 EXITIT(EX_NOUSER);
581 break;
583 case 'C': /* "Cc:" */
584 case 'c':
585 if (strncasecmp(buf, "Cc:", 3) != 0)
586 break;
587 cont = true;
588 goto findme;
590 case 'T': /* "To:" */
591 case 't':
592 if (strncasecmp(buf, "To:", 3) != 0)
593 break;
594 cont = true;
595 goto findme;
597 default:
598 if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
600 cont = false;
601 break;
603 findme:
604 for (cur = Names;
605 !tome && cur != NULL;
606 cur = cur->next)
607 tome = nsearch(cur->name, buf);
610 if (!tome)
611 EXITIT(EX_NOUSER);
612 if (*From == '\0')
614 msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
615 EXITIT(EX_DATAERR);
617 EXITIT(EX_OK);
621 ** NSEARCH --
622 ** do a nice, slow, search of a string for a substring.
624 ** Parameters:
625 ** name -- name to search.
626 ** str -- string in which to search.
628 ** Returns:
629 ** is name a substring of str?
633 static bool
634 nsearch(name, str)
635 register char *name, *str;
637 register size_t len;
638 register char *s;
640 len = strlen(name);
642 for (s = str; *s != '\0'; ++s)
645 ** Check to make sure that the string matches and
646 ** the previous character is not an alphanumeric and
647 ** the next character after the match is not an alphanumeric.
649 ** This prevents matching "eric" to "derick" while still
650 ** matching "eric" to "<eric+detail>".
653 if (tolower(*s) == tolower(*name) &&
654 strncasecmp(name, s, len) == 0 &&
655 (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
656 (!isascii(*(s + len)) || !isalnum(*(s + len))))
657 return true;
659 return false;
663 ** JUNKMAIL --
664 ** read the header and return if automagic/junk/bulk/list mail
666 ** Parameters:
667 ** from -- sender address.
669 ** Returns:
670 ** is this some automated/junk/bulk/list mail?
674 struct ignore
676 char *name;
677 size_t len;
680 typedef struct ignore IGNORE_T;
682 #define MAX_USER_LEN 256 /* maximum length of local part (sender) */
684 /* delimiters for the local part of an address */
685 #define isdelim(c) ((c) == '%' || (c) == '@' || (c) == '+')
687 static bool
688 junkmail(from)
689 char *from;
691 bool quot;
692 char *e;
693 size_t len;
694 IGNORE_T *cur;
695 char sender[MAX_USER_LEN];
696 static IGNORE_T ignore[] =
698 { "postmaster", 10 },
699 { "uucp", 4 },
700 { "mailer-daemon", 13 },
701 { "mailer", 6 },
702 { NULL, 0 }
705 static IGNORE_T ignorepost[] =
707 { "-request", 8 },
708 { "-relay", 6 },
709 { "-owner", 6 },
710 { NULL, 0 }
713 static IGNORE_T ignorepre[] =
715 { "owner-", 6 },
716 { NULL, 0 }
720 ** This is mildly amusing, and I'm not positive it's right; trying
721 ** to find the "real" name of the sender, assuming that addresses
722 ** will be some variant of:
724 ** From site!site!SENDER%site.domain%site.domain@site.domain
727 quot = false;
728 e = from;
729 len = 0;
730 while (*e != '\0' && (quot || !isdelim(*e)))
732 if (*e == '"')
734 quot = !quot;
735 ++e;
736 continue;
738 if (*e == '\\')
740 if (*(++e) == '\0')
742 /* '\\' at end of string? */
743 break;
745 if (len < MAX_USER_LEN)
746 sender[len++] = *e;
747 ++e;
748 continue;
750 if (*e == '!' && !quot)
752 len = 0;
753 sender[len] = '\0';
755 else
756 if (len < MAX_USER_LEN)
757 sender[len++] = *e;
758 ++e;
760 if (len < MAX_USER_LEN)
761 sender[len] = '\0';
762 else
763 sender[MAX_USER_LEN - 1] = '\0';
765 if (len <= 0)
766 return false;
767 #if 0
768 if (quot)
769 return false; /* syntax error... */
770 #endif /* 0 */
772 /* test prefixes */
773 for (cur = ignorepre; cur->name != NULL; ++cur)
775 if (len >= cur->len &&
776 strncasecmp(cur->name, sender, cur->len) == 0)
777 return true;
781 ** If the name is truncated, don't test the rest.
782 ** We could extract the "tail" of the sender address and
783 ** compare it it ignorepost, however, it seems not worth
784 ** the effort.
785 ** The address surely can't match any entry in ignore[]
786 ** (as long as all of them are shorter than MAX_USER_LEN).
789 if (len > MAX_USER_LEN)
790 return false;
792 /* test full local parts */
793 for (cur = ignore; cur->name != NULL; ++cur)
795 if (len == cur->len &&
796 strncasecmp(cur->name, sender, cur->len) == 0)
797 return true;
800 /* test postfixes */
801 for (cur = ignorepost; cur->name != NULL; ++cur)
803 if (len >= cur->len &&
804 strncasecmp(cur->name, e - cur->len - 1,
805 cur->len) == 0)
806 return true;
808 return false;
811 #define VIT "__VACATION__INTERVAL__TIMER__"
814 ** RECENT --
815 ** find out if user has gotten a vacation message recently.
817 ** Parameters:
818 ** none.
820 ** Returns:
821 ** true iff user has gotten a vacation message recently.
825 static bool
826 recent()
828 SMDB_DBENT key, data;
829 time_t then, next;
830 bool trydomain = false;
831 int st;
832 char *domain;
834 memset(&key, '\0', sizeof key);
835 memset(&data, '\0', sizeof data);
837 /* get interval time */
838 key.data = VIT;
839 key.size = sizeof(VIT);
841 st = Db->smdb_get(Db, &key, &data, 0);
842 if (st != SMDBE_OK)
843 next = SECSPERDAY * DAYSPERWEEK;
844 else
845 memmove(&next, data.data, sizeof(next));
847 memset(&data, '\0', sizeof data);
849 /* get record for this address */
850 key.data = From;
851 key.size = strlen(From);
855 st = Db->smdb_get(Db, &key, &data, 0);
856 if (st == SMDBE_OK)
858 memmove(&then, data.data, sizeof(then));
859 if (next == ONLY_ONCE || then == ONLY_ONCE ||
860 then + next > time(NULL))
861 return true;
863 if ((trydomain = !trydomain) &&
864 (domain = strchr(From, '@')) != NULL)
866 key.data = domain;
867 key.size = strlen(domain);
869 } while (trydomain);
870 return false;
874 ** SETINTERVAL --
875 ** store the reply interval
877 ** Parameters:
878 ** interval -- time interval for replies.
880 ** Returns:
881 ** nothing.
883 ** Side Effects:
884 ** stores the reply interval in database.
887 static void
888 setinterval(interval)
889 time_t interval;
891 SMDB_DBENT key, data;
893 memset(&key, '\0', sizeof key);
894 memset(&data, '\0', sizeof data);
896 key.data = VIT;
897 key.size = sizeof(VIT);
898 data.data = (char*) &interval;
899 data.size = sizeof(interval);
900 (void) (Db->smdb_put)(Db, &key, &data, 0);
904 ** SETREPLY --
905 ** store that this user knows about the vacation.
907 ** Parameters:
908 ** from -- sender address.
909 ** when -- last reply time.
911 ** Returns:
912 ** nothing.
914 ** Side Effects:
915 ** stores user/time in database.
918 static void
919 setreply(from, when)
920 char *from;
921 time_t when;
923 SMDB_DBENT key, data;
925 memset(&key, '\0', sizeof key);
926 memset(&data, '\0', sizeof data);
928 key.data = from;
929 key.size = strlen(from);
930 data.data = (char*) &when;
931 data.size = sizeof(when);
932 (void) (Db->smdb_put)(Db, &key, &data, 0);
936 ** XCLUDE --
937 ** add users to vacation db so they don't get a reply.
939 ** Parameters:
940 ** f -- file pointer with list of address to exclude
942 ** Returns:
943 ** nothing.
945 ** Side Effects:
946 ** stores users in database.
949 static void
950 xclude(f)
951 SM_FILE_T *f;
953 char buf[MAXLINE], *p;
955 if (f == NULL)
956 return;
957 while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf))
959 if ((p = strchr(buf, '\n')) != NULL)
960 *p = '\0';
961 setreply(buf, ONLY_ONCE);
966 ** SENDMESSAGE --
967 ** exec sendmail to send the vacation file to sender
969 ** Parameters:
970 ** myname -- user name.
971 ** msgfn -- name of file with vacation message.
972 ** sender -- use as sender address
974 ** Returns:
975 ** nothing.
977 ** Side Effects:
978 ** sends vacation reply.
981 static void
982 sendmessage(myname, msgfn, sender)
983 char *myname;
984 char *msgfn;
985 char *sender;
987 SM_FILE_T *mfp, *sfp;
988 int i;
989 int pvect[2];
990 char *pv[8];
991 char buf[MAXLINE];
993 mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
994 if (mfp == NULL)
996 if (msgfn[0] == '/')
997 msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
998 else
999 msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
1000 myname, msgfn);
1001 exit(EX_NOINPUT);
1003 if (pipe(pvect) < 0)
1005 msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
1006 exit(EX_OSERR);
1008 pv[0] = "sendmail";
1009 pv[1] = "-oi";
1010 pv[2] = "-f";
1011 if (sender != NULL)
1012 pv[3] = sender;
1013 else
1014 pv[3] = myname;
1015 pv[4] = "--";
1016 pv[5] = From;
1017 pv[6] = NULL;
1018 i = fork();
1019 if (i < 0)
1021 msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
1022 exit(EX_OSERR);
1024 if (i == 0)
1026 (void) dup2(pvect[0], 0);
1027 (void) close(pvect[0]);
1028 (void) close(pvect[1]);
1029 (void) sm_io_close(mfp, SM_TIME_DEFAULT);
1030 (void) execv(_PATH_SENDMAIL, pv);
1031 msglog(LOG_ERR, "vacation: can't exec %s: %s",
1032 _PATH_SENDMAIL, sm_errstring(errno));
1033 exit(EX_UNAVAILABLE);
1035 /* check return status of the following calls? XXX */
1036 (void) close(pvect[0]);
1037 if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
1038 (void *) &(pvect[1]),
1039 SM_IO_WRONLY, NULL)) != NULL)
1041 #if _FFR_VAC_WAIT4SM
1042 # ifdef WAITUNION
1043 union wait st;
1044 # else /* WAITUNION */
1045 auto int st;
1046 # endif /* WAITUNION */
1047 #endif /* _FFR_VAC_WAIT4SM */
1049 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
1050 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
1051 "Auto-Submitted: auto-replied\n");
1052 while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf))
1053 (void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
1054 (void) sm_io_close(mfp, SM_TIME_DEFAULT);
1055 (void) sm_io_close(sfp, SM_TIME_DEFAULT);
1056 #if _FFR_VAC_WAIT4SM
1057 (void) wait(&st);
1058 #endif /* _FFR_VAC_WAIT4SM */
1060 else
1062 (void) sm_io_close(mfp, SM_TIME_DEFAULT);
1063 msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
1064 exit(EX_UNAVAILABLE);
1068 static void
1069 usage()
1071 msglog(LOG_NOTICE,
1072 "uid %u: usage: vacation [-a alias] [-C cfpath] [-d] [-f db] [-i] [-j] [-l] [-m msg] [-R returnaddr] [-r interval] [-s sender] [-t time] [-U] [-x] [-z] login\n",
1073 getuid());
1074 exit(EX_USAGE);
1078 ** LISTDB -- list the contents of the vacation database
1080 ** Parameters:
1081 ** none.
1083 ** Returns:
1084 ** nothing.
1087 static void
1088 listdb()
1090 int result;
1091 time_t t;
1092 SMDB_CURSOR *cursor = NULL;
1093 SMDB_DBENT db_key, db_value;
1095 memset(&db_key, '\0', sizeof db_key);
1096 memset(&db_value, '\0', sizeof db_value);
1098 result = Db->smdb_cursor(Db, &cursor, 0);
1099 if (result != SMDBE_OK)
1101 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1102 "vacation: set cursor: %s\n",
1103 sm_errstring(result));
1104 return;
1107 while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
1108 SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
1110 char *timestamp;
1112 /* skip magic VIT entry */
1113 if (db_key.size == strlen(VIT) + 1 &&
1114 strncmp((char *)db_key.data, VIT,
1115 (int)db_key.size - 1) == 0)
1116 continue;
1118 /* skip bogus values */
1119 if (db_value.size != sizeof t)
1121 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1122 "vacation: %.*s invalid time stamp\n",
1123 (int) db_key.size, (char *) db_key.data);
1124 continue;
1127 memcpy(&t, db_value.data, sizeof t);
1129 if (db_key.size > 40)
1130 db_key.size = 40;
1132 if (t <= 0)
1134 /* must be an exclude */
1135 timestamp = "(exclusion)\n";
1137 else
1139 timestamp = ctime(&t);
1141 sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
1142 (int) db_key.size, (char *) db_key.data,
1143 timestamp);
1145 memset(&db_key, '\0', sizeof db_key);
1146 memset(&db_value, '\0', sizeof db_value);
1149 if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
1151 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1152 "vacation: get value at cursor: %s\n",
1153 sm_errstring(result));
1154 if (cursor != NULL)
1156 (void) cursor->smdbc_close(cursor);
1157 cursor = NULL;
1159 return;
1161 (void) cursor->smdbc_close(cursor);
1162 cursor = NULL;
1166 ** DEBUGLOG -- write message to standard error
1168 ** Append a message to the standard error for the convenience of
1169 ** end-users debugging without access to the syslog messages.
1171 ** Parameters:
1172 ** i -- syslog log level
1173 ** fmt -- string format
1175 ** Returns:
1176 ** nothing.
1179 /*VARARGS2*/
1180 static SYSLOG_RET_T
1181 #ifdef __STDC__
1182 debuglog(int i, const char *fmt, ...)
1183 #else /* __STDC__ */
1184 debuglog(i, fmt, va_alist)
1185 int i;
1186 const char *fmt;
1187 va_dcl
1188 #endif /* __STDC__ */
1191 SM_VA_LOCAL_DECL
1193 SM_VA_START(ap, fmt);
1194 sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
1195 SM_VA_END(ap);
1196 SYSLOG_RET;