sortmail-2.4/0040755000470100007640000000000007771505217011647 5ustar falkfalksortmail-2.4/sortmail.c0100644000470100007640000012201207771504442013637 0ustar falkfalk#ifndef lint static const char sccsid[] = "@(#)sortmail.c 2.8 02/03/11 falk" ; static const char rcsid[] = "$Id: sortmail.c,v 1.12 2003/12/22 06:03:14 efalk Exp $" ; #endif #define VERSION "2.4" static char usage[] = "sortmail -- read and classify incoming mail\n\n" "Usage: sortmail [options] user\n" " -v verbose\n" " -terse same, no timestamps\n" " -home path set user's home dir\n" " -mailbox path set user's mailbox\n" " -mailrc path set .mailrc file\n" " -sortmailrc path set .sortmailrc file\n" " -inbox path input from specified mailbox\n" #ifdef TODO " -mbox input is a mailbox with multiple messages\n" #endif /* TODO */ " -pop user:pw@host read pop3 or pop2 mailbox\n" " -pop file same, get user:pw@host from file\n" " -imap user:pw@host read imap\n" " -pop3 user:pw@host read pop3\n" " -pop2 user:pw@host read pop2\n" " -keep leave all messages in inbox\n" " -range m n fetch only messages m-n (inclusive)\n" " -dumpCrcs path dump bouncecheck database\n" " -verify verify .mailrc, .sortmailrc and exit\n" " -version print version and exit\n" " var=value set a variable\n" ; /* Exit codes: * * 0 normal exit * 1 normal exit, but no mail waiting * 2 user error on command line * 3 error in initfiles * 4 out of memory * 5 cannot determine user name * 6 cannot determine user directory * 7 cannot open temporary file * 8 cannot open bounce-check database * 9 cannot open input file */ #include #include /* defines getenv(3) */ #include #include #ifdef __STDC__ #include #else #include #endif #include #include #include #include #include #include #include /* defines MAXPATHLEN */ #include #include "sortmail.h" #include "pop2.h" #include "pop3.h" #include "imap.h" #include "locking.h" #include "utils.h" #if BOUNCECHECK #ifdef LINUX #include #else #include #endif #include #include "crctab.h" #endif extern int errno ; FILE *logfile ; FILE *tempfile ; char *user ; char *home ; char *mailbox ; char *mailrc ; char *sortmailrc ; char *deflt ; char *reject ; char *from ; char *vacation ; char *folder ; char *logfilename ; char *tmpdir ; char *sendmail ; int verbose = 0 ; int terse = 0 ; int delayTime = 0 ; int exitCode ; long sizeLimit = 0 ; /* zero means no limit */ bool keepAll = False ; /* pop/imap: leave messages on server */ int range0 = 1; /* first message to fetch */ int range1 = INT_MAX ; /* last message to fetch */ bool noApop = False ; /* do not attempt apop */ int timeout = 10 ; /* seconds */ int maxlines = 5000 ; /* max lines to download */ static char *infilename = NULL ; Cmd *cmds = NULL, *lastcmd = NULL ; int headers = 0 ; static LockInfo lockStat = {NULL,-1} ; static char tmpfilename[256] ; static char *popBox ; static BoxType boxType ; #ifdef __STDC__ static bool argcheck( char *str, char *argv, int nargs, int *argc) ; static int fileGetMessage(MailMessage *msg, MsgState newstate ) ; static void processFileMessage() ; static int processPopAnyMessages() ; static int processPop2Messages(Pop2Folder *) ; static int processPop3Messages(Pop3Folder *) ; static int processImapMessages() ; static void parseMessageStart( MailMessage *msg) ; static void parseMessageFile( MailMessage *msg ); static void messageFree( MailMessage *msg) ; static void processMessage( MailMessage *msg) ; static void append_line( char *line, char **ptr) ; static bool include_search( MailMessage *msg, char *name, int lineno) ; static bool do_bounceCheck( IncludeInfo *info, MailMessage *msg) ; static void reset_headers() ; static void replace_header( char *line, FILE *tempfile) ; static void append_headers( FILE *tempfile) ; static char *get_data( char *ptr, char *buffer) ; static int copyFile( FILE *ifile, FILE *ofile, bool digest, MailMessage *); static void fromLine( FILE *ofile, MailMessage *) ; static int append( MailMessage *msg, char *outfile, bool digest) ; static int append_file( FILE *, char *, char *, bool, MailMessage *) ; static int put_pipe( MailMessage *, char *dest) ; static int forward( MailMessage *msg, char *dest) ; static void setupPop( BoxType type, char **argv) ; static void getUserPwHost( char **user, char **pw, char **host) ; static void do_dumpCrcs( char *name) ; static void sigHandler(int) ; static int pop3GetMessage(MailMessage *msg, MsgState newstate); #else static bool argcheck() ; static int fileGetMessage() ; static void processFileMessage() ; static int processPop2Messages() ; static int processPop3Messages() ; static int processImapMessages() ; static void processMessage() ; static void parseMessageStart() ; static void messageFree() ; static char *get_data() ; static int copyFile() ; static void fromLine() ; static int append() ; static int append_file() ; static int put_pipe() ; static int forward() ; static void append_line() ; static bool include_search() ; static void reset_headers() ; static void replace_header() ; static void append_headers() ; static void do_dumpCrcs() ; static bool do_bounceCheck() ; static void do_dumpCrcs() ; static void getUserPwHost() ; static void setupPop() ; static void sigHandler() ; #endif extern void read_initfiles() ; #ifndef max #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #endif int main(int argc, char **argv) { bool mbox = False ; bool verifyOnly = False ; char *dumpCrcs = NULL ; boxType = MSG_FILE ; /* read commandline arguments. These override environment variables */ while( --argc > 0 ) { ++argv ; if( argcheck("-v", *argv, 0, &argc) ) ++verbose ; else if( argcheck("-terse", *argv, 0, &argc) ) verbose = terse = 1 ; else if( argcheck("-home", *argv, 1, &argc) ) setenv("HOME", *++argv,1) ; else if( argcheck("-mailbox", *argv, 1, &argc) ) setenv("mailbox", *++argv,1) ; else if( argcheck("-mailrc", *argv, 1, &argc) ) setenv("mailrc", *++argv,1) ; else if( argcheck("-sortmailrc", *argv, 1, &argc) ) setenv("sortmailrc", *++argv,1) ; else if( argcheck("-dumpCrcs", *argv, 1, &argc) ) dumpCrcs = *++argv ; else if( argcheck("-inbox", *argv, 1, &argc) ) infilename = *++argv ; else if( argcheck("-mbox", *argv, 0, &argc) ) mbox = True ; else if( argcheck("-verify", *argv, 0, &argc) ) verifyOnly = True ; else if( argcheck("-pop", *argv, 1, &argc) ) setupPop(POP_ANY, ++argv) ; else if( argcheck("-imap", *argv, 1, &argc) ) setupPop(IMAP, ++argv) ; else if( argcheck("-pop3", *argv, 1, &argc) ) setupPop(POP3, ++argv) ; else if( argcheck("-pop2", *argv, 1, &argc) ) setupPop(POP2, ++argv) ; else if( argcheck("-noapop", *argv, 0, &argc) ) noApop = True ; else if( argcheck("-keep", *argv, 0, &argc) ) keepAll = True ; else if( argcheck("-range", *argv, 2, &argc) ) { range0 = atoi(*++argv); range1 = atoi(*++argv); } else if( argcheck("-help", *argv, 0, &argc) || argcheck("--help", *argv, 0, &argc) || argcheck("-q", *argv, 0, &argc) || argcheck("-h", *argv, 0, &argc) ) { fprintf(stderr, usage) ; exit(USER_ERR) ; } else if( argcheck("-version", *argv, 0, &argc) ) { printf("Sortmail %s\n", VERSION) ; exit(EXIT_OK) ; } else if( **argv == '-' ) { fprintf(stderr, "Argument %s not recognized. Use -help for help\n", *argv) ; exit(USER_ERR) ; } else if( strchr(*argv,'=') != NULL ) putenv(*argv) ; else setenv("user", user = *argv,1) ; } user = getenv("user") ; home = getenv("HOME") ; mailbox = getenv("mailbox") ; folder = getenv("folder") ; mailrc = getenv("mailrc") ; sortmailrc = getenv("sortmailrc") ; logfilename = getenv("logfile") ; if( dumpCrcs != NULL ) do_dumpCrcs(dumpCrcs) ; logfile = stderr ; if( logfilename == NULL && !isatty(2) ) logfilename = "/tmp/sortmail.log" ; if( logfilename != NULL ) { if( (logfile = pathOpen(logfilename,"a")) == NULL ) logfile = stderr ; } #ifdef DEBUG ++verbose ; #endif /* DEBUG */ logFilev(2, "uid=%d, euid=%d\n", getuid(), geteuid()) ; /* Next, read .mailrc and .sortmailrc. Environment variables * and commandline settings override these. */ read_initfiles() ; if( verifyOnly ) exit(EXIT_OK) ; exitCode = 0 ; /* catch signals */ (void) signal(SIGHUP, sigHandler) ; (void) signal(SIGINT, sigHandler) ; (void) signal(SIGQUIT, sigHandler) ; (void) signal(SIGTERM, sigHandler) ; /* Read mail message. */ /* TODO: handle multiple messages, POP boxes, etc.. */ switch( boxType ) { case MSG_FILE: processFileMessage() ; break ; case POP_ANY: exitCode = processPopAnyMessages() ; break ; case POP2: exitCode = processPop2Messages(NULL) ; break ; case POP3: exitCode = processPop3Messages(NULL) ; break ; case IMAP: exitCode = processImapMessages() ; break ; } exit(exitCode) ; } static bool argcheck(char *str, char *argv, int nargs, int *argc) { if( strcmp(str, argv) != 0 ) return False ; if( (*argc -= nargs) <= 0 ) die(2, "%s option requires %d arguments\n", str, nargs) ; return True ; } /* read one message, process it */ static void processFileMessage() { MailMessage msg ; int rval ; FILE *infile = stdin ; long fpos ; reset_headers() ; parseMessageStart(&msg) ; msg.type = MSG_FILE ; msg.n = 1 ; if( infilename != NULL ) { if( (infile=pathOpen(infilename,"r")) == NULL ) { logFile ("cannot open %s\n", infilename, strerror(errno)) ; exit( INPUT_ERR ) ; } } if( infilename == NULL || (fpos = ftell(infile)) < 0 || fseek(infile, 0L, SEEK_SET) < 0 ) { /* Open temporary file. If this fails, try to rescue the situation * by copying the mail to the user's mailbox. */ #ifdef COMMENT sprintf(tmpfilename, "%s/sortmail%d", tmpdir, getpid()) ; #endif /* COMMENT */ msg.name = tmpfilename ; if( (msg.file = tmpOpen(tmpfilename)) == NULL ) { fprintf(stderr, "sortmail: can't open %s, mail to %s may be lost\n", tmpfilename, user) ; logFile("can't open %s, %s\n", tmpfilename, strerror(errno)); /* try to rescue situtation */ append_file(infile, "unknown", mailbox, False, &msg) ; exit(TMPFILE_ERR) ; } msg.infile = infile ; msg.tmpfile = True ; } else { msg.name = infilename ; msg.file = infile ; msg.infile = NULL ; msg.tmpfile = False ; } if( (rval = getMessage(&msg, HAVE_HEADER)) != EXIT_OK ) exit(rval) ; processMessage(&msg) ; messageFree(&msg) ; } /* Get one message or part. Parse headers while we're at it. */ /* TODO: read messages from folders */ int getMessage( MailMessage *msg, MsgState newstate ) { if( msg->state >= newstate ) return EXIT_OK ; assert(msg->type != POP_ANY) ; switch( msg->type ) { case MSG_FILE: return fileGetMessage(msg, newstate) ; case IMAP: return imapGetMessage(msg, newstate) ; case POP3: return pop3GetMessage(msg, newstate) ; case POP2: return pop2GetMessage(msg, newstate) ; default: break ; } return EXIT_OK ; } /*--------------------------------------------------------------------------- * read enough of the message to parse the headers or to * bring the message state up to newstate (or better). */ static int fileGetMessage(MailMessage *msg, MsgState newstate) { char line[MAXLINE] ; FILE *infile ; FILE *ofile ; if( msg->state >= newstate ) return EXIT_OK ; if( msg->tmpfile ) { infile = msg->infile ; ofile = msg->file ; (void) fseek(ofile, 0L, SEEK_END) ; } else { infile = msg->file ; ofile = NULL ; (void) fseek(infile, 0L, SEEK_SET) ; } /* Copy input to tmp file. * Scan for key headers lines while at it. * Ignore case due to brain-damaged mailers. */ if( msg->state < HAVE_HEADER ) { while( msg->inheader && fgets(line, sizeof(line), infile) != NULL ) { ++msg->lines ; parseMessage(msg, line) ; if( ofile != NULL ) fputs(line, ofile) ; } parseMessageDone(msg) ; msg->state = HAVE_HEADER ; } if( !msg->tmpfile ) { msg->state = HAVE_WHOLE ; return EXIT_OK ; } /* read some more? */ if( newstate > HAVE_HEADER ) { while( (newstate == HAVE_WHOLE || msg->lines < maxlines) && fgets(line, sizeof(line), infile) != NULL ) { ++msg->lines ; fputs(line, ofile) ; } } msg->state = newstate ; if( fseek(msg->file, 0L, SEEK_END) != 0 ) { logFilev(1, "seek on file %s failed, %s\n", msg->name, strerror(errno) ) ; msg->size = msg->lines*80 ; /* fake it */ } else if( (msg->size = ftell(msg->file)) < 0 ) msg->size = msg->lines*80 ; /* fake it */ return EXIT_OK ; } static int pop3GetMessage(MailMessage *msg, MsgState newstate) { Pop3Folder *p3 = msg->popinfo; switch( newstate ) { case HAVE_NONE: return EXIT_OK; case HAVE_HEADER: rewind(msg->file); switch( Pop3ReadHeader(p3, msg->file, msg->n, p3->timeout) ) { case POP3_OK: msg->state = HAVE_HEADER; return EXIT_OK; case POP3_TIMEOUT: return msg->error = POP_TIMEOUT; default: return msg->error = POP_ERR; } default: rewind(msg->file); switch( Pop3ReadMessage(p3, msg->file, msg->n, p3->timeout) ) { case POP3_OK: msg->state = HAVE_WHOLE; return EXIT_OK; case POP3_TIMEOUT: return msg->error = POP_TIMEOUT; default: return msg->error = POP_ERR; } } } /* Open pop mailbox, read & process all messages. */ static int processPopAnyMessages() { char *user ; char *pw ; char *hostname ; Pop3Folder *pop3 ; Pop2Folder *pop2 ; getUserPwHost(&user, &pw, &hostname) ; if( hostname == NULL ) { logFile("-pop option: hostname required; -help for help\n") ; return USER_ERR ; } if( pw == NULL ) { logFile("-pop option: password required; -help for help\n") ; return USER_ERR ; } /* TODO: try imap first */ /* try pop3 first */ if( infilename == NULL && (pop3 = Pop3Open(hostname, user, pw, max(timeout,30), noApop)) != NULL ) return processPop3Messages(pop3) ; if( (pop2 = openPop2(hostname, user, pw, max(timeout,30))) != NULL ) return processPop2Messages(pop2) ; return POP_ERR ; } /* Open pop2 mailbox, read & process all messages. */ static int processPop2Messages(Pop2Folder *popinfo) { char *user ; char *pw ; char *hostname ; int rval ; int i ; MailMessage msg ; if( popinfo == NULL ) { getUserPwHost(&user, &pw, &hostname) ; if( hostname == NULL ) { logFile("-pop2 option: hostname required; -help for help\n") ; return USER_ERR ; } if( pw == NULL ) { logFile("-pop2 option: password required; -help for help\n") ; return USER_ERR ; } if( (popinfo = openPop2(hostname,user,pw, max(timeout,30))) == NULL ) return POP_ERR ; } popinfo->timeout = timeout ; msg.popinfo = (void *)popinfo ; if( infilename != NULL && (rval = pop2Folder(popinfo, infilename, timeout)) != EXIT_OK ) return rval ; range0 = max(range0, 1); range1 = min(range1, popinfo->nmsg); for(i=range0; i<=range1; ++i) { reset_headers() ; parseMessageStart(&msg) ; msg.n = i ; if( (rval = pop2GetMessage(&msg, HAVE_HEADER)) != EXIT_OK ) return rval ; processMessage(&msg) ; if( (rval = pop2FinishMessage(popinfo, msg.keep,timeout)) != EXIT_OK) return rval ; messageFree(&msg) ; } closePop2(popinfo) ; return EXIT_OK ; } /* Open pop3 mailbox, read & process all messages. */ static int processPop3Messages( Pop3Folder *popinfo ) { char *user ; char *pw ; char *hostname ; int rval ; int i ; MailMessage msg ; char tmpfilename[MAXPATHLEN]; FILE *tmpfile; if( popinfo == NULL ) { getUserPwHost(&user, &pw, &hostname) ; if( hostname == NULL ) { logFile("-pop3 option: hostname required; -help for help\n") ; return USER_ERR ; } if( pw == NULL ) { logFile("-pop3 option: password required; -help for help\n") ; return USER_ERR ; } if( infilename != NULL ) { logFile("-pop3: inbox may not be specified; -help for help\n") ; return USER_ERR ; } if( (popinfo = Pop3Open(hostname,user,pw, max(timeout,30), noApop)) == NULL ) return POP_ERR ; } msg.popinfo = (void *)popinfo ; /* Start reading and processing messages in the mailbox. Read only * the header to start with; the rest of the message may be read * during processing. If an error occurs during processing, a * flag will be set in the msg structure and we abort the loop. */ range0 = max(range0, 1); range1 = min(range1, popinfo->nmsg); for(i=range0; i<=range1; ++i) { reset_headers() ; parseMessageStart(&msg) ; if( (tmpfile = tmpOpen(tmpfilename)) == NULL ) { logFile("pop3 fatal error creating temporary file %s: %s\n", tmpfilename, strerror(errno)); Pop3Close(popinfo); return TMPFILE_ERR ; } msg.type = POP3; msg.tmpfile = True ; msg.file = tmpfile; msg.name = tmpfilename; msg.n = i ; msg.error = EXIT_OK ; #ifdef COMMENT if( (rval = Pop3ReadMessage( popinfo, msg.file, i, popinfo->timeout)) != POP3_OK ) #endif /* COMMENT */ if( (rval = pop3GetMessage( &msg, HAVE_HEADER )) != EXIT_OK ) { if( rval == POP_TIMEOUT ) logFile("Timeout while attempting to read message %d\n", i); return rval ; } parseMessageFile(&msg) ; processMessage(&msg) ; if( (rval = msg.error) != EXIT_OK ) { if( rval == POP_TIMEOUT ) logFile("Timeout while attempting to read message %d\n", i); return rval ; } if( !msg.keep ) Pop3DeleteMessage(popinfo, msg.n, popinfo->timeout) ; messageFree(&msg) ; } Pop3Close(popinfo) ; return EXIT_OK ; } /* Open Imap mailbox, read & process all messages. */ static int processImapMessages() { char *user ; char *pw ; char *hostname ; ImapFolder *imapinfo ; int rval ; MailMessage msg ; getUserPwHost(&user, &pw, &hostname) ; if( hostname == NULL ) { logFile("-imap option: hostname required; -help for help\n") ; return USER_ERR ; } if((imapinfo = openImap(hostname, user, pw, max(timeout,30))) == NULL) return POP_ERR ; imapinfo->timeout = timeout ; msg.popinfo = (void *)imapinfo ; if( infilename != NULL && (rval = imapFolder(imapinfo, infilename)) != EXIT_OK ) return rval ; else if( (rval = imapFolder(imapinfo, "INBOX")) != EXIT_OK ) return rval ; #ifdef COMMENT for(i=1; i<=imapinfo->nmsg; ++i) { reset_headers() ; parseMessageStart(&msg) ; if( (rval = imapGetMessage(&msg, HAVE_HEADER)) != EXIT_OK ) return rval ; processMessage(&msg) ; if( !msg.keep ) imapDeleteMessage(imapinfo, &msg, timeout) ; messageFree(&msg) ; } #endif /* COMMENT */ closeImap(imapinfo) ; return EXIT_OK ; } /* get ready to parse a message */ static void parseMessageStart(MailMessage *msg) { msg->name = NULL ; msg->file = NULL ; msg->tmpfile = False ; msg->n = 0 ; msg->start = 0 ; msg->size = 0 ; msg->lines = 0 ; msg->state = HAVE_NONE ; msg->fromaddr = msg->fromline = msg->toline = msg->subjectline = msg->msgid = NULL ; msg->keep = keepAll ; msg->keepGoing = False ; msg->disposed = False ; msg->inheader = True ; msg->scanning = NONE ; } /*--------------------------------------------------------------------------- * Examine one line of a message for important header info * Ignore case due to brain-damaged mailers. */ void parseMessage( MailMessage *msg, char *line ) { char *ptr ; if( msg->inheader ) { if( strmatch(line, "From ") ) { append_line(line, &msg->fromaddr) ; if( (ptr = strchr(msg->fromaddr,' ')) != NULL ) *ptr = '\0' ; else if( (ptr = strchr(msg->fromaddr,'\n')) != NULL ) *ptr = '\0' ; } else if( strcmatch(line, "subject: ") ) { msg->scanning = Subject ; append_line(line, &msg->subjectline) ; } else if( strcmatch(line, "from: ") ) { msg->scanning = From ; append_line(line, &msg->fromline) ; } else if( strcmatch(line, "to: ") || strcmatch(line, "cc: ") || strcmatch(line, "apparently-to: ") ) { msg->scanning = To ; append_line(line, &msg->toline) ; } else if( strcmatch(line, "message-id: ") ) { append_line(line, &msg->msgid) ; } else if( strspn(line," \t\n") == strlen(line) ) { msg->inheader = False ; line[0] = '\n' ; } else if( isspace(line[0]) ) { switch(msg->scanning) { case Subject: append_line(line, &msg->subjectline) ; break ; case From: append_line(line, &msg->fromline) ; break ; case To: append_line(line, &msg->toline) ; break ; case NONE: break ; } } else msg->scanning = NONE ; } } /*--------------------------------------------------------------------------- * Examine message file for important header info */ static void parseMessageFile( MailMessage *msg ) { char line[MAXLINE] ; FILE *infile = msg->file; if( msg->state < HAVE_HEADER || infile == NULL ) return ; rewind(infile); /* * Scan for key headers lines. * Ignore case due to brain-damaged mailers. */ while( msg->inheader && fgets(line, sizeof(line), infile) != NULL ) parseMessage(msg, line) ; parseMessageDone(msg) ; } void parseMessageDone(MailMessage *msg) { char *ptr ; fflush(msg->file) ; if( msg->msgid == NULL ) { msg->msgid = strdup("") ; logFilev(1, "message received in %s with no id\n", msg->name) ; } else if( (ptr = strchr(msg->msgid,'\n')) != NULL ) *ptr = '\0' ; logFilev(2, "got message %s in %s\n", msg->msgid, msg->name) ; } /* free resources associated with message */ static void messageFree(MailMessage *msg) { if( msg->file != NULL ) fclose(msg->file) ; if( msg->fromaddr != NULL ) free(msg->fromaddr) ; if( msg->fromline != NULL ) free(msg->fromline) ; if( msg->toline != NULL ) free(msg->toline) ; if( msg->subjectline != NULL ) free(msg->subjectline) ; if( msg->msgid != NULL ) free(msg->msgid) ; if( verbose < 3 && msg->tmpfile ) unlink(msg->name) ; } /* Process one message. Handle all commands in the command * list. Stop when a pattern or expression matches (and does not * contain the :c command. */ static void processMessage(MailMessage *msg) { ExprInfo *expr ; Cmd *cmd ; bool needInclude, foundInclude ; char line[MAXLINE] ; static char sizeStr[20], linesStr[20] ; int rval ; sprintf(sizeStr, "size=%ld", msg->size ) ; putenv(sizeStr) ; sprintf(linesStr, "lines=%d", msg->lines ) ; putenv(linesStr) ; needInclude = False ; foundInclude = False ; msg->disposed = False ; msg->keepGoing = False ; /* Start applying commands until a disposition of the file is found */ for(cmd = cmds; !msg->disposed && cmd != NULL; cmd = cmd->next) { switch( cmd->type ) { case EXPRESSION: /* expressions and patterns are the same thing now, but we * print a little extra about expressions. */ logFilev(3, "testing expression line %d\n", cmd->lineno); /* fall through */ case PATTERN: case IPPATTERN: expr = (ExprInfo *)cmd ; msg->keepGoing = False ; msg->disposed = ExpressionEvaluate(expr, msg) && !msg->keepGoing ; break ; case INCLUDE: case EXCLUDE: if( reject == NULL ) logFile("$reject must be set to use include/exclude\n") ; else if( msg->fromline == NULL ) { dispose(reject, msg) ; msg->disposed = True ; } else if( cmd->type == EXCLUDE ) { IncludeInfo *info = (IncludeInfo *)cmd ; if( include_search(msg, info->filename, cmd->lineno) ) { dispose(reject, msg) ; msg->disposed = True ; } } else if( !foundInclude ) { IncludeInfo *info = (IncludeInfo *)cmd ; needInclude = True ; if( include_search(msg, info->filename, cmd->lineno) ) foundInclude = True ; } break ; case BCHECK: msg->disposed = do_bounceCheck((IncludeInfo *)cmd, msg) ; break ; case HEADER: case REPLACE: ((HeaderInfo *)cmd)->active = True ; break ; case VAR: /* handled elsewhere */ break ; } } #ifdef VACATION /* if vacation is set, and the mail is to the user, pass it through * the vacation program. (Also check length of user name to * guard against buffer overflow exploits) */ if( vacation != NULL && msg->toline != NULL && strlen(user) <= MAXLINE-200 ) { regex_t regex ; if( !regcomp(®ex, user, 0) ) { if( regexec(®ex, msg->toline, 0,NULL, 0) == 0 ) { rewind(msg->file) ; sprintf(line, "%s %s", VACATION, user) ; /* TODO: fetch rest of message from POP or IMAP server */ rval = put_pipe(msg, line) ; logFilev(1, "message %s piped through %s\n", msg->msgid, line) ; } regfree(®ex) ; } } #endif /* VACATION */ if( !msg->disposed ) { logFilev(2, "message did not match\n") ; if( needInclude && !foundInclude ) dispose(reject, msg) ; else dispose(deflt != NULL ? deflt : "m", msg) ; } } /* append line (minus header) to the string in *ptr, if any */ static void append_line(char *line, char **ptr) { char *tmp ; for(; !isspace(*line) && *line != '\0'; ++line) ; for(; isspace(*line); ++line) ; if( *line == '\0' ) return ; if( *ptr == NULL ) { tmp = strdup(line) ; } else { tmp = malloc( strlen(*ptr) + strlen(line) + 2 ) ; strcpy(tmp,*ptr) ; strcat(tmp," ") ; strcat(tmp,line) ; free(*ptr) ; } *ptr = tmp ; } /* search named file for any lines that match the fromline. Since * these are internet addresses, look for anything between "<>" */ static bool include_search(MailMessage *msg, char *name, int lineno) { FILE *file ; char line[MAXPATHLEN] ; bool found = False ; char *ptr, *eptr ; regex_t regex ; if( msg->fromline == NULL ) return False ; if( (file = pathOpen(name,"r")) == NULL ) logFile("cannot open %s, %s\n", line, strerror(errno)) ; else { while( !found && fgets(line, sizeof(line), file) != NULL ) { if( line[0] != '#' && line[0] != '\n' ) { if( (ptr=strchr(line,'\n')) != NULL ) *ptr = '\0' ; if( (ptr=strchr(line,'<')) != NULL && (eptr=strchr(++ptr,'>')) != NULL && ptr != eptr ) { memcpy(line,ptr,eptr-ptr) ; line[eptr-ptr] = '\0' ; } if( !regcomp(®ex, line, 0) ) { if( regexec(®ex, msg->fromline, 0,NULL, 0) == 0 ) { found = True ; break ; } regfree(®ex) ; } } } fclose(file) ; } return found ; } /* Compute CRC32 of input file, compare to database of previous * CRC32's. Database contains entries keyed by CRC32, and containing * the date of the mail. The magic entry 0 contains the date the * database was last cleaned. On the infinitesimal chance that * a file with CRC 0 actually occurs, we bump it to 1. * * Any CRC value that has been seen within the last 60 days is * likely a bounce. */ static bool do_bounceCheck(IncludeInfo *info, MailMessage *msg) { bool rval = False ; #if BOUNCECHECK char *name = info->filename ; DBM *db ; char line[MAXPATHLEN] ; int inheader ; u_long crc ; int c ; time_t today ; datum key, value ; for(; isspace(*name); ++name); filename_expand(line, name) ; db = dbm_open(line, O_RDWR|O_CREAT, 0600) ; if( db == NULL ) { logFile("can't open \"%s\", %s\n", line, strerror(errno)); return False ; } if( reject == NULL ) { logFile("line %d: $reject must be set to use bouncecheck\n", info->c.lineno) ; return False ; } crc = 0xFFFFFFFF ; /* Scan message for alphabetic characters in body */ rewind(msg->file) ; inheader = True ; while( inheader && fgets(line, sizeof(line), msg->file) != NULL ) { if( line[0] == '\n' ) inheader = False ; } while( (c = getc(msg->file)) != EOF ) if( isalpha(c) ) crc = UPDC32(toupper(c), crc) ; if( crc == 0 ) crc = 1 ; logFilev(3, "message crc = %x\n", crc) ; today = time(NULL) ; key.dptr = (char *)&crc ; key.dsize = sizeof(crc) ; value = dbm_fetch(db, key) ; if( value.dptr != NULL ) /* probable bounce */ { logFilev(1, "message %s seen before\n", msg->msgid) ; dispose(reject, msg) ; rval = True ; } value.dptr = (char *)&today ; value.dsize = sizeof(today) ; dbm_store(db, key, value, DBM_REPLACE) ; /* while we're here, let's cull old db entries to keep the * size down */ crc = 0 ; value = dbm_fetch(db, key) ; if( value.dptr == NULL || *(int *)value.dptr < today - 60*24*60*60 ) { for(key=dbm_firstkey(db); key.dptr != NULL; key = dbm_nextkey(db)) { value = dbm_fetch(db, key) ; logFilev(3, "crc %x seen %d\n", *(int *)key.dptr, *(int *)value.dptr) ; if( *(int *)value.dptr < today - 60*24*60*60 ) { dbm_delete(db, key) ; } } key.dptr = (char *)&crc ; key.dsize = sizeof(crc) ; value.dptr = (char *)&today ; value.dsize = sizeof(today) ; dbm_store(db, key, value, DBM_REPLACE) ; } dbm_close(db) ; #endif return rval ; } static void reset_headers() { Cmd *cmd ; for(cmd = cmds; cmd != NULL; cmd = cmd->next) if( cmd->type == HEADER || cmd->type == REPLACE ) ((HeaderInfo *)cmd)->active = False ; } /* search headers list and compare to this line to see if this * line should be replaced. If not, just write it out unchanged */ static void replace_header(char *line, FILE *tempfile) { Cmd *cmd ; HeaderInfo *info ; char *ptr ; for(cmd = cmds; cmd != NULL; cmd = cmd->next) { if( cmd->type == REPLACE ) { info = (HeaderInfo *)cmd ; if( info->active && strncmp(line, info->header, info->len) == 0 ) { /* see if there's anything to replace it with */ for(ptr=info->header+info->len; isspace(*ptr); ++ptr); if( *ptr != '\0' ) { fputs(info->header, tempfile) ; putc('\n', tempfile) ; } info->done = True ; return ; } } } fputs(line, tempfile) ; } /* search headers list for any lines that have not yet been * written and write them now. */ static void append_headers(FILE *tempfile) { Cmd *cmd ; HeaderInfo *info ; for(cmd = cmds; cmd != NULL; cmd = cmd->next) { if( cmd->type == HEADER || cmd->type == REPLACE ) { info = (HeaderInfo *)cmd ; if( info->active && !info->done ) { fputs(info->header, tempfile) ; putc('\n', tempfile) ; info->done = True ; } } } } /* This function deals with the file described by 'file' in * the manner described by "command". * Most commands need the entire message in order to process * it. These will fetch the entire message. * TODO: gate directly from input to output? */ void dispose(register char *command, register MailMessage *msg) { char outname[MAXPATHLEN] ; int dt = delayTime ; int rval ; for(;;) { switch( *command ) { case '\0': case '\n': return ; case 'm': /* mail */ getMessage(msg, HAVE_WHOLE) ; command = get_data(++command, outname) ; if( outname[0] == '\0' ) { if( append(msg, mailbox, False) != 0 ) msg->keep = 1 ; } else { if( dt > 0 ) { sleep(dt) ; dt = 0 ; } if( forward(msg, outname) != 0 ) msg->keep = 1 ; } break ; case 'j': /* junk */ logFilev(1, "message %s deleted\n", msg->msgid) ; ++command ; break ; case 'e': /* set exit code */ command = get_data(++command, outname) ; exitCode = atoi(outname) ; logFilev(1, "exit code %d\n", exitCode) ; break ; case 'E': /* exit */ command = get_data(++command, outname) ; exitCode = atoi(outname) ; logFilev(1, "exit %d\n", exitCode) ; exit(exitCode) ; break ; case 'k': /* keep */ msg->keep = True ; ++command ; break ; case 'c': /* continue */ msg->keepGoing = True ; ++command ; break ; case 'f': /* append to file */ getMessage(msg, HAVE_WHOLE) ; command = get_data(++command, outname) ; if( append(msg, outname, False) != 0 ) msg->keep = 1 ; break ; case 'a': /* append to archive */ case 'd': /* append to digest */ getMessage(msg, HAVE_WHOLE) ; command = get_data(++command, outname) ; if( append(msg, outname, True) != 0 ) msg->keep = 1 ; break ; case '+': /* append to mailbox */ case '/': getMessage(msg, HAVE_WHOLE) ; command = get_data(command, outname) ; if( append(msg, outname, False) != 0 ) msg->keep = 1 ; break ; case '|': /* pipe through command */ getMessage(msg, HAVE_WHOLE) ; if( dt > 0 ) { sleep(dt) ; dt = 0 ; } command = get_data(++command, outname) ; setenv("FROM", msg->fromline == NULL ? "" : msg->fromline, 1) ; setenv("SUBJECT", msg->subjectline == NULL ? "" : msg->subjectline, 1) ; rval = put_pipe(msg, outname) ; unsetenv("SUBJECT") ; unsetenv("FROM") ; if( rval != 0 ) { logFile("unable to pipe message %s through %s, errno = %d\n", msg->msgid, outname, errno); msg->keep = 1 ; } else logFilev(1,"message %s piped through %s\n", msg->msgid,outname); break ; default: /* ignore anything else */ ++command ; break ; } } } /* Get the command pointed to by pointer. Delete leading * blanks, store characters in buffer, stop when EOL or ':' * reached. */ static char * get_data(char *ptr, char *buffer) { while(isspace(*ptr)) ++ptr ; for(;;) { switch( *ptr ) { case ':': *buffer = '\0' ; return ++ptr ; case '\0': case '\n': *buffer = '\0' ; return ptr ; case '\\': ++ptr ; if( *ptr == '\0' || *ptr == '\n' ) { *buffer = '\0' ; return ptr ; } default: *buffer++ = *ptr++ ; } } } /* this copies the tempfile to the output file or pipe, * replacing headers as it goes. */ static int copyFile(FILE *ifile, FILE *ofile, bool digest, MailMessage *msg) { bool firstline, inheader ; char line[1024] ; bool digestAndHeader = digest ; firstline = True ; inheader = True ; while( fgets(line, sizeof(line), ifile) != NULL ) { /* messages are supposed to start with "From " */ /* Note: the format of this line is amazingly picky: * sprintf(mbx, "From %s@%s %s", user,host,ctime(time(0))) ; */ /* TODO: synthesize this from From: and Date: lines in header */ if( firstline && !strmatch(line, "From ") ) fromLine( ofile, msg ) ; if( inheader && strspn(line," \t\n") == strlen(line) ) { inheader = False ; digestAndHeader = False ; /* TODO: make sure this is right */ if( headers ) append_headers(ofile) ; } /* other "From " lines are indented. */ if( !firstline && strmatch(line, "From ") ) putc('>', ofile) ; if( digest && strmatch(line, "-----------") ) putc(' ', ofile) ; if( !digestAndHeader || strmatch(line, "Subject: ") || strmatch(line, "From: ") || strmatch(line, "Date: ") ) { if( inheader && headers ) replace_header(line, ofile) ; else if( fputs(line, ofile) == EOF ) return -1 ; } firstline = False ; } fputc('\n', ofile) ; return 0 ; } /* Transmit a "From " line, fabricate if needed */ static void fromLine( FILE *ofile, MailMessage *msg ) { char *from = msg->fromline ; char *start, *end, *at ; time_t t ; char *ct ; /* Format is: "From user@host Tue Sep dd hh:mm:ss yyyy" */ if( msg->fromaddr != NULL ) { fputs(msg->fromaddr, ofile) ; return ; } /* OK, need to synthesize one */ fprintf(ofile, "From ") ; if( from != NULL ) { if( (start = strchr(from, '<')) != NULL ) { ++start ; if( (end = strchr(start+1, '>')) != NULL && end > start && (at = strchr(start+1, '@')) != NULL && at > start && at < end ) { /* ok, we have the "" part */ fwrite(start, 1, end-start, ofile) ; } else /* '<' but no '>', this is too screwed up to continue */ fprintf(ofile, "stupid_mailer@stupid_vendor") ; } else if( strchr(from, '@') != NULL && strchr(from, ' ') == NULL ) { /* Looks valid, let's write it out. */ if( (end = strchr(from, '\n')) == NULL ) end = from + strlen(from) ; fwrite(from, 1, end-from, ofile) ; } else fprintf(ofile, "stupid_mailer@stupid_vendor") ; } else fprintf(ofile, "stupid_mailer@stupid_vendor") ; /* Trying to parse the date is far too risky. Let's just * use today's date. The ctime() function already generates * the right format. */ t = time(NULL) ; ct = ctime(&t) ; fputc(' ', ofile) ; fputs(ct, ofile) ; } /* TODO: handle message types other than MSG_FILE */ static int append(MailMessage *msg, char *outfile, bool digest) { return append_file(msg->file, msg->msgid, outfile, digest, msg) ; } /* TODO: handle message types other than MSG_FILE */ static int append_file(FILE *file, char *mid, char *outfile, bool digest, MailMessage *msg) { FILE *ofile ; int fd ; char line[MAXPATHLEN] ; int rval = 0 ; off_t size0 ; rewind(file) ; filename_expand(line, outfile) ; if( (fd = lockFile(line, O_WRONLY|O_CREAT|O_APPEND,0664, True, &lockStat)) == -1 || (ofile = fdopen(fd, "a")) == NULL ) { if( fd != -1 ) { unLockFile(&lockStat) ; fclose(ofile) ; } logFile("can't open \"%s\", %s\n", line, strerror(errno)); if( strcmp(outfile, mailbox) == 0 ) { fprintf(stderr, "sortmail: Cannot open \"%s\", %s. Mail to %s may be lost\n", mailbox, strerror(errno), user) ; return -1 ; } else return append_file(file, mid, mailbox, False, msg) ; } else { size0 = fileSize(line) ; fseek(ofile, size0, SEEK_SET) ; if( (rval = copyFile(file, ofile, digest, msg)) != 0 ) { logFile("Error %d writing to %s\n", errno, line) ; return rval ; } fputs("\n", ofile) ; if( digest ) fputs("------------------------------\n\n", ofile) ; unLockFile(&lockStat) ; fclose(ofile) ; logFilev(1, "message %s filed to %s\n", mid, line ) ; } return 0 ; } static int put_pipe(MailMessage *msg, char *dest) { FILE *file = msg->file ; FILE *ofile ; int rval ; if( (ofile = popen(dest, "w")) == NULL ) { logFile("cannot open pipe to command %s\n", dest ) ; append_file(file, msg->msgid, mailbox, False, msg) ; return -1 ; } rewind(file) ; rval = copyFile(file, ofile, False, msg) ; pclose(ofile) ; return rval ; } static int forward(MailMessage *msg, char *dest) { char *scmd ; char cmd[2048] ; char *fpart ; /* if "from=" not specified in options, derive it from * the "From: " line, if we can. */ if( from ) fpart = from ; else if( msg->fromaddr != NULL && msg->fromline != NULL && msg->fromline[0] != '\0' ) fpart = msg->fromaddr ; else fpart = NULL ; /* TODO: expand sendmail variable properly */ /* TODO: SMTP option */ if( (scmd=variable_expand(dest, False)) == NULL ) { logFile("cannot expand %s\n", dest) ; return -1 ; } sprintf(cmd, "%s %s%s %s", sendmail, fpart != NULL ? "-f":"", fpart != NULL ? fpart:"", scmd) ; free(scmd) ; if( put_pipe(msg, cmd) != 0 ) { logFile("unable to mail message %s to %s, errno = %d\n", msg->msgid, dest, errno) ; logFilev(1, "full command was %s\n", cmd) ; return -1 ; } logFilev(1, "message %s mailed to %s\n", msg->msgid, dest) ; logFilev(3, "command was %s\n", cmd) ; return 0 ; } /* Record the information for opening a pop box. To be parsed * later. For security reasons, we copy the data into internal * memory and erase it in the argc list. */ static void setupPop(BoxType type, char **argv) { boxType = type ; popBox = strdup(*argv) ; memset(*argv,'x',strlen(*argv)) ; strcpy(*argv, "pop") ; *argv = "pop" ; } /* parse username, password and hostname from popBox */ static void getUserPwHost(char **user, char **pw, char **host) { char *ptr, *p2 ; if( strchr(popBox, '@') == NULL && popBox[0] == '/' ) { FILE *pwfile ; if( (pwfile = fopen(popBox, "r")) == NULL ) { logFile ("cannot open pop username file %s\n", popBox, strerror(errno)) ; *user = *pw = *host = NULL ; return ; } ptr = malloc(1024) ; if( fgets(ptr, 1023, pwfile) == NULL ) { logFile ("empty username file %s\n", popBox, strerror(errno)) ; *user = *pw = *host = NULL ; fclose(pwfile) ; return ; } if( (p2 = strchr(ptr, '\n')) != NULL ) *p2 = '\0' ; fclose(pwfile) ; } else ptr = strdup(popBox) ; *user = ptr ; if( (p2 = strchr(ptr, ':')) != NULL ) { ptr = p2 ; *ptr++ = '\0' ; *pw = ptr ; } else if( (*pw = getenv("password")) == NULL ) { if( !isatty(2) ) *pw = NULL ; else *pw = getpass("password: ") ; } if( (p2 = strrchr(ptr, '@')) != NULL ) { ptr = p2 ; *ptr++ = '\0' ; *host = ptr ; } else *host = NULL ; } /* Debugging: dump all CRC's in bouncecheck database */ static void do_dumpCrcs(char *name) { #if BOUNCECHECK DBM *db ; u_long crc ; datum key, value ; if( (db = dbm_open(name, O_RDONLY, 0600)) == NULL ) { fprintf(stderr, "sortmail: can't open database \"%s\", %s\n", name, strerror(errno)); exit(BOUNCEDB_ERR) ; } for(key=dbm_firstkey(db); key.dptr != NULL; key = dbm_nextkey(db)) { value = dbm_fetch(db, key) ; crc = *(int *)key.dptr ; if( crc != 0 ) printf("crc %8lx %s", crc, ctime((time_t *)value.dptr)) ; else printf("last purge %s", ctime((time_t *)value.dptr)) ; } #endif exit(EXIT_OK) ; } static void sigHandler(int signum) { if( lockStat.dotname != NULL ) unLockFile(&lockStat) ; /* TODO: can we rescue the situation? */ exit(INTERRUPTED) ; } sortmail-2.4/parse.c0100644000470100007640000002220307771504442013120 0ustar falkfalk#ifndef lint static const char sccsid[] = "@(#)parse.c 2.6 02/03/11 falk" ; static const char rcsid[] = "$Id: parse.c,v 1.4 2003/12/22 06:03:14 efalk Exp $" ; #endif #include #include /* defines getenv(3) */ #include #include #include #include #include #include #include #include "sortmail.h" #include "utils.h" extern int errno ; #ifdef DEBUG #define register #endif static char spooldir[] = SPOOLDIR ; static void includerc(); static void read_initfile() ; static void parse_var() ; static void add_include() ; static void add_exclude() ; static void add_bouncecheck() ; static void add_header() ; #ifdef COMMENT static void expand_variables() ; #endif /* COMMENT */ #define NN(str) ((str)==NULL ? "-none-" : (str)) void read_initfiles() { struct passwd *passwd ; char *ptr ; /* first, try to set up some of the defaults */ if( user == NULL ) { passwd = getpwuid(getuid()) ; if( (user = strdup(passwd->pw_name)) == NULL ) die(4,"out of memory") ; setenv("user",user,1) ; } if( home == NULL ) { if( (passwd = getpwnam(user)) != NULL ) home = strdup(passwd->pw_dir) ; else if( (home = getenv("HOME")) == NULL ) die(6,"can't determine %s's directory",user) ; } if( mailbox == NULL ) { mailbox = (char *) malloc(sizeof(spooldir) + strlen(user)) ; strcpy(mailbox, spooldir) ; strcat(mailbox, user) ; setenv("mailbox",mailbox,1) ; } if( mailrc == NULL ) { mailrc = ".mailrc" ; setenv("mailrc",mailrc,1) ; } if( sortmailrc == NULL ) { sortmailrc = ".sortmailrc" ; setenv("sortmailrc",sortmailrc,1) ; } if( folder == NULL ) { folder = FOLDER ; setenv("folder",folder,1) ; } read_initfile("/usr/lib/sortmailrc", False) ; read_initfile("/usr/local/lib/sortmailrc", False) ; read_initfile("/etc/sortmailrc", False) ; read_initfile("/usr/etc/sortmailrc", False) ; read_initfile("/usr/local/etc/sortmailrc", False) ; read_initfile(mailrc, False) ; read_initfile(sortmailrc, False) ; /* parse the last of the variables */ vacation = getenv("vacation") ; from = getenv("from") ; reject = getenv("reject") ; folder = getenv("folder") ; deflt = getenv("default") ; if( (ptr = getenv("delay")) != NULL ) delayTime = atoi(ptr) ; if( (ptr = getenv("limit")) != NULL ) sizeLimit = getLimit(ptr) ; if( (ptr = getenv("timeout")) != NULL ) timeout = atoi(ptr) ; if( (ptr = getenv("maxlines")) != NULL ) maxlines = atoi(ptr) ; if( (tmpdir = getenv("TMPDIR")) == NULL || strlen(tmpdir) > 100 ) tmpdir = "/tmp" ; if( (sendmail = getenv("sendmail")) == NULL ) sendmail = SENDMAIL ; if( verbose >= 3 ) { logFile("$USER = %s\n", NN(user)) ; logFile("$HOME = %s\n", NN(home)) ; logFile("mailbox = %s\n", NN(mailbox)) ; logFile("mailrc = %s\n", NN(mailrc)) ; logFile("sortmailrc = %s\n", NN(sortmailrc)) ; logFile("deflt = %s\n", NN(deflt)) ; logFile("from = %s\n", NN(from)) ; logFile("vacation = %s\n", NN(vacation)) ; logFile("folder = %s\n", NN(folder)) ; logFile("verbose = %d\n", verbose) ; } } static void read_initfile( char *filename, bool complain ) { register FILE *ifile ; Ictx in ; char line[MAXPATHLEN] ; if( *filename == '~' ) { filename_expand(line, filename) ; filename = line ; } else if( *filename != '/' && /* relative path, relative to home */ strncmp(filename,"./",2) != 0 ) { strcpy(line,home) ; strcat(line,"/") ; strcat(line,filename) ; filename = line ; } if( (ifile = fopen(filename, "r")) != NULL ) { logFilev(2, "read %s\n", filename) ; in.ptr = "" ; in.file = ifile ; in.lineno = in.lineno0 = 0 ; advanceLine(&in) ; while( *in.ptr != EOF ) { if( *in.ptr == '/' || /* pattern */ *in.ptr == '[' || /* ip pattern */ *in.ptr == '(' ) /* expression */ ExpressionAdd(&in) ; else { if( strmatch(in.ptr,"set") ) /* variable */ parse_var(in.ptr, in.lineno) ; else if( strmatch(in.ptr, "includelist")) /* include list */ add_include(in.ptr+11, in.lineno) ; else if( strmatch(in.ptr, "excludelist")) /* exclude list */ add_exclude(in.ptr+11, in.lineno) ; else if( strmatch(in.ptr, "header") ) /* append header */ add_header(in.ptr+6,False, in.lineno) ; else if( strmatch(in.ptr, "replace") ) /* replace header */ add_header(in.ptr+7,True, in.lineno) ; else if( strmatch(in.ptr, "bouncecheck") ) /* bounce DB check */ add_bouncecheck(in.ptr+11, in.lineno) ; else if( strmatch(in.ptr, "includerc") ) /* bounce DB check */ includerc(in.ptr+9, in.lineno) ; /* else ignore it */ advanceLine(&in) ; } } fclose(ifile) ; } else if( complain ) { logFile("cannot open file %s: %s\n", filename, strerror(errno)) ; } } static void parse_var( register char *ptr, int lineno) { char *ptr2, *ptr3 ; ptr += 3 ; /* skip "set" */ if( !isspace(*ptr) ) return ; /* something was wrong */ ptr = firstNonBlank(ptr) ; if( !isalnum(*ptr) ) return ; /* Expand the entire input line */ if( (ptr2 = variable_expand(ptr, False)) == NULL ) { logFile("cannot expand %s\n", ptr) ; return ; } ptr = ptr2 ; /* tighten it up by removing any spaces surrounding the '=' */ /* skip past variable name */ for(ptr2=ptr; isalnum(*ptr2) || *ptr2 == '_'; ++ptr2) ; if( (ptr3 = strchr(ptr2,'=')) == NULL ) strcpy(ptr2,"=") ; else { if( ptr3 > ptr2 ) strcpy(ptr2,ptr3) ; ++ptr2 ; ptr3 = firstNonBlank(ptr2) ; if( ptr3 > ptr2 ) strcpy(ptr2,ptr3) ; } putenv(ptr) ; /* and set it */ /* Update variables that can't wait. */ if( strmatch(ptr,"user") ) user = ptr+4+1 ; else if( strmatch(ptr,"HOME") ) home = ptr+4+1 ; else if( strmatch(ptr,"mailrc") ) mailrc = ptr+6+1 ; else if( strmatch(ptr,"sortmailrc") ) sortmailrc = ptr+10+1 ; else if( strmatch(ptr,"verbose") ) verbose = atoi(ptr+7+1) ; else if( strmatch(ptr,"logfile") ) { logfilename = ptr+7+1 ; if( logfile != stderr ) fclose(logfile) ; if( (logfile = pathOpen(logfilename, "a")) == NULL ) logfile = stderr ; } } static void includerc( register char *ptr, int lineno) { char *p2 ; ptr = firstNonBlank(ptr) ; removeNL(ptr) ; if( (p2 = variable_expand(ptr, False)) == NULL ) { logFile("cannot expand %s\n", ptr) ; return ; } read_initfile(p2, True) ; free(p2) ; } /* append a command to the linked list */ void append_cmd(Cmd *cmd) { cmd->next = NULL ; if( cmds == NULL ) cmds = cmd ; else lastcmd->next = cmd ; lastcmd = cmd ; } /* Add an include/exclude filename to the list */ static void make_include( register char *ptr, cmdType type, int lineno) { IncludeInfo *info ; info = MALLOC(IncludeInfo,1) ; if( info == NULL ) { fprintf(logfile, "out of memory\n") ; return ; } ptr = firstNonBlank(ptr) ; if( (info->filename=variable_expand(ptr, False)) == NULL ) { fprintf(logfile, "cannot expand %s\n", ptr) ; return ; } info->c.type = type ; info->c.lineno = lineno ; append_cmd((Cmd *)info) ; } /* Add an include filename to the list */ static void add_include( char *ptr, int lineno) { make_include(ptr, INCLUDE, lineno) ; } /* Add an exclude filename to the list */ static void add_exclude( char *ptr, int lineno) { make_include(ptr, EXCLUDE, lineno) ; } /* Add a bouncecheck filename to the list */ static void add_bouncecheck( char *ptr, int lineno ) { make_include(ptr, BCHECK, lineno) ; } /* Add a header line to the list */ /* TODO: defer variable expansion? */ static void add_header( register char *ptr, bool replace, int lineno) { HeaderInfo *info ; char *p2 ; char *line ; ptr = firstNonBlank(ptr) ; if( (line=variable_expand(ptr, False)) == NULL ) { fprintf(logfile, "cannot expand %s\n", ptr) ; return ; } if( (p2 = strchr(line,':')) == NULL ) { fprintf(logfile, "invalid header line \"%s\"\n", line) ; free(line) ; return ; } info = MALLOC(HeaderInfo,1) ; if( info == NULL ) { fprintf(logfile, "out of memory\n") ; free(line) ; return ; } append_cmd((Cmd *)info) ; info->c.type = replace ? REPLACE : HEADER ; info->c.lineno = lineno ; info->header = line ; info->len = p2 - line + 1 ; info->done = 0 ; headers = 1 ; } /* advance one character */ void advance( Ictx *in, int n) { while( --n >= 0 ) { ++in->ptr ; if( *in->ptr == '\0' ) advanceLine(in) ; } } /* advance to next token */ void advanceNext(Ictx *in) { while( *in->ptr == '\0' || isspace(*in->ptr) ) if( *in->ptr == '\0' ) advanceLine(in) ; else ++in->ptr ; } /* advance to next line */ void advanceLine(Ictx *in) { if( fgets(in->line, MAXLINE, in->file ) == NULL ) { in->ptr[0] = EOF ; in->ptr[1] = '\0' ; return ; } #ifdef COMMENT expand_variables(in) ; #endif /* COMMENT */ ++in->lineno ; in->ptr = in->line ; in->ptr = firstNonBlank(in->ptr) ; removeNL(in->ptr) ; } #ifdef COMMENT /* copy input line, expanding variables according to * traditional quoting rules. '$$' => '$', thus deferring * variables with $$name. * * Use a primitive state machine. */ static void expand_variables(Ictx *in) { char *tmp ; tmp = variable_expand(in->line, False) ; strcpy(in->line, tmp) ; free(tmp) ; } #endif /* COMMENT */ sortmail-2.4/expr.c0100644000470100007640000007740007771504442012775 0ustar falkfalk#ifndef lint static const char sccsid[] = "@(#)expr.c 1.4 02/03/11 falk" ; static const char rcsid[] = "$Id: expr.c,v 1.4 2003/12/22 06:03:14 efalk Exp $" ; #endif #include #include /* defines getenv(3) */ #include #include #include #include #include #include "sortmail.h" #include "utils.h" /* This is a recursive-descent parser for the expressions used in * sortmail. I find these easier to write than to learn yacc. * * The grammar for these expressions is as follows: * * expression ::= * * commaExpr ::= * , * * orExpr ::= * || * * andExpr ::= * && * * eqExpr ::= * == * != * * cmpExpr ::= * <= * >= * < * > * * addExpr ::= * + * - * * mulExpr ::= * * * / * * notExpr ::= * ! * * value ::= * $ * // * [digits] ip addr or block * [digits/digits] ip range * [digits - digits] ip range * () * * variable ::= * {} * * varname ::= * * alpha ::= [A-Za-z] * * alphanum ::= [A-Za-z0-9_]* * * constant ::= [0-9]+ * * options ::=