remove kerberos/heimdal
[dragonfly.git] / contrib / sendmail-8.14 / vacation / vacation.c
blob10712b8ed8b0dd8afb27566816ee703af0ae1c87
1 /*
2 * Copyright (c) 1999-2002 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-2001 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.144 2007/05/11 18:50:36 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;
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 = 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 = *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 = 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);
375 if (dbfilename == NULL)
376 dbfilename = VDB;
377 if (msgfilename == NULL)
378 msgfilename = VMSG;
380 sff = SFF_CREAT;
381 if (getegid() != getgid())
383 /* Allow a set-group-ID vacation binary */
384 RunAsGid = user_info.smdbu_group_id = getegid();
385 sff |= SFF_OPENASROOT;
387 if (getuid() == 0)
389 /* Allow root to initialize user's vacation databases */
390 sff |= SFF_OPENASROOT|SFF_ROOTOK;
392 /* ... safely */
393 sff |= SFF_NOSLINK|SFF_NOHLINK|SFF_REGONLY;
397 result = smdb_open_database(&Db, dbfilename,
398 O_CREAT|O_RDWR | (initdb ? O_TRUNC : 0),
399 S_IRUSR|S_IWUSR, sff,
400 SMDB_TYPE_DEFAULT, &user_info, NULL);
401 if (result != SMDBE_OK)
403 msglog(LOG_NOTICE, "vacation: %s: %s\n", dbfilename,
404 sm_errstring(result));
405 EXITM(EX_DATAERR);
408 if (list)
410 listdb();
411 (void) Db->smdb_close(Db);
412 exit(EX_OK);
415 if (interval != INTERVAL_UNDEF)
416 setinterval(interval);
418 if (initdb && !exclude)
420 (void) Db->smdb_close(Db);
421 exit(EX_OK);
424 if (exclude)
426 xclude(smioin);
427 (void) Db->smdb_close(Db);
428 EXITM(EX_OK);
431 if ((cur = (ALIAS *) malloc((unsigned int) sizeof(ALIAS))) == NULL)
433 msglog(LOG_NOTICE,
434 "vacation: can't allocate memory for username.\n");
435 (void) Db->smdb_close(Db);
436 EXITM(EX_OSERR);
438 cur->name = name;
439 cur->next = Names;
440 Names = cur;
442 result = readheaders(alwaysrespond);
443 if (result == EX_OK && !recent())
445 time_t now;
447 (void) time(&now);
448 setreply(From, now);
449 (void) Db->smdb_close(Db);
450 sendmessage(name, msgfilename, returnaddr);
452 else
453 (void) Db->smdb_close(Db);
454 if (result == EX_NOUSER)
455 result = EX_OK;
456 exit(result);
460 ** EATMSG -- read stdin till EOF
462 ** Parameters:
463 ** none.
465 ** Returns:
466 ** nothing.
470 static void
471 eatmsg()
474 ** read the rest of the e-mail and ignore it to avoid problems
475 ** with EPIPE in sendmail
477 while (getc(stdin) != EOF)
478 continue;
482 ** READHEADERS -- read mail headers
484 ** Parameters:
485 ** alwaysrespond -- respond regardless of whether msg is to me
487 ** Returns:
488 ** a exit code: NOUSER if no reply, OK if reply, * if error
490 ** Side Effects:
491 ** may exit().
495 static int
496 readheaders(alwaysrespond)
497 bool alwaysrespond;
499 bool tome, cont;
500 register char *p;
501 register ALIAS *cur;
502 char buf[MAXLINE];
504 cont = false;
505 tome = alwaysrespond;
506 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf, sizeof(buf)) &&
507 *buf != '\n')
509 switch(*buf)
511 case 'F': /* "From " */
512 cont = false;
513 if (strncmp(buf, "From ", 5) == 0)
515 bool quoted = false;
517 p = buf + 5;
518 while (*p != '\0')
520 /* escaped character */
521 if (*p == '\\')
523 p++;
524 if (*p == '\0')
526 msglog(LOG_NOTICE,
527 "vacation: badly formatted \"From \" line.\n");
528 EXITIT(EX_DATAERR);
531 else if (*p == '"')
532 quoted = !quoted;
533 else if (*p == '\r' || *p == '\n')
534 break;
535 else if (*p == ' ' && !quoted)
536 break;
537 p++;
539 if (quoted)
541 msglog(LOG_NOTICE,
542 "vacation: badly formatted \"From \" line.\n");
543 EXITIT(EX_DATAERR);
545 *p = '\0';
547 /* ok since both strings have MAXLINE length */
548 if (*From == '\0')
549 (void) sm_strlcpy(From, buf + 5,
550 sizeof From);
551 if ((p = strchr(buf + 5, '\n')) != NULL)
552 *p = '\0';
553 if (junkmail(buf + 5))
554 EXITIT(EX_NOUSER);
556 break;
558 case 'P': /* "Precedence:" */
559 case 'p':
560 cont = false;
561 if (strlen(buf) <= 10 ||
562 strncasecmp(buf, "Precedence", 10) != 0 ||
563 (buf[10] != ':' && buf[10] != ' ' &&
564 buf[10] != '\t'))
565 break;
566 if ((p = strchr(buf, ':')) == NULL)
567 break;
568 while (*++p != '\0' && isascii(*p) && isspace(*p));
569 if (*p == '\0')
570 break;
571 if (strncasecmp(p, "junk", 4) == 0 ||
572 strncasecmp(p, "bulk", 4) == 0 ||
573 strncasecmp(p, "list", 4) == 0)
574 EXITIT(EX_NOUSER);
575 break;
577 case 'C': /* "Cc:" */
578 case 'c':
579 if (strncasecmp(buf, "Cc:", 3) != 0)
580 break;
581 cont = true;
582 goto findme;
584 case 'T': /* "To:" */
585 case 't':
586 if (strncasecmp(buf, "To:", 3) != 0)
587 break;
588 cont = true;
589 goto findme;
591 default:
592 if (!isascii(*buf) || !isspace(*buf) || !cont || tome)
594 cont = false;
595 break;
597 findme:
598 for (cur = Names;
599 !tome && cur != NULL;
600 cur = cur->next)
601 tome = nsearch(cur->name, buf);
604 if (!tome)
605 EXITIT(EX_NOUSER);
606 if (*From == '\0')
608 msglog(LOG_NOTICE, "vacation: no initial \"From \" line.\n");
609 EXITIT(EX_DATAERR);
611 EXITIT(EX_OK);
615 ** NSEARCH --
616 ** do a nice, slow, search of a string for a substring.
618 ** Parameters:
619 ** name -- name to search.
620 ** str -- string in which to search.
622 ** Returns:
623 ** is name a substring of str?
627 static bool
628 nsearch(name, str)
629 register char *name, *str;
631 register size_t len;
632 register char *s;
634 len = strlen(name);
636 for (s = str; *s != '\0'; ++s)
639 ** Check to make sure that the string matches and
640 ** the previous character is not an alphanumeric and
641 ** the next character after the match is not an alphanumeric.
643 ** This prevents matching "eric" to "derick" while still
644 ** matching "eric" to "<eric+detail>".
647 if (tolower(*s) == tolower(*name) &&
648 strncasecmp(name, s, len) == 0 &&
649 (s == str || !isascii(*(s - 1)) || !isalnum(*(s - 1))) &&
650 (!isascii(*(s + len)) || !isalnum(*(s + len))))
651 return true;
653 return false;
657 ** JUNKMAIL --
658 ** read the header and return if automagic/junk/bulk/list mail
660 ** Parameters:
661 ** from -- sender address.
663 ** Returns:
664 ** is this some automated/junk/bulk/list mail?
668 struct ignore
670 char *name;
671 size_t len;
674 typedef struct ignore IGNORE_T;
676 #define MAX_USER_LEN 256 /* maximum length of local part (sender) */
678 /* delimiters for the local part of an address */
679 #define isdelim(c) ((c) == '%' || (c) == '@' || (c) == '+')
681 static bool
682 junkmail(from)
683 char *from;
685 bool quot;
686 char *e;
687 size_t len;
688 IGNORE_T *cur;
689 char sender[MAX_USER_LEN];
690 static IGNORE_T ignore[] =
692 { "postmaster", 10 },
693 { "uucp", 4 },
694 { "mailer-daemon", 13 },
695 { "mailer", 6 },
696 { NULL, 0 }
699 static IGNORE_T ignorepost[] =
701 { "-request", 8 },
702 { "-relay", 6 },
703 { "-owner", 6 },
704 { NULL, 0 }
707 static IGNORE_T ignorepre[] =
709 { "owner-", 6 },
710 { NULL, 0 }
714 ** This is mildly amusing, and I'm not positive it's right; trying
715 ** to find the "real" name of the sender, assuming that addresses
716 ** will be some variant of:
718 ** From site!site!SENDER%site.domain%site.domain@site.domain
721 quot = false;
722 e = from;
723 len = 0;
724 while (*e != '\0' && (quot || !isdelim(*e)))
726 if (*e == '"')
728 quot = !quot;
729 ++e;
730 continue;
732 if (*e == '\\')
734 if (*(++e) == '\0')
736 /* '\\' at end of string? */
737 break;
739 if (len < MAX_USER_LEN)
740 sender[len++] = *e;
741 ++e;
742 continue;
744 if (*e == '!' && !quot)
746 len = 0;
747 sender[len] = '\0';
749 else
750 if (len < MAX_USER_LEN)
751 sender[len++] = *e;
752 ++e;
754 if (len < MAX_USER_LEN)
755 sender[len] = '\0';
756 else
757 sender[MAX_USER_LEN - 1] = '\0';
759 if (len <= 0)
760 return false;
761 #if 0
762 if (quot)
763 return false; /* syntax error... */
764 #endif /* 0 */
766 /* test prefixes */
767 for (cur = ignorepre; cur->name != NULL; ++cur)
769 if (len >= cur->len &&
770 strncasecmp(cur->name, sender, cur->len) == 0)
771 return true;
775 ** If the name is truncated, don't test the rest.
776 ** We could extract the "tail" of the sender address and
777 ** compare it it ignorepost, however, it seems not worth
778 ** the effort.
779 ** The address surely can't match any entry in ignore[]
780 ** (as long as all of them are shorter than MAX_USER_LEN).
783 if (len > MAX_USER_LEN)
784 return false;
786 /* test full local parts */
787 for (cur = ignore; cur->name != NULL; ++cur)
789 if (len == cur->len &&
790 strncasecmp(cur->name, sender, cur->len) == 0)
791 return true;
794 /* test postfixes */
795 for (cur = ignorepost; cur->name != NULL; ++cur)
797 if (len >= cur->len &&
798 strncasecmp(cur->name, e - cur->len - 1,
799 cur->len) == 0)
800 return true;
802 return false;
805 #define VIT "__VACATION__INTERVAL__TIMER__"
808 ** RECENT --
809 ** find out if user has gotten a vacation message recently.
811 ** Parameters:
812 ** none.
814 ** Returns:
815 ** true iff user has gotten a vacation message recently.
819 static bool
820 recent()
822 SMDB_DBENT key, data;
823 time_t then, next;
824 bool trydomain = false;
825 int st;
826 char *domain;
828 memset(&key, '\0', sizeof key);
829 memset(&data, '\0', sizeof data);
831 /* get interval time */
832 key.data = VIT;
833 key.size = sizeof(VIT);
835 st = Db->smdb_get(Db, &key, &data, 0);
836 if (st != SMDBE_OK)
837 next = SECSPERDAY * DAYSPERWEEK;
838 else
839 memmove(&next, data.data, sizeof(next));
841 memset(&data, '\0', sizeof data);
843 /* get record for this address */
844 key.data = From;
845 key.size = strlen(From);
849 st = Db->smdb_get(Db, &key, &data, 0);
850 if (st == SMDBE_OK)
852 memmove(&then, data.data, sizeof(then));
853 if (next == ONLY_ONCE || then == ONLY_ONCE ||
854 then + next > time(NULL))
855 return true;
857 if ((trydomain = !trydomain) &&
858 (domain = strchr(From, '@')) != NULL)
860 key.data = domain;
861 key.size = strlen(domain);
863 } while (trydomain);
864 return false;
868 ** SETINTERVAL --
869 ** store the reply interval
871 ** Parameters:
872 ** interval -- time interval for replies.
874 ** Returns:
875 ** nothing.
877 ** Side Effects:
878 ** stores the reply interval in database.
881 static void
882 setinterval(interval)
883 time_t interval;
885 SMDB_DBENT key, data;
887 memset(&key, '\0', sizeof key);
888 memset(&data, '\0', sizeof data);
890 key.data = VIT;
891 key.size = sizeof(VIT);
892 data.data = (char*) &interval;
893 data.size = sizeof(interval);
894 (void) (Db->smdb_put)(Db, &key, &data, 0);
898 ** SETREPLY --
899 ** store that this user knows about the vacation.
901 ** Parameters:
902 ** from -- sender address.
903 ** when -- last reply time.
905 ** Returns:
906 ** nothing.
908 ** Side Effects:
909 ** stores user/time in database.
912 static void
913 setreply(from, when)
914 char *from;
915 time_t when;
917 SMDB_DBENT key, data;
919 memset(&key, '\0', sizeof key);
920 memset(&data, '\0', sizeof data);
922 key.data = from;
923 key.size = strlen(from);
924 data.data = (char*) &when;
925 data.size = sizeof(when);
926 (void) (Db->smdb_put)(Db, &key, &data, 0);
930 ** XCLUDE --
931 ** add users to vacation db so they don't get a reply.
933 ** Parameters:
934 ** f -- file pointer with list of address to exclude
936 ** Returns:
937 ** nothing.
939 ** Side Effects:
940 ** stores users in database.
943 static void
944 xclude(f)
945 SM_FILE_T *f;
947 char buf[MAXLINE], *p;
949 if (f == NULL)
950 return;
951 while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf))
953 if ((p = strchr(buf, '\n')) != NULL)
954 *p = '\0';
955 setreply(buf, ONLY_ONCE);
960 ** SENDMESSAGE --
961 ** exec sendmail to send the vacation file to sender
963 ** Parameters:
964 ** myname -- user name.
965 ** msgfn -- name of file with vacation message.
966 ** sender -- use as sender address
968 ** Returns:
969 ** nothing.
971 ** Side Effects:
972 ** sends vacation reply.
975 static void
976 sendmessage(myname, msgfn, sender)
977 char *myname;
978 char *msgfn;
979 char *sender;
981 SM_FILE_T *mfp, *sfp;
982 int i;
983 int pvect[2];
984 char *pv[8];
985 char buf[MAXLINE];
987 mfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, msgfn, SM_IO_RDONLY, NULL);
988 if (mfp == NULL)
990 if (msgfn[0] == '/')
991 msglog(LOG_NOTICE, "vacation: no %s file.\n", msgfn);
992 else
993 msglog(LOG_NOTICE, "vacation: no ~%s/%s file.\n",
994 myname, msgfn);
995 exit(EX_NOINPUT);
997 if (pipe(pvect) < 0)
999 msglog(LOG_ERR, "vacation: pipe: %s", sm_errstring(errno));
1000 exit(EX_OSERR);
1002 pv[0] = "sendmail";
1003 pv[1] = "-oi";
1004 pv[2] = "-f";
1005 if (sender != NULL)
1006 pv[3] = sender;
1007 else
1008 pv[3] = myname;
1009 pv[4] = "--";
1010 pv[5] = From;
1011 pv[6] = NULL;
1012 i = fork();
1013 if (i < 0)
1015 msglog(LOG_ERR, "vacation: fork: %s", sm_errstring(errno));
1016 exit(EX_OSERR);
1018 if (i == 0)
1020 (void) dup2(pvect[0], 0);
1021 (void) close(pvect[0]);
1022 (void) close(pvect[1]);
1023 (void) sm_io_close(mfp, SM_TIME_DEFAULT);
1024 (void) execv(_PATH_SENDMAIL, pv);
1025 msglog(LOG_ERR, "vacation: can't exec %s: %s",
1026 _PATH_SENDMAIL, sm_errstring(errno));
1027 exit(EX_UNAVAILABLE);
1029 /* check return status of the following calls? XXX */
1030 (void) close(pvect[0]);
1031 if ((sfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
1032 (void *) &(pvect[1]),
1033 SM_IO_WRONLY, NULL)) != NULL)
1035 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT, "To: %s\n", From);
1036 (void) sm_io_fprintf(sfp, SM_TIME_DEFAULT,
1037 "Auto-Submitted: auto-replied\n");
1038 while (sm_io_fgets(mfp, SM_TIME_DEFAULT, buf, sizeof buf))
1039 (void) sm_io_fputs(sfp, SM_TIME_DEFAULT, buf);
1040 (void) sm_io_close(mfp, SM_TIME_DEFAULT);
1041 (void) sm_io_close(sfp, SM_TIME_DEFAULT);
1043 else
1045 (void) sm_io_close(mfp, SM_TIME_DEFAULT);
1046 msglog(LOG_ERR, "vacation: can't open pipe to sendmail");
1047 exit(EX_UNAVAILABLE);
1051 static void
1052 usage()
1054 msglog(LOG_NOTICE,
1055 "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",
1056 getuid());
1057 exit(EX_USAGE);
1061 ** LISTDB -- list the contents of the vacation database
1063 ** Parameters:
1064 ** none.
1066 ** Returns:
1067 ** nothing.
1070 static void
1071 listdb()
1073 int result;
1074 time_t t;
1075 SMDB_CURSOR *cursor = NULL;
1076 SMDB_DBENT db_key, db_value;
1078 memset(&db_key, '\0', sizeof db_key);
1079 memset(&db_value, '\0', sizeof db_value);
1081 result = Db->smdb_cursor(Db, &cursor, 0);
1082 if (result != SMDBE_OK)
1084 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1085 "vacation: set cursor: %s\n",
1086 sm_errstring(result));
1087 return;
1090 while ((result = cursor->smdbc_get(cursor, &db_key, &db_value,
1091 SMDB_CURSOR_GET_NEXT)) == SMDBE_OK)
1093 char *timestamp;
1095 /* skip magic VIT entry */
1096 if (db_key.size == strlen(VIT) + 1 &&
1097 strncmp((char *)db_key.data, VIT,
1098 (int)db_key.size - 1) == 0)
1099 continue;
1101 /* skip bogus values */
1102 if (db_value.size != sizeof t)
1104 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1105 "vacation: %.*s invalid time stamp\n",
1106 (int) db_key.size, (char *) db_key.data);
1107 continue;
1110 memcpy(&t, db_value.data, sizeof t);
1112 if (db_key.size > 40)
1113 db_key.size = 40;
1115 if (t <= 0)
1117 /* must be an exclude */
1118 timestamp = "(exclusion)\n";
1120 else
1122 timestamp = ctime(&t);
1124 sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%-40.*s %-10s",
1125 (int) db_key.size, (char *) db_key.data,
1126 timestamp);
1128 memset(&db_key, '\0', sizeof db_key);
1129 memset(&db_value, '\0', sizeof db_value);
1132 if (result != SMDBE_OK && result != SMDBE_LAST_ENTRY)
1134 sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
1135 "vacation: get value at cursor: %s\n",
1136 sm_errstring(result));
1137 if (cursor != NULL)
1139 (void) cursor->smdbc_close(cursor);
1140 cursor = NULL;
1142 return;
1144 (void) cursor->smdbc_close(cursor);
1145 cursor = NULL;
1149 ** DEBUGLOG -- write message to standard error
1151 ** Append a message to the standard error for the convenience of
1152 ** end-users debugging without access to the syslog messages.
1154 ** Parameters:
1155 ** i -- syslog log level
1156 ** fmt -- string format
1158 ** Returns:
1159 ** nothing.
1162 /*VARARGS2*/
1163 static SYSLOG_RET_T
1164 #ifdef __STDC__
1165 debuglog(int i, const char *fmt, ...)
1166 #else /* __STDC__ */
1167 debuglog(i, fmt, va_alist)
1168 int i;
1169 const char *fmt;
1170 va_dcl
1171 #endif /* __STDC__ */
1174 SM_VA_LOCAL_DECL
1176 SM_VA_START(ap, fmt);
1177 sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
1178 SM_VA_END(ap);
1179 SYSLOG_RET;