vacation.c revision d5e7c3fca16e5d67fa1a124aced8a4c587b01e1a
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
* All Rights Reserved
*/
/*
* Vacation
* Copyright (c) 1983 Eric P. Allman
* Berkeley, California
*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#ifndef lint
static char SccsId[] = "%W% %E% SMI";
#endif /* not lint */
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <sysexits.h>
#include <pwd.h>
#include <ndbm.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <strings.h>
#include <errno.h>
/*
* VACATION -- return a message to the sender when on vacation.
*
* This program could be invoked as a message receiver
* when someone is on vacation. It returns a message
* specified by the user to whoever sent the mail, taking
* care not to return a message too often to prevent
* "I am on vacation" loops.
*
* For best operation, this program should run setuid to
* root or uucp or someone else that sendmail will believe
* a -f flag from. Otherwise, the user must be careful
* to include a header on his .vacation.msg file.
*
* Positional Parameters:
* the user to collect the vacation message from.
*
* Flag Parameters:
* -I initialize the database.
* -d turn on debugging.
* -tT set the timeout to T. messages arriving more
* often than T will be ignored to avoid loops.
*
* Side Effects:
* A message is sent back to the sender.
*
* Author:
* Eric Allman
*/
#define MsgFile "/.vacation.msg"
#define FilterFile "/.vacation.filter"
#define DbFileBase "/.vacation"
#define _PATH_TMP "/tmp/vacation.XXXXXX"
typedef int bool;
#define FALSE 0
#define TRUE 1
static int AliasCount = 0;
static char *myname; /* name of person "on vacation" */
static char *homedir; /* home directory of said person */
extern bool decode_rfc2047(char *, char *, char *);
static bool ask(char *);
static bool junkmail(char *);
static bool filter_ok(char *, char *);
static bool knows(char *);
static bool sameword(char *, char *);
static char *getfrom(char **);
static char *newstr(char *);
static void AutoInstall();
static void initialize(char *);
static void sendmessage(char *, char *, char *);
static void setknows(char *);
void usrerr(const char *, ...);
int
int argc;
char **argv;
{
char *from;
char *p, *at, *c;
char *shortfrom;
char *message_file = MsgFile;
char *db_file_base = DbFileBase;
char *filter_file = FilterFile;
char *sender;
bool sender_oob = FALSE;
bool initialize_only = FALSE;
/* process arguments */
{
switch (*++p)
{
case 'a': /* add this to list of acceptable aliases */
if (argc > 0) {
}
break;
case 'd': /* debug */
break;
case 'e': /* alternate filter file */
if (argc > 0) {
}
break;
case 'f': /* alternate database file name base */
if (argc > 0) {
}
break;
case 'I': /* initialize */
break;
break;
case 'm': /* alternate message file */
if (argc > 0) {
}
break;
case 's': /* sender: use this instead of getfrom() */
sender_oob = TRUE;
if (argc > 0) {
}
break;
case 't': /* set timeout */
break;
default:
usrerr("Unknown flag -%s", p);
}
}
if (initialize_only)
{
}
/* verify recipient argument */
if (argc == 0)
AutoInstall();
if (argc != 1)
{
usrerr("Usage: vacation username (or) vacation -I");
}
myname = p;
Charset[0] = '\0';
/* find user's home directory */
{
usrerr("user %s look up failed, name services outage ?",
myname);
}
}
if (sender_oob)
{
for (c = at + 1; *c; c++)
*c = (char)tolower((char)*c);
}
else
/* read message from standard input (just from line) */
/* check if junk mail or this person is already informed */
{
/* mark this person as knowing */
/* send the message back */
if (message_file[0] != '/')
if (Debug)
else
{
/*NOTREACHED*/
}
}
return (EX_OK);
}
/*
* GETFROM -- read message from standard input and return sender
*
* Parameters:
* none.
*
* Returns:
* pointer to the sender address.
*
* Side Effects:
* Reads first line from standard input.
*/
static char *
char **shortp;
{
char saveat;
/* read the from line */
{
usrerr("No initial From line");
}
/* find the end of the sender address and terminate it */
if (p == NULL)
{
}
*p = '\0';
/*
* Strip all but the rightmost UUCP host
* to prevent loops due to forwarding.
* Start searching leftward from the leftmost '@'.
* a!b!c!d yields a short name of c!d
* a!b!c!d@e yields a short name of c!d@e
* e@a!b!c yields the same short name
*/
#ifdef VDEBUG
#endif /* VDEBUG */
at = p; /* if none, use end of addr */
*at = '\0';
char *bang2;
*bang = '\0';
/* 2nd rightmost '!' */
*bang = '!';
}
#ifdef VDEBUG
#endif /* VDEBUG */
for (c = at + 1; *c; c++)
*c = (char)tolower((char)*c);
/* return the sender address */
return (start);
}
/*
*
* Parameters:
* from -- the Return-Path of the sender. We assume that
* anything from "*-REQUEST@*" is bulk mail.
*
* Returns:
* TRUE -- if this is junk or bulk mail (that is, if the
* sender shouldn't receive a response).
* FALSE -- if the sender deserves a response.
*
* Side Effects:
* May read the header from standard input. When this
* returns the position on stdin is undefined.
*/
static bool
char *from;
{
register char *p;
/* test for inhuman sender */
if (p != NULL)
{
*p = '\0';
{
*p = '@';
return (TRUE);
}
*p = '@';
}
#define Delims " \n\t:,:;()<>@!"
/* read the header looking for "interesting" lines */
{
return (FALSE); /* no header found */
if (p == NULL)
continue;
{
if (p == NULL)
continue;
} else /* continuation line? */
if (inside)
if (inside) {
int i;
do {
for (i = 0; i < AliasCount; i++)
continue;
}
if (sameword(p, "Precedence"))
{
/* find the value of this field */
if (p == NULL)
continue;
/* see if it is "junk" or "bulk" */
p[4] = '\0';
return (TRUE);
}
if (sameword(p, "Subject"))
{
char *decoded_subject;
*p = '\0';
else
Charset[0] = '\0';
if (Debug)
}
}
if (AnswerAll)
return (FALSE);
else
return (!onlist);
}
/*
* FILTER_OK -- see if the Return-Path is in the filter file.
* Note that a non-existent filter file means everything
* is OK, but an empty file means nothing is OK.
*
* Parameters:
* from -- the Return-Path of the sender.
*
* Returns:
* TRUE -- if this is in the filter file
* (sender should receive a response).
* FALSE -- if the sender does not deserve a response.
*/
static bool
char *from;
char *filter_file;
{
char *match_start;
FILE *f;
if (filter_file[0] != '/')
if (f == NULL) {
/*
* If the file does not exist, then there is no filter to
* apply, so we simply return TRUE.
*/
if (Debug)
(void) printf("%s does not exist, filter ok.\n",
file);
return (TRUE);
}
/* zero out trailing newline */
/* skip blank lines */
if (line_len == 0)
continue;
/* skip comment lines */
if (line[0] == '#')
continue;
if (line[0] == '!') {
line_len--;
} else {
match_start = &line[0];
}
/* @ => full address */
if (Debug)
(void) printf("filter match on %s\n",
line);
break;
}
} else {
/* no @ => domain */
continue;
/*
* Make sure the last part of from is the domain line
* and that the character immediately preceding is an
* '@' or a '.', otherwise we could get false positives
*/
match_start, line_len) == 0 &&
if (Debug)
(void) printf("filter match on %s\n",
line);
break;
}
}
}
(void) fclose(f);
(void) printf("no filter match\n");
}
/*
* KNOWS -- predicate telling if user has already been informed.
*
* Parameters:
* user -- the user who sent this message.
*
* Returns:
* TRUE if 'user' has already been informed that the
* recipient is on vacation.
* FALSE otherwise.
*
* Side Effects:
* none.
*/
static bool
char *user;
{
return (FALSE);
return (FALSE);
if (Debug)
return (TRUE);
}
/*
* SETKNOWS -- set that this user knows about the vacation.
*
* Parameters:
* user -- the user who should be marked.
*
* Returns:
* none.
*
* Side Effects:
* The dbm file is updated as appropriate.
*/
static void
char *user;
{
}
static bool
char *line;
{
char *c;
for (c = line; *c; c++)
if (*c & 0x80)
return (TRUE);
return (FALSE);
}
/*
* SENDMESSAGE -- send a message to a particular user.
*
* Parameters:
* msgf -- filename containing the message.
* user -- user who should receive it.
*
* Returns:
* none.
*
* Side Effects:
*/
static void
char *msgf;
char *user;
char *myname;
{
char *p, *tmpf_name;
bool seen8bitchars = FALSE;
/* find the message to send */
if (f == NULL)
{
if (f == NULL)
usrerr("No message to send");
}
usrerr("pipe() failed");
}
i = fork();
if (i < 0) {
usrerr("fork() failed");
}
if (i == 0) {
fclose(f);
}
usrerr("fdopen() failed");
}
/*
* We used to write directly to the pipe. But now we need to know
* what character set to use, and we need to examine the entire
* message to determine this. So write to a temp file first.
*/
usrerr("newstr: cannot alloc memory");
}
tmpfd = -1;
if (tmpfd == -1) {
}
}
/*
* Check for a line with no ':' character. If it's just \n,
* we're at the end of the headers and all is fine. Or if
* it starts with white-space, then it's a continuation header.
* Otherwise, it's the start of the body, which means the
*/
if (line[0] == '\n')
}
}
*p = '\0';
if (Subject) {
if (in_header)
else {
}
}
continue;
}
}
fclose(f);
/*
* If we haven't seen a funky Subject with Charset, use the default.
* If we have and it's us-ascii, 8-bit chars in the message file will
* still result in iso-8859-1.
*/
if (Charset[0] == '\0')
"us-ascii", sizeof (Charset));
if (Debug)
/*
* Now read back in from the temp file and write to the pipe.
*/
}
}
/*
* INITIALIZE -- initialize the database before leaving for vacation
*
* Parameters:
* none.
*
* Returns:
* none.
*
* Side Effects:
* Initializes the files .vacation.{pag,dir} in the
* caller's home directory.
*/
static void
char *db_file_base;
{
char *homedir;
usrerr("No home!");
}
}
}
/*
* USRERR -- print user error
*
* Parameters:
* f -- format.
*
* Returns:
* none.
*
* Side Effects:
* none.
*/
/* PRINTFLIKE1 */
void
usrerr(const char *f, ...)
{
}
/*
* NEWSTR -- copy a string
*
* Parameters:
* s -- the string to copy.
*
* Returns:
* A copy of the string.
*
* Side Effects:
* none.
*/
static char *
newstr(s)
char *s;
{
char *p;
if (p == NULL)
{
usrerr("newstr: cannot alloc memory");
}
return (p);
}
/*
* SAMEWORD -- return TRUE if the words are the same
*
* Ignores case.
*
* Parameters:
* a, b -- the words to compare.
*
* Returns:
* TRUE if a & b match exactly (modulo case)
* FALSE otherwise.
*
* Side Effects:
* none.
*/
static bool
sameword(a, b)
register char *a, *b;
{
do
{
ca = *a++;
cb = *b++;
}
/*
* When invoked with no arguments, we fall into an automatic installation
* mode, stepping the user through a default installation.
*/
static void
{
char *editor;
FILE *f;
umask(022);
usrerr("User ID unknown");
}
usrerr("Out of memory");
}
usrerr("Home directory unknown");
}
printf("This program can be used to answer your mail automatically\n");
printf("when you go away on vacation.\n");
do {
if (f) {
if (ask("Would you like to see it")) {
}
if (ask("Would you like to edit it"))
f = NULL;
} else {
printf("You need to create a message file"
" in %s first.\n", file);
if (f == NULL) {
}
fprintf(f, "Subject: away from my mail\n");
fprintf(f, "\nI will not be reading my mail"
" for a while.\n");
fprintf(f, "Your mail regarding \"$SUBJECT\" will"
" be read when I return.\n");
fclose(f);
f = NULL;
}
if (f == NULL) {
file);
printf("Please use your editor (%s)"
" to edit this file.\n", editor);
}
} while (f == NULL);
fclose(f);
if (f) {
printf("You have a .forward file"
" in your home directory containing:\n");
fclose(f);
if (!ask("Would you like to remove it and"
" disable the vacation feature"))
perror("Error removing .forward file:");
else
printf("Back to normal reception of mail.\n");
}
printf("To enable the vacation feature"
" a \".forward\" file is created.\n");
if (!ask("Would you like to enable the vacation feature")) {
printf("OK, vacation feature NOT enabled.\n");
}
if (f == NULL) {
perror("Error opening .forward file");
}
fclose(f);
printf("Vacation feature ENABLED."
" Please remember to turn it off when\n");
printf("you get back from vacation. Bon voyage.\n");
}
/*
* Ask the user a question until we get a reasonable answer
*/
static bool
char *prompt;
{
char *res;
for (;;) {
return (FALSE);
return (TRUE);
return (FALSE);
printf("Please reply \"yes\" or \"no\" (\'y\' or \'n\')\n");
}
}