procmail-3.22/0040755006717600001440000000000007347314363012470 5ustar guenthersrcprocmail-3.22/SmartList/0040755006717600001440000000000007347314364014413 5ustar guenthersrcprocmail-3.22/SmartList/FEATURES0100644006717600001440000000431607026357665015562 0ustar guenthersrcSummary of what SmartList provides: + The overseeable management of an arbitrary number of mailinglists + Convenient and simple creation of new mailinglists + Convenient and simple removal of existing mailinglists + Fully automated subscription/unsubscription/help-request processing (no operator intervention needed) + Enough intelligence to overcome the ignorance of some subscribers (will direct subscribe and unsubscribe requests away from the regular list and automatically onto the -request address) + No hardwired format for (un)subscribe requests (i.e. new subscribers need not be educated, unsubscribing users do not need to remember any particular syntax) + *Intelligent* autoremoval of addresses from the list that cause too many bounces + Submissions can be limited to people on the accept list (which could be the current list of subscribers) + The fully automated subscription mechanism allows for a reject list of unwanted subscribers and a general address screening mechanism which allows you to control exactly who is allowed to subscribe + Optional implicit subscription upon first submission to the list + MIME-compliant auto-digest-generation (configurable per list) + Joint management of several mailinglists possible + Customisation per mailinglist or mailinglist group possible (simply remove or create the desired links) + A listmaintainer can be assigned per list; miscellaneous requests that couldn't be handled by the list automatically are then forwarded to his mail address (instead of being accumulated in a file) + Allows for remote maintenance of any mailinglist by a listmaintainer + Integrated archiving service + Integrated diagnostic aid to give hints to the maintainer about possible problems + Moderated mailinglists with an arbitrary number of moderators + Automatically eliminates duplicate submissions + You can set up a mailinglist to function as a standalone mail archive server + Extended MIME support (autorecognition, encapsulation and suitable encoding of well known (and unknown) file formats) + The archive server can send arbitrarily long (even binary) files in MIME-multipart mails + Provides rfc2369-compliant List- headers procmail-3.22/SmartList/README0100644006717600001440000000525407316762750015300 0ustar guenthersrcThe SmartList mailinglist package has been built on top of the procmail mail processing package. In order to install it you'll need the source of the procmail package as well. If you now have just the procmail sources, get the SmartList sources and unpack them on top of the procmail source tree. If you now have just the SmartList sources, get the procmail sources and unpack them on top of the SmartList source tree. See below on where to get the missing part. The two source packages should blend together with only this README file, the FEATURES, ../COPYING and ../Artistic file being duplicated. ---------------------- For installation instructions see the INSTALL file. ---------------------- SmartList mailinglist management package. Copyright (c) 1993-1999, S.R. van den Berg, The Netherlands. Copyright (c) 1999-2000, Philip Guenther, The United States of America Some legal stuff: This package is open source software; you can redistribute it and/or modify it under the terms of either: - the GNU General Public License as published by the Free Software Foundation and can be found in the included file called "../COPYING"; either version 2, or (at your option) any later version, or - the "Artistic License" which can be found in the included file called "../Artistic". This package is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See either the GNU General Public License or the Artistic License for more details. ---------------------- The most recent version can be obtained via http://www.procmail.org/ or ftp://ftp.procmail.org/pub/procmail/ You'll be able to find instructions there which direct you to suitable mirror sites around the world. The current list of mirror sites include: ftp://ftp.psg.com/pub/unix/procmail/ ftp://ftp.ucsb.edu/pub/mirrors/procmail/ ftp://ftp.informatik.rwth-aachen.de/pub/packages/procmail/ ftp://ftp.fu-berlin.de/pub/unix/mail/procmail/ ftp://ftp.net.ohio-state.edu/pub/networking/mail/procmail/ ftp://ftp.fdt.net/pub/unix/packages/procmail/ ftp://ftp.tamu.edu/pub/mirrors/procmail/ ftp://ftp.kfki.hu/pub/packages/mail/procmail/ ftp://giswitch.sggw.waw.pl/pub/unix/procmail/ ftp://ftp.solarisguide.com/pub/procmail/ ftp://ftp.win.ne.jp/pub/network/mail/procmail/ http://www.ring.gr.jp/archives/net/mail/procmail/ ftp://ftp.ring.gr.jp/pub/net/mail/procmail/ ftp://ftp.ayamura.org/pub/procmail/ ftp://sunsite.cnlab-switch.ch/mirror/procmail/ ftp://ftp.linja.net/pub/mirrors/procmail/ ftp://ftp.stealth.net/pub/mirrors/ftp.procmail.org/pub/procmail/ ftp://ftp.mirror.ac.uk/sites/ftp.procmail.org/pub/procmail/ ---------------------- procmail-3.22/examples/0040755006717600001440000000000007347314370014304 5ustar guenthersrcprocmail-3.22/examples/local_procmail_lmtp.m40100644006717600001440000000354007347314367020567 0ustar guenthersrcdivert(-1) # Copyright 2001, Philip Guenther, The United States of America # # This file should be copied to the cf/feature directory of the # sendmail distribution. The following blurb is roughly what would # go in the list of FEATURES in the cf/README file: # # `local_procmail_lmtp # Use procmail as an LMTP capable local mailer. By using # LMTP for the connection between sendmail and procmail, # delivery to multiple recipients can be performed more # efficiently while still allowing a separate status code # for each recipient. With this feature, the local mailer # can make use of the "user+indicator@local.host" syntax; # normally the +indicator is just tossed, but by default # it is passed as the -a argument to procmail. # # This feature can take up to three arguments: # 1. Path to the mailer program # [default: PROCMAIL_MAILER_PATH or /usr/local/bin/procmail] # 2. Argument vector including name of the program # [default: procmail -Y -a $h -z] # 3. Flags for the mailer [default: PSXhmnz9] # # Empty arguments cause the defaults to be taken. # WARNING: This feature sets LOCAL_MAILER_FLAGS unconditionally, # i.e., without respecting any definitions in an OSTYPE setting.' # # This feature would probably be better implemented on top of the # local_lmtp or local_procmail features that are currently distributed, # but the following is known to work with Sendmail 8.11 divert(0) VERSIONID(`$Id: local_procmail_lmtp.m4,v 1.3 2001/09/11 04:45:48 guenther Exp $') divert(-1) define(`LOCAL_MAILER_PATH', ifelse(defn(`_ARG_'), `', ifdef(`PROCMAIL_MAILER_PATH', PROCMAIL_MAILER_PATH, `/usr/local/bin/procmail'), _ARG_)) define(`LOCAL_MAILER_ARGS', ifelse(len(X`'_ARG2_), `1', `procmail -Y -a $h -z', _ARG2_)) define(`LOCAL_MAILER_FLAGS', ifelse(len(X`'_ARG3_), `1', `PSXhmnz9', _ARG3_)) define(`LOCAL_MAILER_DSN_DIAGNOSTIC_CODE', `SMTP') procmail-3.22/examples/1procmailrc0100644006717600001440000000120405571124077016435 0ustar guenthersrc# Please check if all the paths in PATH are reachable, remove the ones that # are not. PATH=$HOME/bin:/usr/bin:/usr/ucb:/bin:/usr/local/bin:. MAILDIR=$HOME/Mail # You'd better make sure it exists DEFAULT=$MAILDIR/mbox LOGFILE=$MAILDIR/from LOCKFILE=$HOME/.lockmail :0 # Anything from thf * ^From.*thf@somewhere.someplace todd # will go to $MAILDIR/todd :0 # Anything from people at uunet * ^From.*@uunet uunetbox # will go to $MAILDIR/uunetbox :0 # Anything from Henry * ^From.*henry henries # will go to $MAILDIR/henries # Anything that has not been delivered by now will go to $DEFAULT # using LOCKFILE=$DEFAULT$LOCKEXT procmail-3.22/examples/1rmail0100644006717600001440000000062406256654517015423 0ustar guenthersrc#!/bin/sh # # specify the mailbox file you want to read on the command line # MAILDIR=$HOME/Mail cd $MAILDIR LOCKFILE=$HOME/.lockmail if lockfile -! -r1 $LOCKFILE then echo Mail is currently arriving, please wait... while lockfile -! -4 -r2 $LOCKFILE do echo Mail is still arriving... done fi trap "rm -f $LOCKFILE;exit 0" 0 1 2 3 13 15 # # Call your favourite mailer here. # /usr/ucb/mail -f $* procmail-3.22/examples/2procmailrc0100644006717600001440000000370505571124100016431 0ustar guenthersrc# Please check if all the paths in PATH are reachable, remove the ones that # are not. PATH=$HOME/bin:/usr/bin:/usr/ucb:/bin:/usr/local/bin:. MAILDIR=$HOME/Mail # You'd better make sure it exists DEFAULT=$MAILDIR/mbox # We don't use a global lockfile here now. # Instead we use local lockfiles everywhere. # This allows mail to arrive in all mailboxes # concurrently, or allows you to read one mailbox # while mail arrives in another. # The next recipe will split up Digests into their individual messages. # Don't do this if you use a global lockfile before this recipe (deadlock) :0 * ^Subject:.*Digest |formail +1 -d -s procmail LOGFILE=$MAILDIR/from # Put it here, in order to avoid logging # the arrival of the digest. # An alternative and probably more efficient solution to splitting up a digest # would be (only works for standard format mailbox files though): :0: * ^Subject:.*Other Digest |formail +1 -ds cat >>this_lists_mailbox # Notice the double : in the next recipe, this will cause a lockfile # named "$MAILDIR/todd.lock" to be used if and only if this mail is going # into the file "todd". :0: # Anything from thf * ^From.*thf@somewhere.someplace todd # will go to $MAILDIR/todd # The next recipe will likewise use $MAILDIR/uunetbox.lock as a lock file. :0: # Anything from people at uunet * ^From.*@uunet uunetbox # will go to $MAILDIR/uunetbox # And here the lockfile will be $MAILDIR/henries.lock of course. :0: # Anything from Henry * ^From.*henry henries # will go to $MAILDIR/henries # But you can specify any lockfile you want, like "myfile". The following # recipe will use "$MAILDIR/myfile" as the lock file. :0:myfile # All 'questions' will go to * ^Subject:.*questions toread # $MAILDIR/toread # Anything that has not been delivered by now will go to $DEFAULT # After procmail sees the end of the rcfile, it pretends that it sees a # LOCKFILE=$DEFAULT$LOCKEXT # Therefore $DEFAULT is always locked. procmail-3.22/examples/2rmail0100644006717600001440000000061406256654517015423 0ustar guenthersrc#!/bin/sh # # specify the mailbox file you want to read on the command line # MAILDIR=$HOME/Mail cd $MAILDIR LOCKFILE=$1.lock if lockfile -! -r1 $LOCKFILE then echo Mail is currently arriving, please wait... while lockfile -! -4 -r2 $LOCKFILE do echo Mail is still arriving... done fi trap "rm -f $LOCKFILE;exit 0" 0 1 2 3 13 15 # # Call your favourite mailer here. # /usr/ucb/mail -f $* procmail-3.22/examples/3procmailrc0100644006717600001440000000277505571124101016441 0ustar guenthersrc# Please check if all the paths in PATH are reachable, remove the ones that # are not. PATH=$HOME/bin:/usr/bin:/global/bin:/usr/ucb:/bin:/usr/local/bin: MAILDIR = $HOME/Mail # You'd better make sure it exists DEFAULT = $MAILDIR/mbox LOGFILE = $MAILDIR/from LOCKFILE= $HOME/.lockmail # This will create a local lockfile named todd.lock :0: # *if* the condition matches * ^From.*thf todd LOCKFILE=$MAILDIR/whatever # This will remove the global lockfile # $HOME/.lockmail and the new lockfile # will be $MAILDIR/whatever # The next recipe will # filter out all messages from "at" # jobs and will put them in a terse format # (only the date and the body) in # a file called $MAILDIR/atjunk :0 fh * ^From root * ^Subject: Output from "at" job |egrep "^Date:" # The next recipe will only be used if # the previous one matched :0 A atjunk MAILDIR=$HOME/News # This will change the current directory # The next recipe will create a local lockfile # named $HOME/News/dustbin.lock (*if* the condition # matches), and will feed the body of the message # through `sort` (sorry, couldn't come up with anything # better :-), after which the result will be # appended to $HOME/News/dustbin :0 b: * ^Subject:.*rubbish |sort >>dustbin # The next recipe will use the games directory as a MH # folder (of course you need MH to read the mail then) :0 * ^Subject:.*games games/. # Anything not delivered by now will go to $HOME/Mail/mbox # Using LOCKFILE=$HOME/Mail/mbox.lock procmail-3.22/examples/3rmail0100644006717600001440000000137106256654517015425 0ustar guenthersrc#!/bin/sh # # specify the mailbox file you want to read on the command line # Use a relative path from your $HOME directory # # For this kind of chaotic procmailrc there is no uniform neat solution # to determine which lockfiles to use. I'll give just one (suboptimal) # solution here. Use your imagination to extend it :-). # MAILDIR=$HOME/Mail cd $HOME # this means all paths are relative to $HOME LOCKFILE=$HOME/.lockmail LOCKFILE2=$HOME/Mail/whatever if lockfile -! -r1 $LOCKFILE $LOCKFILE2 then echo Mail is currently arriving, please wait... while lockfile -! -4 -r2 $LOCKFILE $LOCKFILE2 do echo Mail is still arriving... done fi trap "rm -f $LOCKFILE $LOCKFILE2;exit 0" 0 1 2 3 13 15 # # Call your favourite mailer here. # /usr/ucb/mail -f $* procmail-3.22/examples/advanced0100644006717600001440000003206306651142124015766 0ustar guenthersrc Discusses: 1. One home directory, several machine architectures 2. Procmail as an integrated local mail delivery agent 2a.Special directions for sites with sendmail 2b.Special directions for sites with ZMailer 2c.Special directions for sites with smail 2d.Special directions for sites with SysV /etc/mail/mailsurr 3. Changing the mail spool directory to $HOME for all users 4. Security considerations (when installing procmail suid root) NOTE: This file refers to the procmail binary being located in /usr/bin. Some systems may place procmail in other locations such as /usr/local/bin. Talk with your sysadmin if you are not sure. --- 1. One home directory, several machine architectures ------------------------------------------------- For users that have the very same home directory on machines with differing architectures (i.e. you need different executables), and they have to explicitly use (i.e. the system administrator did not arrange, for example, /usr/bin/procmail to have exactly the right contents depending on from which machine it is called) two executables of procmail, I have the following suggestion to use as a .forward file (examples are for sparc and sun3 architectures): "|IFS=' ';if /usr/bin/sparc;then exec /home/berg/bin.sun4/procmail;else exec /home/berg/bin.sun3/procmail;fi ||exit 75 #YOUR_USERNAME" or alternatively: "|IFS=' ' && export IFS && exec /home/berg/bin.`/usr/bin/arch`/procmail || exit 75 #YOUR_USERNAME" Please note, in the .forward file there can NOT be any newlines between the doublequotes, i.e. the former example *has* to be typed in as one long line. If, on the other hand, you have to log in to every machine to read mail arrived for you on that machine, a different solution might be more appropriate. If you have sendmail v6.xx and later, you simply create two .forward files. In the .forward file you put: YOUR_LOGIN_NAME@your.favourite.machine And, in a second file named .forward.your.favourite.machine you put: "|exec /usr/bin/procmail #YOUR_USERNAME" If you have an older sendmail, you could put something like the following two lines in your .forward file: YOUR_LOGIN_NAME@your.favourite.machine "|IFS=' ';test .`/bin/uname -n` != .your.favourite.machine || exec /usr/bin/procmail #YOUR_USERNAME" The leading dots are important. Check what `/bin/uname -n` returns on your.favourite.machine, and substitute that for your.favourite.machine in the sample .forward file. If your system does not have /bin/uname, check if there is a /bin/hostname. With some sendmails, the last suggestion causes you to get a superfluous copy in the system mailfolder. If that is the case, you'll have to change your .forward to something like: "|IFS=' ';if test .`/bin/uname -n` = .your.favourite.machine ; then exec /usr/bin/procmail; else exec /usr/lib/sendmail -oi YOUR_LOGIN_NAME@your.favourite.machine; fi" --- 2. Procmail as an integrated local mail delivery agent --------------------------------------------------- Completely integrating procmail in the mail delivery means that mail is delivered as normal, unless a .procmailrc file is present in the home directory of the recipient. This will be completely independent of the fact if a .forward file is present. This will not break anything, it just makes the use of procmail easier because people are not required to start up procmail from within their .forward files. Creation of a .procmailrc file will suffice. N.B. If you *are* installing it as the local delivery agent, and users on your system have dormant .procmailrc files without corresponding .forward file. Then, after the installation, these dormant .procmailrc files will be automagically activated/used (so you might want to rename any dormant .procmailrc files out of the way and notify the users; do be careful, since some users might invoke procmail through other means (cron or login) and might be surprised if it stops working). The generic way to accomplish this (works with sendmail, smail and any other mail system that uses a local mail delivery program that takes the mail- to-be-delivered on stdin and the recipient(s) on the command line, with or without the "-d" option) is this: Move your current local mail delivery agent (e.g. /bin/mail, /bin/lmail, /usr/lib/mail/mail.local, etc.) out of the way, and create a (symbolic or hard) link from there to procmail, as in "ln /usr/bin/procmail /bin/lmail". Beware, however, that if you are using this method, /bin/mail can *only* be used to deliver mail. On many systems /bin/mail has several uses (also to read mail or check for mail). So, it would definitely be preferred if you could edit the invocation of /bin/mail from within your mail transport agent to invoke procmail instead (with appropriate flags, if needed). Special directions detailing this process for some of the more popular MTAs are included in subsections below. In addition to needing root privileges upon startup, on some systems procmail needs to be sgid to daemon or mail. One way to check is by looking at the current mail delivery agent (usually /bin/mail) and to mimic its permissions, owner and group. If you're not quite sure, just type "make recommend" and some suitable recommendations will be made for your particular environment. The same might apply to the "lockfile" program, in order for it to be able to create and unlink lockfiles in the mail spool directory it might need to be sgid to daemon or mail, not to worry however, "lockfile" will not enable users to abuse the sgid/suid-ness. --- 2a.Special directions for sites with sendmail ------------------------------------------ The following lines should take the place of the standard Mlocal definition in your sendmail.cf (as for the fields "S=10, R=20": if your system uses others or none on the current Mlocal definition, use those *instead* of "S=10, R=20"): If you're using a sendmail 8.6.x or older: Mlocal, P=/usr/bin/procmail, F=lsSDFMhPfn, S=10, R=20, A=procmail -Y -a $h -d $u If you're using sendmail 8.7 or newer: In your *.mc file, insert FEATURE(local_procmail) or edit the sendmail.cf file and change the Mlocal definition to match: Mlocal, P=/usr/bin/procmail, F=SAw5:|/@glDFMPhsfn, S=10/30, R=20/40, T=DNS/RFC822/X-Unix, A=procmail -Y -a $h -d $u In case you were wondering why there is no 'm' flag on this definition, you can add it if you want, but I recommend omitting it (it would enhance performance very slightly; however, if one of the multiple recipients causes mail to bounce, it will bounce for all recipients (since there is only one exitcode)). To impose a 2MB limit on mails, you could add a `Maxsize=' field like in: Mlocal, P=/usr/bin/procmail, F=lsSDFMhPfn, S=10, R=20, M=2000000, A=procmail -Y -a $h -d $u In order to take advantage of the optional meta argument that can be passed to procmail you'd have to change the sendmail.cf file to add a $#local mailer rule to set the $@ host name (which will be substituted for $h in the mailer definition). There is nothing forcing you to do this, but if you do, you'll gain functionality. If you are using sendmail 8.7.* or newer, and are using the standard FEATURE(local_procmail), then the support for this meta argument is already present. For example: Make sure that the definition of operators in the sendmail.cf file includes the + sign (simply tack a + to the end of the "Do" definition, unless it already contains one). Now look for ruleset zero (S0), skip to the end of it. There usually is a rule there that takes care of local delivery, something like: R$+ $#local $:$1 local names Don't change that rule, leave it there. But, right BEFORE this rule, create a new one similar to: R$++$* $#local $@$2 $:$1 local argument Depending on the actual contents of your sendmail.cf file, there still might be some other $#local rule(s) you need to precede with a corresponding +-handling rule, e.g. in some files you also find: R$+ < $+ @ $+ > $#local $: $1 Preceed that with: R$+ + $* < $+ @ $+ > $#local $@ $2 $: $1 (The spaces are not significant, the tabs are!) Now, if someone sends mail to fred+pizza@your.domain, procmail will be called to deliver the mail as: procmail -a pizza -d fred In the .procmailrc file, you can now do an assignment like: ARGUMENT=$1 which will expand to ARGUMENT=pizza. N.B. that if you do *not* have sendmail v6.* or older, or IDA-sendmail, and would like to make use of the meta-argument, you'll have to drop the 'l' flag on the Mlocal definition and make sure that *every* $#local invocation carries a (possibly empty) $@ host definition. Since you are editing the sendmail.cf file now anyway, you might as well setup an extra `procmail' mailer. This Mprocmail can then be used as a general mail filter. For more information, see the EXAMPLES section the procmail(1) man page. N.B. Do NOT create the extra rules mentioned in the EXAMPLES section of the procmail(1) man page, unless you already have an application demanding those. Only create the completely optional Mprocmail mailer. After having edited the sendmail.cf file you'll have to kill (terminate) the running sendmail daemon. Then restart it. It will *not* suffice to send sendmail a SIGHUP (unless you are running sendmail 8.7.* or newer and started it with an absolute path). --- 2b.Special directions for sites with ZMailer ----------------------------------------- The following line should be inserted into (or take the place of any previous local definition in) your sm.conf file for the Transport Agent: local sSPfn /usr/bin/procmail procmail -a $h -d $u --- 2c.Special directions for sites with smail --------------------------------------- For smail 2.x users there are two options: i. Move the current local-mail-delivery program (probably /bin/lmail) out of the way, make a symbolic or hard link from procmail to the name of that program (e.g. "ln /usr/bin/procmail /bin/lmail") ii.Make sure the following macro is defined in src/defs.h: #define LMAIL(frm,sys) "/usr/bin/procmail -d" For smail 3.x users there are also two options: i. The same solution as for smail 2.x (however, method ii is preferred) ii.Replace any existing "local"-entry in the /usr/lib/smail/transports file (create one, if need be) with the following two lines: local: return_path, local, from, driver=pipe; user=root, cmd="/usr/bin/procmail -d $($user$)" --- 2d.Special directions for sites with SysV /etc/mail/mailsurr --------------------------------------------------------- Some systems use a SysV /bin/mail that supports mailsurr. To interface procmail with mailsurr the following two lines should be inserted in the /etc/mail/mailsurr file (preferably at the bottom): '(.+)' '([^@!]+)' '$DEVNULL # structured-programming spaghetti then flags="$1"; shift else flags=-h # force help page fi while flags=`$expr "X$flags" : 'X.\(.*\)'`; $test ."$flags" != . do case "$flags" in k*) MSkeeplogfile=1;; l*) MSlong=1;; m*) MSmergerror=1;; o*) MSoldlog=1; MSkeeplogfile=1;; t*) MSterse=1;; s*) MSsilent=1;; h*|\?*) $echo 'Usage: mailstat [-klmots] [logfile]' 1>&2 $echo ' -k keep logfile intact' 1>&2 $echo ' -l long display format' 1>&2 $echo ' -m merge any errors into one line' 1>&2 $echo ' -o use the old logfile' 1>&2 $echo ' -t terse display format' 1>&2 $echo ' -s silent in case of no mail' 1>&2 exit $EX_USAGE;; *) $echo 'Usage: mailstat [-klmots] [logfile]' 1>&2; exit $EX_USAGE;; esac done done $test a"$1" = a-- && shift LOGFILE="$1" case "$LOGFILE" in *$OLDSUFFIX) MSkeeplogfile=1; OLDLOGFILE="$LOGFILE";; *) OLDLOGFILE="$LOGFILE$OLDSUFFIX";; esac if test .$MSoldlog = .1 then LOGFILE="$OLDLOGFILE" fi if $test ."$LOGFILE" != .- -a ."$LOGFILE" != . then if $test ! -s "$LOGFILE" then if $test .$MSsilent = . then if $test -f "$LOGFILE" # split up the following nested backquote then # expression, some shells (NET2) choked on it info=`LANG= LC_TIME= $ls -l "$OLDLOGFILE"` $echo No mail arrived since \ `$expr "X$info" : \ '.*[0-9] \(... .[^ ] .....\) [^ ]'` else $echo "Can't find your LOGFILE=$LOGFILE" fi fi exit 1 fi else if $test ."$LOGFILE" != .- && $tty -s then $echo \ "Most people don't type their own logfiles; but, what do I care?" 1>&2 MSterse=1 fi MSkeeplogfile=1; LOGFILE= fi if $test .$MSkeeplogfile = . then $mv "$LOGFILE" "$OLDLOGFILE"; $cat $DEVNULL >>"$LOGFILE" else OLDLOGFILE="$LOGFILE" fi if $test .$MSterse = . then if $test .$MSlong = .1 then $echo "" $echo " Total Average Number Folder" $echo " ----- ------- ------ ------" # We use MStrs here to place the spaces in columns that won't be # converted to tabs by detab when this is checked into CVS MStrs='" ----- ------- ------\n%7d %7d %7d\n",\ gtotal,gtotal/gmessages,gmessages' else $echo "" $echo " Total Number Folder" $echo " ----- ------ ------" MStrs='" ----- ------\n%7d %7d\n",gtotal,gmessages' fi else MStrs='""' fi if $test .$MSlong = .1 then MSlong='"%7d %7d %7d %s\n",total,total/messages,messages,folder' else MSlong='"%7d %7d %s\n",total,messages,folder' fi ######## # And now we descend into the wonderful mix of shell-quoting and # portable awk-programming :-) ######## dq='"' awkscript=" BEGIN { FS=$dq\\t$dq; } { if(folder!=\$1) { if(folder!=$dq$dq) printf($MSlong); gmessages+=messages;gtotal+=total; messages=0;total=0;folder=\$1; } ++messages;total+=\$2; } END { if(folder!=$dq$dq) printf($MSlong); gmessages+=messages;gtotal+=total; printf($MStrs); } " ######## # Only to end in a grand finale with your average sed script ######## if $test .$MSmergerror = . then $sed -e '/^From /d' -e '/^ [Ss][uU][bB][jJ][eE][cC][tT]:/d' \ -e '/^ Folder/s/ */ /' \ -e '/^ Folder/s/\/msg\.[-0-9A-Za-z_][-0-9A-Za-z_]* /\/ /' \ -e '/^ Folder/s/\/new\/[-0-9A-Za-z_][-0-9A-Za-z_.,+:%@]* /\/ /' \ -e '/^ Folder/s/\/[0-9][0-9]* /\/. /' \ -e 's/^ Folder: \(.*\)/\1/' -e t -e 's/ /\\t/g' \ -e 's/^/ ## /' $OLDLOGFILE | $sort | $awk "$awkscript" - else $sed -e '/^From /d' -e '/^ [Ss][uU][bB][jJ][eE][cC][tT]:/d' \ -e '/^ Folder/s/ */ /' \ -e '/^ Folder/s/\/msg\.[-0-9A-Za-z_][-0-9A-Za-z_]* /\/ /' \ -e '/^ Folder/s/\/new\/[-0-9A-Za-z_][-0-9A-Za-z_.,+:%@]* /\/ /' \ -e '/^ Folder/s/\/[0-9][0-9]* /\/. /' \ -e 's/^ Folder: \(.*\)/\1/' -e t \ -e 's/.*/ ## diagnostic messages ##/' $OLDLOGFILE | $sort | \ $awk "$awkscript" - fi ######## # Nifty little script, isn't it? # Now why didn't *you* come up with this truly trivial script? :-) ######## procmail-3.22/examples/procmail-rpm.spec0100644006717600001440000000644507347314367017576 0ustar guenthersrcSummary: procmail mail delivery agent Name: procmail Version: 3.22 Release: 1 Copyright: GPL Group: Daemons Source: ftp://ftp.%{name}.org/pub/%{name}/%{name}-%{version}.tar.gz BuildRoot: /var/tmp/%{name}-rpmroot Provides: localmail procmail Packager: Bruce Guenter %description Most mail servers such as sendmail need to have a local delivery agent. Procmail can be used as the local delivery agent for you mail server. It supports a rich command set that allows you to pre-sort, archive, or re-mail incoming mail automatically. SmartList also needs procmail to operate. %prep %setup perl -ni -le 'next if /^LOCKINGTEST=/; s/^#// if /^#LOCKINGTEST=/; print' Makefile %build LOCKINGTEST='/tmp .' make %install rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/usr/{bin,man/man{1,5}} make BASENAME=$RPM_BUILD_ROOT/usr VISIBLE_BASENAME=/usr install %files %attr(4511,root,root) /usr/bin/procmail %defattr(-,root,root) /usr/bin/lockfile /usr/bin/mailstat /usr/bin/formail %doc /usr/man/*/* %doc [A-Z]* examples %changelog * Mon Sep 10 2001 Philip Guenther - 3.22-1 * Thu Jun 29 2001 Philip Guenther - 3.21-1 * Thu Jun 28 2001 Philip Guenther - 3.20-1 * Mon Jan 08 2001 Bennett Todd - 3.15.1-1: Massive cleanup, simplify - parameterize on %{name} and %{version} - make cleaner buildroot handling with wildcards in %files - moved this changelog to the bottom, cleaned some whitespace out of it. * Sat Nov 18 2000 Bennett Todd - 3.15-1 * Fri Jun 23 2000 Bennett Todd - Release 4: rebuilt from fresh tarball released today * Wed Jun 21 2000 Bennett Todd - Release 3: rebuilt from fresh tarball released today * Sat Dec 26 1999 Bennett Todd - Release 2 --- fixed perms following recommendation from Philip Guenther * Wed Dec 22 1999 Bennett Todd - Version 3.15pre * Wed Nov 24 1999 Bennett Todd - Version 3.14, Release 1 - dropped all patches * Fri Sep 24 1999 Bennett Todd - Added invert-Y patch, to make formail default to not trusting Content-Length. - changed from Name=>procmail Release=>maildir-3 to Name=>procmail-maildir Release=>4, since I couldn't get my rpm(1) to tolerate a Release of "maildir-4". * Thu Sep 09 1999 Bruce Guenter - Fixed permissions on mailstat (shell script must be readable). - Clarified man page information on how deliveries are done. * Tue Apr 06 1999 Bruce Guenter - Added nospoollock patch to avoid creating /var/spool/mail/USERNAME. - Updated to procmail 3.13.1 * Mon Apr 05 1999 Bruce Guenter - Added maildir patch Added no-lock-directory patch * Mon Apr 05 1999 James Bourne - updated to procmail 3.13 * Tue Jan 12 1999 James Bourne - added attr's to files section * Thu Jan 07 1999 James Bourne - Rebuilt RPM and SRPM with pgp signature and proper spec file for rhcn. * Thu Dec 17 1998 James Bourne - built RPM and SRPM. only changes are that the spec file uses it's own install section and does not use the procmail install methods. procmail-3.22/man/0040755006717600001440000000000007347314461013242 5ustar guenthersrcprocmail-3.22/man/formail.man0100644006717600001440000003617207347314422015373 0ustar guenthersrcPlease read the README file in this directory first. .ex .Id $Id: formail.man,v 1.46 2001/08/04 06:08:17 guenther Exp $ .TH FORMAIL 1 \*(Dt BuGless .SH NAME formail \- mail (re)formatter .SH SYNOPSIS .na .B formail .RI [ "\fB\@FM_SKIP@\fPskip" ] .RI [ "\fB\@FM_TOTAL@\fPtotal" ] .RB [ \-@FM_BOGUS@@FM_CONCATENATE@@FM_ZAPWHITE@@FM_FORCE@@FM_REPLY@@FM_KEEPB@@FM_TRUST@@FM_EVERY@@FM_DIGEST@@FM_QUIET@@FM_BABYL@@FM_BERKELEY@ ] .RB [ \-@FM_QPREFIX@ .IR prefix ] .if n .ti +0.5i .RB [ \-@FM_DUPLICATE@ .IR "maxlen idcache" ] .if n .ti +0.5i .RB [ \-@FM_LOGSUMMARY@ .IR folder ] .if n .ti +0.5i .RB [ \-@FM_EXTRACT@ .IR headerfield ] .RB [ \-@FM_EXTRC_KEEP@ .IR headerfield ] .if n .ti +0.5i .RB [ \-@FM_ADD_IFNOT@ .IR headerfield ] .RB [ \-@FM_ADD_ALWAYS@ .IR headerfield ] .if n .ti +0.5i .RB [ \-@FM_REN_INSERT@ .IR headerfield ] .RB [ \-@FM_DEL_INSERT@ .IR headerfield ] .if n .ti +0.5i .RB [ \-@FM_FIRST_UNIQ@ .IR headerfield ] .RB [ \-@FM_LAST_UNIQ@ .IR headerfield ] .if n .ti +0.5i .RB [ \-@FM_ReNAME@ .I oldfield .IR newfield ] .if n .ti +0.5i .RB [ \-@FM_NOWAIT@ .RI [ maxprocs ]] .RB [ \-@FM_MINFIELDS@ .IR minfields ] .RB [ \-@FM_SPLIT@ .RI [ command .RI [ arg \&.\|.\|.\|]]] .br .B formail .B \-@FM_VERSION@ .ad .SH DESCRIPTION .B formail is a filter that can be used to force mail into mailbox format, perform `@FROM@' escaping, generate auto-replying headers, do simple header munging/extracting or split up a mailbox/digest/articles file. The mail/mailbox/article contents will be expected on stdin. .PP If formail is supposed to determine the sender of the mail, but is unable to find any, it will substitute `@UNKNOWN@'. .PP If formail is started without any command line options, it will force any mail coming from stdin into mailbox format and will escape .B all bogus `@FROM@' lines with a `@ESCAP@'. .SH OPTIONS .TP 0.5i .B \-@FM_VERSION@ Formail will print its version number and exit. .TP .B \-@FM_BOGUS@ Don't escape any bogus mailbox headers (i.e., lines starting with `@FROM@'). .TP .I "\fB\-@FM_QPREFIX@\fP prefix" Define a different quotation prefix. If unspecified it defaults to `@ESCAP@'. .TP .B \-@FM_BERKELEY@ Assume traditional Berkeley mailbox format, ignoring any .B Content-Length: fields. .TP .B \-@FM_CONCATENATE@ Concatenate continued fields in the header. Might be convenient when postprocessing mail with standard (line oriented) text utilities. .TP .B \-@FM_ZAPWHITE@ Ensure a whitespace exists between field name and content. Zap fields which contain only a single whitespace character. Zap leading and trailing whitespace on fields extracted with .BR \-@FM_EXTRACT@ . .TP .B \-@FM_FORCE@ Force formail to simply pass along any non-mailbox format (i.e., don't generate a `@FROM@' line as the first line). .TP .B \-@FM_REPLY@ Generate an auto-reply header. This will normally throw away all the existing fields (except X-Loop:) in the original message, fields you wish to preserve need to be named using the .B \-@FM_REN_INSERT@ option. If you use this option in conjunction with .BR \-@FM_KEEPB@ , you can prevent the body from being `escaped' by also specifying .BR \-@FM_BOGUS@ . .TP .B \-@FM_KEEPB@ When generating the auto-reply header or when extracting fields, keep the body as well. .TP .B \-@FM_TRUST@ Trust the sender to have used a valid return address in his header. This causes formail to select the .I header sender instead of the .I envelope sender for the reply. This option should be used when generating auto-reply headers from news articles or when the sender of the message is expecting a reply. .TP .B \-@FM_SPLIT@ The input will be split up into separate mail messages, and piped into a program one by one (a new program is started for every part). .B \-@FM_SPLIT@ has to be the last option specified, the first argument following it is expected to be the name of a program, any other arguments will be passed along to it. If you omit the program, then formail will simply concatenate the split mails on stdout again. See .BR @FILENO@ . .TP .I "\fB\-@FM_NOWAIT@\fP [maxprocs]" Tell formail not to wait for every program to finish before starting the next (causes splits to be processed in parallel). .I Maxprocs optionally specifies an upper limit on the number of concurrently running processes. .TP .B \-@FM_EVERY@ Do not require empty lines to be preceding the header of a new message (i.e., the messages could start on every line). .TP .B \-@FM_DIGEST@ Tell formail that the messages it is supposed to split need not be in strict mailbox format (i.e., allows you to split digests/articles or non-standard mailbox formats). This disables recognition of the .B Content-Length: field. .TP .B \-@FM_LOGSUMMARY@ folder Generate a log summary in the same style as procmail. This includes the entire "From " line, the Subject: header field, the folder, and the size of the message in bytes. The mailstat command can be used to summarize logs in this format. .TP .B \-@FM_BABYL@ Makes formail assume that it is splitting up a BABYL rmail file. .TP .I "\fB\-@FM_MINFIELDS@\fP minfields" Allows you to specify the number of consecutive headerfields formail needs to find before it decides it found the start of a new message, it defaults to @DEFminfields@. .TP .B \-@FM_QUIET@ Tells formail to (still detect but) be quiet about write errors, duplicate messages and mismatched .B Content-Length: fields. This option is on by default, to make it display the messages use .BR \-@FM_QUIET@\- . .TP .I "\fB\-@FM_DUPLICATE@\fP maxlen idcache" Formail will detect if the Message-ID of the current message has already been seen using an .I idcache file of approximately .I maxlen size. If not splitting, it will return success if a duplicate has been found. If splitting, it will not output duplicate messages. If used in conjunction with .BR \-@FM_REPLY@ , formail will look at the .I mail address of the envelope sender .I instead at the Message-ID. .TP .I "\fB\-@FM_EXTRACT@\fP headerfield" Extract the contents of this .I headerfield from the header. Line continuations will be left intact; if you want the value on a single line then you'll also need the .B \-@FM_CONCATENATE@ option. .TP .I "\fB\-@FM_EXTRC_KEEP@\fP headerfield" Same as .BR \-@FM_EXTRACT@ , but also preserves/includes the field name. .TP .I "\fB\-@FM_ADD_IFNOT@\fP headerfield" Append a custom .I headerfield onto the header; but only if a similar field does not exist yet. If you specify either one of the field names .B Message-ID: or .B Resent-Message-ID: with no field contents, then formail will generate a unique message-ID for you. .TP .I "\fB\-@FM_ADD_ALWAYS@\fP headerfield" Append a custom .I headerfield onto the header in any case. .TP .I "\fB\-@FM_REN_INSERT@\fP headerfield" Same as .BR \-@FM_ADD_ALWAYS@ , except that any existing similar fields are renamed by prepending an ``@OLD_PREFIX@'' prefix. If .I headerfield consists only of a field-name, it will not be appended. .TP .I "\fB\-@FM_DEL_INSERT@\fP headerfield" Same as .BR \-@FM_REN_INSERT@ , except that any existing similar fields are simply removed. If .I headerfield consists only of a field-name, it effectively deletes the field. .TP .I "\fB\-@FM_FIRST_UNIQ@\fP headerfield" Make the first occurrence of this field unique, and thus delete all subsequent occurrences of it. .TP .I "\fB\-@FM_LAST_UNIQ@\fP headerfield" Make the last occurrence of this field unique, and thus delete all preceding occurrences of it. .TP .I "\fB\-@FM_ReNAME@\fP oldfield newfield" Renames all occurrences of the fieldname .I oldfield into .IR newfield . .TP .I "\fB\@FM_SKIP@\fPskip" Skip the first .I skip messages while splitting. .TP .I "\fB\@FM_TOTAL@\fPtotal" Output at most .I total messages while splitting. .SH NOTES When renaming, removing, or extracting fields, partial fieldnames may be used to specify all fields that start with the specified value. .PP By default, when generating an auto-reply header procmail selects the envelope sender from the input message. This is correct for vacation messages and other automatic replies regarding the routing or delivery of the original message. If the sender is expecting a reply or the reply is being generated in response to the contents of the original message then the \-@FM_TRUST@ option should be used. .PP .BR RFC822 , the original standard governing the format of Internet mail messages, did not specify whether Resent header fields (those that begin with `Resent\-', such as `Resent\-From:') should be considered when generating a reply. Since then, the recommended usage of the Resent headers has evolved to consider them as purely informational and not for use when generating a reply. This has been codified in .BR RFC2822 , the new Internet Message Format standard, which states in part: .IP Resent fields are used to identify a message as having been reintroduced into the transport system by a user. The purpose of using resent fields is to have the message appear to the final recipient as if it were sent directly by the original sender, with all of the original fields remaining the same.\|\|.\|.\|.\|\|They MUST NOT be used in the normal processing of replies or other such automatic actions on messages. .PP While formail now ignores Resent headers when generating header replies, versions of formail prior to 3.14 gave such headers a high precedence. If the old behavior is needed for established applications it can be specified by calling formail with the option `-@FM_ADD_IFNOT@ Resent-' in addition to the \-@FM_REPLY@ and \-@FM_TRUST@ options. This usage is deprecated and should not be used in new applications. .SH ENVIRONMENT .TP .5i .B @FILENO@ While splitting, formail assigns the message number currently being output to this variable. By presetting @FILENO@, you can change the initial message number being used and the width of the zero-padded output. If @FILENO@ is unset it will default to @DEFfileno@. If @FILENO@ is non-empty and does not contain a number, @FILENO@ generation is disabled. .SH EXAMPLES To split up a digest one usually uses: .RS formail @FM_SKIP@1 \-@FM_DIGEST@@FM_SPLIT@ >>the_mailbox_of_your_choice .RE or .RS formail @FM_SKIP@1 \-@FM_DIGEST@@FM_SPLIT@ procmail .RE .PP To remove all Received: fields from the header: .RS formail \-@FM_DEL_INSERT@ Received: .RE .PP To remove all fields except From: and Subject: from the header: .RS formail \-@FM_KEEPB@ \-@FM_EXTRC_KEEP@ From: \-@FM_EXTRC_KEEP@ Subject: .RE .PP To supersede the Reply-To: field in a header you could use: .RS formail \-@FM_REN_INSERT@ "Reply-To: foo@bar" .RE .PP To convert a non-standard mailbox file into a standard mailbox file you can use: .RS formail \-@FM_DIGEST@@FM_SPLIT@ >new_mailbox .RE .PP Or, if you have a very tolerant mailer: .RS formail \-@FM_ADD_IFNOT@ Date: \-@FM_DIGEST@@FM_SPLIT@ >new_mailbox .RE .PP To extract the header from a message: .RS formail \-@FM_EXTRC_KEEP@ "" .RE or .RS sed \-e '/^$/ q' .RE .PP To extract the body from a message: .RS formail \-@FM_DEL_INSERT@ "" .RE or .RS sed \-e '1,/^$/ d' .RE .SH "SEE ALSO" .na .nh .BR mail (1), .BR binmail (1), .BR sendmail (8), .BR procmail (1), .BR sed (1), .BR sh (1), .BR RFC822 , .BR RFC2822 , .B RFC1123 .hy .ad .SH DIAGNOSTICS .TP 2.3i Can't fork Too many processes on this machine. .TP Content-Length: field exceeds actual length by nnn bytes The Content-Length: field in the header specified a length that was longer than the actual body. This causes this message to absorb a number of subsequent messages following it in the same mailbox. .TP Couldn't write to stdout The program that formail was trying to pipe into didn't accept all the data formail sent to it; this diagnostic can be suppressed by the .B \-@FM_QUIET@ option. .TP Duplicate key found: x The Message-ID or sender x in this message was found in the idcache; this diagnostic can be suppressed by the .B \-@FM_QUIET@ option. .TP Failed to execute "x" Program not in path, or not executable. .TP File table full Too many open files on this machine. .TP Invalid field-name: "x" The specified field-name "x" contains control characters, or cannot be a partial field-name for this option. .SH WARNINGS You can save yourself and others a lot of grief if you try to avoid using this autoreply feature on mails coming through mailinglists. Depending on the format of the incoming mail (which in turn depends on both the original sender's mail agent and the mailinglist setup) formail could decide to generate an autoreply header that replies to the list. .PP In the tradition of UN*X utilities, formail will do exactly what you ask it to, even if it results in a .RB non- RFC822 compliant message. In particular, formail will let you generate header fields whose name ends in a space instead of a colon. While this is correct for the leading `@FROM@' line, that line is not a header field so much as the message separator for the mbox mailbox format. Multiple occurrences of such a line or any other colonless header field will be considered by many mail programs, including formail itself, as the beginning of a new message. Others will consider the message to be corrupt. Because of this, you should not use the .B \-@FM_REN_INSERT@ option with the `@FROM@' line as the resulting renamed line, `@OLD_PREFIX@@FROM@', will probably not do what you want it to. If you want to save the original `@FROM@' line, rename it with the .B \-@FM_ReNAME@ option to a legal header field such as `X-From_:'. .SH BUGS When formail has to generate a leading `@FROM@' line it normally will contain the current date. If formail is given the option `\-@FM_ADD_IFNOT@ Date:', it will use the date from the `Date:' field in the header (if present). However, since formail copies it verbatim, the format will differ from that expected by most mail readers. .PP If formail is instructed to delete or rename the leading `@FROM@' line, it will not automatically regenerate it as usual. To force formail to regenerate it in this case, include \fB\-@FM_ADD_IFNOT@ '@FROM@'\fP. .PP If formail is not called as the first program in a pipe and it is told to split up the input in several messages, then formail will not terminate until the program it receives the input from closes its output or terminates itself. .PP If formail is instructed to generate an autoreply mail, it will .B never put more than one address in the `To:' field. .SH MISCELLANEOUS Formail is eight-bit clean. .PP When formail has to determine the sender's address, every .B RFC822 conforming mail address is allowed. Formail will always strip down the address to its minimal form (deleting excessive comments and whitespace). .PP The regular expression that is used to find `real' postmarks is: .RS "\en\en@FROM@[\et ]*[^\et\en ]+[\et ]+[^\en\et ]" .RE .PP If a .B Content-Length: field is found in a header, formail will copy the number of specified bytes in the body verbatim before resuming the regular scanning for message boundaries (except when splitting digests or Berkeley mailbox format is assumed). .PP Any header lines immediately following the leading `@FROM@' line that start with `@ESCAP@@FROM@' are considered to be a continuation of the `@FROM@' line. If instructed to rename the `@FROM@' line, formail will change each leading `@ESCAP@' into a space, thereby transforming those lines into normal .B RFC822 continuations. .SH NOTES Calling up formail with the \-@HELPOPT1@ or \-@HELPOPT2@ options will cause it to display a command-line help page. procmail-3.22/man/procmail.man0100644006717600001440000006116607347314423015552 0ustar guenthersrcPlease read the README file in this directory first. .ex .Id $Id: procmail.man,v 1.91 2001/08/27 08:44:04 guenther Exp $ .TH PROCMAIL 1 \*(Dt BuGless .na .SH NAME procmail \- autonomous mail processor .SH SYNOPSIS .B procmail .RB [ \-@PRESERVOPT@@TEMPFAILOPT@@OVERRIDEOPT@@BERKELEYOPT@ ] .RB [ "\-@FROMWHOPT@ \fIfromwhom\fP" ] .if n .ti +0.5i .RI [ "parameter\fB=\fPvalue " | " rcfile" ] \&.\|.\|. .br .B procmail .RB [ \-@TEMPFAILOPT@@OVERRIDEOPT@@BERKELEYOPT@ ] .RB [ "\-@FROMWHOPT@ \fIfromwhom\fP" ] .RB [ "\-@ARGUMENTOPT@ \fIargument\fP" ] \&.\|.\|. .if n .ti +0.5i .B \-@DELIVEROPT@ .I recipient \&.\|.\|. .br .B procmail .RB [ \-@PRESERVOPT@@TEMPFAILOPT@@BERKELEYOPT@ ] .B \-@MAILFILTOPT@ .RI [ "parameter\fB=\fPvalue" ] \&.\|.\|. .I rcfile .if n .ti +0.5i .RI [ argument ] \&.\|.\|.@LMTPusage@.br .B procmail .B \-@VERSIONOPT@ .ad .SH DESCRIPTION For a quick start, see .B NOTES at the end. .PP .B Procmail should be invoked automatically over the .B @DOT_FORWARD@ file mechanism as soon as mail arrives. Alternatively, when installed by a system administrator, it can be invoked from within the mailer immediately. When invoked, it first sets some environment variables to default values, reads the mail message from stdin until an EOF, separates the body from the header, and then, if no command line arguments are present, it starts to look for a file named .BR @PROCMAILRC@ . According to the processing recipes in this file, the mail message that just arrived gets distributed into the right folder (and more). If no rcfile is found, or processing of the rcfile falls off the end, procmail will store the mail in the default system mailbox.@ETCRC_desc@ .PP If running suid root or with root privileges, procmail will be able to perform as a functionally enhanced, backwards compatible mail delivery agent. .PP Procmail can also be used as a general purpose mail filter, i.e., provisions have been made to enable procmail to be invoked in a special sendmail rule. .PP The rcfile format is described in detail in the .BR procmailrc (5) man page. .PP The weighted scoring technique is described in detail in the .BR procmailsc (5) man page. .PP Examples for rcfile recipes can be looked up in the .BR procmailex (5) man page. .SS Signals .TP 1.2i .B TERMINATE Terminate prematurely and requeue the mail. .TP .B HANGUP Terminate prematurely and bounce the mail. .TP .B INTERRUPT Terminate prematurely and bounce the mail. .TP .B QUIT Terminate prematurely and silently lose the mail. .TP .B ALARM Force a timeout (see .BR TIMEOUT). .TP .B USR1 Equivalent to a .BR VERBOSE =off. .TP .B USR2 Equivalent to a .BR VERBOSE =on. .SH OPTIONS .TP 0.5i .B \-@VERSIONOPT@ Procmail will print its version number, display its compile time configuration and exit. .TP .B \-@PRESERVOPT@ Preserve any old environment. Normally procmail clears the environment upon startup@KEEPENV@. However, in any case: any default values will override any preexisting environment variables, i.e., procmail will not pay any attention to any predefined environment variables, it will happily overwrite them with its own defaults. For the list of environment variables that procmail will preset see the .BR procmailrc (5) man page. If both \-@PRESERVOPT@ and \-@MAILFILTOPT@ are specified, the list of preset environment variables shrinks to just: LOGNAME, HOME, SHELL, ORGMAIL and MAILDIR. .TP .B \-@TEMPFAILOPT@ Make procmail fail softly, i.e., if procmail cannot deliver the mail to any of the destinations you gave, the mail will not bounce, but will return to the mailqueue. Another delivery-attempt will be made at some time in the future. .TP .I "\fB\-@FROMWHOPT@\fP fromwhom" Causes procmail to regenerate the leading `@FROM@' line with .I fromwhom as the sender (instead of \-@FROMWHOPT@ one could use the alternate and obsolete \-@ALTFROMWHOPT@). If .I fromwhom consists merely of a single `@REFRESH_TIME@', then procmail will only update the timestamp on the `@FROM@' line (if present, if not, it will generate a new one). .TP .B \-@OVERRIDEOPT@ Instead of allowing anyone to generate `@FROM@' lines, simply override the fakes. .TP .B \-@BERKELEYOPT@ Assume traditional Berkeley mailbox format, ignore any .B Content-Length: fields. .TP .I "\fB\-@ARGUMENTOPT@\fP argument" This will set $1 to be equal to .IR argument . Each succeeding .I "\fB\-@ARGUMENTOPT@\fP argument" will set the next number variable ($2, $3, etc). It can be used to pass meta information along to procmail. This is typically done by passing along the $@x information from the sendmail mailer rule. .TP .I "\fB\-@DELIVEROPT@\fP recipient .\|.\|." This turns on explicit delivery mode, delivery will be to the local user .IR recipient . This, of course, only is possible if procmail has root privileges@SETRUID@. Procmail will setuid to the intended recipients and delivers the mail as if it were invoked by the recipient with no arguments (i.e., if no rcfile is found, delivery is like ordinary mail). This option is incompatible with .BR \-@PRESERVOPT@ . .TP .B \-@MAILFILTOPT@ Turns procmail into a general purpose mail filter. In this mode one rcfile must be specified on the command line. After the rcfile, procmail will accept an unlimited number of arguments.@ETCRCS_desc@ For some advanced usage of this option you should look in the .B EXAMPLES section below.@LMTPOPTdesc@.SH ARGUMENTS Any arguments containing an '=' are considered to be environment variable assignments, they will .I all be evaluated after the default values have been assigned and before the first rcfile is opened. .PP Any other arguments are presumed to be rcfile paths (either absolute, or if they start with `@chCURDIR@@MCDIRSEP@' relative to the current directory; .B any other relative path is relative to $HOME, unless the .B \-@MAILFILTOPT@ option has been given, in which case all relative paths are relative to the current directory); procmail will start with the first one it finds on the command line. The following ones will only be parsed if the preceding ones have a not matching HOST-directive entry, or in case they should not exist. .PP If no rcfiles are specified, it looks for .BR @PROCMAILRC@ . If not even that can be found, processing will continue according to the default settings of the environment variables and the ones specified on the command line. .SH EXAMPLES Examples for rcfile recipes can be looked up in the .BR procmailex (5) man page. A small sample rcfile can be found in the .B NOTES section below. .PP Skip the rest of this EXAMPLES section unless you are a system administrator who is vaguely familiar with sendmail.cf syntax. .PP The .B \-@MAILFILTOPT@ option is typically used when procmail is called from within a rule in the sendmail.cf file. In order to be able to do this it is convenient to create an extra `procmail' mailer in your sendmail.cf file (in addition to the perhaps already present `local' mailer that starts up procmail). To create such a `procmail' mailer I'd suggest something like: .Sx 2 Mprocmail, P=@BINDIR@/procmail, F=mSDFMhun, S=11, R=21, A=procmail \-@MAILFILTOPT@ $h $g $u .Ex This enables you to use rules like the following (most likely in ruleset 0) to filter mail through the procmail mailer (please note the leading tab to continue the rule, and the tab to separate the comments): .Sx 4 R$*<@some.where>$* $#procmail $@/etc/procmailrcs/some.rc $:$1@some.where.procmail$2 R$*<@$*.procmail>$* $1<@$2>$3 Already filtered, map back .Ex And /etc/procmailrcs/some.rc could be as simple as: .Sx 9 SENDER = "<$1>" # fix for empty sender addresses SHIFT = 1 # remove it from $@ :0 # sink all junk mail * ^Subject:.*junk /dev/null :0 w # pass along all other mail ! \-oi \-f "$SENDER" "$@" .Ex Do watch out when sending mail from within the /etc/procmailrcs/some.rc file, if you send mail to addresses which match the first rule again, you could be creating an endless mail loop. .SH FILES .TP 2.3i .B /etc/passwd to set the recipient's LOGNAME, HOME and SHELL variable defaults .TP .B @MAILSPOOLDIR@$LOGNAME system mailbox; both the system mailbox and the immediate directory it is in will be created every time procmail starts and either one is not present@ETCRC_files@@ETCRCS_files@ .TP .B @PROCMAILRC@ default rcfile .TP .B @MAILSPOOLDIR@$LOGNAME@DEFlockext@ lockfile for the system mailbox (not automatically used by procmail, unless $DEFAULT equals @MAILSPOOLDIR@$LOGNAME and procmail is delivering to $DEFAULT) .TP .B @DEFsendmail@ default mail forwarder .TP .B @UNIQ_PREFIX@????`hostname` temporary `unique' zero-length files created by procmail .SH "SEE ALSO" .na .nh .BR procmailrc (5), .BR procmailsc (5), .BR procmailex (5), .BR sh (1), .BR csh (1), .BR mail (1), .BR mailx (1), .BR binmail (1), .BR uucp (1), .BR aliases (5), .BR sendmail (8), .BR egrep (1), .BR grep (1), .BR biff (1), .BR comsat (8), .BR lockfile (1), .BR formail (1), .BR cron (1) .hy .ad .SH DIAGNOSTICS .TP 2.3i Autoforwarding mailbox found The system mailbox had its suid or sgid bit set, procmail terminates with EX_NOUSER assuming that this mailbox must not be delivered to. .TP Bad substitution of "x" Not a valid environment variable name specified. .TP Closing brace unexpected There was no corresponding opening brace (nesting block). .TP Conflicting options Not all option combinations are useful .TP Conflicting x suppressed Flag x is not compatible with some other flag on this recipe. .TP Couldn't create "x" The system mailbox was missing and could not/will not be created. .TP Couldn't create maildir part "x" The maildir folder "x" is missing one or more required subdirectories and procmail could not create them. .TP Couldn't create or rename temp file "x" An error occurred in the mechanics of delivering to the directory folder "x". .TP Couldn't determine implicit lockfile from "x" There were no `>>' redirectors to be found, using simply `$LOCKEXT' as locallockfile. .TP Couldn't read "x" Procmail was unable to open an rcfile or it was not a regular file, or procmail couldn't open an MH directory to find the highest numbered file. .TP Couldn't unlock "x" Lockfile was already gone, or write permission to the directory where the lockfile is has been denied. .TP Deadlock attempted on "x" The locallockfile specified on this recipe is equal to a still active $LOCKFILE.@ETCRCS_error@ .TP Descriptor "x" was not open As procmail was started, stdin, stdout or stderr was not connected (possibly an attempt to subvert security) .TP Enforcing stricter permissions on "x" The system mailbox of the recipient was found to be unsecured, procmail secured it. .TP Error while writing to "x" Nonexistent subdirectory, no write permission, pipe died or disk full. .TP Exceeded LINEBUF Buffer overflow detected, LINEBUF was too small, PROCMAIL_OVERFLOW has been set. .TP MAILDIR is not an absolute path .TP MAILDIR path too long .TP ORGMAIL is not an absolute path .TP ORGMAIL path too long .TP default rcfile is not an absolute path .TP default rcfile path too long The specified item's full path, when expanded, was longer than LINEBUF or didn't start with a file separator. .TP Excessive output quenched from "x" The program or filter "x" tried to produce too much output for the current LINEBUF, the rest was discarded and PROCMAIL_OVERFLOW has been set. .TP Extraneous x ignored The action line or other flags on this recipe makes flag x meaningless. .TP Failed forking "x" Process table is full (and NORESRETRY has been exhausted). .TP Failed to execute "x" Program not in path, or not executable. .TP Forced unlock denied on "x" No write permission in the directory where .B lockfile "x" resides, or more than one procmail trying to force a lock at exactly the same time. .TP Forcing lock on "x" .B Lockfile "x" is going to be removed by force because of a timeout (see also: .BR LOCKTIMEOUT ). .TP Incomplete recipe The start of a recipe was found, but it stranded in an EOF. .TP Insufficient privileges Procmail either needs root privileges, or must have the right (e)uid .B and (e)gid to run in delivery mode. The mail will bounce. .TP Invalid regexp "x" The regular expression "x" contains errors (most likely some missing or extraneous parens). .TP Kernel-lock failed While trying to use the kernel-supported locking calls, one of them failed (usually indicates an OS error), procmail ignores this error and proceeds. .TP Kernel-unlock failed See above. .TP Lock failure on "x" Can only occur if you specify some real weird (and illegal) lockfilenames or if the .B lockfile could not be created because of insufficient permissions or nonexistent subdirectories. .TP Lost "x" Procmail tried to clone itself but could not find back rcfile "x" (it either got removed or it was a relative path and you changed directory since procmail opened it last time). .TP Missing action The current recipe was found to be incomplete. .TP Missing closing brace A nesting block was started, but never finished. .TP Missing name The \-@FROMWHOPT@ option needs an extra argument. .TP Missing argument You specified the \-@ARGUMENTOPT@ option but forgot the argument. .TP Missing rcfile You specified the \-@MAILFILTOPT@ option, procmail expects the name of an rcfile as argument. .TP Missing recipient You specified the \-@DELIVEROPT@ option or called procmail under a different name, it expects one or more recipients as arguments.@WARN_RESTRICT_EXEC@ .TP No space left to finish writing "x" The filesystem containing "x" does not have enough free space to permit delivery of the message to the file. .TP Out of memory The system is out of swap space (and NORESRETRY has been exhausted). .TP Processing continued The unrecognised options on the command line are ignored, proceeding as usual. .TP Program failure (nnn) of "x" Program that was started by procmail returned nnn instead of EXIT_SUCCESS (=@EX_OK@); if nnn is negative, then this is the signal the program died on. .TP Quota exceeded while writing "x" The filesize quota for the recipient on the filesystem containing "x" does not permit delivering the message to the file. .TP Renaming bogus "x" into "x" The system mailbox of the recipient was found to be bogus, procmail performed evasive actions. .TP Rescue of unfiltered data succeeded/failed A filter returned unsuccessfully, procmail tried to get back the original text. .TP Skipped: "x" Couldn't do anything with "x" in the rcfile (syntax error), ignoring it. .TP Suspicious rcfile "x" The owner of the rcfile was not the recipient or root, the file was world writable, or the directory that contained it was world writable, or this was the default rcfile (@PROCMAILRC@) and either it was group writable or the directory that contained it was group writable (the rcfile was not used). .TP Terminating prematurely whilst waiting for .\|.\|. Procmail received a signal while it was waiting for .\|.\|. .TP Timeout, terminating "x" Timeout has occurred on program or filter "x". .TP Timeout, was waiting for "x" Timeout has occurred on program, filter or file "x". If it was a program or filter, then it didn't seem to be running anymore. .TP Truncated file to former size The file could not be delivered to successfully, so the file was truncated to its former size. .TP Truncating "x" and retrying lock "x" does not seem to be a valid filename or the file is not empty. .TP Unable to treat as directory "x" Either the suffix on "x" would indicate that it should be an MH or maildir folder, or it was listed as an second folder into which to link, but it already exists and is not a directory. .TP Unexpected EOL Missing closing quote, or trying to escape EOF. .TP Unknown user "x" The specified recipient does not have a corresponding uid. .SH "EXTENDED DIAGNOSTICS" Extended diagnostics can be turned on and off through setting the VERBOSE variable. .TP 2.3i [pid] time & date Procmail's pid and a timestamp. Generated whenever procmail logs a diagnostic and at least a second has elapsed since the last timestamp. .TP Acquiring kernel-lock Procmail now tries to kernel-lock the most recently opened file (descriptor). .TP Assigning "x" Environment variable assignment. .TP Assuming identity of the recipient, VERBOSE=off Dropping all privileges (if any), implicitly turns off extended diagnostics. .TP Bypassed locking "x" The mail spool directory was not accessible to procmail, it relied solely on kernel locks. .TP Executing "x" Starting program "x". If it is started by procmail directly (without an intermediate shell), procmail will show where it separated the arguments by inserting commas. .TP HOST mismatched "x" This host was called "x", HOST contained something else. .TP Locking "x" Creating lockfile "x". .TP Linking to "x" Creating a hardlink between directory folders. .TP Match on "x" Condition matched. .TP Matched "x" Assigned "x" to .BR @MATCHVAR@ . .TP No match on "x" Condition didn't match, recipe skipped. .TP Non-zero exitcode (nnn) by "x" Program that was started by procmail as a condition or as the action of a recipe with the `@WAIT_EXIT_QUIET@' flag returned nnn instead of EXIT_SUCCESS (=@EX_OK@); the usage indicates that this is not an entirely unexpected condition. .TP Notified comsat: "$LOGNAME@offset:file" Sent comsat/biff a notice that mail arrived for user $LOGNAME at `offset' in `file'. .TP Opening "x" Opening file "x" for appending. .TP Rcfile: "x" Rcfile changed to "x". .TP Reiterating kernel-lock While attempting several locking methods, one of these failed. Procmail will reiterate until they all succeed in rapid succession. .TP Score: added newtotal "x" This condition scored `added' points, which resulted in a `newtotal' score. .TP Unlocking "x" Removing lockfile "x" again. .SH WARNINGS You should create a shell script that uses .BR lockfile (1) before invoking your mail shell on any mailbox file other than the system mailbox (unless of course, your mail shell uses the same lockfiles (local or global) you specified in your rcfile). .PP In the unlikely event that you absolutely need to kill procmail before it has finished, first try and use the regular kill command (i.e., .I not kill \-9, see the subsection .I Signals for suggestions), otherwise some .I lockfiles might not get removed. .PP Beware when using the .B \-@TEMPFAILOPT@ option, if procmail repeatedly is unable to deliver the mail (e.g., due to an incorrect rcfile), the system mailqueue could fill up. This could aggravate both the local postmaster and other users.@ETCRC_warn@@ETCRCS_warn@ .PP Procmail is not the proper tool for sharing one mailbox among many users, such as when you have one POP account for all mail to your domain. It can be done if you manage to configure your MTA to add some headers with the envelope recipient data in order to tell Procmail who a message is for, but this is usually not the right thing to do. Perhaps you want to investigate if your MTA offers `virtual user tables', or check out the `multidrop' facility of Fetchmail. .SH BUGS After removing a lockfile by force, procmail waits $SUSPEND seconds before creating a new lockfile so that another process that decides to remove the stale lockfile will not remove the newly created lock by mistake. .PP Procmail uses the regular TERMINATE signal to terminate any runaway filter, but it does not check if the filter responds to that signal and it only sends it to the filter itself, not to any of the filter's children. .PP A continued .B Content-Length: field is not handled correctly. .PP The embedded newlines in a continued header should be skipped when matching instead of being treated as a single space as they are now. .SH MISCELLANEOUS If there is an existing .B Content-Length: field in the header of the mail and the .B \-@BERKELEYOPT@ option is not specified, procmail will trim the field to report the correct size. Procmail does not change the fieldwidth. .PP If there is no .B Content-Length: field or the .B \-@BERKELEYOPT@ option has been specified and procmail appends to regular mailfolders, any lines in the body of the message that look like postmarks are prepended with `@ESCAP@' (disarms bogus mailheaders). The regular expression that is used to search for these postmarks is: .RS `@FROM_EXPR@' .RE .PP If the destination name used in explicit delivery mode is not in /etc/passwd, procmail will proceed as if explicit delivery mode was not in effect. If not in explicit delivery mode and should the uid procmail is running under, have no corresponding /etc/passwd entry, then HOME will default to @ROOT_DIR@, LOGNAME will default to #uid, SHELL will default to @BinSh@, and ORGMAIL will default to @DEAD_LETTER@. .PP When in explicit delivery mode, procmail will generate a leading `@FROM@' line if none is present. If one is already present procmail will leave it intact.@TRUSTED_IDS@ .PP For security reasons procmail will only use an absolute or $HOME-relative rcfile if it is owned by the recipient or root, not world writable, and the directory it is contained in is not world writable. The @PROCMAILRC@ file has the additional constraint of not being group-writable or in a group-writable directory.@RESTRICT_EXEC@ .PP If @MAILSPOOLDIR@$LOGNAME is a bogus mailbox (i.e., does not belong to the recipient, is unwritable, is a symbolic link or is a hard link), procmail will upon startup try to rename it into a file starting with `@BOGUSprefix@$LOGNAME.' and ending in an inode-sequence-code. If this turns out to be impossible, .B ORGMAIL will have .I no initial value, and hence will inhibit delivery without a proper rcfile. .PP If @MAILSPOOLDIR@$LOGNAME already is a valid mailbox, but has got too loose permissions on it, procmail will correct this. To prevent procmail from doing this make sure the u+x bit is set. .PP When delivering to directories, MH folders, or maildir folders, you .B don't need to use lockfiles to prevent several concurrently running procmail programs from messing up. .PP Delivering to MH folders is slightly more time consuming than delivering to normal directories or mailboxes, because procmail has to search for the next available number (instead of having the filename immediately available). .PP On general failure procmail will return EX_CANTCREAT, unless option .B \-@TEMPFAILOPT@ is specified, in which case it will return EX_TEMPFAIL. .PP To make `egrepping' of headers more consistent, procmail concatenates all continued header fields; but only internally. When delivering the mail, line breaks will appear as before. .PP If procmail is called under a name not starting with `procmail' (e.g., if it is linked to another name and invoked as such), it comes up in explicit delivery mode, and expects the recipients' names as command line arguments (as if \-@DELIVEROPT@ had been specified). .PP Comsat/biff notifications are done using @COMSATprotocol@. They are sent off once when procmail generates the regular logfile entry. The notification messages have the following extended format (or as close as you can get when final delivery was not to a file): .RS $LOGNAME@offset_of_message_in_mailbox@COMSATxtrsep@absolute_path_to_mailbox .RE .PP Whenever procmail itself opens a file to deliver to, it @KERNEL_LOCKING@. .PP Procmail is NFS-resistant and eight-bit clean. .br .ne 11 .SH NOTES Calling up procmail with the \-@HELPOPT1@ or \-@HELPOPT2@ options will cause it to display a command-line help and recipe flag quick-reference page. .PP There exists an excellent newbie FAQ about mailfilters (and procmail in particular); it is maintained by Nancy McGough and can be obtained by sending a mail to mail-server@rtfm.mit.edu with the following in the body: .RS send usenet/news.answers/mail/filtering-faq .RE .PP @CF_procmail@ In this case your $HOME/@DOT_FORWARD@ (beware, it .B has to be world readable) file should contain the line below. Be sure to include the single and double quotes, and unless you know your site to be running smrsh (the SendMail Restricted SHell), it must be an .I absolute path.@FW_comment@ .PP .na .nf @FW_content@ .fi .ad .PP Procmail can also be invoked to postprocess an already filled system mailbox. This can be useful if you don't want to or can't use a $HOME/@DOT_FORWARD@ file (in which case the following script could periodically be called from within .BR cron (1), or whenever you start reading mail): .Sx 17 #!/bin/sh ORGMAIL=@MAILSPOOLDIR@$LOGNAME if cd $HOME && test \-s $ORGMAIL && lockfile \-r0 \-l@DEFlocktimeout@ .newmail.lock 2>/dev/null then trap "rm \-f .newmail.lock" 1 2 3 13 15 umask 077 lockfile \-l@DEFlocktimeout@ \-ml cat $ORGMAIL >>.newmail && cat /dev/null >$ORGMAIL lockfile \-mu formail \-@FM_SPLIT@ procmail <.newmail && rm \-f .newmail rm \-f .newmail.lock fi exit 0 .Ex .ne 14 .SS "A sample small @PROCMAILRC@:" .na .nf PATH=/bin:/usr/bin:@BINDIR@ MAILDIR=$HOME/Mail #you'd better make sure it exists DEFAULT=$MAILDIR/mbox #completely optional LOGFILE=$MAILDIR/from #recommended :0: * ^From.*berg from_me :0 * ^Subject:.*Flame /dev/null .fi .ad .PP Other examples for rcfile recipes can be looked up in the .BR procmailex (5) man page. procmail-3.22/man/lockfile.man0100644006717600001440000001340307316762754015535 0ustar guenthersrcPlease read the README file in this directory first. .ex .Id $Id: lockfile.man,v 1.18 2001/06/23 08:22:25 guenther Exp $ .TH LOCKFILE 1 \*(Dt BuGless .na .SH NAME lockfile \- conditional semaphore-file creator .SH SYNOPSIS .B lockfile .I "\fB\-\fPsleeptime" | .I "\fB\-r \fPretries" | .if n .ti +0.5i .I "\fB\-l \fPlocktimeout" | .I "\fB\-s \fPsuspend" | .B "\-!" | .B "\-ml" | .B "\-mu" | .I filename \&.\|.\|. .ad .SH DESCRIPTION .B lockfile can be used to create one or more .I semaphore .IR files . If lockfile can't create all the specified files (in the specified order), it waits .I sleeptime (defaults to @DEFlocksleep@) seconds and retries the last file that didn't succeed. You can specify the number of .I retries to do until failure is returned. If the number of .I retries is \-1 (default, i.e., .BR \-r\-1 ) lockfile will retry forever. .PP If the number of .I retries expires before all files have been created, lockfile returns failure and removes all the files it created up till that point. .PP Using lockfile as the condition of a loop in a shell script can be done easily by using the .B \-! flag to invert the exit status. To prevent infinite loops, failures for any reason other than the lockfile already existing are not inverted to success but rather are still returned as failures. .PP All flags can be specified anywhere on the command line, they will be processed when encountered. The command line is simply parsed from left to right. .PP All files created by lockfile will be read-only, and therefore will have to be removed with .B rm .BR \-f . .PP If you specify a .I locktimeout then a lockfile will be removed by force after locktimeout seconds have passed since the lockfile was last modified/created (most likely by some other program that unexpectedly died a long time ago, and hence could not clean up any leftover lockfiles). Lockfile is clock skew immune. After a lockfile has been removed by force, a suspension of .I suspend seconds (defaults to @DEFsuspend@) is taken into account, in order to prevent the inadvertent immediate removal of any newly created lockfile by another program (compare .BR SUSPEND in .BR procmail (1)). .SS "Mailbox locks" If the permissions on the system mail spool directory allow it, or if lockfile is suitably setgid, it will be able to lock and unlock your system mailbox by using the options .B "\-ml" and .B "\-mu" respectively. .SH EXAMPLES Suppose you want to make sure that access to the file "important" is serialised, i.e., no more than one program or shell script should be allowed to access it. For simplicity's sake, let's suppose that it is a shell script. In this case you could solve it like this: .RS \&.\|.\|. lockfile important.lock \&.\|.\|. access_"important"_to_your_hearts_content \&.\|.\|. rm \-f important.lock \&.\|.\|. .RE Now if all the scripts that access "important" follow this guideline, you will be assured that at most one script will be executing between the `lockfile' and the `rm' commands. .SH ENVIRONMENT .TP 2.3i .B LOGNAME used as a hint to determine the invoker's loginname .SH FILES .TP 2.3i .B /etc/passwd to verify and/or correct the invoker's loginname (and to find out his HOME directory, if needed) .TP .B @MAILSPOOLDIR@$LOGNAME@DEFlockext@ lockfile for the system mailbox, the environment variables present in here will not be taken from the environment, but will be determined by looking in /etc/passwd .SH "SEE ALSO" .na .nh .BR rm (1), .BR mail (1), .BR binmail (1), .BR sendmail (8), .BR procmail (1) .hy .ad .SH DIAGNOSTICS .TP 2.3i Filename too long, .\|.\|. Use shorter filenames. .TP Forced unlock denied on "x" No write permission in the directory where lockfile "x" resides, or more than one lockfile trying to force a lock at exactly the same time. .TP Forcing lock on "x" Lockfile "x" is going to be removed by force because of a timeout (compare .BR LOCKTIMEOUT in .BR procmail (1)). .TP Out of memory, .\|.\|. The system is out of swap space. .TP Signal received, .\|.\|. Lockfile will remove anything it created till now and terminate. .TP Sorry, .\|.\|. The .I retries limit has been reached. .TP Truncating "x" and retrying lock "x" does not seem to be a valid filename. .TP Try praying, .\|.\|. Missing subdirectories or insufficient privileges. .SH BUGS Definitely less than one. .SH WARNINGS The behavior of the .B \-! flag, while useful, is not necessarily intuitive or consistent. When testing lockfile's return value, shell script writers should consider carefully whether they want to use the .B \-! flag, simply reverse the test, or do a switch on the exact exitcode. In general, the .B \-! flag should only be used when lockfile is the conditional of a loop. .SH MISCELLANEOUS Lockfile is NFS-resistant and eight-bit clean. .SH NOTES Calling up lockfile with the \-@HELPOPT1@ or \-@HELPOPT2@ options will cause it to display a command-line help page. Calling it up with the \-@VERSIONOPT@ option will cause it to display its version information. .PP Multiple .B \-! flags will toggle the return status. .PP Since flags can occur anywhere on the command line, any filename starting with a '-' has to be preceded by './'. .PP The number of .I retries will not be reset when any following file is being created (i.e., they are simply used up). It can, however, be reset by specifying .RI \-r newretries after every file on the command line. .PP Although files with any name can be used as lockfiles, it is common practice to use the extension `.lock' to lock mailfolders (it is appended to the mailfolder name). In case one does not want to have to worry about too long filenames and does not have to conform to any other lockfilename convention, then an excellent way to generate a lockfilename corresponding to some already existing file is by taking the prefix `lock.' and appending the i-node number of the file which is to be locked. procmail-3.22/man/procmailex.man0100644006717600001440000004177707347314447016123 0ustar guenthersrcPlease read the README file in this directory first. .ex .Id $Id: procmailex.man,v 1.54 2001/08/04 06:08:20 guenther Exp $ .TH PROCMAILEX 5 \*(Dt BuGless .na .SH NAME procmailex \- procmail rcfile examples .SH SYNOPSIS .B @PROCMAILRC@ examples .ad .SH DESCRIPTION For a description of the rcfile format see .BR procmailrc (5). .PP The weighted scoring technique is described in detail in the .BR procmailsc (5) man page. .PP This man page shows several example recipes. For examples of complete rcfiles you can check the NOTES section in .BR procmail (1), or look at the example rcfiles part of the procmail source distribution (procmail*/examples/?procmailrc). .SH EXAMPLES Sort out all mail coming from the scuba-dive mailing list into the mailfolder scubafile (uses the locallockfile scubafile.lock). .Sx 3 :0: * ^TOscuba scubafile .Ex Forward all mail from peter about compilers to william (and keep a copy of it here in petcompil). .Sx 10 :0 * ^From.*peter * ^Subject:.*compilers { :0 @CONTINUE@ ! william@somewhere.edu :0 petcompil } .Ex An equivalent solution that accomplishes the same: .Sx 7 :0 @CONTINUE@ * ^From.*peter * ^Subject:.*compilers ! william@somewhere.edu :0 @ALSO_NEXT_RECIPE@ petcompil .Ex An equivalent, but slightly slower solution that accomplishes the same: .Sx 9 :0 @CONTINUE@ * ^From.*peter * ^Subject:.*compilers ! william@somewhere.edu :0 * ^From.*peter * ^Subject:.*compilers petcompil .Ex If you are fairly new to procmail and plan to experiment a little bit it often helps to have a .I safety net of some sort. Inserting the following two recipes above all other recipes will make sure that of all arriving mail always the last 32 messages will be preserved. In order for it to work as intended, you have to create a directory named `backup' in $MAILDIR prior to inserting these two recipes. .Sx 5 :0 @CONTINUE@ backup :0 @IGNORE_WRITERR@@CONTINUE@ | cd backup && rm \-f dummy `ls \-t msg.* | sed \-e 1,32d` .Ex If your system doesn't generate or generates incorrect leading `@FROM@' lines on every mail, you can fix this by calling up procmail with the \-@FROMWHOPT@@REFRESH_TIME@ option. To fix the same problem by different means, you could have inserted the following two recipes above all other recipes in your rcfile. They will filter the header of any mail through formail which will strip any leading `@FROM@', and automatically regenerates it subsequently. .Sx 2 :0 @FILTER@@PASS_HEAD@@WAIT_EXIT@ | formail \-@FM_DEL_INSERT@ "@FROM@" \-@FM_ADD_IFNOT@ "@FROM@" .Ex Add the headers of all messages that didn't come from the postmaster to your private header collection (for statistics or mail debugging); and use the lockfile `headc.lock'. In order to make sure the lockfile is not removed until the pipe has finished, you have to specify option `@WAIT_EXIT@'; otherwise the lockfile would be removed as soon as the pipe has accepted the mail. .Sx 3 :0 @PASS_HEAD@@WAIT_EXIT@@CONTINUE@: * !@FROMMkey@ | uncompress headc.Z; cat >>headc; compress headc .Ex Or, if you would use the more efficient gzip instead of compress: .Sx 3 :0 @PASS_HEAD@@WAIT_EXIT@@CONTINUE@: * !@FROMMkey@ | gzip >>headc.gz .Ex Forward all mails shorter than 1000 bytes to my home address (no lockfile needed on this recipe). .Sx 3 :0 * < 1000 ! myname@home .Ex Split up incoming digests from the surfing mailing list into their individual messages, and store them into surfing, using surfing.lock as the locallockfile. .Sx 3 :0: * ^Subject:.*surfing.*Digest | formail @FM_SKIP@1 \-@FM_DIGEST@@FM_SPLIT@ >>surfing .Ex Store everything coming from the postmaster or mailer-daemon (like bounced mail) into the file postm, using postm.lock as the locallockfile. .Sx 3 :0: * @FROMMkey@ postm .Ex A simple autoreply recipe. It makes sure that neither mail from any daemon (like bouncing mail or mail from mailing-lists), nor autoreplies coming from yourself will be autoreplied to. If this precaution would not be taken, disaster could result (`ringing' mail). In order for this recipe to autoreply to all the incoming mail, you should of course insert it before all other recipes in your rcfile. However, it is advisable to put it .I after any recipes that process the mails from subscribed mailinglists; it generally is not a good idea to generate autoreplies to mailinglists (yes, the !@FROMDkey@ regexp should already catch those, but if the mailinglist doesn't follow accepted conventions, this might .I not be .IR enough ). .Sx 6 :0 @PASS_HEAD@ @CONTINUE@ * !@FROMDkey@ * !^X-Loop: your@own.mail.address | (formail \-@FM_REPLY@ \-@FM_DEL_INSERT@"Precedence: junk" \e \-@FM_ADD_ALWAYS@"X-Loop: your@own.mail.address" ; \e echo "Mail received.") | $SENDMAIL \-t .Ex A more complicated autoreply recipe that implements the functional equivalent of the well known .BR vacation (1) program. This recipe is based on the same principles as the last one (prevent `ringing' mail). In addition to that however, it maintains a vacation database by extracting the name of the sender and inserting it in the vacation.cache file if the name was new (the vacation.cache file is maintained by formail which will make sure that it always contains the most recent names, the size of the file is limited to a maximum of approximately 8192 bytes). If the name was new, an autoreply will be sent. .PP As you can see, the following recipe has comments .B between the conditions. This is allowed. Do .B not put comments on the same line as a condition though. .Sx 18 SHELL=/bin/sh # for other shells, this might need adjustment :0 @WAIT_EXIT_QUIET@@PASS_HEAD@@CONTINUE@: vacation.lock # Perform a quick check to see if the mail was addressed to us * $^To:.*\e<$\eLOGNAME\e> # Don't reply to daemons and mailinglists * !@FROMDkey@ # Mail loops are evil * !^X-Loop: your@own.mail.address | formail \-@FM_REPLY@@FM_DUPLICATE@ 8192 vacation.cache :0 @ERROR_DO@@PASS_HEAD@@CONTINUE@ # if the name was not in the cache | (formail \-@FM_REPLY@@FM_DEL_INSERT@"Precedence: junk" \e \-@FM_ADD_ALWAYS@"X-Loop: your@own.mail.address" ; \e echo "I received your mail,"; \e echo "but I won't be back until Monday."; \e echo "-- "; cat $HOME/.signature \e ) | $SENDMAIL \-oi \-t .Ex Store all messages concerning TeX in separate, unique filenames, in a directory named texmail (this directory has to exist); there is no need to use lockfiles in this case, so we won't. .Sx 3 :0 * (^TO|^Subject:.*)TeX[^t] texmail .Ex The same as above, except now we store the mails in numbered files (MH mail folder). .Sx 3 :0 * (^TO|^Subject:.*)TeX[^t] texmail/. .Ex Or you could file the mail in several directory folders at the same time. The following recipe will deliver the mail to two MH-folders and one directory folder. It is actually only one file with two extra hardlinks. .Sx 3 :0 * (^TO|^Subject:.*)TeX[^t] texmail/. wordprocessing dtp/. .Ex Store all the messages about meetings in a folder that is in a directory that changes every month. E.g. if it were January 1994, the folder would have the name `94-01/meeting' and the locallockfile would be `94-01/meeting.lock'. .Sx 3 :0: * meeting `date +%y-%m`/meeting .Ex The same as above, but, if the `94-01' directory wouldn't have existed, it is created automatically: .Sx 9 MONTHFOLDER=`date +%y-%m` :0 @WAIT_EXIT_QUIET@@IGNORE_WRITERR@@CONTINUE@ * ? test ! \-d $MONTHFOLDER | mkdir $MONTHFOLDER :0: * meeting ${MONTHFOLDER}/meeting .Ex The same as above, but now by slightly different means: .Sx 6 MONTHFOLDER=`date +%y-%m` DUMMY=`test \-d $MONTHFOLDER || mkdir $MONTHFOLDER` :0: * meeting ${MONTHFOLDER}/meeting .Ex If you are subscribed to several mailinglists and people cross-post to some of them, you usually receive several duplicate mails (one from every list). The following simple recipe eliminates duplicate mails. It tells formail to keep an 8KB cache file in which it will store the Message-IDs of the most recent mails you received. Since Message-IDs are guaranteed to be unique for every new mail, they are ideally suited to weed out duplicate mails. Simply put the following recipe at the top of your rcfile, and no duplicate mail will get past it. .Sx 2 :0 @WAIT_EXIT_QUIET@@PASS_HEAD@: msgid.lock | formail \-@FM_DUPLICATE@ 8192 msgid.cache .Ex .B Beware if you have delivery problems in recipes below this one and procmail tries to requeue the mail, then on the next queue run, this mail will be considered a duplicate and will be thrown away. For those not quite so confident in their own scripting capabilities, you can use the following recipe instead. It puts duplicates in a separate folder instead of throwing them away. It is up to you to periodically empty the folder of course. .Sx 5 :0 @WAIT_EXIT_QUIET@@PASS_HEAD@@CONTINUE@: msgid.lock | formail \-@FM_DUPLICATE@ 8192 msgid.cache :0 @ALSO_N_IF_SUCC@: duplicates .Ex Procmail can deliver to MH folders directly, but, it does not update the unseen sequences the real MH manages. If you want procmail to update those as well, use a recipe like the following which will file everything that contains the word spam in the body of the mail into an MH folder called spamfold. Note the local lockfile, which is needed because MH programs do not lock the sequences file. Asynchronous invocations of MH programs that change the sequences file may therefore corrupt it or silently lose changes. Unfortunately, the lockfile doesn't completely solve the problem as rcvstore could be invoked while `show' or `mark' or some other MH program is running. This problem is expected to be fixed in some future version of MH, but until then, you'll have to balance the risk of lost or corrupt sequences against the benefits of the unseen sequence. .Sx 3 :0 :spamfold/$LOCKEXT * @BODY_GREP@ ?? spam | rcvstore +spamfold .Ex When delivering to emacs folders (i.e., mailfolders managed by any emacs mail package, e.g., RMAIL or VM) directly, you should use emacs-compatible lockfiles. The emacs mailers are a bit braindamaged in that respect, they get very upset if someone delivers to mailfolders which they already have in their internal buffers. The following recipe assumes that $HOME equals /home/john. .Sx 5 MAILDIR=Mail :0:/usr/local/lib/emacs/lock/!home!john!Mail!mailbox * ^Subject:.*whatever mailbox .Ex Alternatively, you can have procmail deliver into its own set of mailboxes, which you then periodically empty and copy over to your emacs files using .BR movemail . Movemail uses mailbox.lock local lockfiles per mailbox. This actually is the preferred mode of operation in conjunction with procmail. .PP To extract certain headers from a mail and put them into environment variables you can use any of the following constructs: .Sx 5 SUBJECT=`formail \-@FM_EXTRACT@Subject:` # regular field FROM=`formail \-@FM_REPLY@@FM_TRUST@ \-@FM_EXTRACT@To:` # special case :0 @PASS_HEAD@ # alternate method KEYWORDS=| formail \-@FM_EXTRACT@Keywords: .Ex If you are using temporary files in a procmailrc file, and want to make sure that they are removed just before procmail exits, you could use something along the lines of: .Sx 2 TEMPORARY=$HOME/tmp/pmail.$$ TRAP="/bin/rm \-f $TEMPORARY" .Ex The TRAP keyword can also be used to change the exitcode of procmail. I.e. if you want procmail to return an exitcode of `1' instead of its regular exitcodes, you could use: .Sx 3 EXITCODE="" TRAP="exit 1;" # The trailing semi-colon is important # since exit is not a standalone program .Ex Or, if the exitcode does not need to depend on the programs run from the TRAP, you can use a mere: .Sx 1 EXITCODE=1 .Ex The following recipe prints every incoming mail that looks like a postscript file. .Sx 3 :0 @BODY_GREP@@PASS_BODY@ * ^^%! | lpr .Ex The following recipe does the same, but is a bit more selective. It only prints the postscript file if it comes from the print-server. The first condition matches only if it is found in the header. The second condition only matches at the start of the body. .Sx 4 :0 @PASS_BODY@ * ^From[ :].*print-server * B ?? ^^%! | lpr .Ex The same as above, but now by slightly different means: .Sx 7 :0 * ^From[ :].*print-server { :0 @BODY_GREP@ @PASS_BODY@ * ^^%! | lpr } .Ex Likewise: .Sx 4 :0 @HEAD_GREP@@BODY_GREP@ @PASS_BODY@ * ^^(.+$)*From[ :].*print-server * ^^(.+$)*^%! | lpr .Ex Suppose you have two accounts, you use both accounts regularly, but they are in very distinct places (i.e., you can only read mail that arrived at either one of the accounts). You would like to forward mail arriving at account one to account two, and the other way around. The first thing that comes to mind is using .forward files at both sites; this won't work of course, since you will be creating a mail loop. This mail loop can be avoided by inserting the following recipe in front of all other recipes in the @PROCMAILRC@ files on both sites. If you make sure that you add the same X-Loop: field at both sites, mail can now safely be forwarded to the other account from either of them. .Sx 4 :0 @CONTINUE@ * !^X-Loop: yourname@your.main.mail.address | formail \-@FM_ADD_ALWAYS@ "X-Loop: yourname@your.main.mail.address" | \e $SENDMAIL \-oi yourname@the.other.account .Ex If someone sends you a mail with the word `retrieve' in the subject, the following will automatically send back the contents of info_file to the sender. Like in all recipes where we send mail, we watch out for mail loops. .Sx 6 :0 * !^@FROM@+YOUR_USERNAME * !^Subject:.*Re: * !@FROMDkey@ * ^Subject:.*retrieve | (formail \-@FM_REPLY@ ; cat info_file) | $SENDMAIL \-oi \-t .Ex Now follows an example for a very simple fileserver accessible by mail. For more demanding applications, I suggest you take a look at .B SmartList (available from the same place as the procmail distribution). As listed, this fileserver sends back at most one file per request, it ignores the body of incoming mails, the Subject: line has to look like "Subject: send file the_file_you_want" (the blanks are significant), it does not return files that have names starting with a dot, nor does it allow files to be retrieved that are outside the fileserver directory tree (if you decide to munge this example, make sure you do not inadvertently loosen this last restriction). .Sx 18 :0 * ^Subject: send file [0-9a-z] * !^X-Loop: yourname@your.main.mail.address * !^Subject:.*Re: * !@FROMDkey@ * !^Subject: send file .*[/.]\e. { MAILDIR=$HOME/fileserver # chdir to the fileserver directory :0 fhw # reverse mailheader and extract name * ^Subject: send file \e/[^ ]* | formail \-@FM_REPLY@@FM_ADD_ALWAYS@ "X-Loop: yourname@your.main.mail.address" FILE="$MATCH" # the requested filename :0 ah | cat \- ./$FILE 2>&1 | $SENDMAIL \-oi \-t } .Ex The following example preconverts all plain-text mail arriving in certain encoded MIME formats into a more compact 8-bit format which can be used and displayed more easily by most programs. The .BR mimencode (1) program is part of Nathaniel Borenstein's metamail package. .Sx 17 :0 * ^Content-Type: *text/plain { :0 @FILTER@@PASS_BODY@@WAIT_EXIT@ * ^Content-Transfer-Encoding: *quoted-printable | mimencode \-u \-q :0 @ALSO_NEXT_RECIPE@@FILTER@@PASS_HEAD@@WAIT_EXIT@ | formail \-@FM_DEL_INSERT@ "Content-Transfer-Encoding: 8bit" :0 @FILTER@@PASS_BODY@@WAIT_EXIT@ * ^Content-Transfer-Encoding: *base64 | mimencode \-u \-b :0 @ALSO_NEXT_RECIPE@@FILTER@@PASS_HEAD@@WAIT_EXIT@ | formail \-@FM_DEL_INSERT@ "Content-Transfer-Encoding: 8bit" } .Ex The following one is rather exotic, but it only serves to demonstrate a feature. Suppose you have a file in your HOME directory called ".urgent", and the (one) person named in that file is the sender of an incoming mail, you'd like that mail to be stored in $MAILDIR/urgent instead of in any of the normal mailfolders it would have been sorted in. Then this is what you could do (beware, the filelength of $HOME/.urgent should be well below $LINEBUF, increase LINEBUF if necessary): .Sx 5 URGMATCH=`cat $HOME/.urgent` :0: * $^From.*${URGMATCH} urgent .Ex An entirely different application for procmail would be to conditionally apply filters to a certain (outgoing) text or mail. A typical example would be a filter through which you pipe all outgoing mail, in order to make sure that it will be MIME encoded only if it needs to be. I.e. in this case you could start procmail in the middle of a pipe like: .Sx 1 cat newtext | procmail ./mimeconvert | mail chris@where.ever .Ex The .B mimeconvert rcfile could contain something like (the =0x80= and =0xff= should be substituted with the real 8-bit characters): .Sx 10 DEFAULT=| # pipe to stdout instead of # delivering mail as usual :0 @BODY_GREP@@FILTER@@PASS_BODY@@WAIT_EXIT@ * [=0x80=-=0xff=] | mimencode \-q :0 @ALSO_NEXT_RECIPE@@FILTER@@PASS_HEAD@@WAIT_EXIT@ | formail \-I 'MIME-Version: 1.0' \e \-I 'Content-Type: text/plain; charset=ISO-8859-1' \e \-I 'Content-Transfer-Encoding: quoted-printable' .Ex .SH "SEE ALSO" .na .nh .BR procmail (1), .BR procmailrc (5), .BR procmailsc (5), .BR sh (1), .BR csh (1), .BR mail (1), .BR mailx (1), .BR binmail (1), .BR uucp (1), .BR aliases (5), .BR sendmail (8), .BR egrep (1), .BR grep (1), .BR biff (1), .BR comsat (8), .BR mimencode (1), .BR lockfile (1), .BR formail (1) .hy .ad procmail-3.22/man/Makefile0100644006717600001440000000051005626713320014666 0ustar guenthersrc#$Id: Makefile,v 1.7 1994/08/24 18:52:00 berg Exp $ all: init $(MAKE) make $@ # The only real thing that can be made right now is: init: cd ..; $(MAKE) make init .PRECIOUS: Makefile Makefile makefile Makefiles makefiles: init procmail.1 procmailrc.5 procmailsc.5 procmailex.5 lockfile.1 formail.1: init $(MAKE) make $@ procmail-3.22/man/Makefile.00100644006717600001440000000106205642032110015014 0ustar guenthersrc #$Id: Makefile.0,v 1.9 1994/09/27 15:03:36 berg Exp $ all: $(MANSS) make: @$(SHELL) -c "exit 0" .PRECIOUS: Makefile ../config.check: @cd ..; $(MAKE) config.check man.sed: man_sed man_sed: @cd ../src; $(MAKE) ../man/man.sed clean: $(RM) $(MANSS) man.sed* _Makefile *core* Makefile: ../Makefile Makefile.0 @echo "You have made changes to the master Makefile, in order for" @echo "these changes to show through, you will first have to do:" @echo "$(MAKE) makefiles" makefiles Makefiles makefile: cd ..; $(MAKE) makefiles init: cd ..; $(MAKE) $@ procmail-3.22/man/README0100644006717600001440000000065405571124115014114 0ustar guenthersrcPlease note that the *.man files in this directory still need to be converted into their *.1 and *.5 counterparts. You can convert them by typing "make" in this directory or in the directory above. The man pages *.1 and *.5 can then be displayed as readable plain text by typing something like this: nroff -man procmail.1 or they can be moved to man directories in your MANPATH to be be viewed with the normal man command. procmail-3.22/man/mansed0100755006717600001440000000361507223546514014440 0ustar guenthersrc#! /bin/sh : #$Id: mansed,v 1.30 2000/09/28 01:23:07 guenther Exp $ if test -z "$IFS" then IFS=" \ \ " export IFS fi test 5 != $# && echo "Don't start this script directly, use \`make'" && exit 1 SHELL=$1 SRC="$2" DEST="$3" RM="$4" DEVNULL=$5 export SHELL SRC DEST RM DEVNULL TMPF0=/tmp/_mansed.0.$$ TMPF1=/tmp/_mansed.1.$$ if test ! -f "$DEST" then trap "$RM \"$DEST\" $TMPF0 $TMPF1;exit 1" 1 2 3 15 fi (cat <<\HERE .\"if n .pl +(135i-\n(.pu) .de Id .ds Rv \\$3 .ds Dt \\$4 .. HERE sed -e '1,/^.ex/ d' -e '/^\.TH/ q' <$SRC cat <<\HERE .rn SH Sh .de SH .br .ne 11 .Sh "\\$1" .. .rn SS Ss .de SS .br .ne 10 .Ss "\\$1" .. .rn TP Tp .de TP .br .ne 9 .Tp \\$1 .. .rn RS Rs .de RS .na .nf .Rs .. .rn RE Re .de RE .Re .fi .ad .. .de Sx .PP .ne \\$1 .RS .. .de Ex .RE .PP .. HERE sed -e '1,/^\.TH/ d' <$SRC expr "X$DEST" : '.*[18]$' >$DEVNULL && cat <$TMPF0 if test $? != 0 then $RM $TMPF0 exit 1 fi for a in man.sed.[0-9]* do case $a in man.sed.0000) ;; man.sed.????) mv $TMPF0 $TMPF1 if sed -f $a <$TMPF1 >$TMPF0 then : else $RM $TMPF1 $TMPF0 exit 1 fi esac done cat $TMPF0 >"$DEST" result=$? $RM $TMPF0 $TMPF1 exit $result procmail-3.22/man/procmailsc.man0100644006717600001440000001700407347314461016072 0ustar guenthersrcPlease read the README file in this directory first. .ex .Id $Id: procmailsc.man,v 1.15 2001/08/04 06:08:22 guenther Exp $ .TH PROCMAILSC 5 \*(Dt BuGless .na .SH NAME procmailsc \- procmail weighted scoring technique .SH SYNOPSIS .RB [ * ] .B "w^x condition" .ad .SH DESCRIPTION In addition to the traditional true or false conditions you can specify on a recipe, you can use a weighted scoring technique to decide if a certain recipe matches or not. When weighted scoring is used in a recipe, then the final score for that recipe must be positive for it to match. A certain condition can contribute to the score if you allocate it a `weight' .RB ( w ) and an `exponent' .RB ( x ). You do this by preceding the condition (on the same line) with: .RS .B w^x .RE Whereas both .B w and .B x are real numbers between @MIN32@.0 and @MAX32@.0 inclusive. .SH "Weighted regular expression conditions" The first time the regular expression is found, it will add .I w to the score. The second time it is found, .I w*x will be added. The third time it is found, .I w*x*x will be added. The fourth time .I w*x*x*x will be added. And so forth. This can be described by the following concise formula: .Sx 4 n n k\-1 x \- 1 w * Sum x = w * \-\-\-\-\-\-\- k=1 x \- 1 .Ex It represents the total added score for this condition if .B n matches are found. Note that the following case distinctions can be made: .TP 8 x=0 Only the first match will contribute w to the score. Any subsequent matches are ignored. .TP x=1 Every match will contribute the same w to the score. The score grows linearly with the number of matches found. .TP 0 L .Ex will generate an additional score of: .Sx 4 @POW@ / M \e w * | \-\-\- | \e L / .Ex And: .Sx 1 * w^x < L .Ex will generate an additional score of: .Sx 4 @POW@ / L \e w * | \-\-\- | \e M / .Ex .PP In both cases, if L=M, this will add w to the score. In the former case however, larger mails will be favoured, in the latter case, smaller mails will be favoured. Although x can be varied to fine-tune the steepness of the function, typical usage sets x=1. .SH MISCELLANEOUS You can query the final score of all the conditions on a recipe from the environment variable .BR $= . This variable is set .I every time just after procmail has parsed all conditions on a recipe (even if the recipe is not being executed). .SH EXAMPLES The following recipe will ditch all mails having more than 150 lines in the body. The first condition contains an empty regular expression which, because it always matches, is used to give our score a negative offset. The second condition then matches every line in the mail, and consumes up the previous negative offset we gave (one point per line). In the end, the score will only be positive if the mail contained more than 150 lines. .Sx 5 :0 @BODY_GREP@@PASS_HEAD@ * \-150^0 * 1^1 ^.*$ /dev/null .Ex Suppose you have a priority folder which you always read first. The next recipe picks out the priority mail and files them in this special folder. The first condition is a regular one, i.e., it doesn't contribute to the score, but simply has to be satisfied. The other conditions describe things like: john and claire usually have something important to say, meetings are usually important, replies are favoured a bit, mails about Elvis (this is merely an example :\-) are favoured (the more he is mentioned, the more the mail is favoured, but the maximum extra score due to Elvis will be 4000, no matter how often he is mentioned), lots of quoted lines are disliked, smileys are appreciated (the score for those will reach a maximum of 3500), those three people usually don't send interesting mails, the mails should preferably be small (e.g., 2000 bytes long mails will score \-100, 4000 bytes long mails do \-800). As you see, if some of the uninteresting people send mail, then the mail still has a chance of landing in the priority folder, e.g., if it is about a meeting, or if it contains at least two smileys. .Sx 11 :0 @HEAD_GREP@@BODY_GREP@ * !^Precedence:.*(junk|bulk) * 2000^0 ^From:.*(john@home|claire@work) * 2000^0 ^Subject:.*meeting * 300^0 ^Subject:.*Re: * 1000^.75 elvis|presley * \-100^1 ^> * 350^.9 :\-\e) * \-500^0 ^From:.*(boss|jane|henry)@work * \-100^3 > 2000 priority_folder .Ex If you are subscribed to a mailinglist, and just would like to read the quality mails, then the following recipes could do the trick. First we make sure that the mail is coming from the mailinglist. Then we check if it is from certain persons of whom we value the opinion, or about a subject we absolutely want to know everything about. If it is, file it. Otherwise, check if the ratio of quoted lines to original lines is at most 1:2. If it exceeds that, ditch the mail. Everything that survived the previous test, is filed. .Sx 15 :0 ^@FROM@mailinglist-request@some.where { :0: * ^(From:.*(paula|bill)|Subject:.*skiing) mailinglist :0 @BODY_GREP@@PASS_HEAD@ * 20^1 ^> * \-10^1 ^[^>] /dev/null :0: mailinglist } .Ex For further examples you should look in the .BR procmailex (5) man page. .SH CAVEATS Because this speeds up the search by an order of magnitude, the procmail internal egrep will always search for the leftmost .I shortest match, unless it is determining what to assign to .BR @MATCHVAR@ , in which case it searches the leftmost .I longest match. E.g. for the leftmost .I shortest match, by itself, the regular expression: .TP .B .* will always match a zero length string at the same spot. .TP .B .+ will always match one character (except newlines of course). .SH "SEE ALSO" .na .nh .BR procmail (1), .BR procmailrc (5), .BR procmailex (5), .BR sh (1), .BR csh (1), .BR egrep (1), .BR grep (1), .hy .ad .SH BUGS If, in a length condition, you specify an .B x that causes an overflow, procmail is at the mercy of the .BR pow (3) function in your mathematical library. .PP Floating point numbers in `engineering' format (e.g., 12e5) are not accepted. .SH MISCELLANEOUS As soon as `plus infinity' (@MAX32@) is reached, any subsequent .I weighted conditions will simply be skipped. .PP As soon as `minus infinity' (@MIN32@) is reached, the condition will be considered as `no match' and the recipe will terminate early. .SH NOTES If in a regular expression weighted formula .BR 0 .Ex Conditions start with a leading `*', everything after that character is passed on to the internal egrep .BR literally , except for leading and trailing whitespace. These regular expressions are .B completely compatible to the normal .BR egrep (1) extended regular expressions. See also .BR "Extended regular expressions" . .PP Conditions are anded; if there are no conditions the result will be true by default. .PP .I Flags can be any of the following: .TP 0.5i .B @HEAD_GREP@ Egrep the header (default). .TP .B @BODY_GREP@ Egrep the body. .TP .B @DISTINGUISH_CASE@ Tell the internal egrep to distinguish between upper and lower case (contrary to the default which is to ignore case). .TP .B @ALSO_NEXT_RECIPE@ This recipe will not be executed unless the conditions on the last preceding recipe (on the current block-nesting level) without the `@ALSO_NEXT_RECIPE@' or `@ALSO_N_IF_SUCC@' flag matched as well. This allows you to chain actions that depend on a common condition. .TP .B @ALSO_N_IF_SUCC@ Has the same meaning as the `@ALSO_NEXT_RECIPE@' flag, with the additional condition that the immediately preceding recipe must have been .I successfully completed before this recipe is executed. .TP .B @ELSE_DO@ This recipe only executes if the immediately preceding recipe was not executed. Execution of this recipe also disables any immediately following recipes with the '@ELSE_DO@' flag. This allows you to specify `else if' actions. .TP .B @ERROR_DO@ This recipe only executes if the immediately preceding recipe .IR failed (i.e., the action line was attempted, but resulted in an error). .TP .B @PASS_HEAD@ Feed the header to the pipe, file or mail destination (default). .TP .B @PASS_BODY@ Feed the body to the pipe, file or mail destination (default). .TP .B @FILTER@ Consider the pipe as a filter. .TP .B @CONTINUE@ Generate a .B carbon copy of this mail. This only makes sense on .I delivering recipes. The only non-delivering recipe this flag has an effect on is on a nesting block, in order to generate a carbon copy this will .B clone the running procmail process (lockfiles will not be inherited), whereby the clone will proceed as usual and the parent will jump across the block. .TP .B @WAIT_EXIT@ Wait for the filter or program to finish and check its exitcode (normally ignored); if the filter is unsuccessful, then the text will not have been filtered. .TP .B @WAIT_EXIT_QUIET@ Has the same meaning as the `@WAIT_EXIT@' flag, but will suppress any `Program failure' message. .TP .B @IGNORE_WRITERR@ Ignore any write errors on this recipe (i.e., usually due to an early closed pipe). .TP .B @RAW_NONL@ Raw mode, do not try to ensure the mail ends with an empty line, write it out as is. .PP There are some special conditions you can use that are not straight regular expressions. To select them, the condition must start with: .TP 0.5i .B ! Invert the condition. .TP .B $ Evaluate the remainder of this condition according to .BR sh (1) substitution rules inside double quotes, skip leading whitespace, then reparse it. .TP .B ? Use the exitcode of the specified program. .TP .B < Check if the total length of the mail is shorter than the specified (in decimal) number of bytes. .TP .B > Analogous to '<'. .TP .B "variablename \fI??\fP" Match the remainder of this condition against the value of this environment variable (which cannot be a pseudo variable). A special case is if variablename is equal to `B', `H', `HB' or `BH'; this merely overrides the default header/body search area defined by the initial flags on this recipe. .TP .B \e To quote any of the above at the start of the line. .SS "Local lockfile" .PP If you put a second (trailing) ':' on the first recipe line, then procmail will use a .I locallockfile (for this recipe only). You can optionally specify the locallockfile to use; if you don't however, procmail will use the destination filename (or the filename following the first '>>') and will append $LOCKEXT to it. .SS "Recipe action line" .PP The action line can start with the following characters: .TP .B ! Forwards to all the specified mail addresses. .TP .B | Starts the specified program, possibly in $SHELL if any of the characters $SHELLMETAS are spotted. You can optionally prepend this pipe symbol with .IR variable= , which will cause stdout of the program to be captured in the environment .I variable (procmail will .B not terminate processing the rcfile at this point). If you specify just this pipe symbol, without any program, then procmail will pipe the mail to stdout. .TP .B { Followed by at least one space, tab or newline will mark the start of a nesting block. Everything up till the next closing brace will depend on the conditions specified for this recipe. Unlimited nesting is permitted. The closing brace exists merely to delimit the block, it will .I not cause procmail to terminate in any way. If the end of a block is reached processing will continue as usual after the block. On a nesting block, the flags `@HEAD_GREP@' and `@BODY_GREP@' only affect the conditions leading up to the block, the flags `@PASS_HEAD@' and `@PASS_BODY@' have no effect whatsoever. .PP Anything else will be taken as a mailbox name (either a filename or a directory, absolute or relative to the current directory (see MAILDIR)). If it is a (possibly yet nonexistent) filename, the mail will be appended to it. .PP If it is a directory, the mail will be delivered to a newly created, guaranteed to be unique file named $MSGPREFIX* in the specified directory. If the mailbox name ends in "@MCDIRSEP@@chCURDIR@", then this directory is presumed to be an MH folder; i.e., procmail will use the next number it finds available. If the mailbox name ends in "@MCDIRSEP@", then this directory is presumed to be a maildir folder; i.e., procmail will deliver the message to a file in a subdirectory named "tmp" and rename it to be inside a subdirectory named "new". If the mailbox is specified to be an MH folder or maildir folder, procmail will create the necessary directories if they don't exist, rather than treat the mailbox as a non-existent filename. When procmail is delivering to directories, you can specify multiple directories to deliver to (procmail will do so utilising hardlinks). .SS "Environment variable defaults" .TP 2.2i .B "LOGNAME, HOME and SHELL" Your (the recipient's) defaults .TP .B PATH .na \&@DEFpath@ (Except .ad during the processing of an @ETCRC@ file, when it will be set to .na `\&@DEFspath@'.) .ad .TP .B SHELLMETAS \&@DEFshellmetas@ .TP .B SHELLFLAGS \&@DEFshellflags@ .TP .BR ORGMAIL \&@MAILSPOOLDIR@$LOGNAME .br (Unless .B \-@MAILFILTOPT@ has been specified, in which case it is unset) .TP .B MAILDIR \&@DEFmaildir@ .br (Unless the name of the first successfully opened rcfile starts with `@chCURDIR@@MCDIRSEP@' or if .B \-@MAILFILTOPT@ has been specified, in which case it defaults to `@chCURDIR@') .TP .B DEFAULT \&@DEFdefault@ .TP .B MSGPREFIX \&@DEFmsgprefix@ .TP .B SENDMAIL \&@DEFsendmail@ .TP .B SENDMAILFLAGS \&@DEFflagsendmail@ .TP .B HOST The current hostname .TP .B COMSAT \&@DEFcomsat@ .br (If an rcfile is specified on the command line) .TP .B PROCMAIL_VERSION \&@PM_VERSION@ .TP .B LOCKEXT \&@DEFlockext@@PRESTENV@@LD_ENV_FIX@ .SS Environment .PP Before you get lost in the multitude of environment variables, keep in mind that all of them have reasonable defaults. .TP 1.2i .B MAILDIR Current directory while procmail is executing (that means that all paths are relative to $MAILDIR). .TP .B DEFAULT Default .B mailbox file (if not told otherwise, procmail will dump mail in this mailbox). Procmail will automatically use $DEFAULT$LOCKEXT as lockfile prior to writing to this mailbox. You do not need to set this variable, since it already points to the standard system mailbox. .TP .B LOGFILE This file will also contain any error or diagnostic messages from procmail (normally none :\-) or any other programs started by procmail. If this file is not specified, any diagnostics or error messages will @pconsole@@console@@aconsole@ See also .BR LOGABSTRACT . .TP .B VERBOSE You can turn on .I extended diagnostics by setting this variable to `yes' or `on', to turn it off again set it to `no' or `off'. .TP .B LOGABSTRACT Just before procmail exits it logs an abstract of the delivered message in $LOGFILE showing the `@FROM@' and `Subject:' fields of the header, what folder it finally went to and how long (in bytes) the message was. By setting this variable to `no', generation of this abstract is suppressed. If you set it to `all', procmail will log an abstract for every successful .I delivering recipe it processes. .TP .B LOG Anything assigned to this variable will be appended to $LOGFILE. .TP .B ORGMAIL Usually the system mailbox (\fBOR\fPi\fBG\fPinal \fBMAIL\fPbox). If, for some obscure reason (like `\fBfilesystem full\fP') the mail could not be delivered, then this mailbox will be the last resort. If procmail fails to save the mail in here (deep, deep trouble :\-), then the mail will bounce back to the sender. .TP .B LOCKFILE Global semaphore file. If this file already exists, procmail will wait until it has gone before proceeding, and will create it itself (cleaning it up when ready, of course). If more than one .I lockfile are specified, then the previous one will be removed before trying to create the new one. The use of a global lockfile is discouraged, whenever possible use locallockfiles (on a per recipe basis) instead. .TP .B LOCKEXT Default extension that is appended to a destination file to determine what local .I lockfile to use (only if turned on, on a per-recipe basis). .TP .B LOCKSLEEP Number of seconds procmail will sleep before retrying on a .I lockfile (if it already existed); if not specified, it defaults to @DEFlocksleep@ seconds. .TP .B LOCKTIMEOUT Number of seconds that have to have passed since a .I lockfile was last modified/created before procmail decides that this must be an erroneously leftover lockfile that can be removed by force now. If zero, then no timeout will be used and procmail will wait forever until the lockfile is removed; if not specified, it defaults to @DEFlocktimeout@ seconds. This variable is useful to prevent indefinite hangups of .BR sendmail /procmail. Procmail is immune to clock skew across machines. .TP .B TIMEOUT Number of seconds that have to have passed before procmail decides that some child it started must be hanging. The offending program will receive a TERMINATE signal from procmail, and processing of the rcfile will continue. If zero, then no timeout will be used and procmail will wait forever until the child has terminated; if not specified, it defaults to @DEFtimeout@ seconds. .TP .B MSGPREFIX Filename prefix that is used when delivering to a directory (not used when delivering to a maildir or an MH directory). .TP .B HOST If this is not the .I hostname of the machine, processing of the current .I rcfile will immediately cease. If other rcfiles were specified on the command line, processing will continue with the next one. If all rcfiles are exhausted, the program will terminate, but will not generate an error (i.e., to the mailer it will seem that the mail has been delivered). .TP .B UMASK The name says it all (if it doesn't, then forget about this one :\-). Anything assigned to UMASK is taken as an .B octal number. If not specified, the umask defaults to @INIT_UMASK@. If the umask permits o+x, all the mailboxes procmail delivers to directly will receive an o+x mode change. This can be used to check if new mail arrived. .TP .B SHELLMETAS If any of the characters in SHELLMETAS appears in the line specifying a filter or program, the line will be fed to $SHELL instead of being executed directly. .TP .B SHELLFLAGS Any invocation of $SHELL will be like: .br "$SHELL" "$SHELLFLAGS" "$*"; .TP .B SENDMAIL If you're not using the .I forwarding facility don't worry about this one. It specifies the program being called to forward any mail. .br It gets invoked as: "$SENDMAIL" $SENDMAILFLAGS "$@"; .TP .B NORESRETRY Number of retries that are to be made if any `\fBprocess table full\fP', `\fBfile table full\fP', `\fBout of memory\fP' or `\fBout of swap space\fP' error should occur. If this number is negative, then procmail will retry indefinitely; if not specified, it defaults to @DEFnoresretry@ times. The retries occur with a $SUSPEND second interval. The idea behind this is that if, e.g., the .I swap .I space has been exhausted or the .I process .I table is full, usually several other programs will either detect this as well and abort or crash 8\-), thereby freeing valuable .I resources for procmail. .TP .B SUSPEND Number of seconds that procmail will pause if it has to wait for something that is currently unavailable (memory, fork, etc.); if not specified, it will default to @DEFsuspend@ seconds. See also: .BR LOCKSLEEP . .TP .B LINEBUF Length of the internal line buffers, cannot be set smaller than @MINlinebuf@. All lines read from the .I rcfile should not exceed $LINEBUF characters before and after expansion. If not specified, it defaults to @DEFlinebuf@. This limit, of course, does .I not apply to the mail itself, which can have arbitrary line lengths, or could be a binary file for that matter. See also PROCMAIL_OVERFLOW. .TP .B DELIVERED If set to `yes' procmail will pretend (to the mail agent) the mail has been delivered. If mail cannot be delivered after having met this assignment (set to `yes'), the mail will be lost (i.e., it will not bounce). .TP .B TRAP When procmail terminates of its own accord and not because it received a signal, it will execute the contents of this variable. A copy of the mail can be read from stdin. Any output produced by this command will be appended to $LOGFILE. Possible uses for TRAP are: removal of temporary files, logging customised abstracts, etc. See also .B EXITCODE and .BR LOGABSTRACT . .TP .B EXITCODE By default, procmail returns an exitcode of zero (success) if it successfully delivered the message or if the .B HOST variable was misset and there were no more rcfiles on the command line; otherwise it returns failure. Before doing so, procmail examines the value of this variable. If it is set to a positive numeric value, procmail will instead use that value as its exitcode. If this variable is set but empty and .B TRAP is set, procmail will set the exitcode to whatever the .B TRAP program returns. If this variable is not set, procmail will set it shortly before calling up the .B TRAP program. .TP .B LASTFOLDER This variable is assigned to by procmail whenever it is delivering to a folder or program. It always contains the name of the last file (or program) procmail delivered to. If the last delivery was to several directory folders together then $LASTFOLDER will contain the hardlinked filenames as a space separated list. .TP .B @MATCHVAR@ This variable is assigned to by procmail whenever it is told to extract text from a matching regular expression. It will contain all text matching the regular expression past the `\fB\e/\fP' token. .TP .B SHIFT Assigning a positive value to this variable has the same effect as the `shift' command in .BR sh (1). This command is most useful to extract extra arguments passed to procmail when acting as a generic mailfilter. .TP .B INCLUDERC Names an rcfile (relative to the current directory) which will be included here as if it were part of the current rcfile. Nesting is permitted and only limited by systems resources (memory and file descriptors). As no checking is done on the permissions or ownership of the rcfile, users of .B INCLUDERC should make sure that only trusted users have write access to the included rcfile or the directory it is in. Command line assignments to .B INCLUDERC have no effect. .TP .B SWITCHRC Names an rcfile (relative to the current directory) to which processing will be switched. If the named rcfile doesn't exist or is not a normal file or /dev/null then an error will be logged and processing will continue in the current rcfile. Otherwise, processing of the current rcfile will be aborted and the named rcfile started. Unsetting .B SWITCHRC aborts processing of the current rcfile as if it had ended at the assignment. As with .BR INCLUDERC , no checking is done on the permissions or ownership of the rcfile and command line assignments have no effect. .TP .B PROCMAIL_VERSION The version number of the running procmail binary. .TP .B PROCMAIL_OVERFLOW This variable will be set to a non-empty value if procmail detects a buffer overflow. See the .B BUGS section below for other details of operation when overflow occurs. .TP .B COMSAT .BR Comsat (8)/ biff (1) notification is on by default, it can be turned off by setting this variable to `no'. Alternatively the biff-service can be customised by setting it to either `service@SERV_ADDRsep@', `@SERV_ADDRsep@hostname', or `service@SERV_ADDRsep@hostname'. When not specified it defaults to @COMSATservice@@SERV_ADDRsep@@COMSAThost@.@DROPPRIVS@ .SS "Extended regular expressions" The following tokens are known to both the procmail internal egrep and the standard .BR egrep (1) (beware that some egrep implementations include other non-standard extensions): .TP 1.0i .B ^ Start of a line. .TP .B $ End of a line. .TP .B . Any character except a newline. .TP .B a* Any sequence of zero or more a's. .TP .B a+ Any sequence of one or more a's. .TP .B a? Either zero or one a. .TP .B [^-a-d] Any character which is .B not either a dash, a, b, c, d or newline. .TP .B de|abc Either the sequence `de' or `abc'. .TP .B (abc)* Zero or more times the sequence `abc'. .TP .B \e. Matches a single dot; use \e to quote any of the magic characters to get rid of their special meaning. See also $\e variable substitution. .PP These were only samples, of course, any more complex combination is valid as well. .PP The following token meanings are special procmail extensions: .TP 1.0i \fB^\fP or \fB$\fP Match a newline (for multiline matches). .TP .B ^^ Anchor the expression at the very start of the search area, or if encountered at the end of the expression, anchor it at the very end of the search area. .TP \fB\e<\fP or \fB\e>\fP Match the character before or after a word. They are merely a shorthand for `[^a-zA-Z0-9_]', but can also match newlines. Since they match actual characters, they are only suitable to delimit words, not to delimit inter-word space. .TP .B \e/ Splits the expression in two parts. Everything matching the right part will be assigned to the @MATCHVAR@ environment variable. .SH EXAMPLES Look in the .BR procmailex (5) man page. .SH CAVEATS Continued lines in an action line that specifies a program always have to end in a backslash, even if the underlying shell would not need or want the backslash to indicate continuation. This is due to the two pass parsing process needed (first procmail, then the shell (or not, depending on .BR SHELLMETAS )). .PP Don't put comments on the regular expression condition lines in a recipe, these lines are fed to the internal egrep .I literally (except for continuation backslashes at the end of a line). .PP Leading whitespace on continued regular expression condition lines is usually ignored (so that they can be indented), but .B not on continued condition lines that are evaluated according to the .BR sh (1) substitution rules inside double quotes. .PP Watch out for deadlocks when doing unhealthy things like forwarding mail to your own account. Deadlocks can be broken by proper use of .BR LOCKTIMEOUT . .PP Any default values that procmail has for some environment variables will .B always override the ones that were already defined. If you really want to override the defaults, you either have to put them in the .B rcfile or on the command line as arguments. .PP The @ETCRC@ file cannot change the PATH setting seen by user rcfiles as the value is reset when procmail finishes the @ETCRC@ file. While future enhancements are expected in this area, recompiling procmail with the desired value is currently the only correct solution. .PP Environment variables set .B inside the shell-interpreted-`|' action part of a recipe will .B not retain their value after the recipe has finished since they are set in a subshell of procmail. To make sure the value of an environment variable is retained you have to put the assignment to the variable before the leading `|' of a recipe, so that it can capture stdout of the program. .PP If you specify only a `@PASS_HEAD@' or a `@PASS_BODY@' flag on a delivering recipe, and the recipe matches, then, unless the `@CONTINUE@' flag is present as well, the body respectively the header of the mail will be silently lost. .SH "SEE ALSO" .na .nh .BR procmail (1), .BR procmailsc (5), .BR procmailex (5), .BR sh (1), .BR csh (1), .BR mail (1), .BR mailx (1), .BR binmail (1), .BR uucp (1), .BR aliases (5), .BR sendmail (8), .BR egrep (1), .BR regexp (5), .BR grep (1), .BR biff (1), .BR comsat (8), .BR lockfile (1), .BR formail (1) .hy .ad .SH BUGS The only substitutions of environment variables that can be handled by procmail itself are of the type $name, ${name}, ${name:-text}, ${name:+text}, ${name-text}, ${name+text}, $\ename, $#, $n, $$, $?, $_, $\- and $=; whereby $\ename will be substituted by the all-magic-regular-expression-characters-disarmed equivalent of $name, $_ by the name of the current rcfile, $\- by $LASTFOLDER and $= will contain the score of the last recipe. Furthermore, the result of $\ename substitution will never be split on whitespace. When the .B \-@ARGUMENTOPT@ or .B \-@MAILFILTOPT@ options are used, $# will expand to the number of arguments so specified and "$@" (the quotes are required) will expand to the specified arguments. However, "$@" will only be expanded when used in the argument list to a program, and then only one such occurrence will be expanded. .PP Unquoted variable expansions performed by procmail are always split on space, tab, and newline characters; the IFS variable is not used internally. .PP Procmail does not support the expansion of `~'. .PP A line buffer of length $LINEBUF is used when processing the .IR rcfile , any expansions that don't fit within this limit will be truncated and PROCMAIL_OVERFLOW will be set. If the overflowing line is a condition or an action line, then it will be considered failed and procmail will continue processing. If it is a variable assignment or recipe start line then procmail will abort the entire rcfile. .PP If the global lockfile has a .I relative path, and the current directory is not the same as when the global lockfile was created, then the global lockfile will not be removed if procmail exits at that point (remedy: use .I absolute paths to specify global lockfiles). .PP If an rcfile has a .I relative path and when the rcfile is first opened .B MAILDIR contains a relative path, and if at one point procmail is instructed to clone itself and the current directory has changed since the rcfile was opened, then procmail will not be able to clone itself (remedy: use an .I absolute path to reference the rcfile or make sure MAILDIR contains an absolute path as the rcfile is opened). .PP A locallockfile on the recipe that marks the start of a non-forking nested block does not work as expected. .PP When capturing stdout from a recipe into an environment variable, exactly one trailing newline will be stripped. .PP Some non-optimal and non-obvious regexps set MATCH to an incorrect value. The regexp can be made to work by removing one or more unneeded '*', '+', or '?' operator on the left-hand side of the \e/ token. .SH MISCELLANEOUS If the regular expression contains `\fB@TO_key@\fP' it will be substituted by .na .nh `\fB@TO_substitute@\fP', which should catch all destination specifications containing a specific .IR address . .hy .ad .PP If the regular expression contains `\fB@TOkey@\fP' it will be substituted by .na .nh `\fB@TOsubstitute@\fP', which should catch all destination specifications containing a specific .IR word . .hy .ad .PP If the regular expression contains `\fB@FROMDkey@\fP' it will be substituted by .na .nh `\fB@FROMDsubstitute@\fP', which should catch mails coming from most daemons (how's that for a regular expression :\-). .hy .ad .PP If the regular expression contains `\fB@FROMMkey@\fP' it will be substituted by .na .nh `\fB@FROMMsubstitute@\fP' (a stripped down version of `\fB@FROMDkey@\fP'), which should catch mails coming from most mailer-daemons. .hy .ad .PP When assigning boolean values to variables like VERBOSE, DELIVERED or COMSAT, procmail accepts as true every string starting with: a non-zero value, `on', `y', `t' or `e'. False is every string starting with: a zero value, `off', `n', `f' or `d'. .PP If the action line of a recipe specifies a program, a sole backslash-newline pair in it on an otherwise empty line will be converted into a newline. .PP The regular expression engine built into procmail does not support named character classes. .SH NOTES Since unquoted leading whitespace is generally ignored in the rcfile you can indent everything to taste. .PP The leading `|' on the action line to specify a program or filter is stripped before checking for $SHELLMETAS. .PP Files included with the INCLUDERC directive containing only environment variable assignments can be shared with sh. .PP The current behavior of assignments on the command line to .B INCLUDERC and .B SWITCHRC is not guaranteed, has been changed once already, and may be changed again or removed in future releases. .PP For .I really complicated processing you can even consider calling .B procmail recursively. .PP In the old days, the `:0' that marks the beginning of a recipe, had to be changed to `:n', whereby `n' denotes the number of conditions that follow. procmail-3.22/src/0040755006717600001440000000000007347315165013260 5ustar guenthersrcprocmail-3.22/src/comsat.c0100644006717600001440000001106007347314522014701 0ustar guenthersrc/************************************************************************ * Routines for dealing with comsat, as used by procmail * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: comsat.c,v 1.5 2001/07/03 15:05:47 guenther Exp $"; #endif #include "procmail.h" #ifndef NO_COMSAT #include "sublib.h" /* for strtol() and strlcat() */ #include "robust.h" /* for rclose() */ #include "shell.h" /* for tmemmove() */ #include "acommon.h" /* for ultstr() */ #include "mailfold.h" /* for lasttell */ #include "misc.h" #include "variables.h" #include "network.h" #include "comsat.h" static int csvalid; /* is it turned on with a good address? */ static struct sockaddr_in csaddr; static char*cslastf,*cslgname; void setlfcs(folder)const char*folder; /* set lastfolder for comsat */ { char*new,*old; if(strchr(dirsep,*folder)) /* absolute filename? */ new=tstrdup(folder); else /* nope; prepend the current maildir */ { const char*md=tgetenv(maildir); size_t len=strlen(md)+2+strlen(folder); new=malloc(len); strcpy(new,md); strlcat(new,MCDIRSEP_,len); strlcat(new,folder,len); } onguard(); /* protect the change over from signals */ old=cslastf; cslastf=new; offguard(); if(old) free(old); } void setlgcs(name)const char*name; /* set logname for comsat */ { char*new,*old; new=tstrdup(name); onguard(); /* protect the change over from signals */ old=cslgname; cslgname=new; offguard(); if(old) free(old); } int setcomsat(chp)const char*chp; { char*chad;int newvalid; struct sockaddr_in newaddr; chad=strchr(chp,SERV_ADDRsep); /* @ separator? */ if(!chad&&!renvint(-1L,chp)) return csvalid=0; /* turned off comsat */ newvalid=1; if(chad) *chad++='\0'; /* split the specifier */ if(!chad||!*chad) /* no host */ #ifndef IP_localhost /* Is "localhost" preresolved? */ chad=COMSAThost; /* nope, use default */ #else /* IP_localhost */ { static const unsigned char ip_localhost[]=IP_localhost; newaddr.sin_family=AF_INET; tmemmove(&newaddr.sin_addr,ip_localhost,sizeof ip_localhost); } else #endif /* IP_localhost */ { const struct hostent*host; /* what host? paranoid checks */ if(!(host=gethostbyname(chad))||!host->h_0addr_list) { bbzero(&newaddr.sin_addr,sizeof newaddr.sin_addr); newvalid=0; /* host can't be found, too bad */ } else { newaddr.sin_family=host->h_addrtype; /* address number found */ tmemmove(&newaddr.sin_addr,host->h_0addr_list,host->h_length); } endhostent(); } if(newvalid) /* so far, so good */ { int s; if(!*chp) /* no service */ chp=BIFF_serviceport; /* new balls please! */ s=strtol(chp,&chad,10); if(chp!=chad) /* the service is not numeric */ newaddr.sin_port=htons((short)s); /* network order */ else { const struct servent*serv; serv=getservbyname(chp,COMSATprotocol); /* so get its no. */ if(serv) newaddr.sin_port=serv->s_port; else { newaddr.sin_port=htons((short)0); /* no such service */ newvalid=0; } endservent(); } } onguard(); /* update the address atomically */ if(csvalid=newvalid) tmemmove(&csaddr,&newaddr,sizeof(newaddr)); offguard(); return newvalid; } void sendcomsat(folder)const char*folder; { int s;const char*p; if(!csvalid||!buf) /* is comat on and set to a valid address? */ return; if(!*cslgname||strlen(cslgname)+2>linebuf) /* is $LOGNAME bogus? */ return; if(!(p=folder?folder:cslastf)) /* do we have a folder? */ return; strcpy(buf,cslgname); strlcat(buf,"@",linebuf); /* start setting up the message */ if(lasttell>=0) /* was it a file? */ { ultstr(0,(unsigned long)lasttell,buf2); /* yep, include the offset */ strlcat(buf,buf2,linebuf); } strlcat(buf,COMSATxtrsep,linebuf); /* custom seperator */ strlcat(buf,p,linebuf); /* where was it delivered? */ if((s=socket(AF_INET,SOCK_DGRAM,UDP_protocolno))>=0) { sendto(s,buf,strlen(buf),0,(struct sockaddr*)&csaddr,sizeof(csaddr)); rclose(s); yell("Notified comsat:",buf); } } #else int comsat_dummy_var; /* to prevent insanity in some linkers */ #endif procmail-3.22/src/goodies.h0100644006717600001440000000073407223546534015062 0ustar guenthersrc/*$Id: goodies.h,v 1.26 2000/11/22 01:29:58 guenther Exp $*/ int readparse P((char*p,int(*const fpgetc)(),const int sarg,int skipping)); char *simplesplit P((char*to,const char*from,const char*fencepost,int*gotp)); void concatenate P((char*p)), metaparse P((const char*p)), ltstr P((const int minwidth,const long val,char*dest)); #ifdef NOstrtod double strtod P((const char*str,char**const ptr)); #endif extern const char test[]; extern const char*Tmnate,*All_args; procmail-3.22/src/formail.c0100644006717600001440000010306207347314674015060 0ustar guenthersrc/************************************************************************ * formail - The mail (re)formatter * * * * Seems to be relatively bug free. * * * * Copyright (c) 1990-2000, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: formail.c,v 1.102 2001/08/04 07:07:43 guenther Exp $"; #endif static /*const*/char rcsdate[]="$Date: 2001/08/04 07:07:43 $"; #include "includes.h" #include /* iscntrl() */ #include "formail.h" #include "acommon.h" #include "sublib.h" #include "shell.h" #include "common.h" #include "fields.h" #include "ecommon.h" #include "formisc.h" #include "../patchlevel.h" #define ssl(str) str,STRLEN(str) #define bsl(str) {ssl(str)} #define sslbar(str,bar1,bar2) {ssl(str),STRLEN(bar1)-1,STRLEN(bar2)-1} static const char #define X(name,value) name[]=value, #include "header.h" /* pull in the definitions */ #undef X From_[]= FROM, /* VNIX 'From ' line */ Article_[]= "Article ", /* USENET 'Article ' line */ x_[]= "X-", /* general extension */ old_[]= OLD_PREFIX, /* my extension */ xloop[]= "X-Loop:", /* ditto ... */ Resent_[]= "Resent-", /* for tweaking reply preferences */ mdaemon[]="<>",unknown[]=UNKNOWN,re[]=" Re:",fmusage[]=FM_USAGE; static const struct {const char*hedr;int lnr;}cdigest[]= { #define X(name,value) bsl(name), #include "header.h" /* pull in the precalculated references */ #undef X }; /* * sender determination fields in order of importance/reliability * reply-address determination fields (wrepl specifies the weight * for header replies and wrrepl specifies the weight for header * replies where Resent- header are used, while the position in the * table index specifies the weight for envelope replies and From_ * line creation. * * I bet this is the first time you've seen a bar graph in * C-source-code :-) */ static const struct {const char*head;int len,wrepl,wrrepl;}sest[]= { sslbar(replyto ,"*********" ,"********" ), sslbar(Fromm ,"**foo***" ,"**bar**" ), sslbar(sender ,"*******" ,"******" ), sslbar(res_replyto ,"*" ,"***********" ), sslbar(res_from ,"*" ,"**********" ), sslbar(res_sender ,"*" ,"*********" ), sslbar(path ,"**" ,"*" ), sslbar(retreceiptto ,"***" ,"**" ), sslbar(errorsto ,"****" ,"***" ), sslbar(returnpath ,"******" ,"*****" ), sslbar(From_ ,"*****" ,"****" ), }; static struct saved rex[]= { bsl(subject),bsl(references),bsl(messageid),bsl(date) }; #define subj (rex+0) #define refr (rex+1) #define msid (rex+2) #define hdate (rex+3) #ifdef sMAILBOX_SEPARATOR #define emboxsep smboxsep #define MAILBOX_SEPARATOR static const char smboxsep[]=sMAILBOX_SEPARATOR; #endif /* sMAILBOX_SEPARATOR */ #ifdef eMAILBOX_SEPARATOR #ifdef emboxsep #undef emboxsep #else #define MAILBOX_SEPARATOR #endif static const char emboxsep[]=eMAILBOX_SEPARATOR; #endif /* eMAILBOX_SEPARATOR */ const char binsh[]=BinSh,sfolder[]=FOLDER, couldntw[]="Couldn't write to stdout",formailn[]=FORMAILN; int errout,oldstdout,quiet=1,zap,buflast,lenfileno; long initfileno; char ffileno[LEN_FILENO_VAR+8*sizeof(initfileno)*4/10+1+1]=DEFfileno; int lexitcode; /* dummy, for waitfor() */ pid_t child= -1; int childlimit; unsigned long rhash; FILE*mystdout; int nrskip,nrtotal= -1,retval=EXIT_SUCCESS; size_t buflen,buffilled; long Totallen; char*buf,*logsummary; struct field*rdheader,*xheader,*Xheader,*uheader,*Uheader; static struct field*iheader,*Iheader,*aheader,*Aheader,*Rheader,*nheader; static int areply; static void logfolder P((void)) /* estimate the no. of characters needed to */ { size_t i;charNUM(num,Totallen); /* represent Totallen */ static const char tabchar[]=TABCHAR; if(logsummary) { putssn(sfolder,STRLEN(sfolder));putssn(logsummary,i=strlen(logsummary)); i+=STRLEN(sfolder);i-=i%TABWIDTH; do putssn(tabchar,STRLEN(tabchar)); while((i+=TABWIDTH)fld_text)[p->Tot_len-1]='\0'; if(eqFrom_(chp)) /* continued From_ to */ for(;chp=strstr(chp,"\n>");*++chp=' '); /* continued regular field */ if(newl==STRLEN(From_)&&eqFrom_(newname)) { for(chp=p->fld_text;chp=strchr(chp,'\n');) /* continued regular */ if(*++chp==' '||*chp=='\t') /* to continued From_ field */ *chp='>'; for(chp=p->fld_text;chp=strstr(chp,"\n ");*++chp='>'); goto replaceall; } if(newname[newl-1]==HEAD_DELIMITER) /* completely new field */ replaceall: oldl=p->id_len; /* replace the old one entirely */ p->id_len+=(int)newl-(int)oldl;p->fld_text[p->Tot_len-1]='\n'; p->Tot_len=(i=p->Tot_len-oldl)+newl; if(newl>oldl) *pointer=p=realloc(p,FLD_HEADSIZ+p->Tot_len); chp=p->fld_text;tmemmove(chp+newl,chp+oldl,i);tmemmove(chp,newname,newl); } static void procfields(sareply)const int sareply; { struct field*fldp,**afldp; fldp= *(afldp= &rdheader); while(fldp) { struct field*fp2; if(!sareply&& (fp2=findf(fldp,&iheader))&& !(areply&&fldp->id_len>=fp2->Tot_len-1)) /* filled replacement? */ { renfield(afldp,(size_t)0,old_,STRLEN(old_)); /* implicitly rename */ goto fixfldp; } if((fp2=findf(fldp,&Iheader))&& /* delete fields */ !(sareply&&fldp->id_lenTot_len-1)) /* empty replacement? */ goto delfld; if(fp2=findf(fldp,&Rheader)) /* explicitly rename field */ { renfield(afldp,fp2->id_len,(char*)fp2->fld_text+fp2->id_len, fp2->Tot_len-fp2->id_len); fixfldp: fldp= *afldp; } ;{ struct field*uf; if((uf=findf(fldp,&uheader))&&!uf->fld_ref) uf->fld_ref=afldp; /* first uheader, keep it */ else if(fp2=findf(fldp,&Uheader)) { if(fp2->fld_ref) { struct field**ch_afldp; if(afldp==(ch_afldp= &(*fp2->fld_ref)->fld_next)) afldp=fp2->fld_ref; /* deleting own reference */ for(fldp=Uheader;fldp;fldp=fldp->fld_next) if(fldp->fld_ref==ch_afldp) /* rearrange references to */ fldp->fld_ref=fp2->fld_ref; /* vanishing field */ delfield(fp2->fld_ref); /* delete old Uheader */ } fp2->fld_ref=afldp; /* keep last Uheader */ } else if(uf) /* delete all following uheaders */ delfld: { fldp=delfield(afldp); continue; } } fldp= *(afldp= &(*afldp)->fld_next); } } /* checks if the last field in rdheader looks like a known digest header */ static int digheadr P((void)) { char*chp;int i;size_t j;struct field*fp; for(fp=rdheader;fp->fld_next;fp=fp->fld_next); /* skip to the last */ i=maxindex(cdigest);chp=fp->fld_text;j=fp->id_len; while(chp[j-2]==' '||chp[j-2]=='\t') /* whitespace before the colon? */ j--; while((cdigest[i].lnr!=j||strncasecmp(cdigest[i].hedr,chp,j-1))&&i--); return i>=0||j>STRLEN(old_)&&!strncasecmp(old_,chp,STRLEN(old_))|| j>STRLEN(x_)&&!strncasecmp(x_,chp,STRLEN(x_)); } static int artheadr P((void)) /* could it be the start of an article? */ { if(!rdheader&&!strncmp(buf,Article_,STRLEN(Article_))) { addbuf();rdheader->id_len=STRLEN(Article_); return 1; } return 0; } /* lifted out of main() to reduce main()'s size */ static char*getsender(namep,fldp,headreply)char*namep;struct field*fldp; const int headreply; { char*chp;int i,nowm;size_t j;static int lastm; chp=fldp->fld_text;j=fldp->id_len;i=maxindex(sest); while((sest[i].len!=j||strncasecmp(sest[i].head,chp,j))&&i--); if(i>=0&&(i!=maxindex(sest)||fldp==rdheader)) /* found anything? */ { char*saddr;char*tmp; /* determine the weight */ nowm=areply&&headreply?headreply==1?sest[i].wrepl:sest[i].wrrepl:i;chp+=j; tmp=malloc(j=fldp->Tot_len-j);tmemmove(tmp,chp,j);(chp=tmp)[j-1]='\0'; if(sest[i].head==From_) { char*pastad; if(strchr(saddr=chp,'\n')) /* multiple From_ lines */ nowm-=2; /* aren't as trustworthy */ if(*saddr=='\n'&&(pastad=strchr(saddr,' '))) saddr=pastad+1; /* reposition at the address */ chp=saddr; while((pastad=strchr(chp,'\n'))&&(pastad=strchr(pastad,' '))) chp=pastad+1; /* skip to the last uucp >From */ if(pastad=strchr(chp,' ')) /* found an address? */ { char*savetmp; /* lift it out */ savetmp=malloc(1+(j=pastad-chp)+1+1);tmemmove(savetmp,chp,j); savetmp[j]='\0'; /* make work copy */ if(strchr(savetmp,'@')) /* domain attached? */ chp=savetmp,savetmp=tmp,tmp=chp; /* ok, ready */ else /* no domain, bang away! :-) */ { static const char remf[]=" remote from ",fwdb[]=" forwarded by "; char*p1,*p2; chp=tmp; for(;;) { int c; p1=strstr(saddr,remf); if(!(p2=strstr(saddr,fwdb))&&!p1) break; /* no more info */ if(!p1||p2&&p2lastm) /* better than previous ones */ goto pnewname; } else if(sest[i].head==returnpath) /* nill Return-Path: */ { saddr=(char*)mdaemon;nowm=maxindex(sest)+2; /* override */ pnewname: lastm=nowm;saddr=strcpy(malloc(strlen(saddr)+1),saddr); if(namep) free(namep); namep=saddr; } } free(tmp); } /* save headers for later perusal */ return namep; } /* lifted out of main() to reduce main()'s size */ static void elimdups(namep,idcache,maxlen,split)const char*const namep; FILE*idcache;const long maxlen;const int split; { int dupid=0;char*key,*oldnewl; key=(char*)namep; /* not to worry, no change will be noticed */ if(!areply) { key=0; if(msid->rexl) /* any Message-ID: ? */ *(oldnewl=(key=msid->rexp)+msid->rexl-1)='\0'; } /* wipe out trailing newline */ if(key) { long insoffs=maxlen; while(*key==' ') /* strip leading spaces */ key++; do { int j;char*p; /* start reading & comparing the next word */ for(p=key;(j=fgetc(idcache))==*p;p++) if(!j) /* end of word? */ { if(!quiet) nlog("Duplicate key found:"),elog(key),elog("\n"); dupid=1; goto dupfound; /* YES! duplicate found */ } if(!j) /* end of word? */ { if(p==key&&insoffs==maxlen) /* first character? */ { insoffs=ftell(idcache)-1; /* found end of */ goto skiprest; /* circular buffer */ } } else skiprest: for(;;) /* skip the rest of the word */ { switch(fgetc(idcache)) { case EOF: goto noluck; default: continue; case '\0':; } break; } } while(ftell(idcache)=maxlen) /* past our quota? */ insoffs=0; /* start up front again */ fseek(idcache,insoffs,SEEK_SET);fwrite(key,1,strlen(key)+1,idcache); putc('\0',idcache); /* mark new end of buffer */ dupfound: fseek(idcache,(long)0,SEEK_SET); /* rewind, for any next run */ if(!areply) *oldnewl='\n'; /* restore the newline */ } if(!split) /* not splitting? terminate early */ exit(dupid?EXIT_SUCCESS:1); if(dupid) /* duplicate? suppress output */ closemine(),opensink(); } static PROGID; int main(lastm,argv)int lastm;const char*const argv[]; { int i,split=0,force=0,bogus=1,every=0,headreply=0,digest=0,nowait=0,keepb=0, minfields=(char*)progid-(char*)progid,conctenate=0,babyl=0,babylstart, berkeley=0,forgetclen; long maxlen,ctlength;FILE*idcache=0;pid_t thepid; size_t j,lnl,escaplen;char*chp,*namep,*escap=ESCAP; struct field*fldp,*fp2,**afldp,*fdate,*fcntlength,*fsubject,*fFrom_; if(lastm) /* sanity check, any argument at all? */ #define Qnext_arg() if(!*chp&&!(chp=(char*)*++argv))goto usg while(chp=(char*)*++argv) { if((lastm= *chp++)==FM_SKIP) goto number; else if(lastm!=FM_TOTAL) goto usg; for(;;) { switch(lastm= *chp++) { case FM_TRUST:headreply|=1; continue; case FM_REPLY:areply=1; continue; case FM_FORCE:force=1; continue; case FM_EVERY:every=1; continue; case FM_BABYL:babyl=every=1; case FM_DIGEST:digest=1; continue; case FM_NOWAIT:nowait=1;Qnext_arg(); childlimit=strtol(chp,&chp,10); continue; case FM_KEEPB:keepb=1; continue; case FM_CONCATENATE:conctenate=1; continue; case FM_ZAPWHITE:zap=1; continue; case FM_QUIET:quiet=1; if(*chp=='-') chp++,quiet=0; continue; case FM_LOGSUMMARY:Qnext_arg(); if(strlen(logsummary=chp)>MAXfoldlen) chp[MAXfoldlen]='\0'; detab(chp); break; case FM_SPLIT:split=1; if(!*chp) { ++argv; goto parsedoptions; } goto usg; case HELPOPT1:case HELPOPT2:elog(fmusage);elog(FM_HELP); elog(FM_HELP2); /* had to split up FM_HELP, compiler limits */ goto xusg; case FM_DUPLICATE:case FM_MINFIELDS:Qnext_arg();chp++; default:chp--; number: if(*chp-'0'>(unsigned)9) /* the number is not >=0 */ goto usg; i=strtol(chp,&chp,10); switch(lastm) /* where does the number go? */ { case FM_SKIP:nrskip=i; break; case FM_DUPLICATE:maxlen=i;Qnext_arg(); if(!(idcache=fopen(chp,"r+b"))&& /* existing cache? */ !(idcache=fopen(chp,"w+b"))) /* create cache? */ { nlog("Couldn't open");logqnl(chp); return EX_CANTCREAT; } goto nextarg; case FM_MINFIELDS:minfields=i; break; default:nrtotal=i; } continue; case FM_BOGUS:bogus=0; continue; case FM_BERKELEY:berkeley=1; continue; case FM_QPREFIX:Qnext_arg();escap=chp; break; case FM_VERSION:elog(formailn);elog(VERSION); goto xusg; case FM_ADD_IFNOT:case FM_ADD_ALWAYS:case FM_REN_INSERT: case FM_DEL_INSERT:case FM_EXTRACT:case FM_EXTRC_KEEP: case FM_FIRST_UNIQ:case FM_LAST_UNIQ:case FM_ReNAME:Qnext_arg(); i=breakfield(chp,lnl=strlen(chp)); switch(lastm) { case FM_ADD_IFNOT: if(i>0) break; if(i!=-STRLEN(Resent_)||-i!=lnl|| /* the only partial */ strncasecmp(chp,Resent_,STRLEN(Resent_)+1)) /* field */ goto invfield; /* allowed with -a is Resent- */ headreply|=2; goto nextarg; /* don't add to the list */ default: if(-i!=lnl) /* it is not an early ending field */ case FM_ADD_ALWAYS: if(i<=0) /* and it is not a valid field */ goto invfield; /* complain */ case FM_ReNAME:; /* everything allowed */ } chp[lnl]='\n'; /* terminate the line */ afldp=addfield(lastm==FM_REN_INSERT?&iheader: lastm==FM_DEL_INSERT?&Iheader:lastm==FM_ADD_IFNOT?&aheader: lastm==FM_ADD_ALWAYS?&Aheader:lastm==FM_EXTRACT?&xheader: lastm==FM_FIRST_UNIQ?&uheader:lastm==FM_LAST_UNIQ?&Uheader: lastm==FM_EXTRC_KEEP?&Xheader:&Rheader,chp,++lnl); if(lastm==FM_ReNAME) /* then we need a second field */ { int copied=0; for(namep=(chp=(fldp= *afldp)->fld_text)+lnl, chp+=lnl=fldp->id_len;chp0) /* complete first field */ goto invfield; /* impossible combination */ else i= -i; if(i) tmemmove((char*)fldp->fld_text+lnl,chp,i),copied=1; else if(namep>chp|| /* garbage? */ !(chp=(char*)*++argv)|| /* look at next arg */ (!(i=breakfield(chp,strlen(chp)))&& /* fieldish? */ *chp)|| /* but "" is fine */ i<=0&&(i= -i,lastm>0)) /* impossible combination */ invfield: { nlog("Invalid field-name:");logqnl(chp?chp:""); goto usg; } *afldp=fldp= realloc(fldp,FLD_HEADSIZ+(fldp->Tot_len=lnl+i)); if(!copied) /* if not squeezed on yet */ tmemmove((char*)fldp->fld_text+lnl,chp,i); /* do now */ } case '\0':; } break; } nextarg:; } parsedoptions: escaplen=strlen(escap);mystdout=stdout;signal(SIGPIPE,SIG_IGN); #ifdef SIGCHLD signal(SIGCHLD,SIG_DFL); #endif thepid=getpid(); if(babyl) /* skip BABYL leader */ { while(getchar()!=BABYL_SEP1||getchar()!=BABYL_SEP2||getchar()!='\n') while(getchar()!='\n'); while(getchar()!='\n'); } while((buflast=getchar())=='\n'); /* skip leading garbage */ if(split) { char**ep;char**vfileno=0; if(buflast==EOF) /* avoid splitting empty messages */ return EXIT_SUCCESS; for(ep=environ;*ep;ep++) /* gobble through the environment */ if(!strncmp(*ep,ffileno,LEN_FILENO_VAR)) /* look for FILENO= */ vfileno=ep; /* yes, found it */ if(!vfileno) /* FILENO= found in the environment? */ { size_t envlen; /* no, pity */ envlen=(ep-environ+1)*sizeof*environ; /* current length */ tmemmove(ep=malloc(envlen+sizeof*environ),environ,envlen); *(vfileno=(char**)((char*)(environ=ep)+envlen))=0;*--vfileno=ffileno; } /* copy over the array */ if((lenfileno=strlen(chp= *vfileno+LEN_FILENO_VAR))> STRLEN(ffileno)-LEN_FILENO_VAR-1) /* check the desired width */ lenfileno=STRLEN(ffileno)-LEN_FILENO_VAR-1; /* too big, truncate */ if((initfileno=strtol(chp,&chp,10))<0) /* fetch the initial value */ lenfileno--; /* correct it for negatives */ if(*chp) /* no valid number? */ lenfileno= -1; /* disable the FILENO generation */ else *vfileno=ffileno; /* stuff our template in the environment */ oldstdout=dup(STDOUT);fclose(stdout); if(!nrtotal) goto onlyhead; startprog((const char*Const*)argv); if(!minfields) /* no user specified minimum? */ minfields=DEFminfields; /* take our default */ } else if(nrskip>0||nrtotal>=0||every||digest||minfields||nowait) goto usg; /* only valid in combination with split */ if((xheader||Xheader)&&logsummary||keepb&&!(areply||xheader||Xheader)) usg: /* options sanity check */ { elog(fmusage); /* impossible mix */ xusg: return EX_USAGE; } if(headreply==2) /* -aResent- is only allowed */ { chp=(char*)Resent_; /* as a modifier to header replies */ goto invfield; } buf=malloc(buflen=Bsize);Totallen=0;i=maxindex(rex); /* prime some buffers */ do rex[i].rexp=malloc(1); while(i--); fdate=0;addfield(&fdate,date,STRLEN(date)); /* fdate is only for searching */ fcntlength=0;addfield(&fcntlength,cntlength,STRLEN(cntlength)); /* ditto */ fFrom_=0;addfield(&fFrom_,From_,STRLEN(From_)); fsubject=0;addfield(&fsubject,subject,STRLEN(subject)); /* likewise */ forgetclen=digest|| /* forget Content-Length: for a digest */ berkeley|| /* for Berkeley format */ keepb&& /* if we're keeping the body and */ (areply|| /* autoreplying */ Xheader&& /* or eXtracting without */ !findf(fcntlength,&Xheader)); /* getting Content-Length: */ if(areply) /* when auto-replying */ addfield(&iheader,xloop,STRLEN(xloop)); /* preserve X-Loop: fields */ if(!readhead()) /* start looking */ { #ifdef sMAILBOX_SEPARATOR /* check for a leading */ if(!strncmp(smboxsep,buf,STRLEN(smboxsep))) /* mailbox separator */ { buffilled=0; /* skip it */ goto startover; } #endif if(digest&&artheadr()) goto startover; } else startover: while(readhead()); /* read in the whole header */ cleanheader(); ;{ size_t lenparkedbuf;void*parkedbuf;int wasafrom_; if(rdheader) { char*tmp,*tmp2; if(!strncmp(tmp=(char*)rdheader->fld_text,Article_,STRLEN(Article_))) tmp[STRLEN(Article_)-1]=HEAD_DELIMITER; else if(babyl&& !force&& !strncmp(tmp,mailfrom,STRLEN(mailfrom))&& eqFrom_(tmp2=skpspace(tmp+STRLEN(mailfrom)))) { rdheader->id_len=STRLEN(From_); tmemmove(tmp,tmp2,rdheader->Tot_len-=tmp2-tmp); } } namep=0;Totallen=0;i=maxindex(rex); do rex[i].rexl=0; while(i--); /* reset all state information */ clear_uhead(uheader);clear_uhead(Uheader); wasafrom_=!force&&rdheader&&eqFrom_(rdheader->fld_text); procfields(areply); for(fldp= *(afldp= &rdheader);fldp;) { if(zap) /* go through the linked list of header-fields */ { chp=fldp->fld_text+(j=fldp->id_len); if(chp[-1]==HEAD_DELIMITER) if((*chp!=' '&&*chp!='\t')&&fldp->Tot_len>j+1) { chp=j+(*afldp=fldp= realloc(fldp,FLD_HEADSIZ+(i=fldp->Tot_len++)+1))->fld_text; tmemmove(chp+1,chp,i-j);*chp=' '; } else if(fldp->Tot_len<=j+2) { *afldp=fldp->fld_next;free(fldp);fldp= *afldp; continue; } } if(conctenate) concatenate(fldp); /* save fields for later perusal */ namep=getsender(namep,fldp,headreply); i=maxindex(rex);chp=fldp->fld_text;j=fldp->id_len; while((rex[i].lenr!=j||strncasecmp(rex[i].headr,chp,j))&&i--); chp+=j; if(i>=0&&(j=fldp->Tot_len-j)>1) /* found anything? */ { tmemmove(rex[i].rexp=realloc(rex[i].rexp,(rex[i].rexl=j)+1),chp,j); rex[i].rexp[j]='\0'; /* add a terminating \0 */ } fldp= *(afldp= &fldp->fld_next); } if(idcache) elimdups(namep,idcache,maxlen,split); ctlength=0; if(!forgetclen&&(fldp=findf(fcntlength,&rdheader))) { *(chp=(char*)fldp->fld_text+fldp->Tot_len-1)='\0'; /* terminate it */ ctlength=strtol((char*)fldp->fld_text+STRLEN(cntlength),(char**)0,10); *chp='\n'; /* restore the trailing newline */ } tmemmove(parkedbuf=malloc(buffilled),buf,lenparkedbuf=buffilled); buffilled=0; /* moved the contents of buf out of the way temporarily */ if(areply) /* autoreply requested, we clean up the header */ { for(fldp= *(afldp= &rdheader);fldp;) if(!(fp2=findf(fldp,&iheader))||fp2->id_lenTot_len-1) *afldp=fldp->fld_next,free(fldp),fldp= *afldp; /* remove all */ else /* except the ones mentioned */ fldp= *(afldp= &fldp->fld_next); /* as -i ...: */ loadbuf(To,STRLEN(To));loadchar(' '); /* generate the To: field */ if(namep) /* did we find a valid return address at all? */ loadbuf(namep,strlen(namep)); /* then insert it here */ else /* or insert our default */ retval=EX_NOUSER,loadbuf(unknown,STRLEN(unknown)); loadchar('\n');addbuf(); /* add it to rdheader */ if(subj->rexl) /* any Subject: found? */ { loadbuf(subject,STRLEN(subject)); /* sure, check for leading */ if(strncasecmp(skpspace(chp=subj->rexp),Re,STRLEN(Re))) /* Re: */ loadbuf(re,STRLEN(re)); /* no Re: , add one ourselves */ loadsaved(subj);addbuf(); } if(refr->rexl||msid->rexl) /* any References: or Message-ID: */ { loadbuf(references,STRLEN(references)); /* yes insert References: */ if(refr->rexl) { if(msid->rexl) /* if we're going to append a Message-ID */ --refr->rexl; /* suppress the trailing newline */ loadsaved(refr); } if(msid->rexl) loadsaved(msid); /* here's our missing newline */ addbuf(); } if(msid->rexl) /* do we add an In-Reply-To: field? */ loadbuf(inreplyto,STRLEN(inreplyto)),loadsaved(msid),addbuf(); procfields(0); } else if(!force&& /* are we allowed to add From_ lines? */ (!rdheader||!eqFrom_(rdheader->fld_text))&& /* is it missing? */ ((fldp=findf(fFrom_,&aheader))&&STRLEN(From_)+1>=fldp->Tot_len|| !wasafrom_&& /* if there was no From_ */ !findf(fFrom_,&iheader)&& /* and From_ is not being */ !findf(fFrom_,&Iheader)&& /* supressed */ !findf(fFrom_,&Rheader))) { struct field*old;time_t t; /* insert a From_ line up front */ t=time((time_t*)0);old=rdheader;rdheader=0; loadbuf(From_,STRLEN(From_)); if(namep) /* we found a valid return address */ loadbuf(namep,strlen(namep)); else loadbuf(unknown,STRLEN(unknown)); loadchar(' '); /* insert one extra blank */ if(!hdate->rexl||!findf(fdate,&aheader)) /* Date: */ loadchar(' '),chp=ctime(&t),loadbuf(chp,strlen(chp)); /* no Date: */ else /* we generate it ourselves */ loadsaved(hdate); /* yes, found Date:, then copy from it */ addbuf();rdheader->fld_next=old; } for(fldp=aheader;fldp;fldp=fldp->fld_next) if(!findf(fldp,&rdheader)) /* only add what didn't exist */ if(fldp->id_len+1>=fldp->Tot_len&& /* field name only */ (fldp->id_len==STRLEN(messageid)&& !strncasecmp(fldp->fld_text,messageid,STRLEN(messageid))|| fldp->id_len==STRLEN(res_messageid)&& !strncasecmp(fldp->fld_text,res_messageid,STRLEN(res_messageid)) )) { char*p;const char*name;unsigned long h1,h2,h3; static unsigned long h4; /* conjure up a `unique' msg-id field */ h1=time((time_t*)0);h2=thepid;h3=rhash; p=chp=malloc(fldp->id_len+2+((sizeof h1*8+5)/6+1)*4+ strlen(name=hostname())+2); /* allocate worst case length */ memcpy(p,fldp->fld_text,fldp->id_len);*(p+=fldp->id_len)=' '; *++p='<';*(p=ultoan(h3,p+1))='.';*(p=ultoan(h4,p+1))='.'; *(p=ultoan(h2,p+1))='.';*(p=ultoan(h1,p+1))='@';strcpy(p+1,name); *(p=strchr(p,'\0'))='>';*++p='\n';addfield(&nheader,chp,p-chp+1); free(chp);h4++; /* put it in */ } else addfield(&nheader,fldp->fld_text,fldp->Tot_len); if(logsummary) { if(eqFrom_(rdheader->fld_text)) putssn(rdheader->fld_text,rdheader->Tot_len); if(fldp=findf(fsubject,&rdheader)) { concatenate(fldp);(chp=fldp->fld_text)[i=fldp->Tot_len-1]='\0'; detab(chp);putcs(' '); putssn(chp,i>=MAXSUBJECTSHOW?MAXSUBJECTSHOW:i);putcs('\n'); } } /* restore the saved contents of buf */ tmemmove(buf,parkedbuf,buffilled=lenparkedbuf);free(parkedbuf); } flushfield(&rdheader);flushfield(&nheader);dispfield(Aheader); dispfield(iheader);dispfield(Iheader); if(namep) free(namep); if(keepb||!(xheader||Xheader)) /* we're not just extracting fields */ lputcs('\n'); /* make sure it is followed by an empty line */ if(!keepb&&(areply||xheader||Xheader)) /* decision time */ { logfolder(); /* we throw away the rest */ if(split) closemine(); else /* terminate early, only the header was needed */ goto onlyhead; opensink(); /* discard the body */ } lnl=1; /* last line was a newline */ if(buffilled==1) /* the header really ended with a newline */ buffilled=0; /* throw it away, since we already inserted it */ if(babyl) { int c,lc; /* ditch pseudo BABYL header */ for(lc=0;c=getchar(),c!=EOF&&(c!='\n'||lc!='\n');lc=c); buflast=c;babylstart=0; } if(ctlength>0) { if(buffilled) lputssn(buf,buffilled),ctlength-=buffilled,buffilled=lnl=0; ;{ int tbl=buflast,lwr='\n'; while(--ctlength>=0&&tbl!=EOF) /* skip Content-Length: bytes */ lnl=lwr==tbl&&lwr=='\n',putcs(lwr=tbl),tbl=getchar(); if((buflast=tbl)=='\n'&&lwr!=tbl) /* just before a line break? */ putcs('\n'),buflast=getchar(); /* wrap up loose end */ } if(!quiet&&ctlength>0) { charNUM(num,ctlength); nlog(cntlength);elog(" field exceeds actual length by "); ultstr(0,(unsigned long)ctlength,num);elog(num);elog(" bytes\n"); } } while(buffilled||!lnl||buflast!=EOF) /* continue the quest, line by line */ { if(!buffilled) /* is it really empty? */ readhead(); /* read the next field */ if(!babyl||babylstart) /* don't split BABYL files everywhere */ { if(rdheader) /* anything looking like a header found? */ { if(eqFrom_(chp=rdheader->fld_text)) /* check if it's From_ */ fromanyway: { register size_t k; if(split&& (lnl||every)&& /* more thorough check for a postmark */ (k=strcspn(chp=skpspace(chp+STRLEN(From_))," \t\n"))&& *skpspace(chp+k)!='\n') goto accuhdr; /* ok, postmark found, split it */ if(bogus) /* disarm */ lputssn(escap,escaplen); } else if(split&&digest&&(lnl||every)&&digheadr()) /* digest? */ accuhdr: { for(i=minfields;--i&&readhead()&&digheadr();); /* found enough */ if(!i) /* then split it! */ splitit: { if(!lnl) /* did the previous mail end with an empty line? */ lputcs('\n'); /* but now it does :-) */ logfolder(); if(fclose(mystdout)==EOF||errout==EOF) { split= -1; if(!quiet) nlog(couldntw),elog(", continuing...\n"); } if(!nowait&&*argv) /* wait till the child has finished */ { int excode; if((excode=waitfor(child))!=EXIT_SUCCESS&& retval==EXIT_SUCCESS) retval=excode; } if(!nrtotal) goto nconlyhead; startprog((const char*Const*)argv); goto startover; } /* and there we go again */ } } else if(eqFrom_(buf)) /* special case, From_ line */ { addbuf(); /* add it manually, readhead() didn't */ goto fromanyway; } else if(split&&digest&&(lnl||every)&&artheadr()) goto accuhdr; } #ifdef MAILBOX_SEPARATOR if(!strncmp(emboxsep,buf,STRLEN(emboxsep))) /* end of mail? */ { if(split) /* gobble up the next start separator */ { buffilled=0; #ifdef sMAILBOX_SEPARATOR getline();buffilled=0; /* but only if it's defined */ #endif if(buflast!=EOF) /* if any */ goto splitit; break; } #ifdef eMAILBOX_SEPARATOR if(buflast==EOF) break; #endif if(bogus) goto putsp; /* escape it with a space */ } else if(!strncmp(smboxsep,buf,STRLEN(smboxsep))&&bogus) putsp: lputcs(' '); #endif /* MAILBOX_SEPARATOR */ lnl=buffilled==1; /* check if we just read an empty line */ if(babyl&&*buf==BABYL_SEP1) babylstart=1,closemine(),opensink(); /* discard the rest */ if(areply&&bogus) /* escape the body */ if(fldp=rdheader) /* we already read some "valid" fields */ { register char*p; rdheader=0; do /* careful, they can contain newlines */ { fp2=fldp->fld_next;chp=fldp->fld_text; do { lputssn(escap,escaplen); if(p=memchr(chp,'\n',fldp->Tot_len)) p++; else p=(char*)fldp->fld_text+fldp->Tot_len; lputssn(chp,p-chp); } while((chp=p)<(char*)fldp->fld_text+fldp->Tot_len); free(fldp); /* delete it */ } while(fldp=fp2); /* escape all fields we found */ } else { if(buffilled>1) /* we don't escape empty lines, looks neat */ lputssn(escap,escaplen); goto flbuf; } else if(rdheader) { struct field*ox,*oX; ox=xheader;oX=Xheader;xheader=Xheader=0;flushfield(&rdheader); xheader=ox;Xheader=oX; /* beware, after this buf can still be filled */ } else flbuf: lputssn(buf,buffilled),buffilled=0; } /* make sure the mail ends with an empty line */ logfolder(); onlyhead: closemine(); nconlyhead: if(split) /* wait for everyone */ { int excode; close(STDIN); /* close stdin now, we're not reading anymore */ while((excode=waitfor((pid_t)0))!=NO_PROCESS) if(retval==EXIT_SUCCESS&&excode!=EXIT_SUCCESS) retval=excode; } if(retval<0) retval=EX_UNAVAILABLE; return retval!=EXIT_SUCCESS?retval:split<0?EX_IOERR:EXIT_SUCCESS; } int eqFrom_(a)const char*const a; { return !strncmp(a,From_,STRLEN(From_)); } int breakfield(line,len)const char*const line;size_t len; /* look where the */ { const char*p=line; /* fieldname ends (RFC 822 specs) */ while(len) { switch(*p) { default:len--; if(iscntrl(*p)) /* no control characters allowed */ break; p++; continue; case HEAD_DELIMITER: len=p-line; return len?len+1:0; /* eureka! */ case ' ':case '\t': /* whitespace is okay right before the colon */ if(p>line) /* but only if we've seen something else already */ { const char*q=++p; while(--len&&(*q==' '||*q=='\t')) /* skip forward */ q++; if(len&&*q==HEAD_DELIMITER) /* it's okay */ return q-line+1; if(eqFrom_(line)) /* special case, From_ */ return STRLEN(From_); } /* it was bogus after all */ } break; } return -(int)(p-line); /* sorry, does not seem to be a legitimate field */ } procmail-3.22/src/memblk.c0100644006717600001440000001176307316763010014670 0ustar guenthersrc/************************************************************************ * Memory block routines * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 1997-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: memblk.c,v 1.6 2001/06/28 22:55:09 guenther Exp $" #endif #include "procmail.h" #include "robust.h" #include "exopen.h" #include "memblk.h" #include "misc.h" #include "shell.h" #ifdef USE_MMAP int ISprivate; #include #define P_RW (PROT_READ|PROT_WRITE) #define MMAP_FILE_LEN (STRLEN(MMAP_DIR)+UNIQnamelen+1) #define MMAP_PERM (NORMperm&~INIT_UMASK) #define set_fd(mb,num) mb->fd=(num) static void mmapfailed P((const long len)) __attribute__((noreturn)); #else #define set_fd(mb,num) do{}while(0) #endif void makeblock(mb,len)memblk*const mb;const long len; { mb->len=0;mb->p=malloc(1);set_fd(mb,-1); if(len) resizeblock(mb,len,0); } void freeblock(mb)memblk*const mb; { #ifdef USE_MMAP if(mb->fd>=0) { munmap(mb->p,mb->len+1); close(mb->fd); } else #endif free(mb->p); } void lockblock(mb)memblk*const mb; { #ifdef USE_MMAP if(mb->fd>=0) { long len=mb->len+1; if(munmap(mb->p,len)) mmapfailed(len); /* don't want to continue here */ if((mb->p=mmap(0,len,PROT_READ,MAP_PRIVATE,mb->fd,(off_t)0))==MAP_FAILED) mmapfailed(len); close(mb->fd); mb->fd=ropen(devnull,O_RDWR,0); /* XXX Perhaps -1 is better? */ } #endif } int resizeblock(mb,len,nonfatal)memblk*const mb;const long len; const int nonfatal; { if(len==mb->len) goto ret1; if(!len) { freeblock(mb); mb->len=0;mb->p=malloc(1);set_fd(mb,-1); goto ret1; } #ifdef USE_MMAP if(len>MAXinMEM&&mb->fd<0) /* time to switch over */ { char filename[MMAP_FILE_LEN]; strcpy(filename,MMAP_DIR); if(unique(filename,strchr(filename,'\0'),MMAP_FILE_LEN,MMAP_PERM,0,0)&& (mb->fd=ropen(filename,O_RDWR,MMAP_PERM),unlink(filename),mb->fd>=0)) { mb->filelen=len; if(lseek(mb->fd,mb->filelen-1,SEEK_SET)<0||1!=rwrite(mb->fd,empty,1)) dropf: { close(mb->fd);mb->fd= -1; if(verbose)nlog("Unable to extend or use tempfile"); } else if(mb->len) { long towrite,start,wrote; if(lseek(mb->fd,(off_t)0,SEEK_SET)) goto dropf; for(start=0,towrite=mb->len>len?len:mb->len;towrite;) { if(0>(wrote=rwrite(mb->fd,mb->p+start,towrite))) goto dropf; towrite-=wrote;start+=wrote; } free(mb->p); mb->len=len; goto mmap; } } } if(mb->fd>=0) { if(len>mb->filelen) /* need to extend? */ { mb->filelen=len; if(lseek(mb->fd,mb->filelen-1,SEEK_SET)<0||1!=rwrite(mb->fd,empty,1)) { char*p=malloc(len+1); /* can't extend, switch to malloc */ tmemmove(p,mb->p,mb->len); munmap(mb->p,mb->len+1); mb->len=len; goto dropf; } munmap(mb->p,mb->len+1); mmap: if((mb->p=mmap(0,len+1,P_RW,MAP_SHARED,mb->fd,(off_t)0))==MAP_FAILED) mmapfailed(len+1); } mb->len=len; goto ret1; } #endif if(nonfatal) { char*p; p=frealloc(mb->p,(size_t)(len+1)); if(!p) return 0; mb->p=p; } else mb->p=realloc(mb->p,len+1); mb->len=len+1; mb->p[len]='\0'; ret1: return 1; } char*read2blk(mb,filledp,read_func,cleanup_func,data)memblk*const mb; read_func_type*read_func;cleanup_func_type*cleanup_func; long*const filledp;void*data; { int blksiz=BLKSIZ,ok;unsigned int shift=EXPBLKSIZ; long filled= *filledp,origfilled=filled; if(filledlen) /* skip the initial resize if we have space */ goto jumpin; for(;;) { if((size_t)filled>=(size_t)(filled+blksiz)) /* check for overflow */ lcking|=lck_MEMORY,nomemerr(filled); /* dynamically adjust the buffer size */ while(EXPBLKSIZ&&(ok=0,blksiz>BLKSIZ)&& /* backed up all the way? */ !(ok=resizeblock(mb,filled+blksiz,1))) /* no? Then try this size */ blksiz>>=1; /* failed! Try a smaller increment */ if(!EXPBLKSIZ||!ok) resizeblock(mb,filled+blksiz,0); /* last (maybe only) try */ jumpin: ;{ char*newlast; if(newlast=(*read_func)(mb->p+filled,mb->len-filled,data)) { filled=newlast-mb->p; break; } filled=mb->len; } if(EXPBLKSIZ&&shift) /* room for growth? */ { int newbs=blksiz;newbs<<=shift--; /* capped exponential growth */ if(blksizp; } #ifdef USE_MMAP static void mmapfailed(len)const long len; { static const char mmapfailed[]="Unable to mmap file"; nextexit=2;nlog(mmapfailed);elog("\n"); syslog(LOG_NOTICE,"%s of %ld bytes\n",mmapfailed,len); if(retval!=EX_TEMPFAIL) retval=EX_OSERR; Terminate(); } #endif procmail-3.22/src/from.c0100644006717600001440000001470407176357656014405 0ustar guenthersrc/************************************************************************ * From_ line routines used by procmail * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 2000, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: from.c,v 1.2 2000/10/27 20:03:58 guenther Exp $"; #endif #include "procmail.h" #include "robust.h" #include "shell.h" #include "memblk.h" #include "common.h" #include "misc.h" /* for nlog */ #include "from.h" static int privs; /* can we change the From_ line */ static const char From_[]=FROM,Fakefield[]=FAKE_FIELD, attemptst[]="Attempt to fake stamp by"; int eqFrom_(a)const char*const a; { return !strncmp(a,From_,STRLEN(From_)); } const char*skipFrom_(startchar,tobesentp)const char*startchar;long*tobesentp; { if(eqFrom_(startchar)) { long tobesent;char c; tobesent= *tobesentp; do while(c= *startchar++,--tobesent&&c!='\n'); while(*startchar=='>'); *tobesentp=tobesent; } return startchar; } /* tries to locate the timestamp on the From_ line */ static char*findtstamp(start,end)const char*start,*end; { end-=25; if(*start==' '&&(++start==end||*start==' '&&++start==end)) return (char*)start-1; start=skpspace(start);start+=strcspn(start," \t\n"); /* jump over address */ if(skpspace(start)>=end) /* enough space left? */ return (char*)start; /* no, too small for a timestamp, stop here */ while(!(end[13]==':'&&end[16]==':')&&--end>start); /* search for :..: */ ;{ int spc=0; /* found it perhaps */ while(end-->start) /* now skip over the space to the left */ { switch(*end) { case ' ':case '\t':spc=1; continue; } if(!spc) continue; break; } return (char*)end+1; /* this should be right after the address */ } } size_t ctime2buf2 P((void)) { time_t t=time((time_t*)0); /* the current time */ buf2[0]=buf2[1]=' ';strcpy(buf2+2,ctime(&t)); return strlen(buf2); } void makeFrom(from,invoker)const char*from,*const invoker; { static const char mdaemon[]=MAILERDAEMON; const char*fwhom;char*rstart;size_t lfr,linv;int tstamp,extra,r; if(Deliverymode!=2) { tstamp=from&&*from==REFRESH_TIME&&!from[1]; fwhom=from; } else { tstamp=0; fwhom= *from?from:mdaemon; } if(from&&!tstamp) { if(privs!=1&&!strcmp(from,invoker)) privs=1; /* if -f user is the same as the invoker, allow it */ else if(privs==-1&&from) { if(verbose) nlog(insufprivs); /* ignore the bogus -f */ syslog(LOG_ERR,slogstr,attemptst,invoker);from=0; fwhom=invoker; } } else fwhom=invoker; makeblock(&themail,2*linebuf+(lfr=strlen(fwhom))+(linv=strlen(invoker))); private(1); /* we're not yet sharing */ rstart=thebody=themail.p; if(!Deliverymode&&!from) /* need to peek for a leading From_ ? */ return; /* nope */ r=ctime2buf2(); lfr+=STRLEN(From_)+r; /* length of requested From_ line */ if(tstamp) tstamp=r; /* save time stamp length */ if(privs>0) /* privileged user? */ linv=0; /* yes, so no need to insert >From_ */ else linv+=STRLEN(Fakefield)+r; /* length of >From_ line */ extra=0; if(Deliverymode!=2) /* if not LMTP */ { while(1==(r=rread(STDIN,themail.p,1))) /* then read in first line */ if(themail.p[0]!='\n') /* skip leading newlines */ break; if(r>0&&STRLEN(From_)<=(extra=1+rread( /* is it a From_ line? */ STDIN,rstart+1,(int)(linebuf-2-1)))&&eqFrom_(themail.p)) { rstart[extra]='\0'; if(!(rstart=strchr(rstart,'\n'))) { do /* drop long From_ line */ { if((extra=rread(STDIN,themail.p,(int)(linebuf-2)))<=0) break; themail.p[extra]='\0'; /* terminate it for strchr */ } while(!(rstart=strchr(themail.p,'\n'))); extra=rstart?extra-(++rstart-themail.p):0; } else { size_t tfrl= ++rstart-themail.p; /* length of existing From_ line */ extra-=tfrl; /* demarcate it */ if(Deliverymode&&privs<0) { if(verbose) /* discard the bogus From_ */ nlog(insufprivs); syslog(LOG_ERR,slogstr,attemptst,fwhom); } else { if(tstamp) lfr=findtstamp(themail.p+STRLEN(From_),rstart) -themail.p+tstamp; else if(!from) /* leave the From_ line alone */ if(linv) /* fake alert? */ lfr=tfrl; /* yes, so separate From_ from the rest */ else lfr=0,extra+=tfrl; /* no, tack it onto the rest */ goto got_from; } } } } tstamp=0; /* no existing From_, so nothing to stamp */ if(!from) /* no -f ? */ linv=0; /* then it can't be a fake */ got_from: filled=lfr+linv+extra; /* From_ + >From_ + rest */ if(lfr||linv) /* move read text beyond our From_ line */ { r= *rstart;tmemmove(themail.p+lfr+linv,rstart,extra); rstart=themail.p+lfr; /* skip the From_ line, if any */ if(!linv) /* no fake alert */ { rstart[-tstamp]='\0'; /* where do we append */ if(!tstamp) /* no timestamp, so generate it all */ strcat(strcpy(themail.p,From_),fwhom); /* From user */ } else { if(lfr) /* did we skip a From_ line? */ if(tstamp) /* copy the timestamp over the tail */ strcpy(rstart-tstamp,buf2); else if(from) /* whole new From_? */ strcat(strcat(strcpy(themail.p,From_),fwhom),buf2); strcat(strcpy(rstart,Fakefield),invoker); /* fake alert */ } /* overwrite the trailing \0 again */ strcat(themail.p,buf2);themail.p[lfr+linv]=r; } } void checkprivFrom_(euid,logname,override)uid_t euid;const char*logname; int override; { static const char*const trusted_ids[]=TRUSTED_IDS; privs=1; /* assume they're privileged */ if(Deliverymode&&*trusted_ids&&uid!=euid) { struct group*grp;const char*const*kp; if(logname) /* check out the invoker's uid */ for(kp=trusted_ids;*kp;kp++) if(!strcmp(logname,*kp)) /* is it amongst the privileged? */ goto privileged; if(grp=getgrgid(gid)) /* check out the invoker's gid */ for(logname=grp->gr_name,kp=trusted_ids;*kp;kp++) if(!strcmp(logname,*kp)) /* is it among the privileged? */ goto privileged; privs= -override; /* override only matters when not privileged */ } privileged: endgrent(); } procmail-3.22/src/header.h0100644006717600001440000001452506740317106014656 0ustar guenthersrc/************************************************************************ * * * Known fields when formail is splitting messages (the first * * "-m nnn" fields encountered should be among them or one of * * the special From_, Article_ or X- fields). * * * * If you need to add one (be sure to update "cdigest" below as * * well!), drop me a mail, I might be interested in including * * it in the next release. * * * ************************************************************************/ /*$Id: header.h,v 1.44 1999/07/06 06:12:22 guenther Exp $*/ X(returnpath, "Return-Path:") /* RFC 822 */ X(received, "Received:") /* ditto ... */ X(replyto, "Reply-To:") X(Fromm, "From:") X(sender, "Sender:") X(res_replyto, "Resent-Reply-To:") X(res_from, "Resent-From:") X(res_sender, "Resent-Sender:") X(date, "Date:") X(res_date, "Resent-Date:") X(To, "To:") X(res_to, "Resent-To:") X(cc, "Cc:") X(res_cc, "Resent-Cc:") X(bcc, "Bcc:") X(res_bcc, "Resent-Bcc:") X(messageid, "Message-ID:") X(res_messageid, "Resent-Message-ID:") X(inreplyto, "In-Reply-To:") X(references, "References:") X(keywords, "Keywords:") X(subject, "Subject:") X(scomments, "Comments:") X(ncrypted, "Encrypted:") X(notrequpdelry, "Notice-Requested-Upon-Delivery-To:") /* DSN */ X(finrecipient, "Final-Recipient:") /* ditto ... */ X(remmta, "Remote-MTA:") X(diagcode, "Diagnostic-Code:") X(lastattemptdate, "Last-Attempt-Date:") X(errorsto, "Errors-To:") /* sendmail extension */ X(retreceiptto, "Return-Receipt-To:") /* ditto ... */ X(precedence, "Precedence:") X(fullname, "Full-Name:") X(postddate, "Posted-Date:") X(recvddate, "Received-Date:") X(mssage, "Message:") X(text, "Text:") X(via, "Via:") X(apparentlyto, "Apparently-To:") X(apresto, "Apparently-Resent-To:") X(dliveredto, "Delivered-To:") /* qmail extension */ X(mlinglist, "Mailing-List:") /* ditto ... */ X(autforwarded, "Autoforwarded:") /* X.400 extension */ X(cntidentifier, "Content-Identifier:") /* ditto ... */ X(conversion, "Conversion:") X(convwithloss, "Conversion-With-Loss:") X(deldate, "Delivery-Date:") X(dx4ipmsextensions, "Discarded-X400-IPMS-Extensions:") X(dx4mtsextensions, "Discarded-X400-MTS-Extensions:") X(dlexpansionhistory, "DL-Expansion-History:") X(defdelivery, "Deferred-Delivery:") X(expirydate, "Expiry-Date:") X(importance, "Importance:") X(incompletecopy, "Incomplete-Copy:") X(language, "Language:") X(latdeliverytime, "Latest-Delivery-Time:") X(msgtype, "Message-Type:") X(obsoletes, "Obsoletes:") X(supersedes, "Supersedes:") /* depreciated */ X(orgencodedinfts, "Original-Encoded-Information-Types:") X(orgnreturnaddress, "Originator-Return-Address:") X(priority, "Priority:") X(replyby, "Reply-By:") X(reqdeliverymethod, "Requested-Delivery-Method:") X(sensitivity, "Sensitivity:") X(x400contenttype, "X400-Content-Type:") X(x400mtsidentifier, "X400-MTS-Identifier:") X(x400originator, "X400-Originator:") X(x400received, "X400-Received:") X(x400rcipients, "X400-Recipients:") X(x400trace, "X400-Trace:") X(altrecipient, "Alternate-Recipient:") X(prevnondeliveryrep, "Prevent-Nondelivery-Report:") X(gendeliveryrep, "Generate-Delivery-Report:") X(discloserecipients, "Disclose-Recipients:") X(cntreturn, "Content-Return:") X(autsubmitted, "Auto-Submitted:") X(ppwarning, "PP-Warning:") /* ? */ X(fcc, "Fcc:") /* Mush extension */ X(resent, "Resent:") /* MH extension */ X(forwarded, "Forwarded:") /* ditto ... */ X(replied, "Replied:") X(article, "Article:") /* USENET extension */ X(path, "Path:") /* ditto ... */ X(summary, "Summary:") X(organisation, "Organisation:") X(aorganization, "Organization:") X(newsgroups, "Newsgroups:") X(followupto, "Followup-To:") X(approved, "Approved:") X(lines, "Lines:") X(expires, "Expires:") X(control, "Control:") X(distribution, "Distribution:") X(xref, "Xref:") X(originator, "Originator:") X(nntppostinghost, "NNTP-Posting-Host:") X(submittedby, "Submitted-by:") X(postdto, "Posted-To:") X(mailcopiesto, "Mail-Copies-To:") X(title, "Title:") /* antiquated USENET extension */ X(aRticleid, "Article-I.D.:") /* ditto ... */ X(posted, "Posted:") X(relayversion, "Relay-Version:") X(sentby, "Sent-By:") /* UUCP extension */ X(cnttype, "Content-Type:") /* Internet extension */ X(encoding, "Encoding:") /* ditto ... */ X(cntmd5, "Content-MD5:") X(mimeversion, "MIME-Version:") /* MIME extension */ X(cnttransferenc, "Content-Transfer-Encoding:") /* ditto ... */ X(cntid, "Content-ID:") X(cntdescription, "Content-Description:") X(cntdisposition, "Content-Disposition:") X(accept, "Accept:") /* HTTP extension */ X(spublic, "Public:") /* ditto ... */ X(allow, "Allow:") /* or is it Allowed: ? */ X(lastmodified, "Last-Modified:") X(uri, "URI:") X(vversion, "Version:") X(derivedfrom, "Derived-From:") X(cntlanguage, "Content-Language:") X(cost, "Cost:") X(srver, "Server:") X(wwwlink, "WWW-Link:") /* or is it Link: ? */ X(acknowledgeto, "Acknowledge-To:") /* MMDF extension */ X(transportoptions, "Transport-Options:") /* SysV mailer extension */ X(defltoptions, "Default-Options:") X(cntlength, "Content-Length:") X(rference, "Reference:") X(autoforwardedfrom, "Auto-Forwarded-From:") X(autofcount, "Auto-Forward-Count:") X(endofheader, "End-of-Header:") X(orgafrom, "Original-From:") X(orgato, "Original-To:") X(orgacc, "Original-Cc:") X(orgaforwfrom, "Original-Auto-Forwarded-From:") X(orgdate, "Original-Date:") X(notdeliveredto, "Not-Delivered-To:") X(reportversion, "Report-Version:") X(orgreceived, "Original-Received:") /* ? */ X(status, "Status:") /* mailer extension */ X(mailfrom, "Mail-from:") /* emacs BABYL extension */ X(retrreqsted, "Return-Receipt-Requested:") /* cc:Mail extension */ X(mrreceived, "MR-Received:") /* ORION extension */ X(apmesgid, "App-Message-ID:") /* MRIF? extension */ X(hopcount, "Hop-count:") X(fromwarning, "From-warning:") X(reqremailingto, "Request-Remailing-To:") /* remailer extension */ X(anonsendto, "Anon-Send-To:") X(latenttime, "Latent-Time:") X(cutmarks, "Cutmarks:") X(postto, "Post-To:") X(anonpostto, "Anon-Post-To:") X(encrkey, "Encrypt-Key:") X(readreceiptto, "Read-Receipt-To:") /* miscellaneous extension */ X(fakesender, "Fake-Sender:") X(envelopeto, "Envelope-To:") /* exim extension */ procmail-3.22/src/procmail.h0100644006717600001440000000641007347315064015233 0ustar guenthersrc/*$Id: procmail.h,v 1.57 2001/08/25 04:38:40 guenther Exp $*/ #include "includes.h" #ifdef console #define DEFverbose 1 #else #define DEFverbose 0 #endif #ifdef GROUP_PER_USER #define NO_CHECK_stgid 0 #else #define NO_CHECK_stgid 1 #endif #ifdef TOGGLE_SGID_OK #define CAN_toggle_sgid 1 #else #define CAN_toggle_sgid 0 #endif #ifndef DEFsendmail #define DEFsendmail SENDMAIL #endif #ifndef DEFflagsendmail #define DEFflagsendmail "-oi" #endif #ifndef DEFSPATH #define DEFSPATH defSPATH #endif #ifndef DEFPATH #define DEFPATH defPATH #endif #ifndef ETCRC #define ETCRC 0 #endif #define mAX32 ((long)(~(unsigned long)0>>1)) /* LONG_MAX */ #define maxMAX32 2147483647L /* the largest we'll use = (2^31)-1 */ #define MAX32 (mAX32>maxMAX32&&maxMAX32>0?maxMAX32:mAX32) /* the minmax */ #define MIN32 (-(long)MAX32) #define XTRAlinebuf 2 /* surplus of LINEBUF (assumed by readparse()) */ #ifdef MAXPATHLEN #if MAXPATHLEN>DEFlinebuf /* to protect people from themselves */ #undef DEFlinebuf #define DEFlinebuf MAXPATHLEN #endif #endif #define priv_DONTNEED 1 /* don't need root to sgid */ #define priv_START 2 /* we might have root */ #define MCDIRSEP (dirsep+STRLEN(dirsep)-1) /* most common DIRSEP */ #define MCDIRSEP_ (dirsep+STRLEN(DIRSEP)-1) #define lck_DELAYSIG 1 /* crosscheck the order of this with msg[] */ #define lck_ALLOCLIB 2 /* in sterminate() in retint.c */ #define lck_MEMORY 4 #define lck_FORK 8 #define lck_FILDES 16 #define lck_KERNEL 32 #define lck_LOGGING 64 #define lck__NOMSG (lck_DELAYSIG|lck_ALLOCLIB|lck_LOGGING) extern struct varval{const char*const name;long val;}strenvvar[]; #define locksleep (strenvvar[0].val) #define locktimeout (strenvvar[1].val) #define suspendv (strenvvar[2].val) #define noresretry (strenvvar[3].val) #define timeoutv (strenvvar[4].val) #define verbose (*(volatile long*)&strenvvar[5].val) #define lgabstract (strenvvar[6].val) extern struct varstr{const char*const sname,*sval;}strenstr[]; #define shellmetas (strenstr[0].sval) #define lockext (strenstr[1].sval) #define msgprefix (strenstr[2].sval) #define traps (strenstr[3].sval) #define shellflags (strenstr[4].sval) #define fdefault (*(const char*volatile*)&strenstr[5].sval) #define sendmail (strenstr[6].sval) #define flagsendmail (strenstr[7].sval) /* #define PM_version (strenstr[8].sval) */ extern char*buf,*buf2,*loclock,*thebody; extern const char shell[],lockfile[],newline[],binsh[],unexpeof[],*const*gargv, *const*restargv,*sgetcp,pmrc[],*rcfile,dirsep[],devnull[],empty[],lgname[], executing[],oquote[],cquote[],whilstwfor[],procmailn[],Mail[],home[],host[], *defdeflock,*argv0,exceededlb[],curdir[],slogstr[],conflicting[],orgmail[], insufprivs[],defpath[],errwwriting[],Version[]; extern long filled,lastscore; extern int sh,pwait,retval,retvl2,rc,privileged,ignwerr, lexitcode,accspooldir,crestarg,savstdout,berkeley,mailfilter,erestrict, Deliverymode,ifdepth; extern struct dyna_array ifstack; extern size_t linebuf; extern volatile int nextexit,lcking; extern pid_t thepid; extern uid_t uid; extern gid_t gid,sgid; /* * External variables that are checked/changed by the signal handlers: * volatile time_t alrmtime; * pid_t pidfilt,pidchild; * volatile int nextexit,lcking; * size_t linebuf; * static volatile int mailread; in mailfold.c */ procmail-3.22/src/goodies.c0100644006717600001440000002761607347314706015066 0ustar guenthersrc/************************************************************************ * Collection of library-worthy routines * * * * Copyright (c) 1990-1998, S.R. van den Berg, The Netherlands * * Copyright (c) 1998-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: goodies.c,v 1.74 2001/08/04 07:17:44 guenther Exp $"; #endif #include "procmail.h" #include "sublib.h" #include "robust.h" #include "shell.h" #include "misc.h" #include "pipes.h" #include "common.h" #include "acommon.h" #include "cstdio.h" #include "variables.h" #include "goodies.h" const char test[]="test"; const char*Tmnate,*All_args; static const char*evalenv(skipping) /* expects the variable name in buf2 */ int skipping; { int j=buf2[0]-'0'; return skipping?(const char*)0: /* speed this up when skipping */ (unsigned)j>9?getenv(buf2): !j?argv0: j<=crestarg?restargv[j-1]:(const char*)0; } #define NOTHING_YET (-1) /* readparse understands a very complete */ #define SKIPPING_SPACE 0 /* subset of the standard /bin/sh syntax */ #define NORMAL_TEXT 1 /* that includes single-, double- and back- */ #define DOUBLE_QUOTED 2 /* quotes, backslashes and $subtitutions */ #define SINGLE_QUOTED 3 #define fgetc() (*fpgetc)() /* some compilers previously choked on it */ #define CHECKINC() (fencepostNORMAL_TEXT) /* condition expansion code */ early_eof: nlog(unexpeof); ready: if(got!=SKIPPING_SPACE||sarg) /* not terminated yet or sarg==2 ? */ *p++='\0'; Tmnate=p; if(skipping&1) { nlog(exceededlb);setoverflow(); } return skipping&1; case '\\': if(got==SINGLE_QUOTED) break; i=fgetc(); Quoted: switch(i) { case EOF: goto early_eof; /* can't quote EOF */ case '\n': continue; /* concatenate lines */ case '#': if(got>SKIPPING_SPACE) /* escaped comment at start of word? */ goto noesc; /* apparently not, literally */ case ' ':case '\t':case '\'': if(got==DOUBLE_QUOTED) goto noesc; case '"':case '\\':case '$':case '`': goto nodelim; case '}': if(got<=NORMAL_TEXT&&bracelev|| got==DOUBLE_QUOTED&&bracelev>qbracelev) goto nodelim; } if(got>NORMAL_TEXT) noesc: *p++='\\'; /* nothing to escape, just echo both */ break; case '`': if(got==SINGLE_QUOTED) goto nodelim; for(startb=p;;) /* mark your position */ { switch(i=fgetc()) /* copy till next backquote */ { case '"': if(got!=DOUBLE_QUOTED) /* missing closing backquote? */ break; forcebquote: case EOF:case '`': if(skipping) *(p=startb)='\0'; else { int osh=sh; *p='\0'; if(!(sh=!!strpbrk(startb,shellmetas))) { const char*save=sgetcp,*sAll_args; sgetcp=p=tstrdup(startb);sAll_args=All_args; if(readparse(startb,sgetc,0,0) /* overflow? */ #ifndef GOT_bin_test ||!strcmp(test,startb) /* oops, `test' found */ #endif )strcpy(startb,p),sh=1; All_args=sAll_args; free(p);sgetcp=save; /* chopped up */ } /* drop source buffer, read from program */ startb=fromprog( p=startb,startb,(size_t)(buf-startb+linebuf-3)); sh=osh; /* restore sh */ } if(got!=DOUBLE_QUOTED) { i=0;startb=p; goto simplsplit; /* split it up */ } if(i=='"'||got<=SKIPPING_SPACE) /* missing closing ` ? */ got=NORMAL_TEXT; p=startb; goto loop; case '\\': switch(i=fgetc()) { case EOF:nlog(unexpeof); goto forcebquote; case '\n': continue; case '"': if(got!=DOUBLE_QUOTED) break; case '\\':case '$':case '`': goto escaped; } *p++='\\'; } escaped: CHECKINC();*p++=i; } case '"': switch(got) { case DOUBLE_QUOTED: if(qbracelevqbracelev) { bracelev--; if(skipback&&bracelev==skipbracelev) { skipping-=2;p=skipback;skipback=0;startb=(char*)oldstartb; got=bracegot; goto closebrace; } continue; } goto nodelim; case '#': if(got>SKIPPING_SPACE) /* comment at start of word? */ break; while((i=fgetc())!=EOF&&i!='\n'); /* skip till EOL */ goto ready; case '$': if(got==SINGLE_QUOTED) break; startb=buf2; switch(i=fgetc()) { case EOF:*p++='$';got=NORMAL_TEXT; goto ready; case '@': if(got!=DOUBLE_QUOTED) goto normchar; if(!skipping) /* don't do it while skipping (braces) */ All_args=p; continue; case '{': /* ${name} */ while(EOF!=(i=fgetc())&&alphanum(i)) { if(startb>=fencepost2) startb=buf2+2,skipping|=1; *startb++=i; } *startb='\0'; if(numeric(*buf2)&&buf2[1]) goto badsub; startb=(char*)evalenv(skipping); switch(i) { default: goto badsub; case ':': switch(i=fgetc()) { case '-': if(startb&&*startb) goto noalt; goto doalt; case '+': if(startb&&*startb) goto doalt; goto noalt; default: badsub: nlog("Bad substitution of");logqnl(buf2); continue; } case '+': if(startb) goto doalt; goto noalt; case '-': if(startb) noalt: if(!skipping) { skipping+=2;skipback=p;skipbracelev=bracelev; oldstartb=startb;bracegot=got; } doalt: bracelev++; continue; #if 0 case '%': /* this is where processing of ${var%%pat} */ case '#': /* and friends would/will go */ #endif case '}': closebrace: if(!startb) startb=(char*)empty; break; } goto ibreak; /* $$ =pid */ case '$':ultstr(0,(unsigned long)thepid,startb=num); goto ieofstr; case '?':ltstr(0,(long)lexitcode,startb=num); goto ieofstr; case '#':ultstr(0,(unsigned long)crestarg,startb=num); goto ieofstr; case '=':ltstr(0,lastscore,startb=num); ieofstr: i='\0'; goto copyit; case '_':startb=incnamed?incnamed->ename:(char*)empty; goto ibreak; case '-':startb=(char*)tgetenv(lastfolder); /* $- =$LASTFOLDER */ ibreak: i='\0'; break; default: { int quoted=0; if(numeric(i)) /* $n positional argument */ { *startb++=i;i='\0'; goto finsb; } if(i=='\\') quoted=1,i=fgetc(); if(alphanum(i)) /* $name */ { do { if(startb>=fencepost2) startb=buf2+2,skipping|=1; *startb++=i; } while(EOF!=(i=fgetc())&&alphanum(i)); if(i==EOF) i='\0'; finsb: *startb='\0'; if(!(startb=(char*)evalenv(skipping))) startb=(char*)empty; if(quoted) { *p++='(';CHECKINC(); /* protect leading character */ *p++=')'; for(;CHECKINC(),*startb;*p++= *startb++) if(strchr("(|)*?+.^$[\\",*startb)) /* specials? */ *p++='\\'; /* take them literally */ normchar: quoted=0; } else break; } else /* not a substitution */ *p++='$'; /* pretend nothing happened */ if(got<=SKIPPING_SPACE) got=NORMAL_TEXT; if(quoted) goto Quoted; goto eeofstr; } } if(got!=DOUBLE_QUOTED) simplsplit: { char*q; if(sarg) goto copyit; if(q=simplesplit(p,startb,fencepost,&got)) /* simply split */ p=q; /* it up in arguments */ else skipping|=1,p=fencepost; } else copyit: { size_t len=fencepost-p+1; if(strlcpy(p,startb,len)>=len) /* simply copy it */ skipping|=1; /* did we truncate it? */ if(got<=SKIPPING_SPACE) /* can only occur if sarg!=0 */ got=NORMAL_TEXT; p=strchr(p,'\0'); } eeofstr: if(i) /* already read next character? */ goto newchar; continue; #if 0 /* autodetect quoted specials? */ case '~': if(got==NORMAL_TEXT&&p[-1]!='='&&p[-1]!=':') break; case '&':case '|':case '<':case '>':case ';': case '?':case '*':case '[': if(got<=NORMAL_TEXT) sh=1; break; #endif case ' ':case '\t': switch(got) { case NORMAL_TEXT: if(sarg==1) goto ready; /* already fetched a single argument */ got=SKIPPING_SPACE;*p++=sarg?' ':'\0'; /* space or \0 sep. */ case NOTHING_YET:case SKIPPING_SPACE: continue; /* skip space */ } case '\n': if(got<=NORMAL_TEXT) goto ready; /* EOL means we're ready */ } nodelim: *p++=i; /* ah, a normal character */ if(got<=SKIPPING_SPACE) /* should we bother to change mode? */ got=NORMAL_TEXT; } } char*simplesplit(to,from,fencepost,gotp)char*to;const char*from,*fencepost; int*gotp; { register int got= *gotp; for(;to<=fencepost;from++) { switch(*from) { case ' ':case '\t':case '\n': if(got>SKIPPING_SPACE) *to++='\0',got=SKIPPING_SPACE; continue; case '\0': goto ret; } *to++= *from;got=NORMAL_TEXT; } to=0; ret: *gotp=got; return to; } void concatenate(p)register char*p; { while(p!=Tmnate) /* concatenate all other arguments */ { while(*p) p++; *p++=' '; } *p=p[-1]='\0'; } void metaparse(p)const char*p; /* result in buf */ { if(sh=!!strpbrk(p,shellmetas)) strcpy(buf,p); /* copy literally, shell will parse */ else { sgetcp=p=tstrdup(p); if(readparse(buf,sgetc,0,0) /* parse it yourself */ #ifndef GOT_bin_test ||!strcmp(test,buf) #endif ) strcpy(buf,p),sh=1; /* oops, overflow or `test' found */ free((char*)p); } } void ltstr(minwidth,val,dest)const int minwidth;const long val;char*dest; { if(val<0) { *dest=' ';ultstr(minwidth-1,-val,dest+1); while(*++dest==' '); /* look for the first non-space */ dest[-1]='-'; /* replace it with a minus */ } else ultstr(minwidth,val,dest); /* business as usual */ } #ifdef NOstrtod double strtod(str,ptr)const char*str;char**const ptr; { int sign,any;unsigned i;char*chp;double acc,fracc; fracc=1;acc=any=sign=0; switch(*(chp=skpspace(str))) /* the sign */ { case '-':sign=1; case '+':chp++; } while((i=(unsigned)*chp++-'0')<=9) /* before the decimal point */ acc=acc*10+i,any=1; switch(i) { case (unsigned)'.'-'0':case (unsigned)','-'0': while(fracc/=10,(i=(unsigned)*chp++-'0')<=9) /* the fractional part */ acc+=fracc*i,any=1; } if(ptr) *ptr=any?chp-1:(char**)str; return sign?-acc:acc; } #endif procmail-3.22/src/robust.h0100644006717600001440000000135607316763027014751 0ustar guenthersrc/*$Id: robust.h,v 1.13 2001/06/21 09:43:53 guenther Exp $*/ void nomemerr Q((const size_t len)) __attribute__((noreturn)), *tmalloc Q((const size_t len)), *trealloc Q((void*const old,const size_t len)), *fmalloc Q((const size_t len)), *frealloc Q((void*const old,const size_t len)), tfree P((void*const p)), opnlog P((const char*file)), ssleep P((const unsigned seconds)), doumask Q((const mode_t mask)); pid_t sfork P((void)); int opena P((const char*const a)), ropen Q((const char*const name,const mode,const mode_t mask)), rpipe P((int fd[2])), rdup P((const int p)), rclose P((const int fd)), rread P((const int fd,void*const a,const int len)), rwrite P((const int fd,const void*const a,const int len)); extern mode_t cumask; procmail-3.22/src/fields.c0100644006717600001440000001064407347314644014675 0ustar guenthersrc/************************************************************************ * Routines to deal with the header-field objects in formail * * * * Copyright (c) 1990-2000, S.R. van den Berg, The Netherlands * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: fields.c,v 1.31 2001/08/04 07:09:42 guenther Exp $"; #endif #include "includes.h" #include "formail.h" #include "sublib.h" #include "shell.h" #include "common.h" #include "fields.h" #include "ecommon.h" #include "formisc.h" /* find a field in the linked list of fields */ struct field*findf(p,ah)const struct field*const p;register struct field**ah; { size_t i;int uhead;char*chp;register struct field*h; uhead=ah==&uheader||ah==&Uheader; for(i=p->id_len,chp=(char*)p->fld_text,h= *ah;h;h= *(ah= &h->fld_next)) if(i>=h->id_len&&!strncasecmp(chp,h->fld_text,h->id_len)) { if(i>h->id_len&&uhead) /* finalise the header? */ *ah=0,(*(ah=addfield(ah,chp,i)))->fld_next=h,(h= *ah)->fld_ref=0; return h; } return (struct field*)0; } void cleanheader P((void)) /* zorch whitespace before the ':' */ { struct field**pp,*p;char*cp; for(pp=&rdheader;p= *pp;pp= &(*pp)->fld_next) if((cp=p->fld_text+p->id_len-1,*cp==HEAD_DELIMITER)&& /* has : */ (*--cp==' '||*cp=='\t')) /* has ws */ { char*q=cp++;int diff; while(*--q==' '||*q=='\t'); /* find the field name */ tmemmove(++q,cp,p->Tot_len-p->id_len+1); /* zappo! */ p->id_len-=(diff=cp-q); p->Tot_len-=diff; } } void clear_uhead(hdr)register struct field*hdr; { for(;hdr;hdr=hdr->fld_next) hdr->fld_ref=0; } struct field**addfield(pointer,text,totlen)struct field**pointer; const char*const text;const size_t totlen; /* add field to a linked list */ { register struct field*p,**pp;int idlen; for(pp=pointer;*pp;pp= &(*pp)->fld_next); /* skip to the end of the list */ (*pp=p=malloc(FLD_HEADSIZ+totlen))->fld_next=0;idlen=breakfield(text,totlen); p->id_len=idlen>0?idlen:pp==&rdheader?0:-idlen; /* copy contents */ tmemmove(p->fld_text,text,p->Tot_len=totlen); return pp; } struct field*delfield(pointer)struct field**pointer; { struct field*fldp; *pointer=(fldp= *pointer)->fld_next;free(fldp); return *pointer; } void concatenate(fldp)struct field*const fldp; { register char*p;register size_t l; /* concatenate a continued field */ l=fldp->Tot_len; if(!eqFrom_(p=fldp->fld_text)) /* don't concatenate From_ lines */ while(l--) if(*p++=='\n'&&l) /* by substituting all newlines except the last */ p[-1]=' '; } static void extractfield(p)register const struct field*p; { if(xheader||Xheader) /* extracting only? */ { if(findf(p,&xheader)) /* extract field contents */ { char*chp,*echp; echp=(chp=(char*)p->fld_text+p->id_len)+(int)(p->Tot_len-p->id_len-1); if(zap) { chp=skpspace(chp); while(chpfld_text,p->Tot_len); /* display it entirely */ } void flushfield(pointer)register struct field**pointer; /* delete and print */ { register struct field*p,*q; /* them as you go */ for(p= *pointer,*pointer=0;p;p=q) q=p->fld_next,extractfield(p),free(p); } void dispfield(p)register const struct field*p; { for(;p;p=p->fld_next) /* print list non-destructively */ if(p->id_len+1Tot_len) /* any contents to display? */ extractfield(p); } /* try and append one valid field to rdheader from stdin */ int readhead P((void)) { int idlen; getline(); if((idlen=breakfield(buf,buffilled))<=0) /* not the start of a valid field */ return 0; if(idlen==STRLEN(FROM)&&eqFrom_(buf)) /* it's a From_ line */ { if(rdheader) return 0; /* the From_ line was a fake! */ for(;buflast=='>';getline()); /* gather continued >From_ lines */ } else for(;;getline()) /* get the rest of the continued field */ { switch(buflast) /* will this line be continued? */ { case ' ':case '\t': /* yep, it sure is */ continue; } break; } addbuf(); /* phew, got the field, add it to rdheader */ return 1; } void addbuf P((void)) { addfield(&rdheader,buf,buffilled);buffilled=0; } procmail-3.22/src/acommon.c0100644006717600001440000000351207347314510015044 0ustar guenthersrc/************************************************************************ * Some routine common to procmail, formail and lockfile * * * * Copyright (c) 1993-1997, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: acommon.c,v 1.12 2001/08/04 07:14:59 guenther Exp $"; #endif #include "includes.h" #include "acommon.h" #include "robust.h" #include "shell.h" const char*hostname P((void)) { #ifdef NOuname #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif static char name[MAXHOSTNAMELEN]=""; if(!name[0]) gethostname(name,MAXHOSTNAMELEN),name[MAXHOSTNAMELEN-1]='\0'; #else static char*name=0; if(!name) { struct utsname names; Uname(&names); if(!(name=malloc(strlen(names.nodename)+1))) return ""; /* can happen when called from within lockfile */ strcpy(name,names.nodename); } #endif return name; } char*ultoan(val,dest)unsigned long val;char*dest; /* convert to a number */ { register int i; /* within the set [A-Za-z0-9-_] */ do { i=val&0x3f; /* collating sequence dependency! */ *dest++=i+(i<26?'A':i<26+26?'a'-26:i<26+26+10?'0'-26-26: i==26+26+10?'-'-26-26-10:'_'-26-26-11); } while(val>>=6); *dest='\0'; return dest; } char*ultstr(minwidth,val,dest)int minwidth;unsigned long val;char*dest; { int i;unsigned long j;char*ret; j=val;i=0; /* a beauty, isn't it :-) */ do i++; /* determine needed width */ while(j/=10); while(--minwidth>=i) /* fill up any excess width */ *dest++=' '; *(ret=dest+=i)='\0'; do *--dest='0'+val%10; /* display value backwards */ while(val/=10); return ret; } procmail-3.22/src/common.c0100644006717600001440000000430607316762767014726 0ustar guenthersrc/************************************************************************ * Some routines common to procmail and formail * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: common.c,v 1.28 2001/06/23 08:18:39 guenther Exp $"; #endif #include "procmail.h" #include "sublib.h" #include "robust.h" #include "shell.h" #include "misc.h" #include "common.h" void shexec(argv)const char*const*argv; { int i;char**newargv;const char**p; #ifdef SIGXCPU signal(SIGXCPU,SIG_DFL);signal(SIGXFSZ,SIG_DFL); #endif #ifdef SIGLOST signal(SIGLOST,SIG_DFL); #endif /* or is it a shell script ? */ signal(SIGPIPE,SIG_DFL);execvp(*argv,(char*const*)argv); for(p=(const char**)argv,i=1;i++,*p++;); /* count the arguments */ newargv=malloc(i*sizeof*p); /* no shell script? -> trouble */ for(*(p=(const char**)newargv)=binsh;*++p= *argv++;); execv(*newargv,newargv);free(newargv);nlog("Failed to execute"); logqnl(*argv); exit(EX_UNAVAILABLE); } void detab(p)char*p; { while(p=strpbrk(p,"\t\n\v\f\r")) *p=' '; /* take out all tabs and other specials */ } char*skpspace(chp)const char*chp; { for(;;chp++) switch(*chp) { case ' ':case '\t': continue; default: return (char*)chp; } } #ifdef NOstrcspn int strcspn(whole,sub)const char*const whole,*const sub; { const register char*p; p=whole; while(*p&&!strchr(sub,*p)) p++; return p-whole; } #endif int waitfor(pid)const pid_t pid; /* wait for a specific process */ { int i;pid_t j; while(pid!=(j=wait(&i))||WIFSTOPPED(i)) if(-1==j) return NO_PROCESS; else if(!pid) break; return lexitcode=WIFEXITED(i)?WEXITSTATUS(i):-WTERMSIG(i); } #ifdef NOstrncasecmp int strncasecmp(a,b,l)register const char*a,*b;register size_t l; { unsigned i,j; if(l) /* case insensitive strncmp */ do { while(*a&&*a==*b&&--l) a++,b++; if(!l) break; if((i= *a++)-'A'<='Z'-'A') i+='a'-'A'; if((j= *b++)-'A'<='Z'-'A') j+='a'-'A'; if(j!=i) return i>j?1:-1; } while(i&&j&&--l); return 0; } #endif procmail-3.22/src/mailfold.h0100644006717600001440000000065507223546542015221 0ustar guenthersrc/*$Id: mailfold.h,v 1.24 2000/10/25 08:13:20 guenther Exp $*/ long dump P((const int s,const int type,const char*source,long len)); int writefolder P((char*boxname,char*linkfolder,const char*source,long len, const int ignwerr,const int dolock)); void logabstract P((const char*const lstfolder)), concon P((const int ch)), readmail P((int rhead,const long tobesent)); extern int logopened,rawnonl; extern off_t lasttell; procmail-3.22/src/cstdio.c0100644006717600001440000002102607223546524014705 0ustar guenthersrc/************************************************************************ * Custom standard-io library * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: cstdio.c,v 1.52 2000/11/22 01:29:56 guenther Exp $"; #endif #include "procmail.h" #include "robust.h" #include "misc.h" #include "lmtp.h" #include "variables.h" #include "shell.h" #include "cstdio.h" static uchar rcbuf[STDBUF],*rcbufp,*rcbufend; /* buffer for custom stdio */ static off_t blasttell; static struct dyna_array inced; /* includerc stack */ struct dynstring*incnamed; static void refill(offset)const int offset; /* refill the buffer */ { int ret=rread(rc,rcbuf,STDBUF); if(ret>0) { rcbufend=rcbuf+ret; rcbufp=rcbuf+offset; /* restore position */ } else { rcbufend=rcbuf; rcbufp=rcbuf+1; /* looks like EOF */ } } void pushrc(name)const char*const name; /* open include rcfile */ { if(*name&&strcmp(name,devnull)) { struct stat stbuf; if(stat(name,&stbuf)||!S_ISREG(stbuf.st_mode)) goto rerr; if(stbuf.st_size) /* only if size>0 */ { app_vali(inced,rcbufp?rcbufp-rcbuf:0); /* save old */ app_valo(inced,blasttell);app_vali(inced,ifdepth);/* position, brace */ app_vali(inced,rc); /* depth & fd */ ifdepth=ifstack.filled; /* new stack depth */ if(bopen(name)<0) /* try to open the new one */ { poprc(); /* we couldn't, so restore rc */ rerr: readerr(name); } } } } void changerc(name)const char*const name; /* change rcfile */ { if(!*name||!strcmp(name,devnull)) pr:{ ifstack.filled=ifdepth; /* lose all the braces to avoid a warning */ rclose(rc);rcbufp=rcbufend+1; /* make it look like EOF */ return; } if(!strcmp(name,incnamed->ename)) /* just restart this one */ lseek(rc,0,SEEK_SET),refill(0); else { struct stat stbuf;int orc;uchar*orbp,*orbe;struct dynstring*dp; if(stat(name,&stbuf)||!S_ISREG(stbuf.st_mode)) rerr: { readerr(name); /* skip irregularities */ return; } if(!stbuf.st_size) /* avoid opening trivial rcfiles */ goto pr; if(orbp=rcbufp,orbe=rcbufend,orc=rc,bopen(name)<0) { rcbufp=orbp;rcbufend=orbe;rc=orc; /* restore state */ goto rerr; } rclose(orc); /* success! drop the old and */ if(dp=incnamed->enext) /* fixup the name list */ incnamed->enext=dp->enext,free(dp); } ifstack.filled=ifdepth; /* close all the braces */ } void duprcs P((void)) /* `duplicate' all the fds of opened rcfiles */ { size_t i;struct dynstring*dp; dp=incnamed;rclose(rc); if(0>(rc=ropen(dp->ename,O_RDONLY,0))) /* first reopen the current one */ goto dupfailed; lseek(rc,blasttell+STDBUF,SEEK_SET); /* you'll never know the difference */ for(i=inced.filled;dp=dp->enext,i;i-=3) { int fd; rclose(acc_vali(inced,--i)); if(0>(fd=ropen(dp->ename,O_RDONLY,0))) /* reopen all (nested) others */ dupfailed: /* oops, file disappeared */ nlog("Lost"),logqnl(dp->ename),exit(EX_NOINPUT); /* panic */ acc_vali(inced,i)=fd; /* new & improved fd, decoupled from */ } /* fd in the parent */ } static void closeonerc P((void)) { struct dynstring*last; if(rc>=0) rclose(rc),rc= -1,last=incnamed,incnamed=last->enext,free(last); } int poprc P((void)) { closeonerc(); /* close it in any case */ if(ifstack.filled>ifdepth) /* force the matching of braces */ ifstack.filled=ifdepth,nlog("Missing closing brace\n"); if(!inced.filled) /* include stack is empty? */ return 0; /* restore rc, seekpos, prime rcbuf and restore rcbufp */ rc=acc_vali(inced,--inced.filled); ifdepth=acc_vali(inced,--inced.filled); blasttell=lseek(rc,acc_valo(inced,--inced.filled),SEEK_SET); refill(acc_vali(inced,--inced.filled)); return 1; } void closerc P((void)) /* {while(poprc());} */ { while(closeonerc(),inced.filled) rc=acc_vali(inced,inced.filled-1),inced.filled-=4; ifstack.filled=ifdepth=0; } /* destroys buf2 */ int bopen(name)const char*const name; /* my fopen */ { rcbufp=rcbufend=0;rc=ropen(name,O_RDONLY,0); if(rc>=0) { char*md;size_t len; /* if it's a relative name and an absolute $MAILDIR */ if(!strchr(dirsep,*name)&& *(md=(char*)tgetenv(maildir))&& strchr(dirsep,*md)&& (len=strlen(md))+strlen(name)+2end-2) /* space enough for getbl? */ target=end-linebuf,overflow=1; /* toss what we have */ continue; } case 0: if(overflow) { nlog(exceededlb);setoverflow(); } return overflow; } } #ifdef LMTP static int origfd= -1; /* flush the input buffer and switch to a new input fd */ void pushfd(fd)int fd; { origfd=rc;rc=fd; rcbufend=rcbufp; } /* restore the original input fd */ static int popfd P((void)) { rclose(rc);rc=origfd; if(0>origfd) return 0; origfd= -1; return 1; } /* * Are we at the end of an input read? If so, we'll need to flush our * output buffer to prevent a possible deadlock from the pipelining */ int endoread P((void)) { return rcbufp>=rcbufend; } /* * refill the LMTP input buffer, switching back to the original input * stream if needed */ void refillL P((void)) { int retcode; refill(0); if(rcbufp>=rcbufend) /* we must have run out */ { if(popfd()) /* try the original fd */ { refill(0); /* fill the buffer */ if(rcbufplen) min=len; tmemmove(p,rcbufp,min); rcbufp+=min; return min; } /* * read exactly len bytes from the LMTP input stream * return 1 on success, 0 on EOF, and -1 on read error */ int readLe(p,len)char*p;int len; { long got=rcbufend-rcbufp; if(got>0) /* first, copy from the buffer */ { if(got>len) /* is that more than we need? */ got=len; tmemmove(p,rcbufp,got); rcbufp+=got; p+=got;len-=got; } while(len) /* read the rest directly */ { if(0>(got=rread(rc,p,len))) return -1; if(!got&&!popfd()) return 0; p+=got;len-=got; } return 1; } #endif procmail-3.22/src/formisc.h0100644006717600001440000000105306706550107015062 0ustar guenthersrc/*$Id: formisc.h,v 1.10 1999/04/19 06:42:15 guenther Exp $*/ void loadsaved P((const struct saved*const sp)), loadbuf Q((const char*const text,const size_t len)), loadchar P((const int c)), elog P((const char*const a)), tputssn Q((const char*a,size_t l)), ltputssn Q((const char*a,size_t l)), lputcs P((const int i)), startprog P((const char*Const*const argv)), nofild P((void)), nlog P((const char*const a)), logqnl P((const char*const a)), closemine P((void)), opensink P((void)); char* skipwords P((char*start)); int getline P((void)); procmail-3.22/src/authenticate.c0100644006717600001440000001645707316762764016123 0ustar guenthersrc/************************************************************************ * Modify to taste in order to comply with your authentication * * (e.g. Radius or shadow passwd) and mailbox conventions * * * * You have the liberty to redefine the identity typedef in * * any way you see fit, so that it can hold state information * * you need to authenticate your users * * * * Copyright (c) 1996-1997, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" or "README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: authenticate.c,v 1.10 2001/06/27 17:07:19 guenther Exp $"; #endif #ifdef PROCMAIL #include "includes.h" #include "robust.h" #include "shell.h" #include "misc.h" #else #include "config.h" #include #include #include #include #include #define bbzero(s,l) memset(s,'\0',l) #ifdef SHADOW_PASSWD #include #endif #endif /* PROCMAIL */ #include "authenticate.h" #ifndef MAILSPOOLDIR #define MAILSPOOLDIR "/var/spool/mail/" /* watch the trailing / */ #endif #ifndef MAILSPOOLSUFFIX #define MAILSPOOLSUFFIX "" /* suffix to force maildir or MH style */ #endif #ifndef MAILSPOOLHASH #define MAILSPOOLHASH 0 /* 2 would deliver to /var/spool/mail/b/a/bar */ #endif /*#define MAILSPOOLHOME "/.mail" /* watch the leading / */ /* delivers to $HOME/.mail */ #define STRLEN(x) (sizeof(x)-1) struct auth_identity { const struct passwd*pw; char*mbox; int sock; }; static auth_identity authi; /* reuse copy, only one active */ static void castlower(str)register char*str; /* and I'll take the low road */ { for(;*str;str++) if((unsigned)*str-'A'<='Z'-'A') /* uppercase character? */ *str+='a'-'A'; /* cast it to lowercase */ } static const struct passwd*cgetpwnam(user,sock)const char*const user; const int sock; { return getpwnam(user); /* this should be selfexplanatory :-) */ } static const struct passwd*cgetpwuid(uid,sock)const uid_t uid;const int sock; { return getpwuid(uid); /* no comment */ } /*const*/auth_identity*auth_finduser(user,sock)char*const user;const int sock; { if(!(authi.pw=cgetpwnam(user,sock))) /* /etc/passwd user lookup */ { char*p; if(p=strchr(user,'@')) /* does the username contain an @? */ *p='\0'; /* clueless user using the mailaddress */ castlower(user); /* make it all lowercase (luser problem no. 1) */ if(!(authi.pw=cgetpwnam(user,sock))) /* ok, be nice and try again */ return 0; /* sorry, no such user on this planet */ } authi.sock=sock; /* save the filedescriptor for virtual server separation */ if(authi.mbox) /* any old mailbox reference left? */ free(authi.mbox),authi.mbox=0; /* clear the reference */ return &authi; /* user found */ } /*const*/auth_identity*auth_finduid(uid,sock)const uid_t uid;const int sock; { if(!(authi.pw=cgetpwuid(uid,sock))) /* /etc/passwd user lookup */ return 0; /* nada */ authi.sock=sock; /* save filedescriptor for later perusal */ if(authi.mbox) /* old mailbox reference? */ free(authi.mbox),authi.mbox=0; /* nix old mailbox reference */ return &authi; /* user found */ } #ifndef PROCMAIL int auth_checkpassword(pass,pw,allowemptypw)const auth_identity*const pass; const char*const pw;const int allowemptypw; { const char*rpw; rpw=pass->pw->pw_passwd; /* get the regular (encrypted) password */ #ifdef SHADOW_PASSWD ;{ struct spwd*spwd; if(spwd=getspnam(pass->pw->pw_name)) /* any shadow password? */ rpw=spwd->sp_pwdp; /* override the regular one */ } #endif if(!*rpw) /* empty password found */ return allowemptypw; /* should we allow this? */ return !strcmp(rpw,crypt(pw,rpw)); /* compare the passwords */ } const char*auth_getsecret(pass)const auth_identity*const pass; { return 0; /* no standard way to get a secret, add function here */ } #else /* PROCMAIL */ auth_identity*auth_newid P((void)) { auth_identity*pass; /* create a new auth_identity placeholder */ (pass=malloc(sizeof*pass))->pw=0;pass->mbox=0;return pass; } void auth_copyid(newpass,oldpass)auth_identity*newpass; const auth_identity*oldpass; { struct passwd*np;const struct passwd*op; if(newpass->mbox) free(newpass->mbox),newpass->mbox=0; newpass->sock=oldpass->sock; if(!(np=(struct passwd*)newpass->pw)) { np=(struct passwd*)(newpass->pw=malloc(sizeof*np)); bbzero(np,sizeof*np); } np->pw_uid=(op=oldpass->pw)->pw_uid;np->pw_gid=op->pw_gid; np->pw_name=cstr(np->pw_name,op->pw_name); np->pw_dir=cstr(np->pw_dir,op->pw_dir); np->pw_shell=cstr(np->pw_shell,op->pw_shell); #ifndef NOpw_passwd if(op->pw_passwd) bbzero(op->pw_passwd,strlen(op->pw_passwd)); #endif } static void auth_zeroout(pass)auth_identity*pass; { struct passwd*p; if(p=(struct passwd*)pass->pw) { bbzero(p->pw_name,strlen(p->pw_name)); #ifndef NOpw_passwd if(p->pw_passwd)bbzero(p->pw_passwd,strlen(p->pw_passwd)); #endif #ifndef NOpw_class if(p->pw_class)bbzero(p->pw_class,strlen(p->pw_class)); #endif #ifndef NOpw_gecos if(p->pw_gecos)bbzero(p->pw_gecos,strlen(p->pw_gecos)); #endif bbzero(p->pw_dir,strlen(p->pw_dir)); bbzero(p->pw_shell,strlen(p->pw_shell)); bbzero(p,sizeof(*p)); } if(pass->mbox) bbzero(pass->mbox,strlen(pass->mbox)); } void auth_freeid(pass)auth_identity*pass; { struct passwd*p; auth_zeroout(pass); if(p=(struct passwd*)pass->pw) free(p->pw_name),free(p->pw_dir),free(p->pw_shell),free(p); if(pass->mbox) free(pass->mbox); free(pass); } int auth_filledid(pass)const auth_identity*pass; { return !!pass->pw; } #endif /* PROCMAIL */ const char*auth_mailboxname(pass)auth_identity*const pass; { if(!pass->mbox) #ifdef MAILSPOOLHOME { static const char mailfile[]=MAILSPOOLHOME;size_t i; if(!(pass->mbox=malloc((i=strlen(pass->pw->pw_dir))+STRLEN(mailfile)+1))) return ""; strcpy(pass->mbox,pass->pw->pw_dir); strcpy(pass->mbox+i,mailfile); #else { static const char mailspooldir[]=MAILSPOOLDIR; if(!(pass->mbox=malloc(STRLEN(mailspooldir)+MAILSPOOLHASH*2+ strlen(pass->pw->pw_name)+1+STRLEN(MAILSPOOLSUFFIX)))) return ""; strcpy(pass->mbox,mailspooldir); ;{ char*p,*n;size_t i;int c; for(p=pass->mbox+STRLEN(mailspooldir),n=pass->pw->pw_name, i=MAILSPOOLHASH;i--;*p++='/') { if(*n) c= *n++; *p++=c; } strcpy(p,pass->pw->pw_name); if(STRLEN(MAILSPOOLSUFFIX)) strcat(p,MAILSPOOLSUFFIX); } #endif /* MAILSPOOLHOME */ } return pass->mbox; } uid_t auth_whatuid(pass)const auth_identity*const pass; { return pass->pw->pw_uid; } uid_t auth_whatgid(pass)const auth_identity*const pass; { return pass->pw->pw_gid; } const char*auth_homedir(pass)const auth_identity*const pass; { return pass->pw->pw_dir; } const char*auth_shell(pass)const auth_identity*const pass; { return pass->pw->pw_shell; } const char*auth_username(pass)const auth_identity*const pass; { return pass->pw->pw_name; } void auth_end P((void)) { auth_zeroout(&authi); if(authi.mbox) free(authi.mbox),authi.mbox=0; /* discard the mailbox reference */ endpwent(); #ifdef SHADOW_PASSWD endspent(); #endif } procmail-3.22/src/autoconf0100755006717600001440000012522507347314522015024 0ustar guenthersrc#! /bin/sh : # Copyright (c) 1990-1997, S.R. van den Berg, The Netherlands #$Id: autoconf,v 1.136 2001/08/10 18:52:30 guenther Exp $ # All possible entries in autoconf.h: # #define UNISTD_H_MISSING # #define STDDEF_H_MISSING # #define STDLIB_H_MISSING # #define DIRENT_H_MISSING # #define SYS_DIRENT_H_MISSING # #define NDIR_H_MISSING # #define SYS_NDIR_H_MISSING # #define SYS_DIR_H_MISSING # #define SYS_WAIT_H_MISSING # #define SYS_UTSNAME_H_MISSING # #define STRING_H_MISSING # #define SYSEXITS_H_MISSING #Ok #define SYS_FILE_H_MISSING #Ok #define SYSLOG_H_MISSING # #define MATH_H_MISSING # #define LIMITS_H_MISSING # #define NO_const # #define volatile # #define P(args) () # #define NO_enum # #define void char # typedef int mode_t; # typedef int pid_t; # typedef int uid_t; # typedef int gid_t; # typedef unsigned size_t; # typedef long off_t; # typedef long time_t; # #define NOmemmove #Ok #define NObcopy # #define NOstrcspn # #define NOstrpbrk # #define NOopendir # #define NOrename # #define NOsetrgid # #define NOsetegid # #define NOsetregid # #define NOsetresgid # #define setrgid_BRAIN_DAMAGE # #define setrgid_RUNTIME_CHECK #Ok #define TOGGLE_SGID_OK # #define NOpow # #define NOmkdir # #define NOfstat # #define NOuname # #define NO_exit # #define NOstrtol # #define NOstrtod # #define NOstrerror #Ok #define NOstrlcat #Ok #define NOstrncasecmp #Ok #define NOsys_errlist #Ok #define NObzero # #define NOmemset # #define NOwaitpid #Ok #define NOftruncate # #define SLOWstrstr #Ok #define CAN_chown # #define strchr(s,c) index(s,c) # #define NOinitgroups # #define NOfsync #Ok #define endpwent() #Ok #define endgrent() #Ok #define endhostent() #Ok #define endservent() #Ok #define endprotoent() # #define h_0addr_list h_addr #Ok #define NOpw_passwd #Ok #define NOpw_class #Ok #define NOpw_gecos #Ok #define NO_COMSAT # #define _GUID_T #Ok #define UDP_protocolno 17 #Ok #define BIFF_serviceport "512" #Ok #define IP_localhost {127,0,0,1} # #define WMACROS_NON_POSIX # #define oBRAIN_DAMAGE #Ok #define GOT_bin_test #Ok #define MAX_argc 128 #Ok #define MAX_envc 128 #Ok #define SMALLHEAP # #define NOfcntl_lock #Ok #define USElockf #Ok #define USEflock #Ok #define MAILSPOOLDIR "/var/spool/mail/" #Ok #define SENDMAIL "/usr/sbin/sendmail" # #define buggy_SENDMAIL # #define DEFflagsendmail "" #Ok #define CF_no_procmail_yet #Ok #define defSPATH "PATH=/bin:/usr/bin:/usr/local/bin" #Ok #define defPATH "PATH=$HOME/bin:/bin:/usr/bin:/usr/local/bin" #Ok #define INEFFICIENTrealloc #Ok #define PM_VERSION "n.nn" # A conforming ISO C compiler and POSIX library should not have any defines # not marked as "Ok" in the autoconf.h file. # Anything else indicates failure of your system to comply with either # the ISO or POSIX standards (but procmail should be installable anyway). if test -z "$IFS" then IFS=" \ \ " export IFS fi SHELL=$1 PATH=.:$PATH shift; RM="$1" shift; MV="$1" shift; DEVNULL=$1 shift; FGREP="$1" shift; MAKE="$1" shift; O=$1 shift; LOCKINGTEST="$1" shift; BINDIR=$1 shift; ACONF=$1 test 1 != $# && echo "Don't start this script directly, use \`make ../autoconf.h'" && exit 1 if test -f make_n then $RM make_n else # We were mistakenly invoked under "make -n", so pretend this didn't happen. exit 0 fi export SHELL PATH test "X$LOCKINGTEST" = X__defaults__ && LOCKINGTEST="" OLDTESTDIRS="" tailpid="" echo "" >_locktst.pid if test -f $ACONF then if $FGREP -e "/* autoconf completed */" $ACONF >$DEVNULL then cat <$DEVNULL;cp $ACONF $ACONF.tmp test ! -z \"\$OLDTESTDIRS\" && $RM -r \$OLDTESTDIRS \$OLDTESTDIRS 2>$DEVNULL test ! -z \"\$tailpid\" && kill \$tailpid 2>$DEVNULL kill \`cat _locktst.pid\` 2>$DEVNULL; exit 1" 1 2 3 13 15 else trap "kill \`cat _locktst.pid\` 2>$DEVNULL;$RM -r $ACONF.tmp test ! -z \"\$OLDTESTDIRS\" && $RM -r \$OLDTESTDIRS \$OLDTESTDIRS 2>$DEVNULL mv $ACONF $ACONF.tmp 2>$DEVNULL test ! -z \"\$tailpid\" && kill \$tailpid 2>$DEVNULL kill \`cat _locktst.pid\` 2>$DEVNULL; exit 1" 1 2 3 13 15 fi nocore=yes set dummy *core* test -f $2 && nocore=no set dummy $LOCKINGTEST shift LOCKINGTEST="$*" test ! -z "$LOCKINGTEST" && exec 9>&1 1>$DEVNULL # make it shut up cat <$DEVNULL then b="$b $a" TESTDIRS="$TESTDIRS $a/_locktest" else echo 2>&1 "Can't create $a/_locktest" fi done OLDTESTDIRS="$TESTDIRS" testdirs="$b" echo "" echo "I will temporarily use a testdirectory named _locktest" echo "in the following directories:" echo "" echo $testdirs echo "" echo "If you would like to add any, please specify them below," echo "press return to continue:" echo "" if test -z "$LOCKINGTEST" then read b elif test dummy = "$LOCKINGTEST" then b="" else testdirs=""; b="$LOCKINGTEST"; LOCKINGTEST=dummy echo $b fi testdirs="$testdirs $b" done if test dummy = "$LOCKINGTEST" then exec 1>&9 9>&- # you can speak again echo "Using the following directories for the LOCKINGTESTs:" echo " $testdirs" fi cat >grepfor <$DEVNULL && echo "\$2" >>$ACONF HERE chmod 0755 grepfor cat >$ACONF <_locktst.h $RM _autotst.$O cat >_autotst.c < #include void*vvoid; int main(){int i=0;char*p="t"; vvoid=p; {time_t vtime;i+=vtime=1;} {off_t voff;i+=voff=1;} return !vvoid+!i;} #include int empty() { return setsid(); } HERE if $MAKE _autotst.$O 2>&1 | $FGREP -v include/ >_autotst.rrr test -f _autotst.$O then $MAKE _autotst >$DEVNULL 2>&1 || echo "#define setsid()" >>_locktst.h else grepfor void '#define void char' grepfor time_t 'typedef long time_t;' grepfor off_t 'typedef long off_t;' if $MAKE _autotst.$O >$DEVNULL 2>&1 then $MAKE _autotst >$DEVNULL 2>&1 || echo "#define setsid()" >>_locktst.h else echo "#define UNISTD_H_MISSING" >>$ACONF echo "#define setsid()" >>_locktst.h fi fi $RM _autotst.$O _autotst cat >_autotst.c < #ifndef UNISTD_H_MISSING #include #endif #include #ifndef SYS_FILE_H_MISSING #include #endif int main(){ #ifdef F_SETLKW fcntl(0,F_SETLKW,0); #endif #ifdef F_LOCK lockf(0,F_LOCK,(off_t)0); #endif #ifdef LOCK_EX flock(0,LOCK_EX); #endif return 0;} HERE echo 'Initiating fcntl()/kernel-locking-support tests' if $MAKE _autotst.$O >$DEVNULL 2>&1 then : else $RM _autotst.$O echo "#define SYS_FILE_H_MISSING" >>$ACONF if $MAKE _autotst.$O >_autotst.rrr 2>&1 then : else echo 2>&1 "Whoeaaa! There's something fishy going on here." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 "I suggest you take a look at the definition of CFLAGS* and CC" echo 2>&1 "in the Makefile before you try make again." kill -15 $$ fi fi $MAKE _autotst >_autotst.rrr 2>&1 $RM _autotst.$O _autotst $FGREP fcntl _autotst.rrr >$DEVNULL && echo "#undef F_SETLKW" >>_locktst.h $FGREP lockf _autotst.rrr >$DEVNULL && echo "#undef F_LOCK" >>_locktst.h $FGREP flock _autotst.rrr >$DEVNULL && cat >>_locktst.h <_autotst.c < #ifndef UNISTD_H_MISSING #include /* getpid() getppid() */ #endif #include #include #include #include /* SIGKILL */ #ifndef tell #define tell(fd) lseek(fd,(off_t)0,SEEK_CUR) #endif #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #include "_locktst.h" #ifndef SYS_FILE_H_MISSING #include #endif #ifndef SEEK_SET #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif #define MIN_locks 128 /* minimum of consecutive successful locks */ #define SLEEP_loop 64 /* iterations in the busy loop */ #define SLEEP_delay 0 /* sleep() time per busy iteration */ #define SLEEP_retreat 1 /* extra sleep() when quota reached */ #define TIME_OUT_SEC 16 /* initial timeout per lock */ #define NR_of_forks 8 /* concurrent test-programs */ #define NR_of_lockfiles 2 /* lockfiles used in testing (<=4) */ #define GROW 3 /* when timing got worse */ #define DECAY 7/8 /* when timing got better */ #define GOBBLE 256 /* for the SunOS crash test */ #define LOCKS_per_child (((MIN_locks+NR_of_forks-1)/NR_of_forks+1)/2) #define SHIFT_childno 3 #define MASK_fileno ((1<>8; } unsigned sfork() { int pid; while((pid=fork())<0) fprintf(stderr,"Couldn't fork _locktst, retrying\n"),sleep(TIME_OUT_SEC); return pid; } int main(argc,argv)char*argv[]; { int goodlock,testlock,i,pip[2],pipw[2];time_t otimet;unsigned dtimet; static char filename[]="_locktst.l0"; close(0);goodlock=0;testlock=FIRST_lock;signal(SIGPIPE,SIG_DFL); if(argc==2) { char*p; for(p=argv[1];;) { switch(*p++) /* parse the hotwire */ { case '1':goodlock<<=1;goodlock++; continue; case '0':goodlock<<=1; continue; case '\0': #ifndef F_SETLKW /* sanity check, for the less gifted */ goodlock&=~MSK_fcntl; #endif #ifndef F_LOCK goodlock&=~MSK_lockf; #endif #ifndef LOCK_EX goodlock&=~MSK_flock; #endif goto skip_tests; } break; /* oops, no hotwire */ } goodlock=0; } if(sfork()) /* try to ditch the controlling tty */ return EXIT_SUCCESS; /* to prevent rather messy kernel-diagnostics */ setsid(); /* from appearing on your tty */ fprintf(stderr,"\ Every two digits a file is locked. First digit: which file, second digit:\n\ which program, XX indicates a lock conflict or failure\n"); do { int countlocks; signal(SIGTERM,SIG_DFL);argc=1;dolock=goodlock|testlock; do { char*curdir; curdir=argv[argc];otimet=time((time_t*)0); if(sfork()) { close(1);close(2);i=NR_of_lockfiles;chdir(curdir); chdir(dirlocktest); do /* prime the lock, see if it vanishes */ { filename[sizeof filename-2]='0'+--i; lseek(goodlock=open(filename,O_WRONLY),(off_t)0,SEEK_END); fdlock(goodlock); } while(i); for(;;) /* synchronising "busy"-wait */ sleep(TIME_OUT_SEC); /* until the games can be started */ } sleep(1);pipe(pip);pipe(pipw);i=NR_of_forks;*child=0; fprintf(stderr,"\nStarting test %x on %s\n",dolock,curdir); while((child[--i]=sfork())&&i); /* divide and conquer */ if(!*child) { unsigned char lockflag;int fd,childno=i<>SHIFT_childno); lseek(fd,(off_t)0,SEEK_END);write(fd,&lockflag,1); for(i=SLEEP_loop;i;i--,time((time_t*)0),sleep(SLEEP_delay)); lockflag&=~1;write(pip[1],&lockflag,1); if(fdunlock()||close(fd)) { write(pip[1],&lockflag,1);fprintf(stderr,"XX%x",lockflag); return 1; } if(--mylocks<0) sleep(SLEEP_retreat); } } signal(SIGTERM,Terminate); ;{ unsigned char lckdfil[NR_of_lockfiles],curflag; int j,ppid; /* give all children a chance */ ppid=getppid(); if(sfork()) /* discard the old body, let init adopt our children */ return EXIT_SUCCESS; ;{ FILE*fp; if(fp=fopen("_locktst.pid","w")) fprintf(fp,"%ld",(long)getpid()),fclose(fp); } countlocks=MIN_locks;close(pip[1]);close(pipw[1]); for(j=NR_of_lockfiles;lckdfil[--j]=0,j;); timeout=0;sleep(1);read(pipw[0],&curflag,1);close(pipw[0]); kill(ppid,SIGKILL);fdcollect=pip[0];signal(SIGALRM,stimeout); dtimet=(time((time_t*)0)-otimet)*GROW+TIME_OUT_SEC; do { unlink("_locktst.alive");alarm(dtimet+2); /* watchdog */ dtimet=(otimet=time((time_t*)0)-otimet)>dtimet? otimet*GROW:otimet+(dtimet-otimet)*DECAY+1; /* load dependent */ otimet=time((time_t*)0); } while(1==read(fdcollect,&curflag,1)&& (j=lckdfil[i=(curflag&MASK_fileno)>>1],!timeout)&& ((j^(lckdfil[i]=curflag))==1||!(j&1)&&curflag&1)&&--countlocks); alarm(0);close(fdcollect);killchildren(); fprintf(stdout, "/*locktype: %x, countlocks: %x, timeout %x, watchdog %x, %s*/\n", dolock,countlocks,timeout,dtimet,curdir);fflush(stdout); } } while(!countlocks&&argv[++argc]); if(!countlocks) /* Hurray! Locking was flawless */ goodlock=dolock; /* make a mark on our colt */ } while(testlock>>=1); skip_tests: printf("/* Hotwire LOCKINGTEST=%c%c%c */\n",goodlock&0x4?'1':'0', goodlock&0x2?'1':'0',goodlock&0x1?'1':'0'); printf("/* Procmail will lock via: dotlocking"); /* report our findings */ if(goodlock&MSK_fcntl) printf(", fcntl()"); if(goodlock&MSK_lockf) printf(", lockf()"); if(goodlock&MSK_flock) printf(", flock()"); puts(" */"); if(!(goodlock&MSK_fcntl)) /* and in machine readable format */ puts("#define NOfcntl_lock"); if(goodlock&MSK_lockf) puts("#define USElockf"); if(goodlock&MSK_flock) { puts("#define USEflock"); #ifdef SYS_FILE_H_MISSING puts("#define SYS_FILE_H_MISSING"); #endif } puts("Kernel-locking tests completed.");fprintf(stderr,"\n"); return EXIT_SUCCESS; } int killchildren() { int i; i=NR_of_forks; do if(child[--i]>0) kill(child[i],SIGTERM),child[i]=0; while(i); return 0; } int sfdlock(fd) { int i;unsigned gobble[GOBBLE>>2]; for(i=GOBBLE>>2;i;gobble[--i]=~(unsigned)0); /* SunOS crash test */ return fdlock(fd); } static oldfdlock; #ifdef F_SETLKW static struct flock flck; /* why can't it be a local variable? */ #endif #ifdef F_LOCK static off_t oldlockoffset; #endif int fdlock(fd) { int i;unsigned gobble[GOBBLE>>2]; for(i=GOBBLE>>2;i;gobble[--i]=~(unsigned)0); /* SunOS crash test */ oldfdlock=fd;fd=0; if(MSK_fcntl&dolock) #ifdef F_SETLKW { static unsigned extra; flck.l_type=F_WRLCK;flck.l_whence=SEEK_SET;flck.l_start=tell(oldfdlock); if(!extra--) extra=MIN_locks/4,flck.l_len=2,i|=fcntl(oldfdlock,F_SETLK,&flck); flck.l_len=0;fd|=fcntl(oldfdlock,F_SETLKW,&flck); } #else fd=1; #endif if(MSK_lockf&dolock) #ifdef F_LOCK oldlockoffset=tell(oldfdlock),fd|=lockf(oldfdlock,F_LOCK,(off_t)0); #else fd=1; #endif if(MSK_flock&dolock) #ifdef LOCK_EX fd|=flock(oldfdlock,LOCK_EX); #else fd=1; #endif return fd; } int fdunlock() { int i;unsigned gobble[GOBBLE]; for(i=GOBBLE;i;gobble[--i]=~(unsigned)0); /* some SunOS libs mess this up */ if(MSK_flock&dolock) #ifdef LOCK_EX i|=flock(oldfdlock,LOCK_UN); #else i=1; #endif if(MSK_lockf&dolock) #ifdef F_LOCK { lseek(oldfdlock,oldlockoffset,SEEK_SET); i|=lockf(oldfdlock,F_LOCK,(off_t)2);i|=lockf(oldfdlock,F_ULOCK,(off_t)0); } #else i=1; #endif if(MSK_fcntl&dolock) #ifdef F_SETLKW flck.l_type=F_UNLCK,flck.l_len=0,i|=fcntl(oldfdlock,F_SETLK,&flck); #else i=1; #endif if(!i) for(i=GOBBLE;i&&gobble[--i]==~(unsigned)0;); return i; } HERE if $MAKE _autotst >_autotst.rrr 2>&1 then : else echo 2>&1 "Whoeaaa! There's something fishy going on here." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 "I suggest you take a look at the definition of LDFLAGS*" echo 2>&1 "in the Makefile before you try make again." kill -15 $$ fi $MV _locktst _locktst.$$ 2>$DEVNULL $RM _autotst.$O _autotst.rrr _locktst* 2>$DEVNULL echo "" >_locktst.pid $MV _autotst _locktst case "$testdirs" in *[a-zA-Z/]*) for b in $TESTDIRS do for a in 0 1 2 3 do echo dummy_file >$b/_locktst.l$a done done ;; *) TESTDIRS=$testdirs ;; esac echo 2>&1 "" _locktst $TESTDIRS >_locktst.rrr 2>lock.log # will finish in the background echo 'Proceeding with kernel-locking-support tests in the background' cat >_autotst.c < /* execvp() */ #endif int a(b)const int b[]; { return *++b; /* watcom 10.6 on QNX refuses this */ } int main() { char r[]="",*const*p;char*q="";const char*s=""; static const char*const nullp=0,*const*d= &nullp; /* AIX 3.2.3 failure */ static struct{const int a;int b;}c[2]={{0,0},{0,0}};/* IRIX 7.3.1 compiler */ /* requires initializers on static const objects */ --(c+1)->b; /* AIX 3.1.5 machines can't do this */ p= &q; ;{ int pip[2]; pipe(pip); if(fork()) /* this should hide core dumps from the shell */ { close(pip[1]); /* close the writing end */ return 1==read(pip[0],r,1)?0:1; /* wait for the rendevouz */ } close(pip[0]); /* close the writing end */ if(!*d&&c[1].b) /* some magic to confuse the optimiser */ d=(const char*const*)p; /* core dumps Ultrix 4.3 ANSI C */ else /* so that it can't complain about the uselessness of this program */ execvp(q,p); /* IRIX 4.0.1 system-includes are wrong on this point */ if(write(pip[1],r,1)!=1) /* notify mam that we survived */ return 1; /* oops, lost mam somehow */ } return r==s; /* Domain/OS warns here, not about "r==s" though */ } HERE echo 'Testing for const' if $MAKE _autotst.$O 2>&1 | $FGREP -v include/ >_autotst.rrr test -f _autotst.$O && $MAKE _autotst >$DEVNULL 2>&1 && _autotst then grepfor const '#define NO_const' else echo '#define NO_const' >>$ACONF set dummy *core* if test -f $2 -a $nocore = yes then echo "Removing core file (bogus readonly segment)" $RM *core* fi fi $RM _autotst.$O _autotst cat >_autotst.c <$DEVNULL 2>&1 && test -f _autotst.$O then : else echo '#define volatile' >>$ACONF not_ISO=yep fi $RM _autotst.$O if $FGREP NO_const $ACONF >$DEVNULL then echo '#define P(args) ()' >>$ACONF not_ISO=yep else cat >_autotst.c <$DEVNULL 2>&1 && test -f _autotst.$O then : else echo '#define P(args) ()' >>$ACONF not_ISO=yep fi $RM _autotst.$O fi cat >_autotst.c <$DEVNULL 2>&1 && test -f _autotst.$O && $MAKE _autotst >$DEVNULL 2>&1 && _autotst then : else echo '#define NO_enum' >>$ACONF not_ISO=yep fi $RM _autotst.$O if test -n "$not_ISO" then cat < explaining why you need procmail to continue to support K&R C. HERE fi echo 'Checking for POSIX and ANSI/ISO system include files' cat >_autotst.c <&1 | $FGREP -v include/ >_autotst.rrr test ! -f _autotst.$O do test -z "$i0" && grepfor stddef.h "#define STDDEF_H_MISSING" && i0=I test -z "$i1" && grepfor stdlib.h "#define STDLIB_H_MISSING" && i1=I test -z "$i2" && grepfor dirent.h "#define DIRENT_H_MISSING" && i2=I test -z "$i2" && grepfor DIR "#define DIRENT_H_MISSING \ /* is there, but empty */" && i2=I test -z "$i3" && grepfor ndir.h "#define NDIR_H_MISSING" && i3=I test I = "$i3$i4" && grepfor sys/ndir.h "#define SYS_NDIR_H_MISSING" && i4=I test I = "$i4$i5" && grepfor sys/dir.h "#define SYS_DIR_H_MISSING" && i5=I test I = "$i5$i6" && grepfor sys/dirent.h "#define SYS_DIRENT_H_MISSING" \ && i6=I test -z "$i7" && grepfor sys/wait.h "#define SYS_WAIT_H_MISSING" && i7=I test -z "$i8" && grepfor sys/utsname.h "#define SYS_UTSNAME_H_MISSING" && i8=I test -z "$i9" && grepfor string.h "#define STRING_H_MISSING" && i9=I test -z "$i10" && grepfor sysexits.h "#define SYSEXITS_H_MISSING" && i10=I test -z "$i11" && grepfor math.h "#define MATH_H_MISSING" && i11=I test -z "$i12" && grepfor limits.h "#define LIMITS_H_MISSING" && i12=I test -z "$i13" && grepfor syslog.h "#define SYSLOG_H_MISSING" && i13=I newi="$i0$i1$i2$i3$i4$i5$i6$i7$i8$i9$i10$i11$i12$i13" if test a$oldi = a$newi then echo 2>&1 "Whoeaaa! There's something fishy going on here." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" $MAKE _autotst.$O >_autotst.rrr 2>&1 cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 "I suggest you take a look at the definition of CFLAGS*" echo 2>&1 "in the Makefile before you try make again." kill -15 $$ fi oldi=$newi echo \ " ...missing `expr $oldi : '.*'` include files, doublechecking..." done $RM _autotst.$O cat >_autotst.c <$DEVNULL 2>&1 test -f _autotst.$O then : else echo "#define NO_COMSAT" >>$ACONF fi $RM _autotst.$O cat >_autotst.c <&1 | $FGREP -v include/ >_autotst.rrr test ! -f _autotst.$O do test -z "$i1" && grepfor size_t 'typedef unsigned size_t;' && i1=I test -z "$i2" && grepfor pid_t 'typedef int pid_t;' && i2=I test -z "$i3" && grepfor mode_t 'typedef int mode_t;' && i3=I test -z "$i4" && grepfor uid_t 'typedef int uid_t;' && i4=I test -z "$i5" && grepfor gid_t 'typedef int gid_t;' && i5=I test -z "$i6" && grepfor h_addr_list '#define h_0addr_list h_addr' && i6=I test -z "$i6" && grepfor hostent '#define h_0addr_list h_addr' && i6=I test -z "$i6" && grepfor member '#define h_0addr_list h_addr' && i6=I test -z "$i7" && grepfor utsname "#define NOuname \ /* is there, but empty */" && i7=I newi="$i1$i2$i3$i4$i5$i6$i7" if test a$oldi = a$newi then echo 2>&1 "Whoeaaa! There's something fishy going on here." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" $MAKE _autotst.$O >_autotst.rrr 2>&1 cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 "I suggest you take a look at the definition of CFLAGS*" echo 2>&1 "in the Makefile before you try make again." kill -15 $$ fi oldi=$newi echo \ " ...missing `expr $oldi : '.*'` types, doublechecking..." done $RM _autotst.$O cat >_autotst.c < #ifndef STDLIB_H_MISSING #include #define INITIAL 60 #else void*realloc(),*malloc(); #endif int main() { char*p=malloc(1),*q=0; size_t len,last,max=BLKSIZ*64; /* 1M on non-SMALLHEAP systems */ int count=0; for(last=len=INITIAL;len<=max+INITIAL;len+=BLKSIZ) { if(!(p=realloc(p,len))) break; if(p!=q) count++,q=p,fprintf(stderr,"len=%lu p=%p\n",(unsigned long)len,p); for(;lastmax) { puts("#define INEFFICIENTrealloc"); exit(1); } exit(0); } HERE echo 'Checking realloc implementation' if $MAKE _autotst >$DEVNULL 2>&1 test -f _autotst then _autotst >_autotst.rrr 2>realloc.log case "$?" in 0) cat _autotst.rrr >>$ACONF;; 1) cat _autotst.rrr >>$ACONF echo 'Sorry, repeatedly reallocing is inefficient';; *) echo "The test program failed somehow. Assuming realloc is fine";; esac fi $RM _autotst.$O _autotst _autotst.rrr cat >_autotst.c <_autotst.rrr 2>&1 then $FGREP -v include/ <_autotst.rrr >_autotst.$O $MV _autotst.$O _autotst.rrr grepfor struct '#define WMACROS_NON_POSIX' || grepfor union '#define WMACROS_NON_POSIX' else echo '#define WMACROS_NON_POSIX' >>$ACONF fi $RM _autotst.$O _autotst.rrr cat >_autotst.c < int main() { struct passwd*pw=getpwuid(0); return (pw->pw_passwd==0)+(pw->pw_class==0)+(pw->pw_gecos==0); } HERE echo 'Testing for various struct passwd members' $MAKE _autotst.$O >_autotst.rrr 2>&1 grepfor pw_passwd '#define NOpw_passwd' grepfor pw_class '#define NOpw_class' grepfor pw_gecos '#define NOpw_gecos' $RM _autotst.$O _autotst.rrr cat >_autotst.c <$DEVNULL 2>&1 then : else echo 2>&1 "Whoeaaa! There's something fishy going on here." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 "Your include files seem to be beyond repair ." echo 2>&1 "I give up." kill -15 $$ fi $MAKE _autotst >_autotst.rrr 2>&1 $RM _autotst _autotst.$O # The simple ones for func in strcspn strpbrk rename pow mkdir fstat initgroups \ strtol waitpid ftruncate fsync strtod strncasecmp strlcat memset bzero _exit do grepfor $func "#define NO$func" done NOstrerror=no grepfor strerror "#define NOstrerror" && NOstrerror=yes grepfor opendir " #define NOopendir /* the readdir library does not seem to be available this will slightly affect the way a filenumber is selected in MH-folders by procmail */ " a=no grepfor setrgid '#define NOsetrgid' && a=yes if grepfor setegid '#define NOsetegid' || test $a = yes then grepfor setregid '#define NOsetregid' && grepfor setresgid '#define NOsetresgid' fi grepfor strchr '#define strchr(s,c) index(s,c)' grepfor uname "\ #define NOuname \ /* defines it, the libraries don't */" grepfor endpwent '#define endpwent()' grepfor endgrent '#define endgrent()' if grepfor gethostbyname '#define NO_COMSAT' then : else grepfor getprotobyname '#define UDP_protocolno 17' grepfor endhostent '#define endhostent()' grepfor endservent '#define endservent()' grepfor endprotoent '#define endprotoent()' fi grepfor strstr '#define SLOWstrstr' || grepfor clock '#define SLOWstrstr' grepfor memmove '#define NOmemmove' && if $FGREP -e bcopy _autotst.rrr >$DEVNULL then echo '#define NObcopy' >>$ACONF if test $NOstrerror = yes then cat >_autotst.c <_autotst.rrr 2>&1 then if $MAKE _autotst >_autotst.rrr 2>&1 then echo '' >_autotst.rrr fi fi grepfor sys_errlist '#define NOsys_errlist' $RM _autotst _autotst.$O _autotst.rrr fi echo 'Testing for brain damage' cat >_autotst.c <$DEVNULL 2>&1 then : else echo 'Yep, it is' # ISC chokes on its own offsetof() echo '#define oBRAIN_DAMAGE' >>$ACONF fi $RM _autotst.$O else cat >_autotst.c <$DEVNULL 2>&1 if _autotst then echo 'Sorry, incompetent bcopy, using substitute instead' echo '#define NObcopy' >>$ACONF fi $RM _autotst.$O _autotst fi test -f /bin/test && echo "#define GOT_bin_test" >>$ACONF cat >_autotst.c <<\HERE #define BENCHSIZE 16384 #define GRANULARITY 64 /* maximal benchmark granularity */ #include "sublib.c" #ifndef NO_COMSAT #include "network.h" /* also for ntohs() */ #endif #undef malloc /* from shell.h */ #ifdef BENCHSIZE #undef strstr /* from includes.h */ #undef free /* from shell.h */ unsigned long dobench(strstr,iter,haystack)char*(*const strstr)(); unsigned long iter;const char*const haystack; { unsigned long to; to=(unsigned long)clock(); do (*strstr)(haystack,FROM_EXPR); while(--iter); return (unsigned long)clock()-to; } #endif int main(argc,argv)int argc;const char*argv[]; { if(argc==1) { char*haystack; #ifdef BENCHSIZE if(haystack=malloc(BENCHSIZE)) { unsigned c1,c2,i;time_t t; unsigned long iter,titer,syscnt; for(i=c1=c2=0;i=iter?(double)syscnt/iter:(double)iter/syscnt, syscnt>=iter?"SLOW":"FAST"); if(syscnt>iter+iter/16) /* if at least 1.0625 times slower */ printf("\ #define SLOWstrstr\t\t\t /* using my substitute instead */\n"); } else printf("/* Insufficient memory to perform the benchmark! */\n"); #endif /* SLOWstrstr */ #ifndef NO_COMSAT #ifndef UDP_protocolno ;{ const struct protoent*p; if(p=getprotobyname(COMSATprotocol)) { printf("#define UDP_protocolno %d\n",p->p_proto); #else ;{ if(1) { #endif ;{ const struct servent*serv; if(serv=getservbyname(COMSATservice,COMSATprotocol)) printf("#define BIFF_serviceport \"%d\"\n", ntohs(serv->s_port)); } #ifdef AF_INET ;{ const struct hostent*host; if(!strcmp("localhost",COMSAThost)&& (host=gethostbyname(COMSAThost))&& host->h_0addr_list&&host->h_addrtype==AF_INET&& host->h_length) { int j=host->h_length; const unsigned char*ad=(void*)host->h_0addr_list; printf("#define IP_localhost {"); printf("%d",*ad++); while(--j) printf(",%d",*ad++); puts("}"); } } #endif /* AF_INET */ } else puts("#define NO_COMSAT"); } #endif /* NO_COMSAT */ ;{ unsigned long s=(size_t)~0;int bits; for(bits=1;s>>=1;bits++); if(bits<=16) { puts("#define SMALLHEAP"); return 0; } } #ifdef _GUID_T /* ISC cc hack */ ;{ struct passwd*p; if(sizeof(int)>sizeof(uid_t)&&(p=getpwnam("root"))&&(*p->pw_dir!='/'|| (*p->pw_shell&&!(p->pw_shell)[1]))) { puts("#define _GUID_T\ntypedef int uid_t;\ntypedef int gid_t;"); goto skipsetrgid; } } #endif #ifndef NOinitgroups if(geteuid()==ROOT_uid) { setgid(2); if(!setrgid(3)&&(getgid()!=3||getegid()!=2)) puts("#define setrgid_BRAIN_DAMAGE"); else { setuid(2); if(!setgid(3)&&!setgid(2)) puts("#define TOGGLE_SGID_OK"); } } else #ifndef NGROUPS_MAX /* is this safeguard really needed? */ #define NGROUPS_MAX 3 #endif /* some OSes expect an array of ints? */ { gid_t groups[NGROUPS_MAX*sizeof(int)/sizeof(gid_t)];unsigned i; for(i=getgroups(NGROUPS_MAX,groups); --i4) #define MAX_ENVMEM 2048 /* typical environment size */ #define MAX_ARGMEM (2048*1024L) /* upper limit arguments size */ { static const char arg[]="e%04u=6789012345";unsigned diff; charNUM(num,diff);char**nargv;const char*einfo=""; #define MAXT(type) (~(type)0>>1) sscanf(argv[1],"%u",&diff); /* what did our previous incarnation want? */ if(!diff) /* first run? initialise the environment */ { char*envs,**nenviron,**nenv; diff=MAX_ENVMEM/sizeof arg+1; if(!(nenviron=malloc(diff*sizeof*environ+MAX_ENVMEM))) goto printit; /* oops, panic already */ envs=(char*)((nenv=nenviron)+diff); while(--diff) /* put in some colour */ sprintf(*nenviron++=envs,arg,diff),envs+=sizeof arg; *(environ=nenv)="PATH=.";*nenviron=0;diff=argc; /* start doubling */ } if(MAX_ARGMEM/sizeof arg<=argc) /* we are satisfied */ { einfo=" /* soft limit */"; /* no reason to hog memory */ goto printit; } ;{ unsigned narg; while(narg=argc+diff+1, /* allocate space for the new args */ !(nargv=malloc(narg*sizeof*argv+(size_t)diff*sizeof arg))) if(!(diff>>=1)) /* oops, no space, back off */ goto printit; /* panic */ tmemmove(nargv,argv,argc*sizeof*argv);nargv[1]=num; /* copy old */ ;{ char**pargv,*args;unsigned i; pargv=nargv+argc;args=(char*)(nargv+narg);i=diff; do { const char*p; /* and paint in the new ones */ for(*pargv++=args,p=arg;*args++= *p++;); } while(--i); } } for(;;diff>>=1) { unsigned newdiff=diff>>1; /* take a small step for mankind */ if(argc==diff) /* or shall we boldly go */ newdiff=(unsigned)argc<<1; /* where no man has gone before? */ ;{ unsigned maxdiff=MAXT(unsigned)-argc; if(newdiff>maxdiff) /* that would overflow argc */ newdiff=maxdiff; /* and we wouldn't want that, would we now? */ } if(!newdiff) /* reductio ad adsurdum? */ break; /* yes, break off the engagement */ sprintf(num,"%u",newdiff);nargv[argc+diff]=0; /* pass on the target */ execv(argv[0],nargv); /* jeronimo! */ } printit: /* we proudly present the findings */ printf("#define MAX_argc %d%s\n",argc-2,einfo); /* of the jury! */ } return EXIT_SUCCESS; } HERE echo "Determining the maximum number of 16 byte arguments execv() takes" if $MAKE _autotst >$DEVNULL 2>_autotst.rrr && _autotst 0 012345678901234 012345678901234 012345678901234 \ >>$ACONF 2>>_autotst.rrr then : else echo 2>&1 "Whoeaaa! This actually can't happen." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 "I suggest you take a look at the definition of LDFLAGS*" echo 2>&1 "in the Makefile before you try make again." kill -15 $$ fi $RM _autotst.rrr echo "Benchmarking your system's strstr() implementation" _autotst >>$ACONF found=no for a in /var/spool/mail /usr/spool/mail /var/mail /usr/mail /spool/mail do test -d $a -a $found != yes && echo '#define MAILSPOOLDIR "'$a'/"' >>$ACONF && found=yes done if test $found != yes then echo '#define MAILSPOOLDIR "/var/spool/mail/"' >>$ACONF echo Could not find the system-mailbox directory, supplied default. fi cat >lookfor <>$ACONF exit 0 fi done exit 1 HERE chmod 0755 lookfor if _autotst 2 3 >>$ACONF test $? != 0 || lookfor sendmail || lookfor smail || lookfor mail #|| lookfor rmail then : else echo 'Could not find any mailer. It should be a mailer accepting at least' echo 'one plain destination address as its only argument (any sendmail' echo 'compatible mailer will do), and the mail-to-be-sent on stdin.' echo 'What is your mailer called? (You can override this in config.h)' read a echo "#define SENDMAIL \"$a\"" >>$ACONF fi grep '^#define SENDMAIL ".*/sendmail"' $ACONF >$DEVNULL || echo "#define DEFflagsendmail \"\"" >>$ACONF a=/tmp/_chowntst.$$ $RM -r $a OLDTESTDIRS="$a $OLDTESTDIRS" mkdir $a mkdir $a/__ _autotst $a/__ $a/__/__ 4 >>$ACONF cat /usr/lib/sendmail.cf /etc/sendmail.cf /etc/mail/sendmail.cf 2>$DEVNULL | grep 'Mlocal.*procmail' >$DEVNULL || echo '#define CF_no_procmail_yet' >>$ACONF cat /usr/lib/sendmail.cf /etc/sendmail.cf /etc/mail/sendmail.cf 2>$DEVNULL | grep '^V' >$DEVNULL || echo '#define buggy_SENDMAIL' >>$ACONF lpath='/bin' bins="/bin" for newd in /usr/bin /usr/ucb /usr/5bin $BINDIR /local/bin /usr/local/bin \ /global/bin /usr/bin/X11 /usr/X*/bin do if test -d $newd then oldid=`cd $newd; ls -id . .. ../.. 2>&1` for oldd in $bins do test "X`cd $oldd; ls -id . .. ../.. 2>&1`" = "X$oldid" && oldid=found done test found != "$oldid" && bins="$bins $newd" && lpath="$lpath:$newd" fi done echo "#define defPATH \"PATH=\$HOME/bin:$lpath\"" >>$ACONF echo "#define defSPATH \"PATH=$lpath\"" >>$ACONF sed -n -e 's/^ v\([^ ]*\) .*$/#define PM_VERSION "\1"/p' \ ../patchlevel.h >>$ACONF while $FGREP -e "Kernel-locking tests completed." _locktst.rrr >$DEVNULL; \ test 0 != $? do if test -z "$tailpid" then echo "Waiting for the kernel-locking tests to finish..." exec 9>&2 2>$DEVNULL tail -f lock.log & tailpid=$! exec 2>&9 9>&- fi # if test ! -f _locktst.alive # then # echo >_locktst.alive # echo "Waiting for kernel-locking tests to finish..." # fi sleep 4 done exec 9>&2 2>$DEVNULL test ! -z "$tailpid" && kill $tailpid && echo "" && echo "" tailpid="" wait # wait now, to prevent a notification message afterward exec 2>&9 9>&- echo "Collecting results from kernel-locking tests" sed -e '/^Kernel-locking tests completed./,$ d' <_locktst.rrr >>$ACONF set dummy *core* if test -f $2 -a $nocore = yes then echo "Removing core file (probably from the locking tests)" $RM *core* fi echo "/* autoconf completed */" >>$ACONF $RM -r $OLDTESTDIRS $OLDTESTDIRS 2>$DEVNULL $RM _autotst* _locktst* lookfor grepfor 2>$DEVNULL echo =============================autoconf.h=================================== cat $ACONF echo ========================================================================== procmail-3.22/src/includes.h0100644006717600001440000003056307347314733015243 0ustar guenthersrc/*$Id: includes.h,v 1.77 2001/08/25 04:38:37 guenther Exp $*/ #include "../autoconf.h" #ifdef NO_const #ifdef const #undef const #endif #define const #endif #include "../config.h" /* not all the "library identifiers" specified here need to be available for all programs in this package; some have substitutes as well (see autoconf); this is just an informal list */ #ifndef _HPUX_SOURCE #define _HPUX_SOURCE /* sad, but needed on HP-UX when compiling -Aa */ #endif #ifndef NO_FIX_MALLOC #define NO_FIX_MALLOC /* we don't need a `fixed' malloc(0) call */ #endif /* saves a few bytes in some implementations */ #include /* pid_t mode_t uid_t gid_t off_t */ #ifndef LIMITS_H_MISSING #include /* absolutely nothing, just for fun */ #undef LIMITS_H_MISSING #endif #ifndef UNISTD_H_MISSING #include /* open() read() write() close() dup() pipe() /* fork() getuid() geteuid() getgid() getegid() getpid() execv() execvp() sleep() setuid() setgid() setruid() setrgid() setegid() chown() nice() ftruncate() truncate() */ #undef EX_OK #else #undef UNISTD_H_MISSING #endif #include /* setbuf() fclose() stdin stdout stderr /* fopen() fread() fwrite() fgetc() getc() getchar() fdopen() putc() fputs() printf() sprintf() fprintf() sscanf() FILE EOF fileno() */ #ifndef STDDEF_H_MISSING #include /* ptrdiff_t size_t */ #else #undef STDDEF_H_MISSING #endif #ifndef STDLIB_H_MISSING #include /* getenv() malloc() realloc() free() /* strtol() strtod() exit() EXIT_SUCCESS */ #endif #include /* time() ctime() time_t */ #include /* fcntl() struct flock O_RDONLY O_WRONLY /* O_APPEND O_CREAT O_EXCL */ #include /* getgrgid() struct group */ #include /* getpwuid() getpwnam() struct passwd */ #ifndef DIRENT_H_MISSING #include /* opendir() readdir() closedir() DIR /* struct dirent */ #endif #ifndef SYS_WAIT_H_MISSING #include /* wait() waitpid() WIFEXITED() WIFSTOPPED() /* WEXITSTATUS() WTERMSIG() WNOHANG */ #else #undef SYS_WAIT_H_MISSING #endif #ifndef SYS_UTSNAME_H_MISSING #include /* uname() utsname */ #endif #include /* stat() S_ISDIR() S_ISREG() struct stat /* chmod() mkdir() */ #include /* signal() kill() alarm() SIG_IGN SIGHUP /* SIGINT SIGQUIT SIGALRM SIGTERM */ #ifndef STRING_H_MISSING #include /* strcpy() strcat() strlen() /* strspn() strcspn() strchr() strcmp() strncmp() strpbrk() strstr() memmove() strncasecmp() memset() */ #endif #ifndef MATH_H_MISSING #include /* pow() */ #endif #ifndef SYSLOG_H_MISSING #include /* openlog() syslog() closelog() LOG_EMERG /* LOG_ALERT LOG_CRIT LOG_ERR LOG_NOTICE LOG_PID LOG_MAIL */ #endif #include /* EINTR EEXIST ENFILE EACCES EAGAIN EXDEV */ /* EDQUOT ENOSPC strerror() */ #ifndef SYSEXITS_H_MISSING #include /* EX_USAGE EX_DATAERR EX_NOINPUT EX_NOUSER EX_UNAVAILABLE EX_OSERR EX_OSFILE EX_CANTCREAT EX_IOERR EX_TEMPFAIL EX_NOPERM EX__BASE */ #endif #ifdef STDLIB_H_MISSING #undef STDLIB_H_MISSING void*malloc(),*realloc(); const char*getenv(); #endif #ifdef DIRENT_H_MISSING #undef DIRENT_H_MISSING #ifndef NDIR_H_MISSING #include #define dirent direct #else #undef NDIR_H_MISSING #ifndef SYS_NDIR_H_MISSING #include #define dirent direct #else #undef SYS_NDIR_H_MISSING #ifndef SYS_DIR_H_MISSING #include #define dirent direct #else /* due to brain-damaged NeXT sys/dirent.h contents */ #undef SYS_DIR_H_MISSING #ifndef SYS_DIRENT_H_MISSING /* sys/dirent.h must be moved down here */ #include #else /*#undef SYS_DIRENT_H_MISSING /* needed by autoconf */ /* I give up, I can only hope that your system defines DIR and struct dirent */ #endif #endif #endif #endif #endif /* DIRENT_H_MISSING */ #ifdef STRING_H_MISSING #undef STRING_H_MISSING #include #ifndef strchr char*strchr(); #endif char*strpbrk(); #endif #ifdef SYS_UTSNAME_H_MISSING #undef SYS_UTSNAME_H_MISSING #define NOuname #endif #ifdef MATH_H_MISSING #undef MATH_H_MISSING double pow(); #endif #ifdef SYSEXITS_H_MISSING #undef SYSEXITS_H_MISSING /* Standard exitcodes, original list maintained by Eric Allman (eric@Sendmail.COM) */ #define EX__BASE 64 #define EX_USAGE 64 #define EX_DATAERR 65 #define EX_NOINPUT 66 #define EX_NOUSER 67 #define EX_UNAVAILABLE 69 #define EX_OSERR 71 #define EX_OSFILE 72 #define EX_CANTCREAT 73 #define EX_IOERR 74 #define EX_TEMPFAIL 75 #define EX_NOPERM 77 #endif #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #ifdef NO_exit #define _exit(sig) exit(sig) #endif #if O_SYNC #else #undef O_SYNC #define O_SYNC 0 #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #endif #ifndef SEEK_SET #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif #ifndef tell #define tell(fd) lseek(fd,(off_t)0,SEEK_CUR) #endif #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif #ifndef EAGAIN #define EAGAIN EINTR #endif #ifndef EOF #define EOF (-1) #endif #ifndef S_ISDIR #define S_ISDIR(mode) (((mode)&S_IFMT)==S_IFDIR) #ifndef S_IFDIR #define S_IFDIR 0040000 #endif #endif #ifndef S_ISREG #define S_ISREG(mode) (((mode)&S_IFMT)==S_IFREG) #ifndef S_IFREG #define S_IFREG 0100000 #endif #endif #ifndef S_ISLNK #ifndef S_IFLNK #define lstat(path,stbuf) stat(path,stbuf) #define S_ISLNK(mode) 0 #else #define S_ISLNK(mode) (((mode)&S_IFMT)==S_IFLNK) #endif #endif #ifndef S_IFMT #define S_IFMT 0170000 #endif #ifndef S_IRWXU #define S_IRWXU 00700 #define S_IRWXG 00070 #define S_IRWXO 00007 #endif #ifndef S_IWUSR #ifdef S_IREAD #define S_IRUSR S_IREAD #define S_IWUSR S_IWRITE #define S_IXUSR S_IEXEC #else #define S_IRUSR 0400 #define S_IWUSR 0200 #define S_IXUSR 0100 #endif /* S_IREAD */ #define S_IRGRP 0040 #define S_IWGRP 0020 #define S_IXGRP 0010 #define S_IROTH 0004 #define S_IWOTH 0002 #define S_IXOTH 0001 #endif /* S_IWUSR */ #ifndef S_ISGID #define S_ISUID 04000 #define S_ISGID 02000 #endif #ifndef S_ISVTX #define S_ISVTX 01000 #endif #ifdef WMACROS_NON_POSIX #undef WMACROS_NON_POSIX #ifdef WIFEXITED #undef WIFEXITED #endif #ifdef WIFSTOPPED #undef WIFSTOPPED #endif #ifdef WEXITSTATUS #undef WEXITSTATUS #endif #ifdef WTERMSIG #undef WTERMSIG #endif #endif /* WMACROS_NON_POSIX */ #ifndef WIFEXITED #define WIFEXITED(waitval) (!((waitval)&255)) #endif #ifndef WIFSTOPPED #define WIFSTOPPED(waitval) (((waitval)&255)==127) #endif #ifndef WEXITSTATUS #define WEXITSTATUS(waitval) ((waitval)>>8&255) #endif #ifndef WTERMSIG #define WTERMSIG(waitval) ((waitval)&255) #endif extern /*const*/char**environ; extern int errno; #ifndef STDIN_FILENO #define STDIN 0 #define STDOUT 1 #define STDERR 2 #else #define STDIN STDIN_FILENO #define STDOUT STDOUT_FILENO #define STDERR STDERR_FILENO #endif #ifdef NO_fcntl_LOCK #ifndef NOfcntl_lock #define NOfcntl_lock #endif #endif #ifdef NO_lockf_LOCK #ifdef USElockf #undef USElockf #endif #endif #ifdef NO_flock_LOCK #ifdef USEflock #undef USEflock #endif #endif #ifdef SYSLOG_H_MISSING #undef SYSLOG_H_MISSING #define Openlog(ident,logopt,facility) 0 #define syslog (void) #define closelog() #define LOG_EMERG 0 #define LOG_CRIT 0 #define LOG_ALERT 0 #define LOG_ERR 0 #define LOG_NOTICE 0 #else #ifdef LOG_MAIL #define Openlog(ident,logopt,facility) openlog(ident,logopt,facility) #else #define Openlog(ident,logopt,facility) openlog(ident,logopt) #endif #endif #ifndef NOuname #ifndef P /* SINIX V5.23 has the wrong prototype for uname() */ extern int uname(); /* so we fix it :-) */ #define Uname(name) ((int(*)(struct utsname*))uname)(name) #else #define Uname(name) uname(name) /* no fix needed */ #endif /* P */ #endif /* NOuname */ /* NEWS OS 5.X has the wrong prototype here */ #define Fdopen(fd,type) ((FILE*)fdopen(fd,type)) #ifdef u #undef u /* and the winner is: AIX 3.2 */ #endif #ifndef strchr /* for very old K&R compatible include files with */ #ifdef P /* new K&R libraries */ #ifdef void #ifdef NO_const extern char*strchr(); extern char*strpbrk(); extern char*strstr(); extern void*memmove(); #endif #endif #endif #endif #define Const /*const*/ /* Convex cc doesn't grok this */ #ifndef P /* no prototypes without const */ #ifdef NO_const #define P(args) () #endif #endif #ifdef NOrename #undef NOrename #define rename(old,new) (-(link(old,new)||unlink(old))) #endif #ifndef NOsetregid #ifdef NOsetrgid #define setrgid(gid) setregid(gid,-1) #define setruid(uid) setreuid(uid,-1) #endif #ifdef NOsetegid #define setegid(gid) setregid(-1,gid) #endif #else #ifndef NOsetresgid #ifdef NOsetrgid #define setrgid(gid) setresgid(gid,-1,-1) #define setruid(uid) setresuid(uid,-1,-1) #endif #ifdef NOsetegid #define setegid(gid) setresgid(-1,gid,-1) #endif #else #ifdef NOsetrgid #define setrgid(gid) (-1) #define setruid(uid) (-1) #endif #ifdef NOsetegid #define setegid(gid) setgid(gid) #endif #endif #endif #ifdef NOsetrgid #undef NOsetrgid #endif #ifdef NOsetegid #undef NOsetegid #endif #ifdef NOsetregid #undef NOsetregid #endif #ifdef NOsetresgid #undef NOsetresgid #endif #ifdef setrgid_BRAIN_DAMAGE #undef setrgid_BRAIN_DAMAGE #ifdef setrgid #undef setrgid #endif #ifdef setruid #undef setruid #endif #define setrgid(gid) (-1) /* and you think POSIX is broken? */ #define setruid(uid) (-1) /* BSD 4.4 just topped it */ #endif #ifdef setrgid_RUNTIME_CHECK #undef setrgid_RUNTIME_CHECK #define setRgid(gid) (setrgid(gid)||getgid()!=(gid)) #define setRuid(uid) (setruid(uid)||getuid()!=(uid)) #else #define setRgid(gid) setrgid(gid) #define setRuid(uid) setruid(uid) #endif #ifdef NOinitgroups /*#undef NOinitgroups need this macro in autoconf */ #define initgroups(n,g) #endif #ifdef INEFFICIENTrealloc #undef INEFFICIENTrealloc #define EXPBLKSIZ 4 /* 4+3+2+1 = 10 bits total shift */ #else #define EXPBLKSIZ 0 #endif #ifdef NOpow #define tpow(x,y) (x) #else #define tpow(x,y) pow(x,y) #endif #ifdef NOmkdir #undef NOmkdir #define mkdir(dir,mode) (-1) #endif #ifdef NOftruncate #undef NOftruncate #define ftruncate(fildes,length) (-1) #define truncate(file,length) (-1) #endif #ifdef NOfsync #define fsync(fd) 0 #endif #ifdef NOfstat #define fstat(fd,st) (-1) #endif #ifdef NOwaitpid #undef NOwaitpid #define waitpid(pid,stat_loc,options) 0 #else #ifndef WNOHANG #ifdef waitpid #undef waitpid #endif #define waitpid(pid,stat_loc,options) 0 #endif #endif #ifdef NOmemmove #define memmove(to,from,count) smemmove(to,from,count) #endif #ifdef NOstrpbrk #define strpbrk(s,d) sstrpbrk(s,d) #endif #ifdef SLOWstrstr #ifdef strstr #undef strstr #endif #define strstr(haystack,needle) sstrstr(haystack,needle) #endif #ifdef NOmemset #ifdef NObzero #define NEEDbbzero #else #define bbzero(s,l) bzero(s,l) #endif #else #define bbzero(s,l) memset(s,'\0',l) #endif #ifdef NOstrlcat #define strlcat(d,s,z) sstrlcat(d,s,z) #define strlcpy(d,s,z) sstrlcpy(d,s,z) #endif #ifdef NOstrerror #define strerror(e) sstrerror(e) #endif #ifdef NOstrtol #define strtol(s,p,b) sstrtol(s,p,b) #endif #ifndef P #define P(args) args #endif /* * Until function definitions are ANSI, any function whose argument list * includes a size_t, uid_t, gid_t, mode_t, pid_t, or time_t type variable * should be declared with Q() instead of P(). This is done to prevent * problems caused by one of those types being shorter than int and thereby * being passed differently under ANSI rules. */ #define Q(args) () #ifdef oBRAIN_DAMAGE #undef oBRAIN_DAMAGE #undef offsetof #endif #ifndef offsetof #define offsetof(s,m) ((char*)&(((s*)sizeof(s))->m)-(char*)sizeof(s)) #endif #define SETerrno(v) (errno=(v)) /* multi-threading errno hook */ #define PROGID /*const*/char progid[]="Stephen R. van den Berg" #define maxindex(x) (sizeof(x)/sizeof((x)[0])-1) #define STRLEN(x) (sizeof(x)-1) #define ioffsetof(s,m) ((int)offsetof(s,m)) #define numeric(x) (alphanum((unsigned char)(x))>1) #define sizeNUM(v) (8*sizeof(v)*4/10+1+1) #define charNUM(num,v) char num[sizeNUM(v)] #define mx(a,b) ((a)>(b)?(a):(b)) typedef unsigned char uschar; /* sometimes uchar is already typedef'd */ #ifdef uchar #undef uchar #endif #define uchar uschar #if ! (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) #define __attribute__(foo) #endif #if defined(__OpenBSD__) && defined(DEBUGGING) #define setuid(x) 0 /* Under OpenBSD, you can't trace a */ #define setgid(x) 0 /* program after it calls any of these */ #define setegid(x) 0 #endif procmail-3.22/src/cstdio.h0100644006717600001440000000117107223546525014712 0ustar guenthersrc/*$Id: cstdio.h,v 1.15 2000/09/28 01:23:16 guenther Exp $*/ void pushrc P((const char*const name)), changerc P((const char*const name)), duprcs P((void)), closerc P((void)), ungetb P((const int x)), skipline P((void)); int poprc P((void)), bopen P((const char*const name)), getbl P((char*p,char*end)), getb P((void)), testB P((const int x)), sgetc P((void)), skipspace P((void)), getlline P((char*target,char*end)); extern struct dynstring*incnamed; #ifdef LMTP /* extensions for LMTP */ void pushfd P((int fd)); int endoread P((void)), getL P((void)), readL P((char*,const int)), readLe P((char*,int)); #endif procmail-3.22/src/ecommon.c0100644006717600001440000000152407024670356015056 0ustar guenthersrc/************************************************************************ * Some common routines to all programs but procmail * * * * Copyright (c) 1993-1997, S.R. van den Berg, The Netherlands * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: ecommon.c,v 1.10 1999/12/12 08:50:50 guenther Exp $"; #endif #include "includes.h" #include "ecommon.h" void nlog P((const char*const a)); static const char outofmem[]="Out of memory\n"; void*tmalloc(len)const size_t len; { void*p; if(p=malloc(len)) return p; nlog(outofmem);exit(EX_OSERR); } void*trealloc(old,len)void*old;const size_t len; { if(old=realloc(old,len)) return old; nlog(outofmem);exit(EX_OSERR); } void tfree(a)void*a; { free(a); } #include "shell.h" procmail-3.22/src/exopen.c0100644006717600001440000001425307347314632014722 0ustar guenthersrc/************************************************************************ * Collection of NFS resistant exclusive creat routines * * * * Copyright (c) 1990-1997, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: exopen.c,v 1.44 2001/08/26 21:10:11 guenther Exp $"; #endif #include "procmail.h" #include "acommon.h" #include "robust.h" #include "misc.h" #include "exopen.h" #include "lastdirsep.h" #include "sublib.h" static const char*safehost P((void)) /* return a hostname safe for filenames */ { static const char*sname=0; if(!sname) { char*to;const char*from=hostname(); int badchars=0; for(to=(char*)from;*to;to++) /* check for characters that shouldn't */ if(*to=='/'||*to==':'||*to=='\\') /* be in hostnames */ badchars++; if(!badchars) /* it's clean, pass it through */ sname=from; else if(!(sname=to=malloc(3*badchars+strlen(from)+1))) sname=""; /* no memory! */ else { int c; while(badchars) switch(c=(unsigned char)*from++) { default:*to++=c; break; case '\0':from--;to--; /* "this cannot happen" */ break; case '/':case ':':case '\\': /* we'll remap them to \ooo */ *to++='\\'; *to++='0'+(c>>6); *to++='0'+((c>>3)&7); *to++='0'+(c&7); badchars--; } strcpy(to,from); /* copy the remaining characters */ } } return sname; } int unique(full,p,len,mode,verbos,flags)char*const full;char*p; const size_t len;const mode_t mode;const int verbos,flags; { static const char s2c[]=".,+%";static int serial=STRLEN(s2c); static time_t t;char*dot,*end,*host;struct stat filebuf; int nicediff,i,didnice,retry=RETRYunique; if(flags&doCHOWN) /* semi-critical, try raising the priority */ { nicediff=nice(0);SETerrno(0);nicediff-=nice(-NICE_RANGE); didnice=!errno; /* did we succeed? */ } end=full+len; if(end-p<=UNIQnamelen-1) /* were we given enough space? */ goto ret0; /* nope, give up */ if(flags&doMAILDIR) /* 'official' maildir format */ dot=p; else /* 'traditional' format */ *p=UNIQ_PREFIX,dot=ultoan((unsigned long)thepid,p+1); if(serialSTRLEN(s2c)-1) /* roll over the count? */ { ;{ time_t t2; while(t==(t2=time((time_t*)0))) /* make sure time has passed */ ssleep(1); /* tap tap tap... */ serial=0;t=t2; } in: if(flags&doMAILDIR) { dot=ultstr(0,(unsigned long)t,p); /* time.pid_s.hostname */ *dot='.'; dot=ultstr(0,(unsigned long)thepid,dot+1); *dot++='_'; host=dot+2; } else host=1+ultoan((unsigned long)t,dot+1); /* _pid%time.hostname */ host[-1]='.'; /* add the ".hostname" part */ strlcpy(host,safehost(),end-host); } *dot=(flags&doMAILDIR)?'0'+serial:s2c[serial]; serial++; i=lstat(full,&filebuf); #ifdef ENAMETOOLONG if(i&&errno==ENAMETOOLONG) { char*op,*ldp; op=lastdirsep(full); ldp=op+1; /* keep track to avoid shortening past it */ if(end-op>MINnamelen+1) /* guess at a safe length */ op+=MINnamelen+1; /* start at MINnamelen out */ else op=end-1; /* this shouldn't happen, but... */ do *--op='\0'; /* try chopping */ while((i=lstat(full,&filebuf))&&errno==ENAMETOOLONG&&op>ldp); } /* either it stopped being a problem or we ran out of filename */ #endif } #ifndef O_CREAT #define ropen(path,type,mode) creat(path,mode) #endif while((!i||errno!=ENOENT|| /* casually check if it already exists */ (0>(i=ropen(full,O_WRONLY|O_CREAT|O_EXCL,mode))&&errno==EEXIST))&& (i= -1,retry--)); if(flags&doCHOWN&&didnice) nice(nicediff); /* put back the priority to the old level */ if(i<0) { if(verbos) /* this error message can be confusing */ writeerr(full); /* for casual users */ goto ret0; } #ifdef NOfstat if(flags&doCHOWN) { if( #else if(flags&doCHECK) { struct stat fdbuf; fstat(i,&fdbuf); /* match between the file descriptor */ #define NEQ(what) (fdbuf.what!=filebuf.what) /* and the file? */ if(lstat(full,&filebuf)||filebuf.st_nlink!=1||filebuf.st_size|| NEQ(st_dev)||NEQ(st_ino)||NEQ(st_uid)||NEQ(st_gid)|| flags&doCHOWN&& #endif chown(full,uid,sgid)) { rclose(i);unlink(full); /* forget it, no permission */ ret0: return flags&doFD?-1:0; } } if(flags&doLOCK) rwrite(i,"0",1); /* pid 0, `works' across networks */ if(flags&doFD) return i; rclose(i); return 1; } /* rename MUST fail if already existent */ int myrename(old,newn)const char*const old,*const newn; { int fd,serrno; fd=hlink(old,newn);serrno=errno;unlink(old); if(fd>0)rclose(fd-1); SETerrno(serrno); return fd<0?-1:0; } /* NFS-resistant link() */ int rlink(old,newn,st)const char*const old,*const newn;struct stat*st; { if(link(old,newn)) { register int serrno,ret;struct stat sto,stn; serrno=errno;ret= -1; #undef NEQ /* compare files to see if the link() */ #define NEQ(what) (sto.what!=stn.what) /* actually succeeded */ if(lstat(old,&sto)||(ret=1,lstat(newn,&stn)|| NEQ(st_dev)||NEQ(st_ino)||NEQ(st_uid)||NEQ(st_gid)|| S_ISLNK(sto.st_mode))) /* symlinks are also bad */ { SETerrno(serrno); if(st&&ret>0) { *st=sto; /* save the stat data */ return ret; /* it was a real failure */ } return -1; } /*SETerrno(serrno);*/ /* we really succeeded, don't bother with errno */ } return 0; } /* hardlink with fallback for systems that don't support it */ int hlink(old,newn)const char*const old,*const newn; { int ret;struct stat stbuf; if(0<(ret=rlink(old,newn,&stbuf))) /* try a real hardlink */ { int fd; #ifdef O_CREAT /* failure due to filesystem? */ if(stbuf.st_nlink<2&&errno==EXDEV&& /* try it by conventional means */ 0<=(fd=ropen(newn,O_WRONLY|O_CREAT|O_EXCL,stbuf.st_mode))) return fd+1; #endif return -1; } return ret; /* success, or the stat failed also */ } procmail-3.22/src/procmail.c0100644006717600001440000007517107347315042015234 0ustar guenthersrc/************************************************************************ * procmail - The autonomous mail processor * * * * It has been designed to be able to be run suid root and (in * * case your mail spool area is *not* world writable) sgid * * mail (or daemon), without creating security holes. * * * * Seems to be perfect. * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: procmail.c,v 1.183 2001/08/31 04:57:36 guenther Exp $"; #endif #include "../patchlevel.h" #include "procmail.h" #include "acommon.h" #include "sublib.h" #include "robust.h" #include "shell.h" #include "misc.h" #include "memblk.h" #include "pipes.h" #include "common.h" #include "cstdio.h" #include "exopen.h" #include "mcommon.h" #include "goodies.h" #include "locking.h" #include "mailfold.h" #include "lastdirsep.h" #include "authenticate.h" #include "lmtp.h" #include "foldinfo.h" #include "variables.h" #include "comsat.h" #include "from.h" static const char*const nullp,exflags[]=RECFLAGS,drcfile[]="Rcfile:", pmusage[]=PM_USAGE,*etcrc=ETCRC,misrecpt[]="Missing recipient\n", extrns[]="Extraneous ",ignrd[]=" ignored\n",pardir[]=chPARDIR, defspath[]=DEFSPATH,defmaildir[]=DEFmaildir; char*buf,*buf2,*loclock; const char shell[]="SHELL",lockfile[]="LOCKFILE",newline[]="\n",binsh[]=BinSh, unexpeof[]="Unexpected EOL\n",*const*gargv,*const*restargv= &nullp,*sgetcp, pmrc[]=PROCMAILRC,*rcfile,dirsep[]=DIRSEP,devnull[]=DevNull,empty[]="", lgname[]="LOGNAME",executing[]="Executing",oquote[]=" \"",cquote[]="\"\n", procmailn[]="procmail",whilstwfor[]=" whilst waiting for ",home[]="HOME", host[]="HOST",*defdeflock=empty,*argv0=empty,curdir[]={chCURDIR,'\0'}, slogstr[]="%s \"%s\"",conflicting[]="Conflicting ",orgmail[]="ORGMAIL", insufprivs[]="Insufficient privileges\n",defpath[]=DEFPATH, exceededlb[]="Exceeded LINEBUF\n",errwwriting[]="Error while writing to", Version[]=VERSION; int retval=EX_CANTCREAT,retvl2=EXIT_SUCCESS,sh,pwait,rc= -1, privileged=priv_START,lexitcode=EXIT_SUCCESS,ignwerr,crestarg,savstdout, berkeley,mailfilter,erestrict,Deliverymode,ifdepth; /* depth of outermost */ struct dyna_array ifstack; size_t linebuf=mx(DEFlinebuf,1024/*STRLEN(systm_mbox)<<1*/); volatile int nextexit,lcking; /* if termination is imminent */ pid_t thepid; long filled,lastscore; /* the length of the mail, and the last score */ memblk themail; /* the mail */ char*thebody; /* the body of the message */ uid_t uid; gid_t gid,sgid; static auth_identity*savepass(spass,uid)auth_identity*const spass; const uid_t uid; { const auth_identity*tpass; if(auth_filledid(spass)&&auth_whatuid(spass)==uid) goto ret; if(tpass=auth_finduid(uid,0)) /* save by copying */ { auth_copyid(spass,tpass); ret: return spass; } return (auth_identity*)0; } #define rct_ABSOLUTE 0 /* rctypes for tryopen() */ #define rct_CURRENT 1 #define rct_DEFAULT 2 #define rcs_DELIVERED 1 /* rc exit codes for mainloop() */ #define rcs_EOF 2 #define rcs_HOST 3 static void usage P((void)); static int tryopen P((const int delay_setid,const int rctype,const int dowarning)), mainloop P((void)); int main(argc,argv)int argc;const char*const argv[]; { register char*chp,*chp2; #if 0 /* enable this if you want to trace procmail */ kill(getpid(),SIGSTOP);/*raise(SIGSTOP);*/ #endif newid(); ;{ int presenviron,override;char*fromwhom=0; const char*idhint=0;gid_t egid=getegid(); presenviron=Deliverymode=mailfilter=override=0; Openlog(procmailn,LOG_PID,LOG_MAIL); /* for the syslogd */ if(argc) /* sanity check, any argument at all? */ { Deliverymode=!!strncmp(lastdirsep(argv0=argv[0]),procmailn, STRLEN(procmailn)); for(argc=0;(chp2=(char*)argv[++argc])&&*chp2=='-';) for(;;) /* processing options */ { switch(*++chp2) { case VERSIONOPT: usage(); return EXIT_SUCCESS; case HELPOPT1:case HELPOPT2:elog(pmusage);elog(PM_HELP); elog(PM_QREFERENCE); return EX_USAGE; case PRESERVOPT:presenviron=1; continue; case MAILFILTOPT:mailfilter=1; continue; case OVERRIDEOPT:override=1; continue; case BERKELEYOPT:case ALTBERKELEYOPT:berkeley=1; continue; case TEMPFAILOPT:retval=EX_TEMPFAIL; continue; case FROMWHOPT:case ALTFROMWHOPT: if(*++chp2) fromwhom=chp2; else if(chp2=(char*)argv[argc+1]) argc++,fromwhom=chp2; else nlog("Missing name\n"); break; case ARGUMENTOPT: { static struct dyna_array newargv; if(*++chp2) goto setarg; else if(chp2=(char*)argv[argc+1]) { argc++; setarg: app_valp(newargv,(const char*)chp2); restargv=&(acc_valp(newargv,0)); crestarg++; } else nlog("Missing argument\n"); break; } case DELIVEROPT: if(!*(chp= ++chp2)&&!(chp=(char*)argv[++argc])) { nlog(misrecpt); break; } else { Deliverymode=1; goto last_option; } case LMTPOPT: #ifdef LMTP Deliverymode=2; goto last_option; #else nlog("LMTP support not enabled in this binary\n"); return EX_USAGE; #endif case '-': if(!*++chp2) { argc++; goto last_option; } default:nlog("Unrecognised options:");logqnl(chp2); elog(pmusage);elog("Processing continued\n"); case '\0':; } break; } } if(Deliverymode==1&&!(chp=chp2)) nlog(misrecpt),Deliverymode=0; last_option: switch(Deliverymode) { case 0: idhint=getenv(lgname); if(mailfilter&&crestarg) { crestarg=0; /* -m will supersede -a */ conflopt: nlog(conflicting);elog("options\n");elog(pmusage); } break; #ifdef LMTP case 2: if(fromwhom) { fromwhom=0; /* -z disables -f, */ goto confldopt; /* -p and -m */ } #endif case 1: if(presenviron||mailfilter) confldopt: { presenviron=mailfilter=0; /* -d disables -p and -m */ goto conflopt; } break; default: /* this cannot happen */ abort(); } cleanupenv(presenviron); ;{ auth_identity*pass,*passinvk;auth_identity*spassinvk; uid_t euid=geteuid(); uid=getuid();gid=getgid(); spassinvk=auth_newid();passinvk=savepass(spassinvk,uid); /* are we */ checkprivFrom_(euid,passinvk?auth_username(passinvk):0,override); doumask(INIT_UMASK); /* allowed to set the From_ line? */ while((savstdout=rdup(STDOUT))<=STDERR) { rclose(savstdout); /* move stdout out of the way */ if(0>(savstdout=opena(devnull))) goto nodevnull; syslog(LOG_ALERT,"Descriptor %d was not open\n",savstdout); } fclose(stdout);rclose(STDOUT); /* just to make sure */ if(0>opena(devnull)) nodevnull: { writeerr(devnull);syslog(LOG_EMERG,slogstr,errwwriting,devnull); return EX_OSFILE; /* couldn't open /dev/null */ } #ifdef console opnlog(console); #endif setbuf(stdin,(char*)0);allocbuffers(linebuf,1); #ifdef SIGXCPU signal(SIGXCPU,SIG_IGN);signal(SIGXFSZ,SIG_IGN); #endif #ifdef SIGLOST signal(SIGLOST,SIG_IGN); #endif #if DEFverbose verboff();verbon(); #else verbon();verboff(); #endif #ifdef SIGCHLD signal(SIGCHLD,SIG_DFL); #endif signal(SIGPIPE,SIG_IGN); setcomsat(empty); /* turn on biff by default */ ultstr(0,(unsigned long)uid,buf);filled=0; if(!passinvk||!(chp2=(char*)auth_username(passinvk))) chp2=buf; #ifdef LMTP if(Deliverymode==2) { auth_identity**rcpts,**lastrcpt,**currcpt; currcpt=rcpts=lmtp(&lastrcpt,chp2); if(currcpt+1!=lastrcpt) /* if there's more than one recipient */ lockblock(&themail); /* then no one can write to the block */ else /* otherwise the only recipient */ private(1); /* gets the original */ while(currcptbopen(buf)) /* try opening the rcfile */ { if(dowarning) rerr: readerr(buf); return 0; } if(!delay_setid&&privileged) /* if we're not supposed to delay */ { closerc(); /* and we haven't changed yet, then close it, */ setids(); /* transmogrify to prevent peeking, */ if(0>bopen(buf)) /* and try again */ goto rerr; /* they couldn't read it, so it was bogus */ } #ifndef NOfstat if(fstat(rc,&stbuf)) /* the right way */ #else if(stat(buf,&stbuf)) /* the best we can */ #endif { static const char susprcf[]="Suspicious rcfile"; suspicious_rc: closerc();nlog(susprcf);logqnl(buf); syslog(LOG_ALERT,slogstr,susprcf,buf); goto rerr; } if(delay_setid) /* change now if we haven't already */ setids(); if(rctype==rct_CURRENT) /* opened rcfile in the current directory? */ { if(!didchd) setmaildir(curdir); } else /* * OK, so now we have opened an absolute rcfile, but for security * reasons we only accept it if it is owned by the recipient or * root and is not world writable, and the directory it is in is * not world writable or has the sticky bit set. If this is the * default rcfile then we also outlaw group writability. */ { register char*chp=lastdirsep(buf),c; c= *chp; if(((stbuf.st_uid!=uid&&stbuf.st_uid!=ROOT_uid|| /* check uid, */ (stbuf.st_mode&S_IWOTH)|| /* writable by others, */ rctype==rct_DEFAULT&& /* if the default then also check */ (stbuf.st_mode&S_IWGRP)&& /* for writable by group, */ (NO_CHECK_stgid||stbuf.st_gid!=gid) )&&strcmp(devnull,buf)|| /* /dev/null is a special case, */ (*chp='\0',stat(buf,&stbuf))|| /* check the directory, */ #ifndef CAN_chown /* sticky and can't chown */ !(stbuf.st_mode&S_ISVTX)&& /* means we don't care if */ #endif /* it's group or world writable */ ((stbuf.st_mode&(S_IWOTH|S_IXOTH))==(S_IWOTH|S_IXOTH)|| rctype==rct_DEFAULT&& (stbuf.st_mode&(S_IWGRP|S_IXGRP))==(S_IWGRP|S_IXGRP)&& (NO_CHECK_stgid||stbuf.st_gid!=gid)))) { *chp=c; goto suspicious_rc; } *chp=c; } yell(drcfile,buf); /* * Chdir now if we haven't already */ if(!didchd) /* have we done this already? */ { const char*chp; if(buildpath(maildir,defmaildir,(char*)0)) exit(EX_OSERR); /* something was wrong: give up the ghost */ if(chdir(chp=buf)) { chderr(buf); /* no, well, then try an initial chdir */ chp=tgetenv(home); if(!strcmp(chp,buf)||chdir(chp)) chderr(chp),chp=curdir; /* that didn't work, use "." */ } setmaildir(chp); } return 1; /* we're good to go */ } static int mainloop P((void)) { int lastsucc,lastcond,prevcond,i,skiprc;register char*chp,*tolock; lastsucc=lastcond=prevcond=skiprc=0; tolock=0; do { unlock(&loclock); /* unlock any local lockfile */ goto commint; do { skipline(); commint:do skipspace(); /* skip whitespace */ while(testB('\n')); } while(testB('#')); /* no comment :-) */ if(testB(':')) /* check for a recipe */ { int locknext,succeed;char*startchar;long tobesent; static char flags[maxindex(exflags)]; do { int nrcond; if(readparse(buf,getb,0,skiprc)) return rcs_EOF; /* give up on this one */ ;{ char*temp; /* so that chp isn't forced */ nrcond=strtol(buf,&temp,10);chp=temp; /* into memory */ } if(chp==buf) /* no number parsed */ nrcond= -1; if(tolock) /* clear temporary buffer for lockfile name */ free(tolock); for(i=maxindex(flags);i;i--) /* clear the flags */ flags[i]=0; for(tolock=0,locknext=0;;) { chp=skpspace(chp); switch(i= *chp++) { default: ;{ char*flg; if(!(flg=strchr(exflags,i))) /* a valid flag? */ { chp--; break; } flags[flg-exflags]=1; /* set the flag */ } case '\0': if(chp!=Tmnate) /* if not the real end, skip */ continue; break; case ':':locknext=1; /* yep, local lockfile specified */ if(*chp||++chp!=Tmnate) tolock=tstrdup(chp),chp=strchr(chp,'\0')+1; } concatenate(chp);skipped(chp); /* display leftovers */ break; } /* parse & test the conditions */ i=conditions(flags,prevcond,lastsucc,lastcond,skiprc!=0,nrcond); if(!skiprc) { if(!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC]) lastcond=i==1; /* save the outcome for posterity */ if(!prevcond||!flags[ELSE_DO]) prevcond=i==1; /* ditto for `else if' like constructs */ } } while(i==2); /* missing in action, reiterate */ startchar=themail.p;tobesent=filled; if(flags[PASS_HEAD]) /* body, header or both? */ { if(!flags[PASS_BODY]) tobesent=thebody-themail.p; } else if(flags[PASS_BODY]) tobesent-=(startchar=thebody)-themail.p; Stdout=0;succeed=sh=0; pwait=flags[WAIT_EXIT]|flags[WAIT_EXIT_QUIET]<<1; ignwerr=flags[IGNORE_WRITERR];skipspace(); if(i) zombiecollect(),concon('\n'); progrm: if(testB('!')) /* forward the mail */ { if(!i) skiprc|=1; if(strlcpy(buf,sendmail,linebuf)>=linebuf) goto fail; chp=strchr(buf,'\0'); if(*flagsendmail) { char*q;int got=0; if(!(q=simplesplit(chp+1,flagsendmail,buf+linebuf-1,&got))) goto fail; *(chp=q)='\0'; } if(readparse(chp+1,getb,0,skiprc)) goto fail; if(i) { if(startchar==themail.p) { startchar[filled]='\0'; /* just in case */ startchar=(char*)skipFrom_(startchar,&tobesent); } /* leave off leading From_ -- it confuses some mailers */ goto forward; } skiprc&=~1; } else if(testB('|')) /* pipe the mail */ { chp=buf2; if(getlline(chp,buf2+linebuf)) /* get the command to start */ goto commint; if(i) { metaparse(buf2); if(!sh&&buf+1==Tmnate) /* just a pipe symbol? */ { *buf='|';*(char*)(Tmnate++)='\0'; /* fake it */ goto tostdout; } forward: if(locknext) { if(!tolock) /* an explicit lockfile specified already */ { *buf2='\0'; /* find the implicit lockfile ('>>name') */ for(chp=buf;i= *chp++;) if(i=='>'&&*chp=='>') { chp=skpspace(chp+1); tmemmove(buf2,chp,i=strcspn(chp,EOFName)); buf2[i]='\0'; if(sh) /* expand any environment variables */ { chp=tstrdup(buf);sgetcp=buf2; if(readparse(buf,sgetc,0,0)) { *buf2='\0'; goto nolock; } strcpy(buf2,buf);strcpy(buf,chp);free(chp); } break; } if(!*buf2) nolock: { nlog("Couldn't determine implicit lockfile from"); logqnl(buf); } } lcllock(tolock,buf2); if(!pwait) /* try and protect the user from his */ pwait=2; /* blissful ignorance :-) */ } rawnonl=flags[RAW_NONL]; if(flags[CONTINUE]&&(flags[FILTER]||Stdout)) nlog(extrns),elog("copy-flag"),elog(ignrd); inittmout(buf); if(flags[FILTER]) { if(startchar==themail.p&&tobesent!=filled) /* if only 'h' */ { if(!pipthrough(buf,startchar,tobesent)) readmail(1,tobesent),succeed=!pipw; } else if(!pipthrough(buf,startchar,tobesent)) { filled=startchar-themail.p; readmail(0,tobesent); succeed=!pipw; } } else if(Stdout) /* capturing stdout again? */ succeed=!pipthrough(buf,startchar,tobesent); else if(!pipin(buf,startchar,tobesent,1)) /* regular program */ { succeed=1; if(flags[CONTINUE]) goto logsetlsucc; else goto frmailed; } goto setlsucc; } } else if(testB(EOF)) nlog("Incomplete recipe\n"); else /* dump the mail into a mailbox file or directory */ { int ofiltflag;char*end=buf+linebuf-4; /* reserve some room */ if(ofiltflag=flags[FILTER]) flags[FILTER]=0,nlog(extrns),elog("filter-flag"),elog(ignrd); if(chp=gobenv(buf,end)) /* can it be an environment name? */ { if(chp==end) { getlline(buf,buf+linebuf); goto fail; } if(skipspace()) chp++; /* keep pace with argument breaks */ if(testB('=')) /* is it really an assignment? */ { int c; *chp++='=';*chp='\0'; if(skipspace()) chp++; ungetb(c=getb()); switch(c) { case '!':case '|': /* ok, it's a pipe */ if(i) Stdout = tstrdup(buf); goto progrm; } } } /* find the end, start of a nesting recipe? */ else if((chp=strchr(buf,'\0'))==buf&& testB('{')&& (*chp++='{',*chp='\0',testB(' ')|| /* } } */ testB('\t')|| testB('\n'))) { if(locknext&&!flags[CONTINUE]) nlog(extrns),elog("locallockfile"),elog(ignrd); if(flags[PASS_BODY]) nlog(extrns),elog("deliver-body flag"),elog(ignrd); if(flags[PASS_HEAD]) nlog(extrns),elog("deliver-head flag"),elog(ignrd); if(flags[IGNORE_WRITERR]) nlog(extrns),elog("ignore-write-error flag"),elog(ignrd); if(flags[RAW_NONL]) nlog(extrns),elog("raw-mode flag"),elog(ignrd); if(!i) /* no match? */ skiprc+=2; /* increase the skipping level */ else { app_vali(ifstack,prevcond); /* push prevcond */ app_vali(ifstack,lastcond); /* push lastcond */ if(locknext) { lcllock(tolock,""); if(!pwait) /* try and protect the user from his */ pwait=2; /* blissful ignorance :-) */ } succeed=1; if(flags[CONTINUE]) { yell("Forking",procmailn); private(0); /* can't share anymore */ inittmout(procmailn);onguard(); if(!(pidchild=sfork())) /* clone yourself */ { if(loclock) /* lockfiles are not inherited */ free(loclock),loclock=0; if(globlock) free(globlock),globlock=0; /* clear up the */ newid();offguard();duprcs(); /* identity crisis */ } else { offguard(); if(forkerr(pidchild,procmailn)) succeed=0; /* tsk, tsk, no cloning today */ else { int excode; /* wait for our significant other? */ if(pwait&& (excode=waitfor(pidchild))!=EXIT_SUCCESS) { if(!(pwait&2)||verbose) /* do we report it? */ progerr(procmailn,excode,pwait&2); succeed=0; } pidchild=0;skiprc+=2; /* skip over the braces */ ifstack.filled-=2; /* retract the stack */ } } } goto setlsucc; /* definitely no logabstract */ } continue; } if(!i) /* no match? */ skiprc|=1; /* temporarily disable subprograms */ if(readparse(chp,getb,0,skiprc)) fail: { succeed=0; goto setlsucc; } if(i) { if(ofiltflag) /* protect who use bogus filter-flags */ startchar=themail.p,tobesent=filled; /* whole message */ tostdout: rawnonl=flags[RAW_NONL]; if(locknext) /* write to a file or directory */ lcllock(tolock,buf); inittmout(buf); /* to break messed-up kernel locks */ if(writefolder(buf,strchr(buf,'\0')+1,startchar,tobesent, ignwerr,0)&& (succeed=1,!flags[CONTINUE])) frmailed: { if(ifstack.vals) free(ifstack.vals); return rcs_DELIVERED; } logsetlsucc: if(succeed&&flags[CONTINUE]&&lgabstract==2) logabstract(tgetenv(lastfolder)); setlsucc: rawnonl=0;lastsucc=succeed;lasttell= -1; /* for comsat */ resettmout(); /* clear any pending timer */ } skiprc&=~1; /* reenable subprograms */ } } /* { */ else if(testB('}')) /* end block */ { if(skiprc>1) /* just skipping */ skiprc-=2; /* decrease level */ else if(ifstack.filled>ifdepth) /* restore lastcond from stack */ { lastcond=acc_vali(ifstack,--ifstack.filled); prevcond=acc_vali(ifstack,--ifstack.filled); /* prevcond */ } /* as well */ else nlog("Closing brace unexpected\n"); /* stack empty */ } else /* then it must be an assignment */ { char*end=buf+linebuf; if(!(chp=gobenv(buf,end))) { if(!*buf) /* skip a word first */ getbl(buf,end); /* then a line */ skipped(buf); /* display leftovers */ continue; } if(chp==end) /* overflow => give up */ break; skipspace(); if(testB('=')) /* removal or assignment? */ { *chp='='; if(readparse(++chp,getb,1,skiprc)) continue; } else *++chp='\0'; /* throw in a second terminator */ if(!skiprc) { const char*p; p=sputenv(buf); chp[-1]='\0'; asenv(p); } } if(rc<0) /* abnormal exit from the rcfile? */ return rcs_HOST; } while(!testB(EOF)||(skiprc=0,poprc())); return rcs_EOF; } procmail-3.22/src/lmtp.h0100644006717600001440000000037507316763004014402 0ustar guenthersrc/* $Id: lmtp.h,v 1.4 2001/02/20 09:35:23 guenther Exp $ */ extern int childserverpid; struct auth_identity **lmtp P((struct auth_identity***lrout,char*invoker)); void flushoverread P((void)), freeoverread P((void)), lmtpresponse P((int retcode)); procmail-3.22/src/misc.h0100644006717600001440000000430507317250642014356 0ustar guenthersrc/*$Id: misc.h,v 1.56 2001/06/30 01:14:19 guenther Exp $*/ struct dyna_array{int filled,tspace;char*vals;}; union offori{off_t o;int i;}; #define app_val_type(sp,t,s,v) (*(t*)app_val_(&sp,sizeof(s))=(v)) #define app_valo(sp,val) app_val_type(sp,off_t,union offori,val) #define app_vali(sp,val) app_val_type(sp,int,union offori,val) #define app_vall(sp,val) app_val_type(sp,long,long,val) #define app_valp(sp,val) app_val_type(sp,const char*,const char*,val) #define acc_val_(sp,t,s,off) (*(t*)&(((s*)sp.vals)[off])) /* these are lvalues */ #define acc_valo(sp,off) acc_val_(sp,off_t,union offori,off) #define acc_vali(sp,off) acc_val_(sp,int,union offori,off) #define acc_vall(sp,off) acc_val_(sp,long,long,off) #define acc_valp(sp,off) acc_val_(sp,const char*,const char*,off) struct dynstring{struct dynstring*enext;char ename[255];}; void elog P((const char*const newt)), ignoreterm P((void)), shutdesc P((void)), checkroot P((const int c,const unsigned long Xid)), setids P((void)), writeerr P((const char*const line)), progerr P((const char*const line,int xitcode,int okay)), chderr P((const char*const dir)), readerr P((const char*const file)), verboff P((void)), verbon P((void)), newid P((void)), zombiecollect P((void)), yell P((const char*const a,const char*const b)), nlog P((const char*const a)), logqnl P((const char*const a)), skipped P((const char*const x)), onguard P((void)), offguard P((void)), Terminate P((void)) __attribute__((noreturn)), suspend P((void)), *app_val_ P((struct dyna_array*const sp,int size)), setupsigs P((void)); int forkerr Q((const pid_t pid,const char*const a)), buildpath P((const char*name,const char*const path,const char*const file)), nextrcfile P((void)), enoughprivs Q((const auth_identity*const passinvk,const uid_t euid, const gid_t egid,const uid_t uid,const gid_t gid)), conditions P((char flags[],const int prevcond,const int lastsucc, const int lastcond,const int skipping,int nrcond)); char *tstrdup P((const char*const a)), *cstr P((char*const a,const char*const b)), *egrepin P((char*expr,const char*source,const long len,int casesens)); const char *newdynstring P((struct dynstring**const adrp,const char*const chp)); extern int fakedelivery; procmail-3.22/src/Makefile.00100644006717600001440000001617507316762763015073 0ustar guenthersrc#$Id: Makefile.0,v 1.63 2001/06/21 11:59:25 guenther Exp $ PM_OBJ=cstdio.$(O) common.$(O) exopen.$(O) goodies.$(O) locking.$(O) \ mailfold.$(O) foldinfo.$(O) misc.$(O) pipes.$(O) regexp.$(O) robust.$(O) \ sublib.$(O) acommon.$(O) mcommon.$(O) lastdirsep.$(O) authenticate.$(O) \ lmtp.$(O) memblk.$(O) variables.$(O) from.$(O) comsat.$(O) LF_OBJ=exopen.$(O) sublib.$(O) acommon.$(O) mcommon.$(O) authenticate.$(O) \ lastdirsep.$(O) FM_OBJ=common.$(O) fields.$(O) formisc.$(O) sublib.$(O) ecommon.$(O) \ acommon.$(O) MG_OBJ=sublib.$(O) ecommon.$(O) mcommon.$(O) hsort.$(O) lastdirsep.$(O) all: $(BINSS) make: # fake target @$(SHELL) -c "exit 0" .PRECIOUS: Makefile procmail: procmail.$(O) $(PM_OBJ) setid $(CC) $(CFLAGS) $@.$(O) $(PM_OBJ) -o $@ $(LDFLAGS) @test -z "$(STRIP)" || ( echo $(STRIP) $@; $(STRIP) $@ ) lockfile: lockfile.$(O) $(LF_OBJ) $(CC) $(CFLAGS) $@.$(O) $(LF_OBJ) -o $@ $(LDFLAGS) @test -z "$(STRIP)" || ( echo $(STRIP) $@; $(STRIP) $@ ) formail: formail.$(O) $(FM_OBJ) $(CC) $(CFLAGS) $@.$(O) $(FM_OBJ) -o $@ $(LDFLAGS) @test -z "$(STRIP)" || ( echo $(STRIP) $@; $(STRIP) $@ ) mailstat: ../examples/mailstat cp ../examples/$@ $@ @chmod 0755 $@ multigram: multigram.$(O) $(MG_OBJ) setid $(CC) $(CFLAGS) $@.$(O) $(MG_OBJ) -o $@ $(LDFLAGS) ../config.check: @cd ..; $(MAKE) config.check _autotst: _autotst.$(O) sublib.c sublib.h $(CC) $(CFLAGS) $@.$(O) -o $@ $(LDFLAGS) ../autoconf.h: autoconf Makefile ../patchlevel.h @echo No this was not make -n >make_n $(SHELL) ./autoconf $(SHELL) "$(RM)" "$(MV)" $(DEVNULL) \ "$(FGREP)" "$(MAKE)" $(O) "$(LOCKINGTEST)" \ "$(VISIBLE_BINDIR)" $@ autoconf.h: ../autoconf.h targetdir.h: echo "You only can/need to make multigram if you are installing" echo "the mailinglist scripts. Read ../SmartList/INSTALL for" echo "more directions." exit 64 acommon.$(O): ../autoconf.h ../config.h includes.h acommon.h robust.h shell.h authenticate.$(O): ../autoconf.h ../config.h includes.h robust.h shell.h authenticate.$(O): misc.h authenticate.h authenticate.c $(CC) -c -DPROCMAIL $(CFLAGS) $*.c common.$(O): ../autoconf.h ../config.h includes.h procmail.h sublib.h robust.h common.$(O): shell.h misc.h common.h comsat.$(O): ../autoconf.h ../config.h includes.h procmail.h sublib.h robust.h comsat.$(O): shell.h common.h mailfold.h variables.h comsat.h network.h cstdio.$(O): ../autoconf.h ../config.h includes.h procmail.h robust.h cstdio.h cstdio.$(O): misc.h shell.h variables.h ecommon.$(O): ../autoconf.h ../config.h includes.h ecommon.h common.h shell.h exopen.$(O): ../autoconf.h ../config.h includes.h procmail.h acommon.h robust.h exopen.$(O): misc.h exopen.h fields.$(O): ../autoconf.h ../config.h includes.h formail.h sublib.h shell.h fields.$(O): common.h fields.h ecommon.h formisc.h foldinfo.$(O): ../autoconf.h ../config.h includes.h procmail.h misc.h foldinfo.$(O): lastdirsep.h robust.h exopen.h foldinfo.h formail.$(O): ../autoconf.h ../config.h includes.h formail.h acommon.h sublib.h formail.$(O): shell.h common.h fields.h ecommon.h formisc.h header.h formail.$(O): ../patchlevel.h formisc.$(O): ../autoconf.h ../config.h includes.h formail.h sublib.h shell.h formisc.$(O): common.h ecommon.h formisc.h goodies.$(O): ../autoconf.h ../config.h includes.h procmail.h sublib.h robust.h goodies.$(O): shell.h misc.h pipes.h common.h cstdio.h variables.h goodies.h hsort.$(O): ../autoconf.h ../config.h includes.h hsort.h lastdirsep.$(O): ../autoconf.h ../config.h includes.h lastdirsep.h lmtp.$(O): ../autoconf.h ../config.h includes.h procmail.h sublib.h robust.h lmtp.$(O): shell.h misc.h common.h authenticate.h cstdio.h lmtp.$(O): mailfold.h lmtp.h memblk.h foldinfo.h from.h lockfile.$(O): ../autoconf.h ../config.h includes.h sublib.h exopen.h mcommon.h lockfile.$(O): authenticate.h ../patchlevel.h locking.$(O): ../autoconf.h ../config.h includes.h procmail.h robust.h shell.h locking.$(O): misc.h pipes.h exopen.h locking.h lastdirsep.h mailfold.$(O): ../autoconf.h ../config.h includes.h procmail.h acommon.h mailfold.$(O): sublib.h robust.h shell.h misc.h pipes.h common.h exopen.h mailfold.$(O): variables.h locking.h mailfold.h memblk.h from.h mcommon.$(O): ../autoconf.h ../config.h includes.h mcommon.h memblk.$(O): ../autoconf.h ../config.h includes.h procmail.h shell.h memblk.$(O): exopen.h memblk.h misc.$(O): ../autoconf.h ../config.h includes.h procmail.h misc.$(O): acommon.h sublib.h robust.h shell.h misc.h pipes.h common.h cstdio.h misc.$(O): exopen.h regexp.h mcommon.h goodies.h locking.h comsat.h mailfold.h misc.$(O): lastdirsep.h authenticate.h memblk.h foldinfo.h variables.h multigram.$(O): ../autoconf.h ../config.h includes.h sublib.h hsort.h shell.h multigram.$(O): ecommon.h mcommon.h lastdirsep.h targetdir.h pipes.$(O): ../autoconf.h ../config.h includes.h procmail.h robust.h shell.h pipes.$(O): misc.h pipes.h common.h cstdio.h mcommon.h variables.h memblk.h pipes.$(O): mailfold.h procmail.$(O): ../patchlevel.h ../autoconf.h ../config.h includes.h procmail.h procmail.$(O): acommon.h sublib.h robust.h shell.h misc.h pipes.h common.h procmail.$(O): cstdio.h exopen.h regexp.h mcommon.h goodies.h locking.h procmail.$(O): mailfold.h lastdirsep.h authenticate.h lmtp.h memblk.h procmail.$(O): variables.h comsat.h from.h regexp.$(O): ../autoconf.h ../config.h includes.h procmail.h sublib.h robust.h regexp.$(O): shell.h misc.h regexp.h variables.h robust.$(O): ../autoconf.h ../config.h includes.h procmail.h robust.h shell.h robust.$(O): misc.h pipes.h common.h mailfold.h shell.h sublib.$(O): ../autoconf.h ../config.h includes.h sublib.h shell.h variables.$(O): ../autoconf.h ../config.h includes.h procmail.h shell.h variables.$(O): authenticate.h goodies.h misc.h comsat.h variables.h from.$(O): ../autoconf.h ../config.h includes.h procmail.h robust.h shell.h from.$(O): memblk.h from.h gethome.$(O) setid.$(O) recommend.$(O): ../autoconf.h ../config.h includes.h gethome.$(O): gethome.c @$(CC) -c $(CFLAGS) $*.c setid.$(O): setid.c @$(CC) -c $(CFLAGS) $*.c recommend.$(O): recommend.c @$(CC) -c $(CFLAGS) $*.c .c.$(O): $(CC) -c $(CFLAGS) $< gethome: gethome.$(O) setid @$(CC) $(CFLAGS) $@.$(O) -o $@ $(LDFLAGS) getparams: @echo "ln=\"$(LN)\"" >../SmartList/targetdir.tmp setid: setid.$(O) @$(CC) $(CFLAGS) $@.$(O) -o $@ $(LDFLAGS) recommend: recommend.$(O) sublib.$(O) @$(CC) $(CFLAGS) $@.$(O) sublib.$(O) -o $@ $(LDFLAGS) ../man/man.sed: manconf.c ../autoconf.h ../config.h includes.h procmail.h ../man/man.sed: ../patchlevel.h @$(CC) $(CFLAGS) "-DBINDIR=\"$(VISIBLE_BINDIR)\"" -o _autotst \ manconf.c $(LDFLAGS) @./_autotst $@ @echo Housekeeping file >$@ @$(RM) _autotst clean: $(RM) -r _locktest $(RM) procmail.$(O) $(PM_OBJ) lockfile.$(O) $(LF_OBJ) formail.$O \ $(FM_OBJ) multigram.$(O) $(MG_OBJ) $(BINSS) multigram ../autoconf.h \ _autotst* lookfor _locktst* grepfor recommend recommend.$(O) manconf \ _Makefile lock.log *core* targetdir.h setid setid.$(O) gethome \ gethome.$(O) make_n realloc.log Makefile: ../Makefile Makefile.0 @echo "You have made changes to the master Makefile, in order for" @echo "these changes to show through, you will first have to do:" @echo "$(MAKE) makefiles" makefiles Makefiles makefile: cd ..; $(MAKE) makefiles init: cd ..; $(MAKE) $@ procmail-3.22/src/pipes.c0100644006717600001440000002527607347315004014545 0ustar guenthersrc/************************************************************************ * Routines related to setting up pipes and filters * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 1998-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: pipes.c,v 1.73 2001/08/27 08:43:59 guenther Exp $"; #endif #include "procmail.h" #include "robust.h" #include "shell.h" #include "misc.h" #include "memblk.h" #include "pipes.h" #include "common.h" #include "mcommon.h" #include "foldinfo.h" #include "mailfold.h" #include "goodies.h" #include "variables.h" static const char comma[]=","; pid_t pidchild; volatile time_t alrmtime; volatile int toutflag; static char*lastexec,*backblock; static long backlen; /* length of backblock, filter recovery block */ static pid_t pidfilt; static int pbackfd[2]; /* the emergency backpipe :-) */ int pipw; #define PRDO poutfd[0] #define PWRO poutfd[1] #define PRDI pinfd[0] #define PWRI pinfd[1] #define PRDB pbackfd[0] #define PWRB pbackfd[1] void inittmout(progname)const char*const progname; { lastexec=cstr(lastexec,progname);toutflag=0; alrmtime=timeoutv?time((time_t*)0)+(unsigned)timeoutv:0; alarm((unsigned)timeoutv); } void ftimeout P((void)) { alarm(0);alrmtime=0;toutflag=1;nlog("Timeout, "); /* careful, killing */ elog(pidchild>0&&!kill(pidchild,SIGTERM)?"terminating":"was waiting for"); logqnl(lastexec);signal(SIGALRM,(void(*)())ftimeout); } void resettmout P((void)) { if(alrmtime) /* any need to reset timeout? */ alarm((unsigned)(alrmtime=0)); /* reset timeout */ } static void stermchild P((void)) { static const char rescdata[]="Rescue of unfiltered data "; if(pidfilt>0) /* don't kill what is not ours, we might be root */ kill(pidfilt,SIGTERM); rawnonl=1; /* give back the raw contents */ if(PWRB<0); /* previously unset? */ else if(dump(PWRB,ft_PIPE,backblock,backlen)) /* pump data back via */ nlog(rescdata),elog("failed\n"); /* the backpipe */ else if(verbose||pwait!=4) /* are we not looking the other way? */ nlog(rescdata),elog("succeeded\n"); exit(lexitcode); } static void childsetup P((void)) { lexitcode=EX_UNAVAILABLE;qsignal(SIGTERM,stermchild); qsignal(SIGINT,stermchild);qsignal(SIGHUP,stermchild); qsignal(SIGQUIT,stermchild);shutdesc(); } static void getstdin(pip)const int pip; { rclose(STDIN);rdup(pip);rclose(pip); } static void callnewprog(newname)const char*const newname; { #ifdef RESTRICT_EXEC if(erestrict&&uid>=RESTRICT_EXEC) { syslog(LOG_ERR,slogstr,"Attempt to execute",newname); nlog("No permission to execute");logqnl(newname); return; } #endif if(sh) /* should we start a shell? */ { const char*newargv[4]; yell(executing,newname);newargv[3]=0;newargv[2]=newname; newargv[1]=shellflags;*newargv=tgetenv(shell);shexec(newargv); } ;{ register const char*p;int argc; argc=1;p=newname; /* If no shell, chop up the arguments ourselves */ if(verbose) { nlog(executing);elog(oquote); goto no_1st_comma; } do /* show chopped up command line */ { if(verbose) { elog(comma); no_1st_comma: elog(p); } while(*p++); if(verbose&&p-1==All_args&&crestarg) /* any "$@" found? */ { const char*const*walkargs=restargv; goto No_1st_comma; do { elog(comma); No_1st_comma: elog(*walkargs); /* expand it */ } while(*++walkargs); } if(p-1==All_args) argc+=crestarg-1; /* and account for it */ } while(argc++,p!=Tmnate); if(verbose) elog(cquote); /* allocate argv array */ ;{ const char**newargv; newargv=malloc(argc*sizeof*newargv);p=newname;argc=0; do { newargv[argc++]=p; while(*p++); if(p-1==All_args&&crestarg) { const char*const*walkargs=restargv; /* expand "$@" */ argc--; while(newargv[argc]= *walkargs++) argc++; } } while(p!=Tmnate); newargv[argc]=0;shexec(newargv); } } } int pipthrough(line,source,len)char*line,*source;const long len; { int pinfd[2],poutfd[2];char*eq; if(Stdout) { *(eq=strchr(Stdout,'\0')-1)='\0'; /* chop the '=' */ if(!(backblock=getenv(Stdout))) /* no current value? */ PRDB=PWRB= -1; else { backlen=strlen(backblock); goto pip; } } else pip: rpipe(pbackfd); rpipe(pinfd); /* main pipes setup */ if(!(pidchild=sfork())) /* create a sending procmail */ { if(Stdout&&backblock) backlen=strlen(backblock); else backblock=source,backlen=len; childsetup();rclose(PRDI);rclose(PRDB); rpipe(poutfd);rclose(STDOUT); if(!(pidfilt=sfork())) /* create the filter */ { rclose(PWRB);rclose(PWRO);rdup(PWRI);rclose(PWRI);getstdin(PRDO); callnewprog(line); } rclose(PWRI);rclose(PRDO); if(forkerr(pidfilt,line)) rclose(PWRO),stermchild(); if(dump(PWRO,ft_PIPE,source,len)&&!ignwerr) /* send in the text */ writeerr(line),lexitcode=EX_IOERR,stermchild(); /* to be filtered */ ;{ int excode; /* optionally check the exitcode of the filter */ if(pwait&&(excode=waitfor(pidfilt))!=EXIT_SUCCESS) { pidfilt=0; if(pwait&2) /* do we put it on report? */ { pwait=4; /* no, we'll look the other way */ if(verbose) goto perr; } else perr: progerr(line,excode,pwait==4); /* I'm going to tell my mommy! */ stermchild(); } } rclose(PWRB);exit(EXIT_SUCCESS); /* allow parent to proceed */ } rclose(PWRB);rclose(PWRI);getstdin(PRDI); if(forkerr(pidchild,procmailn)) return -1; if(Stdout) { char*name;memblk temp; /* ick. This is inefficient XXX */ *eq='=';name=Stdout;Stdout=0;primeStdout(name);free(name); makeblock(&temp,Stdfilled); tmemmove(temp.p,Stdout,Stdfilled); readdyn(&temp,&Stdfilled,Stdfilled+backlen+1); Stdout=realloc(Stdout,&Stdfilled+1); tmemmove(Stdout,temp.p,Stdfilled+1); freeblock(&temp); retStdout(Stdout,pwait&&pipw,!backblock); return pipw; } return 0; /* we stay behind to read back the filtered text */ } long pipin(line,source,len,asgnlastf)char*const line;char*source;long len; int asgnlastf; { int poutfd[2]; #if 0 /* not supported (yet?) */ if(!sh) /* shortcut builtin commands */ { const char*t1,*t2,*t3; static const char pbuiltin[]="Builtin"; t1=strchr(line,'\0')+1; if(!strcmp(test,line)) { if(t1!=Tmnate) { t2=strchr(t1,'\0')+1; if(t2!=Tmnate) { t3=strchr(t2,'\0')+1; if(t3!=Tmnate&&!strcmp(t2,"=")&&strchr(t3,'\0')==Tmnate-1) { int excode; if(verbose) { nlog(pbuiltin);elog(oquote);elog(test);elog(comma), if(!ignwerr) writeerr(line); else len=0; if(pwait&&(excode=strcmp(t1,t3)?1:EXIT_SUCCESS)!=EXIT_SUCCESS) { if(!(pwait&2)||verbose) /* do we put it on report? */ progerr(line,excode,pwait&2); len=1; } goto builtin; } } } } } #endif rpipe(poutfd); if(!(pidchild=sfork())) /* spawn program */ rclose(PWRO),shutdesc(),getstdin(PRDO),callnewprog(line); rclose(PRDO); if(forkerr(pidchild,line)) { rclose(PWRO); return -1; /* dump mail in the pipe */ } if((len=dump(PWRO,ft_PIPE,source,len))&&(!ignwerr||(len=0))) writeerr(line); /* pipe was shut in our face, get mad */ ;{ int excode; /* optionally check the exitcode */ if(pwait&&(excode=waitfor(pidchild))!=EXIT_SUCCESS) { if(!(pwait&2)||verbose) /* do we put it on report? */ progerr(line,excode,pwait&2); len=1; } } pidchild=0; builtin: if(!sh) concatenate(line); if(asgnlastf) setlastfolder(line); return len; } static char*read_read(p,left,data)char*p;long left;void*data; { long got; do if(0>=(got=rread(STDIN,p,left))) /* read mail */ return p; while(p+=got,left-=got); /* change listed buffer size */ return 0; } static int read_cleanup(mb,filledp,origfilled,data)memblk*mb; long*filledp,origfilled;void*data; { long oldfilled= *(long*)data; if(pidchild>0) { if(PRDB>=0) { getstdin(PRDB); /* filter ready, get backpipe */ if(1==rread(STDIN,buf,1)) /* backup pipe closed? */ { resizeblock(mb,oldfilled,0); mb->p[origfilled]= *buf; *filledp=origfilled+1; PRDB= -1;pwait=2; /* break loop, definitely reap */ return 1; /* filter goofed, rescue data */ } } if(pwait) pipw=waitfor(pidchild); /* reap your child in any case */ } pidchild=0; /* child must be gone by now */ return 0; } char*readdyn(mb,filled,oldfilled)memblk*const mb;long*const filled,oldfilled; { return read2blk(mb,filled,&read_read,&read_cleanup,&oldfilled); } char*fromprog(name,dest,max)char*name;char*const dest;size_t max; { int pinfd[2],poutfd[2];int i;char*p; concon('\n');rpipe(pinfd);inittmout(name); if(!(pidchild=sfork())) /* create a sending procmail */ { Stdout=name;childsetup();rclose(PRDI);rpipe(poutfd);rclose(STDOUT); if(!(pidfilt=sfork())) /* spawn program/filter */ rclose(PWRO),rdup(PWRI),rclose(PWRI),getstdin(PRDO),callnewprog(name); rclose(PWRI);rclose(PRDO); if(forkerr(pidfilt,name)) rclose(PWRO),stermchild(); dump(PWRO,ft_PIPE,themail.p,filled);waitfor(pidfilt);exit(lexitcode); } rclose(PWRI);p=dest; if(!forkerr(pidchild,name)) { name=tstrdup(name); while(0<(i=rread(PRDI,p,(int)max))&&(p+=i,max-=i)); /* read its lips */ if(0=dest&&*p=='\n'); /* trailing newlines should be discarded */ rclose(PRDI);free(name); p++;waitfor(pidchild); } else rclose(PRDI); resettmout(); pidchild=0;*p='\0'; return p; } void exectrap(tp)const char*const tp; { int forceret; forceret=setexitcode(*tp); /* whether TRAP is set matters */ if(*tp) { int poutfd[2]; rawnonl=0; /* force a trailing newline */ metaparse(tp);concon('\n'); /* expand $TRAP */ rpipe(poutfd);inittmout(buf); if(!(pidchild=sfork())) /* connect stdout to stderr before exec */ { rclose(PWRO);getstdin(PRDO);rclose(STDOUT);rdup(STDERR); callnewprog(buf); /* trap your heart out */ } rclose(PRDO); /* neat & clean */ if(!forkerr(pidchild,buf)) { int newret; dump(PWRO,ft_PIPE,themail.p,filled); /* try and shove down the mail */ if((newret=waitfor(pidchild))!=EXIT_SUCCESS&&forceret==-2) retval=newret; /* supersede the return value */ pidchild=0; } else rclose(PWRO); resettmout(); } } procmail-3.22/src/lockfile.c0100644006717600001440000002163307347314734015217 0ustar guenthersrc/************************************************************************ * lockfile - The conditional semaphore-file creator * * * * It has been designed to be able to be run sgid mail or * * any gid you see fit (in case your mail spool area is *not* * * world writable, but group writable), without creating * * security holes. * * * * Seems to be relatively bug free. * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: lockfile.c,v 1.49 2001/08/04 07:12:16 guenther Exp $"; #endif static /*const*/char rcsdate[]="$Date: 2001/08/04 07:12:16 $"; #include "includes.h" #include "sublib.h" #include "exopen.h" #include "mcommon.h" #include "authenticate.h" #include "lastdirsep.h" #include "../patchlevel.h" static volatile int exitflag; pid_t thepid; uid_t uid; gid_t sgid; const char dirsep[]=DIRSEP; static const char lockext[]=DEFlockext, nameprefix[]="lockfile: ",lgname[]="LOGNAME"; static void failure P((void)) /* signal trap */ { exitflag=2; /* merely sets a flag */ } /* see locking.c for comment on xcreat() */ static int xcreat(name,tim)const char*const name;time_t*const tim; { char*p;int j= -1;size_t i;struct stat stbuf; i=lastdirsep(name)-name; if(!(p=malloc(i+UNIQnamelen))) return exitflag=1; memcpy(p,name,i); if(unique(p,p+i,i+UNIQnamelen,LOCKperm,0,doCHECK|doLOCK)) stat(p,&stbuf),*tim=stbuf.st_mtime,j=myrename(p,name); free(p); return j; } void elog(a)const char*const a; { write(STDERR,a,strlen(a)); } void nlog(a)const char*const a; { elog(nameprefix);elog(a); /* decent error messages should start with this */ } static PROGID; int main(argc,argv)int argc;const char*const argv[]; { const char*const*p;char*cp;uid_t uid; int sleepsec,retries,invert,force,suspend,retval=EXIT_SUCCESS,virgin=1; static const char usage[]="Usage: lockfile -v | -nnn | -r nnn | -l nnn \ | -s nnn | -! | -ml | -mu | file ...\n"; if(argc<=1) /* sanity check, any argument at all? */ goto usg; sleepsec=DEFlocksleep;retries= -1;suspend=DEFsuspend;thepid=getpid();force=0; uid=getuid();signal(SIGPIPE,SIG_IGN); if(setuid(uid)||geteuid()!=uid) /* resist setuid operation */ sp:{ nlog("Unable to give up special permissions"); return EX_OSERR; } again: invert=(char*)progid-(char*)progid;qsignal(SIGHUP,failure); qsignal(SIGINT,failure);qsignal(SIGQUIT,failure);qsignal(SIGTERM,failure); for(p=argv;--argc>0;) if(*(cp=(char*)*++p)=='-') for(cp++;;) { char*cp2=cp;int i; switch(*cp++) { case '!':invert^=1; /* invert the exitcode */ continue; case 'r':case 'l':case 's': if(!*cp&&(cp=(char*)*++p,!--argc)) /* concatenated/seperate */ { p--; goto eusg; } i=strtol(cp,&cp,10); switch(*cp2) { case 'r':retries=i; goto checkrdec; case 'l':force=i; goto checkrdec; default: if(i<0) /* suspend should be >=0 */ goto eusg; suspend=i; goto checkrdec; } case VERSIONOPT:elog("lockfile");elog(VERSION); elog("\nYour system mailbox's lockfile:\t"); elog(auth_mailboxname(auth_finduid(getuid(),0))); elog(lockext);elog("\n"); goto xusg; case HELPOPT1:case HELPOPT2:elog(usage); elog( "\t-v\tdisplay the version number and exit\ \n\t-nnn\twait nnn seconds between locking attempts\ \n\t-r nnn\tmake at most nnn retries before giving up on a lock\ \n\t-l nnn\tset locktimeout to nnn seconds\ \n\t-s nnn\tsuspend nnn seconds after a locktimeout occurred\ \n\t-!\tinvert the exitcode of lockfile\ \n\t-ml\tlock your system mail-spool file\ \n\t-mu\tunlock your system mail-spool file\n"); goto xusg; default: if(sleepsec>=0) /* is this still the first pass? */ { if((sleepsec=strtol(cp2,&cp,10))<0) goto eusg; checkrdec: if(cp2==cp) eusg: { elog(usage); /* regular usage message */ xusg: retval=EX_USAGE; goto nfailure; } } else /* no second pass, so leave sleepsec<0 */ strtol(cp2,&cp,10); /* and discard the number */ continue; case 'm': /* take $LOGNAME as a hint, check if valid */ { auth_identity*pass;static char*ma; if(*cp&&cp[1]||ma&&sleepsec>=0) /* second pass? */ goto eusg; if(!ma) /* ma initialised last time? */ { if(!((ma=(char*)getenv(lgname))&& (pass=auth_finduser(ma,0))&& auth_whatuid(pass)==uid|| (pass=auth_finduid(uid,0)))) { nlog("Can't determine your mailbox, who are you?\n"); goto nfailure; /* panic, you're not in /etc/passwd */ } ;{ const char*p; if(!*(p=auth_mailboxname(pass))|| !(ma=malloc(strlen(p)+STRLEN(lockext)+1))) goto outofmem; strcat(strcpy(ma,p),lockext); } } switch(*cp) { default: goto eusg; /* somebody goofed again */ case 'l': /* lock the mailbox */ if(sleepsec>=0) /* first pass? */ { cp=ma; goto stilv; /* yes, lock it! */ } case 'u': /* unlock the mailbox */ if(unlink(ma)) { nlog("Can't unlock \"");elog(ma);elog("\""); if(*cp=='l') /* they messed up, give them a hint */ elog(" again,\n already dropped my privileges"); elog("\n"); } else virgin=0; } } case '\0':; } break; } else if(sleepsec<0) /* second pass, release everything we acquired */ unlink(cp); else { time_t t;int permanent;gid_t gid=getgid(); if(setgid(gid)||getegid()!=gid) /* just to be on the safe side */ goto sp; stilv: virgin=0;permanent=nfsTRY; while(0>xcreat(cp,&t)) /* try and lock */ { struct stat stbuf; if(exitflag) /* time to stop? */ { if(exitflag==1) /* was it failure() or malloc() */ outofmem: retval=EX_OSERR,nlog("Out of memory"); else retval=EX_TEMPFAIL,nlog("Signal received"); goto lfailure; } switch(errno) /* why did the lock not succeed? */ { case EEXIST: /* hmmm..., by force then? */ if(force&&!lstat(cp,&stbuf)&&force=i|| /* either we have enough room in the buffer or */ (MAXlogbuf>=i&& /* the buffer won't get too big and */ !nextexit))) /* we're not in a signal handler, then it's safe */ { if(i>lmax) /* to use or expand the buffer */ { char*p;size_t newmax=lmax*2; /* exponential expansion by default */ if(i>newmax) /* ...unless we need more */ newmax=i; if(MINlogbuf>newmax) /* ...or that would be too small */ newmax=MINlogbuf; lcking|=lck_LOGGING; /* about to change old or lmax */ if(p=lmax?frealloc(old,newmax):fmalloc(newmax))/* fragile allocation */ lmax=newmax,old=p; /* update the values */ lcking&=~lck_LOGGING; /* okay, they're stable again */ if(!p) /* couldn't expand the buffer? */ goto flush; /* then flush it now */ } /* okay, we now have enough buffer space */ tmemmove(old+lold,newt,lnew); /* append the new text */ lnew=0;lold=i; /* the text in newt is now in the buffer */ if(old[i-1]!='\n') /* if we don't need to flush now */ return; /* then we're done */ } flush: #ifndef O_CREAT lseek(STDERR,(off_t)0,SEEK_END); /* locking should be done actually */ #endif if(lold) /* anything buffered? */ { rwrite(STDERR,old,lold); lold=0; /* we never free the buffer */ } if(lnew) rwrite(STDERR,newt,lnew); } void ignoreterm P((void)) { signal(SIGTERM,SIG_IGN);signal(SIGHUP,SIG_IGN);signal(SIGINT,SIG_IGN); signal(SIGQUIT,SIG_IGN); } void shutdesc P((void)) { rclose(savstdout);closelog();closerc(); } /* On systems with `capabilities', setuid/setgid can fail for root! */ void checkroot(c,Xid)const int c;const unsigned long Xid; { uid_t eff; if((eff=geteuid())!=ROOT_uid&&getuid()!=ROOT_uid) return; syslog(LOG_CRIT,"set%cid(%lu) failed with ruid/euid = %lu/%lu",c,Xid, (unsigned long)getuid(),(unsigned long)eff); nlog(insufprivs); exit(EX_NOPERM); } void setids P((void)) { if(privileged) { if(setRgid(gid)&& /* due to these !@#$%^&*() POSIX semantics, setgid() */ setgid(gid)) /* sets the saved gid as well; we can't use that! */ checkroot('g',(unsigned long)gid); /* did setgid fail as root? */ setruid(uid); if(setuid(uid)) /* "This cannot happen" */ checkroot('u',(unsigned long)uid); /* Whoops... */ setegid(gid); privileged=0; #if !DEFverbose verbose=0; /* to avoid peeking in rcfiles using SIGUSR1 */ #endif } } void writeerr(line)const char*const line; { nlog(errwwriting);logqnl(line); } int forkerr(pid,a)const pid_t pid;const char*const a; { if(pid==-1) { nlog("Failed forking");logqnl(a); return 1; } return 0; } void progerr(line,xitcode,okay)const char*const line;int xitcode,okay; { charNUM(num,thepid); nlog(okay?"Non-zero exitcode (":"Program failure ("); ltstr(0,(long)xitcode,num);elog(num);elog(okay?") from":") of"); logqnl(line); } void chderr(dir)const char*const dir; { nlog("Couldn't chdir to");logqnl(dir); } void readerr(file)const char*const file; { nlog("Couldn't read");logqnl(file); } int buildpath(name,path,file)const char*name,*const path,*const file; { static const char toolong[]=" path too long", notabsolute[]=" is not an absolute path"; sgetcp=path; if(readparse(buf,sgetc,2,0)) { syslog(LOG_CRIT,"%s%s for LINEBUF for uid \"%lu\"\n",name,toolong, (unsigned long)uid); bad: nlog(name);elog(toolong);elog(newline); return 1; } if(!strchr(dirsep,buf[0])) { nlog(name);elog(notabsolute);elog(newline); syslog(LOG_CRIT,"%s%s for uid \"%lu\"\n",name,notabsolute, (unsigned long)uid); return 1; } if(file) { char*chp=strchr(buf,'\0'); if(chp-buf+strlen(file)+2>linebuf) /* +2 for / and \0 */ { name="full rcfile"; /* this should be passed in... XXX */ goto bad; } *chp++= *MCDIRSEP_; strcpy(chp,file); /* append the filename */ } return 0; } void verboff P((void)) { verbose=0; #ifdef SIGUSR1 qsignal(SIGUSR1,verboff); #endif } void verbon P((void)) { verbose=1; #ifdef SIGUSR2 qsignal(SIGUSR2,verbon); #endif } void yell(a,b)const char*const a,*const b; /* log if VERBOSE=on */ { if(verbose) nlog(a),logqnl(b); } static time_t oldtime; void newid P((void)) { thepid=getpid();oldtime=0; } void zombiecollect P((void)) { while(waitpid((pid_t)-1,(int*)0,WNOHANG)>0); /* collect any zombies */ } void nlog(a)const char*const a; { time_t newtime; static const char colnsp[]=": "; elog(procmailn);elog(colnsp); if(verbose&&!nextexit&&oldtime!=(newtime=time((time_t*)0))) /* don't call */ { charNUM(num,thepid); /* ctime from a sighandler */ elog("[");oldtime=newtime;ultstr(0,(unsigned long)thepid,num);elog(num); elog("] ");elog(ctime(&oldtime));elog(procmailn);elog(colnsp); } elog(a); } void logqnl(a)const char*const a; { elog(oquote);elog(a);elog(cquote); } void skipped(x)const char*const x; { if(*x) nlog("Skipped"),logqnl(x); } int nextrcfile P((void)) /* next rcfile specified on the command line */ { const char*p;int rval=2; while(p= *gargv) { gargv++; if(!strchr(p,'=')) { if(strlen(p)>linebuf-1) { nlog("Excessively long rcfile path skipped\n"); continue; } rcfile=p; return rval; } rval=1; /* not the first argument encountered */ } return 0; } char*tstrdup(a)const char*const a; { int i; i=strlen(a)+1; return tmemmove(malloc(i),a,i); } char*cstr(a,b)char*const a;const char*const b; /* dynamic buffer management */ { if(a) free(a); return tstrdup(b); } void onguard P((void)) { lcking|=lck_DELAYSIG; } void offguard P((void)) { lcking&=~lck_DELAYSIG; if(nextexit==1) /* make sure we are not inside Terminate() already */ elog(newline),Terminate(); } static void sterminate P((void)) { static const char*const msg[]={"memory","fork", /* crosscheck with */ "a file descriptor","a kernel-lock"}; /* lck_ defs in procmail.h */ ignoreterm(); if(pidchild>0) /* don't kill what is not ours, we might be root */ kill(pidchild,SIGTERM); if(!nextexit) { nextexit=1;nlog("Terminating prematurely"); if(!(lcking&lck_DELAYSIG)) { register unsigned i,j; if(i=(lcking&~lck__NOMSG)>>1) { elog(whilstwfor); for(j=0;!((i>>=1)&1);j++); elog(msg[j]); } elog(newline);Terminate(); } } } int fakedelivery; void Terminate P((void)) { ignoreterm(); if(getpid()==thepid) { const char*lstfolder; if(retval!=EXIT_SUCCESS) { lasttell= -1; /* mark it for sendcomsat */ lstfolder=fakedelivery?"**Lost**": retval==EX_TEMPFAIL?"**Requeued**":"**Bounced**"; sendcomsat(lstfolder); } else { lstfolder=tgetenv(lastfolder); sendcomsat(0); } logabstract(lstfolder); if(!nextexit) /* these are unsafe from sighandlers */ { shutdesc(); exectrap(traps); fdunlock(); } nextexit=2;unlock(&loclock);unlock(&globlock); } /* flush the logfile & exit procmail */ elog(empty); _exit(retvl2!=EXIT_SUCCESS?retvl2: /* unsuccessful child? */ fakedelivery==2?EXIT_SUCCESS: /* told to throw it away? */ retval); /* okay, use the real status */ } void suspend P((void)) { ssleep((unsigned)suspendv); } static void srequeue P((void)) { retval=EX_TEMPFAIL;sterminate(); } static void slose P((void)) { fakedelivery=2;sterminate(); } static void sbounce P((void)) { retval=EX_CANTCREAT;sterminate(); } void setupsigs P((void)) { qsignal(SIGTERM,srequeue);qsignal(SIGINT,sbounce); qsignal(SIGHUP,sbounce);qsignal(SIGQUIT,slose); signal(SIGALRM,(void(*)())ftimeout); } static void squeeze(target)char*target; { int state;char*src; for(state=0,src=target;;target++,src++) { switch(*target= *src) { case '\n': if(state==1) target-=2; /* throw out \ \n pairs */ state=2; continue; case '\\':state=1; continue; case ' ':case '\t': if(state==2) /* skip leading */ { target--; /* whitespace */ continue; } default:state=0; continue; case '\0':; } break; } } char*egrepin(expr,source,len,casesens)char*expr;const char*source; const long len;int casesens; { if(*expr) /* only do the search if the expression is nonempty */ { source=(const char*)bregexec((struct eps*)(expr=(char*) bregcomp(expr,!casesens)),(const uchar*)source,(const uchar*)source, len>0?(size_t)len:(size_t)0,!casesens); free(expr); } return (char*)source; } int enoughprivs(passinvk,euid,egid,uid,gid)const auth_identity*const passinvk; const uid_t euid,uid;const gid_t egid,gid; { return euid==ROOT_uid|| passinvk&&auth_whatuid(passinvk)==uid|| euid==uid&&egid==gid; } const char*newdynstring(adrp,chp)struct dynstring**const adrp; const char*const chp; { struct dynstring*curr;size_t len; curr=malloc(ioffsetof(struct dynstring,ename[0])+(len=strlen(chp)+1)); tmemmove(curr->ename,chp,len);curr->enext= *adrp;*adrp=curr; return curr->ename; } void*app_val_(sp,size)struct dyna_array*const sp;int size; { if(sp->filled==sp->tspace) /* growth limit reached? */ { size_t len=(sp->tspace+=4)*size; sp->vals=sp->vals?realloc(sp->vals,len):malloc(len); /* expand */ } return &sp->vals[size*sp->filled++]; /* append to it */ } /* lifted out of main() to reduce main()'s size */ int conditions(flags,prevcond,lastsucc,lastcond,skipping,nrcond)char flags[]; const int prevcond,lastsucc,lastcond,skipping;int nrcond; { char*chp,*chp2,*startchar;double score;int scored,i,skippedempty; long tobesent;static const char suppressed[]=" suppressed\n"; score=scored=0; if(nrcond<0) /* assume appropriate default nr of conditions */ nrcond=!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC]&&!flags[ELSE_DO]&& !flags[ERROR_DO]; startchar=themail.p;tobesent=thebody-themail.p; if(flags[BODY_GREP]) /* what needs to be egrepped? */ if(flags[HEAD_GREP]) tobesent=filled; else { startchar=thebody;tobesent=filled-tobesent; goto noconcat; } if(!skipping) concon(' '); noconcat: i=!skipping; /* init test value */ if(flags[ERROR_DO]) { i&=prevcond&&!lastsucc; if(flags[ELSE_DO]) nlog(conflicting),elog("else-if-flag"),elog(suppressed), flags[ELSE_DO]=0; if(flags[ALSO_N_IF_SUCC]) nlog(conflicting),elog("also-if-succeeded-flag"),elog(suppressed), flags[ALSO_N_IF_SUCC]=0; } if(flags[ELSE_DO]) i&=!prevcond; if(flags[ALSO_N_IF_SUCC]) i&=lastcond&&lastsucc; if(flags[ALSO_NEXT_RECIPE]) i=i&&lastcond; for(skippedempty=0;;) { skipspace();--nrcond; if(!testB('*')) /* marks a condition, new syntax */ if(nrcond<0) /* keep counting, for backward compatibility */ { if(testB('#')) /* line starts with a comment? */ { skipline(); /* skip the line */ continue; } if(testB('\n')) /* skip empty lines */ { skippedempty=1; /* make a note of this fact */ continue; } if(skippedempty&&testB(':')) { nlog("Missing action\n");i=2; goto ret; } break; /* no more conditions, time for action! */ } skipspace(); if(getlline(buf2,buf2+linebuf)) i=0; /* assume failure on overflow */ if(i) /* check out all conditions */ { int negate,scoreany;double weight,xponent,lscore; char*lstartchar=startchar;long ltobesent=tobesent,sizecheck=filled; for(chp=strchr(buf2,'\0');--chp>=buf2;) { switch(*chp) /* strip off whitespace at the end */ { case ' ':case '\t':*chp='\0'; continue; } break; } negate=scoreany=0;lscore=score; for(chp=buf2+1;;strcpy(buf2,buf)) copydone: { switch(*(sgetcp=buf2)) { case '0':case '1':case '2':case '3':case '4':case '5':case '6': case '7':case '8':case '9':case '-':case '+':case '.':case ',': { char*chp3;double w; w=strtod(buf2,&chp3);chp2=chp3; if(chp2>buf2&&*(chp2=skpspace(chp2))=='^') { double x; x=strtod(chp2+1,&chp3); if(chp3>chp2+1) { if(score>=MAX32) goto skiptrue; xponent=x;weight=w;scored=scoreany=1; chp2=skpspace(chp3); goto copyrest; } } chp--; goto normalregexp; } default:chp--; /* no special character, backup */ { if(alphanum(*(chp2=chp))==1) { char*chp3; while(alphanum(*++chp2)); if(!strncmp(chp3=skpspace(chp2),"??",2)) { *chp2='\0';lstartchar=themail.p; if(!chp[1]) { ltobesent=thebody-themail.p; switch(*chp) { case 'B':lstartchar=thebody; ltobesent=filled-ltobesent; goto partition; case 'H': goto docon; } } else if(!strcmp("HB",chp)|| !strcmp("BH",chp)) { ltobesent=filled; docon: concon(' '); goto partition; } ltobesent=strlen(lstartchar=(char*)tgetenv(chp)); partition: chp2=skpspace(chp3+2);chp++;sizecheck=ltobesent; goto copyrest; } } } case '\\': normalregexp: { int or_nocase; /* case-distinction override */ static const struct {const char*regkey,*regsubst;} *regsp,regs[]= { {FROMDkey,FROMDsubstitute}, {TO_key,TO_substitute}, {TOkey,TOsubstitute}, {FROMMkey,FROMMsubstitute}, {0,0} }; squeeze(chp);or_nocase=0; goto jinregs; do /* find special keyword in regexp */ if((chp2=strstr(chp,regsp->regkey))&& (chp2==buf2||chp2[-1]!='\\')) /* escaped? */ { size_t lregs,lregk; /* no, so */ lregk=strlen(regsp->regkey); /* insert it */ tmemmove(chp2+(lregs=strlen(regsp->regsubst)), chp2+lregk,strlen(chp2)-lregk+1); tmemmove(chp2,regsp->regsubst,lregs); if(regsp==regs) /* daemon regexp? */ or_nocase=1; /* no case sensitivity! */ jinregs: regsp=regs; /* start over and look again */ } else regsp++; /* next keyword */ while(regsp->regkey); ;{ int igncase; igncase=or_nocase||!flags[DISTINGUISH_CASE]; if(scoreany) { struct eps*re; re=bregcomp(chp,igncase);chp=lstartchar; if(negate) { if(weight&&!bregexec(re,(const uchar*)chp, (const uchar*)chp,(size_t)ltobesent,igncase)) score+=weight; } else { double oweight=weight*weight; while(weight!=0&& MIN32=0&& (chp2=bregexec(re,(const uchar*)lstartchar, (const uchar*)chp,(size_t)ltobesent, igncase))) { score+=weight;weight*=xponent; if(chp>=chp2) /* break off empty */ { if(0=1&&weight!=0) score+=weight<0?MIN32:MAX32; break; /* matches early */ } ;{ volatile double nweight=weight*weight; if(nweight=0) { int j=lexitcode; if(negate) while(--j>=0&&(score+=weight)MIN32) weight*=xponent; else score+=j?xponent:weight; } else if(!!lexitcode^negate) i=0; strcpy(buf2,buf); break; case '>':case '<': { long pivot; if(readparse(buf,sgetc,2,0)&&(i=0,1)) break; ;{ char*chp3; pivot=strtol(buf+1,&chp3,10);chp=chp3; } skipped(skpspace(chp));strcpy(buf2,buf); if(scoreany) { double f; if((*buf=='<')^negate) if(sizecheck) f=(double)pivot/sizecheck; else if(pivot>0) goto plusinfty; else goto mininfty; else if(pivot) f=(double)sizecheck/pivot; else goto plusinfty; score+=weight*tpow(f,xponent); } else if(!((*buf=='<'?sizecheckpivot)^ negate)) i=0; } } break; } if(score>MAX32) /* chop off at plus infinity */ plusinfty: score=MAX32; if(score<=MIN32) /* chop off at minus infinity */ mininfty: score=MIN32,i=0; if(verbose) { if(scoreany) /* not entirely correct, but it will do */ { charNUM(num,long); nlog("Score: ");ltstr(7,(long)(score-lscore),num); elog(num);elog(" "); ;{ long iscore=score; ltstr(7,iscore,num); if(!iscore&&score>0) num[7-2]='+'; /* show +0 for (0,1) */ } elog(num); } else nlog(i?"M":"No m"),elog("atch on"); if(negate) elog(" !"); logqnl(buf2); } skiptrue:; } } if(!(lastscore=score)&&score>0) /* save it for $= */ lastscore=1; /* round up +0 to 1 */ if(scored&&i&&score<=0) i=0; /* it was not a success */ ret: return i; } procmail-3.22/src/manconf.c0100644006717600001440000003133107347314772015046 0ustar guenthersrc/* A sed script generator (for transmogrifying the man pages automagically) */ /*$Id: manconf.c,v 1.73 2001/08/27 08:43:58 guenther Exp $*/ #include "../patchlevel.h" #include "procmail.h" #define pn(name,val) pnr(name,(long)(val)) static char pm_version[]=VERSION,ffileno[]=DEFfileno; const char offvalue[]="no",empty[]=""; static int lines; const char dirsep[]=DIRSEP; static const char*const keepenv[]=KEEPENV,*const prestenv[]=PRESTENV, *const trusted_ids[]=TRUSTED_IDS,*const etcrc=ETCRC, *const krnllocks[]={ #ifndef NOfcntl_lock "\1.BR fcntl (2)", #endif #ifdef USElockf "\1.BR lockf (3)", #endif #ifdef USEflock "\1.BR flock (2)", #endif 0}; static char*skltmark(nl,current)int nl;char**current; { char*from= *current,*p; while(nl--) /* skip some newlines first */ from=strchr(from,'\n')+1; while(*from=='\t') from++; *(p=strchr(from,'\n'))='\0';*current=p+1; return from; } static void putcesc(i)int i; { switch(i) { case '\\':i='e'; goto twoesc; case '\1':i='\n';lines--; /* \1 doubles for nroff newlines */ goto singesc; case '\2':i='\\'; /* \2 doubles for nroff backslashes */ goto singesc; case '\t':i='t'; goto fin; case '\n':i='n';lines--; fin: putchar('\\');putchar('\\'); twoesc: putchar('\\'); singesc: case '&':case '/':putchar('\\'); } putchar(i); } static void putsesc(a)const char*a; { int c,k; for(c=0;;putcesc(c=k)) switch(k= *a++) { case '\0': return; case '|':case ':': if(c!=' ') /* only insert these if there wasn't a space before */ printf("\\\\h'-\\\\w' 'u' "); /* breaking nospace */ } } const char*const*gargv; static void pname(name,supps)const char*const name;int supps; { static unsigned filecount; static char*filebuf; if(!filebuf&&!(filebuf=malloc(strlen(*gargv)+1+4+1))) exit(EX_OSERR); if(lines<=0) { sprintf(filebuf,"%s.%04d",*gargv,filecount++);freopen(filebuf,"w",stdout); lines=64; /* POSIX says commands are limited */ } /* some !@#$%^&*() implementations limit lines instead */ if(!supps)putchar('s'); putchar('/');putchar('@');putsesc(name);putchar('@');putchar('/'); } static void pnr(name,value)const char*const name;const long value; { pname(name,0);printf("%s%ld/g\n",value<0?"\\":"",value);lines--; } static void putsg P((void)) { puts("/g");lines--; } static void plist(name,preamble,list,postamble,ifno,andor) const char*const name,*const preamble,*const postamble,*const ifno, *const andor;const char*const*list; { pname(name,0); if(!*list) putsesc(ifno); else { putsesc(preamble); goto jin; do { putsesc(list[1]?", ":andor); jin: putsesc(*list); } while(*++list); putsesc(postamble); } putsg(); } static void ps(name,value)const char*const name,*const value; { pname(name,0);putsesc(value);putsg(); } static void pc(name,value)const char*const name;const int value; { pname(name,0);putcesc(value);putsg(); } int main(argc,argv)int argc;const char*const argv[]; { char*p; gargv=argv+1; #ifdef CF_no_procmail_yet ps("CF_procmail","If procmail is\1\ .I not\1\ installed globally as the default mail delivery agent (ask your system \ administrator), you have to make sure it is invoked when your mail arrives."); #else ps("CF_procmail","Instead of using the system provided invocation of \ procmail when mail arrives, you can control the invocation of procmail \ yourself."); #endif #ifndef MAILBOX_SEPARATOR ps("DOT_FORWARD",".forward"); #ifdef buggy_SENDMAIL ps("FW_content","\"|IFS=' '&&p=@BINDIR@/procmail&&test -f $p&&exec $p -Yf-||\ exit 75 \2fB#\2fP\2fIYOUR_USERNAME\2fP\""); ps("FW_comment","The \\fB#\\fP\\fIYOUR_USERNAME\\fP is not actually a\1\ parameter that is required by procmail, in fact, it will be discarded by\1\ sh before procmail ever sees it; it is however a necessary kludge against\1\ overoptimising sendmail programs:\1"); #else ps("FW_content","\"|exec @BINDIR@/procmail\""); ps("FW_comment",""); #endif #else ps("DOT_FORWARD",".maildelivery"); ps("FW_content","* - | ? \"IFS=' '&&p=@BINDIR@/procmail&&test -f $p&&\ exec $p||exit 75 \2fB#\2fP\2fIYOUR_USERNAME\2fP\""); #endif plist("PRESTENV", "\1.na\1.PP\1Other cleared or preset environment variables are ", prestenv,".\1.ad",""," and "); plist("KEEPENV",", except for the value of ",keepenv,"",""," and "); plist("TRUSTED_IDS", " If procmail is not invoked with one of the following user or group ids: ", trusted_ids,", but still has to generate or accept a new `@FROM@' line,\1\ it will generate an additional `@FAKE_FIELD@' line to help distinguish\1\ fake mails.",""," or "); plist("KERNEL_LOCKING", "consistently uses the following kernel locking strategies:",krnllocks,"", "doesn't use any additional kernel locking strategies","\1and"); #ifdef RESTRICT_EXEC ps("RESTRICT_EXEC","\1.PP\1Users with userids >= @RESTRICT_EXEC_ID@ are\1\ prevented from executing external programs from\1\ within their own rcfiles"); pn("RESTRICT_EXEC_ID",RESTRICT_EXEC); ps("WARN_RESTRICT_EXEC","\1.TP\1No permission to execute \"x\"\1\ An attempt to execute a program from within the rcfile was blocked."); #else ps("RESTRICT_EXEC",""); ps("WARN_RESTRICT_EXEC",""); #endif ps("LD_ENV_FIX","\1.PP\1For security reasons, upon startup procmail will\ wipe out all environment variables that are suspected of modifying the\ behavior of the runtime linker."); ps("MAILSPOOLDIR",MAILSPOOLDIR); ps("ETCRC_desc",etcrc?"\1.PP\1If no rcfiles and no\1.B \2-@PRESERVOPT@\1have\ been specified on the command line, procmail will, prior to reading\ @PROCMAILRC@, interpret commands from\1.B @ETCRC@\1(if present).\1\ Care must be taken when creating @ETCRC@, because, if circumstances\ permit, it will be executed with root privileges (contrary to the\ @PROCMAILRC@ file of course).":""); ps("ETCRC_files",etcrc?"\1.TP\1.B @ETCRC@\1initial global rcfile":""); ps("DROPPRIVS",etcrc?"\1.TP\1.B DROPPRIVS\1If set to `yes' procmail\ will drop all privileges it might have had (suid or sgid). This is\ only useful if you want to guarantee that the bottom half of the\ @ETCRC@ file is executed on behalf of the recipient.":""); ps("ETCRC_warn",etcrc?"\1.PP\1The\1.B @ETCRC@\1file might be executed\ with root privileges, so be very careful of what you put in it.\1\ .B SHELL\1\ will be equal to that of the current recipient, so if procmail has to invoke\ the shell, you'd better set it to some safe value first.\1\ See also:\1.BR DROPPRIVS .":""); ps("ETCRC",etcrc?etcrc:""); #ifdef ETCRCS ps("ETCRCS_desc","\1If the rcfile is an absolute path starting with\ \1.B @ETCRCS@\ \1without backward references (i.e. the parent directory cannot\ be mentioned) procmail will, only if no security violations are found,\ take on the identity of the owner of the rcfile (or symbolic link)."); ps("ETCRCS_files","\1.TP\1.B @ETCRCS@\1special privileges path for rcfiles"); ps("ETCRCS_warn","\1.PP\1Keep in mind that if\1.BR chown (1)\1is permitted\ on files in\1.BR @ETCRCS@ ,\1that they can be chowned to root\ (or anyone else) by their current owners.\1For maximum security, make\ sure this directory is\1.I executable\1to root only."); ps("ETCRCS_error","\1.TP\1Denying special privileges for \"x\"\1\ Procmail will not take on the identity that comes with the rcfile because\1\ a security violation was found (e.g. \1.B \2-@PRESERVOPT@\1or variable\ assignments on the command line) or procmail had insufficient privileges\ to do so."); ps("ETCRCS",ETCRCS); #else ps("ETCRCS_desc","");ps("ETCRCS_files","");ps("ETCRCS_warn",""); ps("ETCRCS_error",""); #endif #ifdef console ps("pconsole","appear on\1.BR "); ps("console",console); ps("aconsole"," ."); #else ps("pconsole","be mailed back to the "); ps("console","sender"); ps("aconsole","."); #endif #ifdef LMTP ps("LMTPusage","\1.br\1.B procmail\1.RB [ \ \2-@TEMPFAILOPT@@OVERRIDEOPT@@BERKELEYOPT@ ]\1.RB [ \"\2-@ARGUMENTOPT@ \ \2fIargument\2fP\" ]\1.B \2-@LMTPOPT@\1"); ps("LMTPOPTdesc","\1.TP\1.B \2-@LMTPOPT@\1This turns on LMTP mode, wherein\ procmail acts as an RFC2033 LMTP server.\1Delivery takes place in the same \ manner and under the same restrictions as\1the delivery mode enabled \ with\1.BR \2-@DELIVEROPT@ .\1This option is incompatible with\1.B \ \2-@PRESERVOPT@\1and\1.BR \2-@FROMWHOPT@ .\1"); pc("LMTPOPT",LMTPOPT); #else ps("LMTPOPTdesc","");ps("LMTPusage",""); #endif pname("INIT_UMASK",0);printf("0%lo/g\n",(unsigned long)INIT_UMASK);lines--; pn("DEFlinebuf",DEFlinebuf); ps("BOGUSprefix",BOGUSprefix); ps("FAKE_FIELD",FAKE_FIELD); ps("PROCMAILRC",PROCMAILRC); pn("RETRYunique",RETRYunique); pn("DEFsuspend",DEFsuspend); pn("DEFlocksleep",DEFlocksleep); ps("TO_key",TO_key); ps("TO_substitute",TO_substitute); ps("TOkey",TOkey); ps("TOsubstitute",TOsubstitute); ps("FROMDkey",FROMDkey); ps("FROMDsubstitute",FROMDsubstitute); ps("FROMMkey",FROMMkey); ps("FROMMsubstitute",FROMMsubstitute); ps("DEFshellmetas",DEFshellmetas); ps("DEFmaildir",DEFmaildir); ps("DEFdefault",DEFdefault); ps("DEFmsgprefix",DEFmsgprefix); ps("DEFsendmail",DEFsendmail); ps("DEFflagsendmail",DEFflagsendmail); ps("DEFlockext",DEFlockext); ps("DEFshellflags",DEFshellflags); ps("DEFpath",strchr(DEFPATH,'=')+1); ps("DEFspath",strchr(DEFSPATH,'=')+1); pn("DEFlocktimeout",DEFlocktimeout); pn("DEFtimeout",DEFtimeout); pn("DEFnoresretry",DEFnoresretry); ps("MATCHVAR",MATCHVAR); ps("COMSAThost",COMSAThost); ps("COMSATservice",COMSATservice); ps("COMSATprotocol",COMSATprotocol); ps("COMSATxtrsep",COMSATxtrsep); pc("SERV_ADDRsep",SERV_ADDRsep); ps("DEFcomsat",DEFcomsat); ps("BinSh",BinSh); ps("ROOT_DIR",ROOT_DIR); ps("DEAD_LETTER",DEAD_LETTER); pc("MCDIRSEP",*MCDIRSEP); pc("chCURDIR",chCURDIR); pc("HELPOPT1",HELPOPT1); pc("HELPOPT2",HELPOPT2); pc("VERSIONOPT",VERSIONOPT); pc("PRESERVOPT",PRESERVOPT); pc("TEMPFAILOPT",TEMPFAILOPT); pc("MAILFILTOPT",MAILFILTOPT); pc("FROMWHOPT",FROMWHOPT); pc("REFRESH_TIME",REFRESH_TIME); pc("ALTFROMWHOPT",ALTFROMWHOPT); pc("OVERRIDEOPT",OVERRIDEOPT); pc("BERKELEYOPT",BERKELEYOPT); pc("ARGUMENTOPT",ARGUMENTOPT); pc("DELIVEROPT",DELIVEROPT); pn("MINlinebuf",MINlinebuf); ps("FROM",FROM); pc("HEAD_GREP",RECFLAGS[HEAD_GREP]); pc("BODY_GREP",RECFLAGS[BODY_GREP]); pc("DISTINGUISH_CASE",RECFLAGS[DISTINGUISH_CASE]); pc("ALSO_NEXT_RECIPE",RECFLAGS[ALSO_NEXT_RECIPE]); pc("ALSO_N_IF_SUCC",RECFLAGS[ALSO_N_IF_SUCC]); pc("ELSE_DO",RECFLAGS[ELSE_DO]); pc("ERROR_DO",RECFLAGS[ERROR_DO]); pc("PASS_HEAD",RECFLAGS[PASS_HEAD]); pc("PASS_BODY",RECFLAGS[PASS_BODY]); pc("FILTER",RECFLAGS[FILTER]); pc("CONTINUE",RECFLAGS[CONTINUE]); pc("WAIT_EXIT",RECFLAGS[WAIT_EXIT]); pc("WAIT_EXIT_QUIET",RECFLAGS[WAIT_EXIT_QUIET]); pc("IGNORE_WRITERR",RECFLAGS[IGNORE_WRITERR]); pc("RAW_NONL",RECFLAGS[RAW_NONL]); ps("FROM_EXPR",FROM_EXPR); pc("UNIQ_PREFIX",UNIQ_PREFIX); ps("ESCAP",ESCAP); ps("UNKNOWN",UNKNOWN); ps("OLD_PREFIX",OLD_PREFIX); ps("DEFfileno",ffileno+LEN_FILENO_VAR); ffileno[LEN_FILENO_VAR-1]='\0'; ps("FILENO",ffileno); pn("MAX32",MAX32); pn("MIN32",MIN32); pc("FM_SKIP",FM_SKIP); pc("FM_TOTAL",FM_TOTAL); pc("FM_BOGUS",FM_BOGUS); pc("FM_BERKELEY",FM_BERKELEY); pc("FM_QPREFIX",FM_QPREFIX); pc("FM_CONCATENATE",FM_CONCATENATE); pc("FM_ZAPWHITE",FM_ZAPWHITE); pc("FM_LOGSUMMARY",FM_LOGSUMMARY); pc("FM_FORCE",FM_FORCE); pc("FM_REPLY",FM_REPLY); pc("FM_KEEPB",FM_KEEPB); pc("FM_TRUST",FM_TRUST); pc("FM_SPLIT",FM_SPLIT); pc("FM_NOWAIT",FM_NOWAIT); pc("FM_EVERY",FM_EVERY); pc("FM_MINFIELDS",FM_MINFIELDS); pn("DEFminfields",DEFminfields); pc("FM_DIGEST",FM_DIGEST); pc("FM_BABYL",FM_BABYL); pc("FM_QUIET",FM_QUIET); pc("FM_DUPLICATE",FM_DUPLICATE); pc("FM_EXTRACT",FM_EXTRACT); pc("FM_EXTRC_KEEP",FM_EXTRC_KEEP); pc("FM_ADD_IFNOT",FM_ADD_IFNOT); pc("FM_ADD_ALWAYS",FM_ADD_ALWAYS); pc("FM_REN_INSERT",FM_REN_INSERT); pc("FM_DEL_INSERT",FM_DEL_INSERT); pc("FM_FIRST_UNIQ",FM_FIRST_UNIQ); pc("FM_LAST_UNIQ",FM_LAST_UNIQ); pc("FM_ReNAME",FM_ReNAME); pc("FM_VERSION",FM_VERSION); pn("EX_OK",EXIT_SUCCESS); ps("PM_VERSION",PM_VERSION); ps("BINDIR",BINDIR); #ifdef NOpow pc("POW",'1'); #else pc("POW",'x'); #endif ps("SETRUID",setRuid(getuid())?"": /* is setruid() a valid system call? */ " (or if procmail is already running with the recipient's euid and egid)"); if(lines<20)lines=0; /* make sure we have space */ pname("AUTHORS",1);putchar('c'); p=strchr(pm_version,'\n')+1; while(*p!='\n') { char*q=strchr(p,',')+2; puts("\\");lines--; *(p=strchr(q,'\t'))='\0'; putsesc(q);puts("\\\n.RS\\");lines-=2; while(*++p=='\t');*(q=strchr(p,'\n'))='\0'; putsesc(p);printf("\\\n.RE");lines--; p=q+1; } putchar('\n');lines--; ps("PM_MAILINGLIST",skltmark(2,&p)); ps("PM_MAILINGLISTR",skltmark(2,&p)); return EXIT_SUCCESS; } procmail-3.22/src/exopen.h0100644006717600001440000000112207316762772014726 0ustar guenthersrc/*$Id: exopen.h,v 1.20 2001/06/23 08:18:42 guenther Exp $*/ int unique Q((const char*const full,char*p,const size_t len,const mode_t mode, const int verbos,const int flags)), myrename P((const char*const old,const char*const newn)), rlink P((const char*const old,const char*const newn,struct stat*st)), hlink P((const char*const old,const char*const newn)); #define UNIQnamelen 30 /* require how much space as a first guess? */ #define MINnamelen 14 /* cut to this on ENAMETOOLONG */ #define doCHOWN 1 #define doCHECK 2 #define doLOCK 4 #define doFD 8 #define doMAILDIR 16 procmail-3.22/src/regexp.c0100644006717600001440000004564007223546555014726 0ustar guenthersrc/************************************************************************ * Custom regular expression library, *fully* egrep compatible * * * * Seems to be perfect. * * * * Copyright (c) 1991-1999, S.R. van den Berg, The Netherlands * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: regexp.c,v 1.66 2000/10/23 09:04:25 guenther Exp $"; #endif #include "procmail.h" #include "sublib.h" #include "robust.h" #include "shell.h" #include "misc.h" #include "variables.h" #include "regexp.h" #define R_BEG_GROUP '(' #define R_OR '|' #define R_END_GROUP ')' #define R_0_OR_MORE '*' #define R_0_OR_1 '?' #define R_1_OR_MORE '+' #define R_DOT '.' #define R_SOL '^' #define R_EOL '$' #define R_BEG_CLASS '[' #define R_NOT_CLASS '^' #define R_RANGE '-' #define R_END_CLASS ']' #define R_ESCAPE '\\' #define R_BEG_WORD '<' #define R_END_WORD '>' #define NO_WORD_CLASS "[^a-zA-Z0-9_]" #define R_SPLIT_EXPR '/' #define BITS_P_CHAR 8 #define OPB (1<opc=OPC_EPS;spot->next=Ceps to;spot->spawn=0; /* epsilon transition */ } #define Cc(p,memb) (((struct chclass*)(p))->memb) #define rAc Cc(r,c) static void bseti(i,j)unsigned i;const int j; { bit_set(rAc,i,j); /* mark 'i' as being in the class */ if(case_ignore) /* mark the other case too */ { if(i-'A'<='Z'-'A') /* uppercase */ i+='a'-'A'; else if(i-'a'<='z'-'a') /* lowercase */ i-='a'-'A'; else return; /* no case */ bit_set(rAc,i,j); } } /* general purpose length routine */ static struct eps*skiplen(ep)const struct eps*const ep; { return epso(ep,(ep->opc&DONE_MASK)opc&DONE_MASK)-OPC_EPS]); } static int por P((const struct eps*const e)); static void psimp(e)const struct eps*const e; { switch(*p) { case R_BEG_GROUP:p++; /* not so simple after all */ if(por(e)) errorno=1; return; case R_BEG_CLASS: /* a simple class */ { unsigned i,j=R_NOT_CLASS==*++p; if(e) { r->opc=OPC_CLASS;r->next=Ceps e;Cc(r,pos1.st_)=Cc(r,pos2.st_)=0; i=maxindex(rAc); do rAc[i]=j?~0:0; /* preset the bit field */ while(i--); } if(j) /* skip the 'not' modifier */ { p++; if(e) bit_toggle(rAc,'\n'); } if(*p==R_END_CLASS) /* right at the start, cannot mean the end */ { p++; if(e) i=R_END_CLASS,bit_toggle(rAc,R_END_CLASS); } else if(*p==R_RANGE) /* take it literally */ { p++; if(e) i=R_RANGE,bit_toggle(rAc,R_RANGE); } for(;;p++) { switch(*p) { case R_END_CLASS:p++; case '\0':r=epso(r,SZ(chclass)); return; case R_RANGE: switch(*++p) { default: if(e) while(++i<*p) /* mark all in the range */ bseti(i,!j); break; case '\0':case R_END_CLASS:p--; /* literally */ } } if(e) bseti(i= *p,!j); /* a normal character, mark it */ } } case '\0': return; case R_DOT: /* matches everything but a newline */ if(e) { r->opc=OPC_DOT; goto fine; } goto fine2; case R_SOL: /* match a newline (in effect) */ if(p[1]==R_SOL) { p++; if(e) { r->opc=e==opcfin?OPC_EOTEXT:OPC_BOTEXT; goto fine; } } else case R_EOL: if(e) { r->opc='\n'; goto fine; } goto fine2; case R_ESCAPE: /* quote something */ switch(*++p) { case R_SPLIT_EXPR: if(e) r->opc=OPC_BOM; r=epso(r,sizeof(union seps)); goto fine3; case R_BEG_WORD:case R_END_WORD: { uchar*pold=p; p=(uchar*)NO_WORD_CLASS;psimp(e);p=pold+1; if(e) bit_toggle(Cc(epso(r,-(int)SZ(chclass)),c),'\n'); return; } case '\0':p--; /* nothing to quote */ } } if(e) /* a regular character */ { r->opc=case_ignore&&(unsigned)*p-'A'<='Z'-'A'?*p+'a'-'A':*p; fine: r->next=Ceps e;Cc(r,pos1.st_)=Cc(r,pos2.st_)=0; } fine2: r=epso(r,SZ(mchar)); fine3: p++; } #define EOS(x) (jj?Ceps e:(x)) static int endgroup(p)register const uchar*const p; { switch(*p) { case R_OR:case R_END_GROUP:case '\0': return 1; } return 0; } static void pnorm(e)const struct eps*const e; { void*pold;struct eps*rold; for(;;) { pold=p;rold=r;psimp(Ceps 0);ii= *p; /* skip it first */ if(endgroup(p)) { if(e) p=pold,r=rold,psimp(e); return; } jj=endgroup(p+1); if(e) p=pold,pold=r; switch(ii) /* check for any of the postfix operators */ { case R_0_OR_MORE:r++; if(e) /* first an epsilon, then the rest */ puteps(rold,EOS(r)),r=rold+1,psimp(rold); goto incagoon; case R_1_OR_MORE: /* first the rest */ if(e) /* and then an epsilon */ { puteps(r,rold); if(jj) (r+1)->opc=OPC_JUMP,(r+1)->next=Ceps e; r=rold;psimp(Ceps pold); } r++; if(endgroup(p+1)) r=epso(r,SZ(jump)); goto incagoon; case R_0_OR_1:r++; if(e) /* first an epsilon, then the rest */ puteps(rold,r=EOS(r)),pold=r,r=rold+1,psimp(Ceps pold); incagoon: if(endgroup(++p)) /* at the end of this group already? */ return; continue; /* regular end of the group */ } if(e) /* no fancy postfix operators, plain vanilla */ r=rold,psimp(Ceps pold); } } static int por(e)const struct eps*const e; { uchar*pvold;struct eps*rvold; if(!e) { rvold=r; if(cachea==(pvold=p)) { p=cachep;r=epso(rvold,cacher); goto ret0; } } for(;;) { uchar*pold;struct eps*rold; for(pold=p,rold=r;;) { switch(*p) { default: pnorm(Ceps 0);r=rold; /* still in this group */ continue; case '\0':case R_END_GROUP: /* found the end of the group */ if(p==pold) /* empty 'or' group */ { if(e) r->opc=OPC_JUMP,r->next=Ceps e; r=epso(r,SZ(jump)); } else p=pold,pnorm(e); /* normal last group */ if(!e) { if(*p) p++; cachea=pvold;cachep=p;cacher=(char*)r-(char*)rvold; goto ret0; } if(*p) { p++; ret0: return 0; } return 1; case R_OR:r++; if(p==pold) /* empty 'or' group */ { if(e) puteps(rold,e); /* special epsilon */ } else { p=pold;pnorm(e); /* normal 'or' group, first an */ if(e) /* epsilon, then the rest */ puteps(rold,r); } p++; } break; } } } /* go down recursively, mark loopbacks on the way up again */ static struct eps*maxback(down)struct eps*down; { ii=0; /* didn't find a loop at this level (yet) */ for(;;) { switch(down->opc&LOOP_MASK) /* chase JUMP chains */ { default: goto ret0; /* oops, not an EPS, return */ case OPC_JUMP:down->opc=OPC_JUMP|DONE_NODE; /* mark them as used */ case OPC_JUMP|DONE_NODE:down=down->next; continue; case OPC_EPS|DONE_NODE:ii=1; /* used EPS found, return loop number */ return down->spawn==Ceps&aleps?down:down->spawn; case OPC_EPS:; /* unused EPS found, the work starts */ } break; } if(!down->spawn) /* has it been visited (belongs to previous group?) */ { struct eps*left; /* no, so process it */ down->opc=OPC_EPS|DONE_NODE;down->spawn=Ceps&aleps; /* mark as used */ left=maxback(down->next); /* init loop no. and recurse left */ if(ii) /* loop found directly below us? */ down->opc|=LOOPL_NODE; /* mark a left-loop */ ;{ struct eps*right; /* recurse right, take the smallest */ if((right=maxback(down+1))&&(char*)left>(char*)right) /* loop no. */ left=right; } if(ii) /* loop found directly below? */ { down->opc|=LOOPR_NODE; /* mark a right-loop */ if(!(down->opc&LOOPL_NODE)) /* if we didn't also have a left-loop */ ii=0; /* we tell our predecessor we are not a loop */ } if(!left) /* found no loop at all? */ { down->spawn=down; /* then give ourselves our own loop no. */ goto ret0; } if((down->spawn=left)!=down) /* save the loop no., check if it's us */ return left; /* if not, pass the number up */ } /* otherwise we are the end of the loop */ ret0: return 0; /* no loop whatsoever */ } struct eps*bregcomp(a,ign_case)const char*const a;const unsigned ign_case; { struct eps*st;size_t i; skplen[OPC_FILL-OPC_EPS]=SZ(eps)-ioffsetof(struct eps,sp); /* a constant! */ errorno=0;p=(uchar*)a;case_ignore=ign_case;r=Ceps&aleps;cachea=0; por(Ceps 0);st=r=malloc((i=(char*)r-(char*)&aleps)+sizeof r->opc); p=(uchar*)a; /* first a trial run, determine memory needed */ if(!por(opcfin=epso(st,i))) /* really compile */ errorno=1; r->opc=OPC_FIN; /* by now r should be == opcfin */ if(errorno) nlog("Invalid regexp"),logqnl(a); for(r=st;;st=skiplen(st)) /* simplify the compiled code (i.e. */ switch(st->opc) /* take out cyclic epsilon references) */ { case OPC_FIN: return r; /* finished */ case OPC_EPS: /* check for any closed epsilon circles */ if(!st->spawn) /* they can't be executed */ { maxback(st); /* if not visited yet, recurse and mark loops */ ;{ register struct eps*i; for(i=r;;i=skiplen(i)) /* search the whole program */ { switch(i->opc&LOOP_MASK) { default: /* renumber regulars */ { register struct eps*f; /* if needed */ if(((f=i->next)->opc&DONE_MASK)==OPC_EPS&&f->spawn) { for(;f->spawn!=f;f=f->spawn); /* search start */ i->next=f; /* of loop */ } } /* spare the used nodes in this group */ case OPC_EPS|DONE_NODE:case OPC_JUMP|DONE_NODE: case OPC_FILL:case OPC_BOM: continue; case OPC_FIN:; } break; } } ;{ register struct eps*i; for(i=r;;i=skiplen(i)) /* search the whole program */ { switch(i->opc) /* unmark/transform the used nodes */ { case OPC_EPS|DONE_NODE|LOOPL_NODE:i->next=i+1; case OPC_EPS|DONE_NODE|LOOPR_NODE:i->sp.sopc=OPC_FILL; case OPC_JUMP|DONE_NODE:i->opc=OPC_JUMP; continue; case OPC_EPS|DONE_NODE|LOOPL_NODE|LOOPR_NODE: case OPC_EPS|DONE_NODE:i->opc=OPC_EPS; default: continue; case OPC_FIN:; } break; } } } } } #define XOR1 \ (ioffsetof(struct chclass,pos1)^ioffsetof(struct chclass,pos2)) #define PC(thiss,t) (((struct evoi*)geno(thiss,t))->st_) #define PCp(thiss,t) (((struct evoi*)geno(thiss,t))->wh_) #define PcP(reg) (*(const void**)\ geno(reg,ioffsetof(struct evoi,wh_)-ioffsetof(struct evoi,st_))) static struct mchar tswitch={OPC_TSWITCH,Ceps&tswitch}; static struct eps*cleantail(start,thiss,th1)const char*const start; register struct eps*thiss;const unsigned th1; { register struct eps**reg,*save=Ceps&tswitch,*oldthis; while(thiss= *(reg= &PC(oldthis=thiss,th1))) /* wipe out list till you */ if(start<(char*)PcP(reg)) *reg=0; /* reach tswitch */ else *reg=save,save=oldthis; return save; } char*bregexec(code,text,str,len,ign_case)struct eps*code; const uchar*const text;const uchar*str;size_t len;unsigned ign_case; { register struct eps*reg,*stack,*other,*thiss;unsigned i,th1,ot1; struct eps*initcode;const char*eom,*pend; static struct eps sempty={OPC_SEMPTY,&sempty}; static const struct jump nop={OPC_FILL}; sempty.spawn= &sempty; /* static initialisers */ ign_case=ign_case?~(unsigned)0:0;eom=0;stack= &sempty;initcode=code; th1=ioffsetof(struct chclass,pos1);ot1=ioffsetof(struct chclass,pos2); other=Ceps&tswitch;pend=(const char*)str+len+1; /* two past end */ if(str--==text||*str=='\n') goto begofline; /* make sure any beginning-of-line-hooks catch */ if(!len) { str++; begofline: i='\n';len++; if(initcode->opc!=OPC_BOTEXT) goto setups; reg=initcode;initcode=Ceps&nop;thiss=Ceps&tswitch; goto dobotext; } do { i= *++str; /* get the next real-text character */ if(i-'A'<='Z'-'A') i+=ign_case&'a'-'A'; /* transmogrify it to lowercase */ setups: /* switch this & other pc-stack */ th1^=XOR1;ot1^=XOR1;thiss=other;other=Ceps&tswitch;reg=initcode; /* pop */ for(;;thiss=PC(reg=thiss,th1),PC(reg,th1)=0,reg=reg->next) /* pc-stack */ { for(;;reg=stack->next,stack=stack->spawn) /* pop from work-stack */ for(;;) { switch(reg->opc-OPB) { default: if(i==reg->opc) /* regular character match */ goto yep; break; /* push spawned branch on the work-stack */ case OPC_EPS-OPB:reg->spawn=stack;reg=(stack=reg)+1; continue; case OPC_JUMP-OPB:reg=reg->next; continue; case OPC_BOM-OPB: goto foundbom; case OPC_FILL-OPB: /* nop, nothing points at it */ if(thiss==Ceps&tswitch) goto nomatch; /* so the stack is always empty */ case OPC_SEMPTY-OPB: goto empty_stack; case OPC_TSWITCH-OPB: goto pcstack_switch; case OPC_EOTEXT-OPB: if(ign_case==2) /* only at the very end */ case OPC_FIN-OPB: goto nobom; case OPC_BOTEXT-OPB: dobotext: if(strc,i)) goto yep; /* character in class */ break; case OPC_DOT-OPB: /* dot-wildcard */ if(i!='\n') yep: if(!PC(reg,ot1)) /* state not yet pushed */ PC(reg,ot1)=other,PCp(other=reg,ot1)=pend; } break; } empty_stack:; /* the work-stack is empty */ } pcstack_switch:; /* this pc-stack is empty */ } while(--len); /* still text to search */ goto wrapup; ;{ const char*start,*bom; do { i= *++str; /* get the next real-text character */ if(i-'A'<='Z'-'A') i+=ign_case&'a'-'A'; /* transmogrify it to lowercase */ th1^=XOR1;ot1^=XOR1;start=pend;thiss=other;other=Ceps&tswitch; reg=initcode; for(;; /* pc-stack */ thiss=PC(reg=thiss,th1),PC(reg,th1)=0,start=PCp(reg,th1), reg=reg->next) { for(;;reg=stack->next,stack=stack->spawn) /* pop from work-stack */ for(;;) { switch(reg->opc-OPB) { default: if(i==reg->opc) /* regular character match */ goto Yep; break; /* push spawned branch on the work-stack */ case OPC_EPS-OPB:reg->spawn=stack;reg=(stack=reg)+1; continue; case OPC_JUMP-OPB:reg=reg->next; continue; case OPC_BOM-OPB: if(!eom) foundbom: start=(const char*)str; reg=epso(reg,sizeof(union seps)); continue; case OPC_FILL-OPB: /* nop, nothing points at it */ if(thiss==Ceps&tswitch) goto checkmatch; /* so the stack is always empty */ case OPC_SEMPTY-OPB: goto Empty_stack; case OPC_TSWITCH-OPB: goto Pcstack_switch; case OPC_EOTEXT-OPB: if(ign_case==2) /* only at the very end */ case OPC_FIN-OPB: { if(startc,i)) goto Yep; /* character in class */ break; case OPC_DOT-OPB: /* dot-wildcard */ if(i!='\n') Yep: if(!PC(reg,ot1)) /* state not yet pushed */ { PC(reg,ot1)=other;other=reg; /* push location */ earlier: PCp(reg,ot1)=start; /* onto other pc-stack */ } else if(start<(char*)PCp(reg,ot1)) goto earlier; } break; } Empty_stack:; /* the work-stack is empty */ } Pcstack_switch:; /* this pc-stack is empty */ } while(--len); /* still text to search */ wrapup: switch(ign_case) { case 0:case ~(unsigned)0:ign_case=1;i='\n'; /* just finished? */ case 2:ign_case++;str++;len=1;th1^=XOR1;ot1^=XOR1;start=pend; thiss=other;other=Ceps&tswitch; goto Empty_stack; /* check if we just matched */ } checkmatch: if(eom) { static const char match[]=MATCHVAR,amatch[]=AMATCHVAR;char*q; if(bom<(char*)text) bom=(const char*)text; if(eom>--pend) eom=pend; len=eom>bom?eom-bom:0; if(getenv(match)==(const char*)text) /* anal retentive match */ tmemmove(q=(char*)text,bom,len),q[len]='\0',bom=q; else { char*p; primeStdout(amatch);p=realloc(Stdout,(Stdfilled+=len)+1); tmemmove(q=p+Stdfilled-(int)len,bom,len);retbStdout(p); } yell("Matched",q); } } nomatch: return (char*)eom; /* match? */ } procmail-3.22/src/pipes.h0100644006717600001440000000115507316763024014545 0ustar guenthersrc/*$Id: pipes.h,v 1.14 2001/06/07 21:03:51 guenther Exp $*/ struct memblk; /* predeclare the tag */ void inittmout P((const char*const progname)), ftimeout P((void)), resettmout P((void)), exectrap P((const char*const tp)); int pipthrough P((char*line,char*source,const long len)); long pipin P((char*const line,char*source,long len,int asgnlastf)); char *readdyn P((struct memblk*const mb,long*const filled,long oldfilled)), *fromprog Q((char*name,char*const dest,size_t max)); extern int setxit; extern pid_t pidchild; extern volatile time_t alrmtime; extern volatile int toutflag; extern int pipw; procmail-3.22/src/locking.c0100644006717600001440000001755307347314746015066 0ustar guenthersrc/************************************************************************ * Whatever is needed for (un)locking files in various ways * * * * Copyright (c) 1990-1997, S.R. van den Berg, The Netherlands * * Copyright (c) 1998-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: locking.c,v 1.63 2001/08/04 07:12:17 guenther Exp $"; #endif #include "procmail.h" #include "robust.h" #include "shell.h" #include "misc.h" #include "pipes.h" #include "foldinfo.h" #include "exopen.h" #include "locking.h" #include "lastdirsep.h" char*globlock; int lockit(name,lockp)char*name;char**const lockp; { int permanent=nfsTRY,triedforce=0,locktype=doLOCK;struct stat stbuf;time_t t; zombiecollect(); if(*lockp) { if(!strcmp(name,*lockp)) /* compare the previous lockfile to this one */ { free(name);return 1; /* they're equal, save yourself some effort */ } unlock(lockp); /* unlock any previous lockfile FIRST */ } /* to prevent deadlocks (I hate deadlocks) */ if(!*name) { free(name);return 1; } if(!strcmp(name,defdeflock)) /* is it the system mailbox lockfile? */ { locktype=doCHECK|doLOCK; if(sgid!=gid&&setegid(sgid)) /* try and get some extra permissions */ #ifndef fdlock if(!accspooldir) { yell("Bypassed locking",name); free(name);return 0; } #endif ; } for(lcking|=lck_DELAYSIG;;) { yell("Locking",name); /* in order to cater for clock skew: get */ if(!xcreat(name,LOCKperm,&t,locktype)) /* time t from the filesystem */ { *lockp=name; /* lock acquired, hurray! */ break; } switch(errno) { case EEXIST: /* check if it's time for a lock override */ if(!lstat(name,&stbuf)&&stbuf.st_size<=MAX_locksize&&locktimeout &&!lstat(name,&stbuf)&&locktimeout #endif #define REITflock 1 #else #define REITflock 0 #endif /* USEflock */ static int oldfdlock= -1; /* the fd we locked last */ #ifndef NOfcntl_lock static struct flock flck; /* why can't it be a local variable? */ #define REITfcntl 1 #else #define REITfcntl 0 #endif /* NOfcntl_lock */ #ifdef USElockf static off_t oldlockoffset; #define REITlockf 1 #else #define REITlockf 0 #endif /* USElockf */ int fdlock(fd)int fd; { int ret; if(verbose) nlog("Acquiring kernel-lock\n"); #if REITfcntl+REITflock+REITlockf>1 for(;!toutflag;verbose&&(nlog("Reiterating kernel-lock\n"),0), ssleep((unsigned)locksleep)) #endif { zombiecollect(); #ifdef USElockf oldlockoffset=tell(fd); #endif #ifndef NOfcntl_lock flck.l_type=F_WRLCK;flck.l_whence=SEEK_SET;flck.l_len=0; #ifdef USElockf flck.l_start=oldlockoffset; #else flck.l_start=tell(fd); #endif #endif lcking|=lck_KERNEL; #ifndef NOfcntl_lock ret=fcntl(fd,F_SETLKW,&flck); #ifdef USElockf if((ret|=lockf(fd,F_TLOCK,(off_t)0))&&(errno==EAGAIN||errno==EACCES|| errno==EWOULDBLOCK)) ufcntl: { flck.l_type=F_UNLCK;fcntl(fd,F_SETLK,&flck); continue; } #ifdef USEflock if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| errno==EWOULDBLOCK)) { lockf(fd,F_ULOCK,(off_t)0); goto ufcntl; } #endif /* USEflock */ #else /* USElockf */ #ifdef USEflock if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| errno==EWOULDBLOCK)) { flck.l_type=F_UNLCK;fcntl(fd,F_SETLK,&flck); continue; } #endif /* USEflock */ #endif /* USElockf */ #else /* NOfcntl_lock */ #ifdef USElockf ret=lockf(fd,F_LOCK,(off_t)0); #ifdef USEflock if((ret|=flock(fd,LOCK_EX|LOCK_NB))&&(errno==EAGAIN||errno==EACCES|| errno==EWOULDBLOCK)) { lockf(fd,F_ULOCK,(off_t)0); continue; } #endif /* USEflock */ #else /* USElockf */ #ifdef USEflock ret=flock(fd,LOCK_EX); #endif /* USEflock */ #endif /* USElockf */ #endif /* NOfcntl_lock */ oldfdlock=fd;lcking&=~lck_KERNEL; return ret; } return 1; /* timed out */ } int fdunlock P((void)) { int i; if(oldfdlock<0) return -1; i=0; #ifdef USEflock i|=flock(oldfdlock,LOCK_UN); #endif #ifdef USElockf ;{ off_t curp=tell(oldfdlock); /* restore the position later */ lseek(oldfdlock,oldlockoffset,SEEK_SET); i|=lockf(oldfdlock,F_ULOCK,(off_t)0);lseek(oldfdlock,curp,SEEK_SET); } #endif #ifndef NOfcntl_lock flck.l_type=F_UNLCK;i|=fcntl(oldfdlock,F_SETLK,&flck); #endif oldfdlock= -1; return i; } #endif /* fdlock */ procmail-3.22/src/authenticate.h0100644006717600001440000000213606706547413016107 0ustar guenthersrc/*$Id: authenticate.h,v 1.5 1999/04/19 06:36:59 guenther Exp $*/ /* Generic authentication interface, substitute a suitable module to accomodate arbitrary other authentication databases */ typedef struct auth_identity auth_identity; #ifndef P #define P(x) x #define Q(x) () #endif /*const*/auth_identity *auth_finduser P((char*const user,const int sock)), *auth_finduid Q((const uid_t uid,const int sock)); auth_identity *auth_newid P((void)); int auth_checkpassword P((const auth_identity*const pass,const char*const pw, const int allowemptypw)), auth_filledid P((const auth_identity*pass)); const char *auth_getsecret P((const auth_identity*const pass)), *auth_mailboxname P((auth_identity*const pass)), *auth_homedir P((const auth_identity*const pass)), *auth_shell P((const auth_identity*const pass)), *auth_username P((const auth_identity*const pass)); uid_t auth_whatuid P((const auth_identity*const pass)), auth_whatgid P((const auth_identity*const pass)); void auth_copyid P((auth_identity*newpass,const auth_identity*oldpass)), auth_freeid P((auth_identity*pass)), auth_end P((void)); procmail-3.22/src/foldinfo.c0100644006717600001440000002541607347314674015235 0ustar guenthersrc/************************************************************************ * Routines that deal with understanding the folder types * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: foldinfo.c,v 1.11 2001/08/04 07:07:42 guenther Exp $"; #endif #include "procmail.h" #include "misc.h" #include "lastdirsep.h" #include "robust.h" #include "exopen.h" #include "goodies.h" #include "locking.h" #include "foldinfo.h" static const char maildirtmp[]=MAILDIRtmp,maildircur[]=MAILDIRcur; const char maildirnew[]=MAILDIRnew; int accspooldir; /* can we write to the spool directory? */ /* determine the requested type, chopping off the type specifier and any extra trailing slashes */ static int folderparse P((void)) { char*chp;int type; type=ft_FILE;chp=strchr(buf,'\0'); switch(chp-buf) { case 2: if(chp[-1]==*MCDIRSEP_) /* "//" or "x/" */ chp--,type=ft_MAILDIR; case 0:case 1: /* "" or "x" */ goto ret; } /* Okay, put chp right before the type specifier */ if(chp[-1]==chCURDIR&&chp[-2]==*MCDIRSEP_) /* foo/. */ chp-=2,type=ft_MH; else if(chp[-1]==*MCDIRSEP_) /* foo/ */ chp--,type=ft_MAILDIR; else /* no specifier */ goto ret; while(chp-1>buf&&strchr(dirsep,chp[-1])) /* chop extra /s */ chp--; ret: *chp='\0'; return type; } int rnmbogus(name,stbuf,i,dolog)const char*const name; /* move a file */ const struct stat*const stbuf;const int i,dolog; /* out of the way */ { static const char renbogus[]="Renamed bogus \"%s\" into \"%s\"", renfbogus[]="Couldn't rename bogus \"%s\" into \"%s\"", bogusprefix[]=BOGUSprefix;char*p; p=strchr(strcpy(strcpy(buf2+i,bogusprefix)+STRLEN(bogusprefix), getenv(lgname)),'\0'); *p++='.';ultoan((unsigned long)stbuf->st_ino,p); /* i-node numbered */ if(dolog) { nlog("Renaming bogus mailbox \"");elog(name);elog("\" info"); logqnl(buf2); } if(rename(name,buf2)) /* try and move it out of the way */ { syslog(LOG_ALERT,renfbogus,name,buf2); /* danger! danger! */ return 1; } syslog(LOG_CRIT,renbogus,name,buf2); return 0; } /* return the named object's mode, making it a directory if it doesn't exist * and renaming it out of the way if it's not _just_right_ and we're being * paranoid */ static mode_t trymkdir(dir,paranoid,i)const char*const dir; const int paranoid,i; { struct stat stbuf;int tries=3-1; /* minus one for post-decrement */ do { if(!(paranoid?lstat(dir,&stbuf):stat(dir,&stbuf))) /* does it exist? */ { if(!paranoid|| /* is it okay? If we're trusting it is */ (S_ISDIR(stbuf.st_mode)&& /* else it must be a directory */ (stbuf.st_uid==uid|| /* and have the correct owner */ !stbuf.st_uid&&!chown(dir,uid,sgid)))) /* or be safely fixable */ return stbuf.st_mode; /* bingo! */ else if(rnmbogus(dir,&stbuf,i,1)) /* try and move it out of the way */ break; /* couldn't! */ } else if(errno!=ENOENT) /* something more fundamental went wrong */ break; else if(!mkdir(dir,NORMdirperm)) /* it's not there, can we make it? */ { if(!paranoid) /* do we need to double check the permissions? */ return S_IFDIR|NORMdirperm&~cumask; /* nope */ tries++; /* the mkdir succeeded, so take another shot */ } }while(tries-->0); return 0; } static int mkmaildir(buffer,chp,paranoid)char*const buffer,*const chp; const int paranoid; { mode_t mode;int i; if(paranoid) memcpy(buf2,buffer,i=chp-buffer+1),buf2[i-1]= *MCDIRSEP_,buf2[i]='\0'; return (strcpy(chp,maildirnew),mode=trymkdir(buffer,paranoid,i),S_ISDIR(mode))&& (strcpy(chp,maildircur),mode=trymkdir(buffer,paranoid,i),S_ISDIR(mode))&& (strcpy(chp,maildirtmp),mode=trymkdir(buffer,paranoid,i),S_ISDIR(mode)); } /* leave tmp in buf on success */ int foldertype(type,forcedir,modep,paranoid)int type,forcedir; mode_t*const modep;struct stat*const paranoid; { struct stat stbuf;mode_t mode;int i;char*chp; if(!type) type=folderparse(); switch(type) { case ft_MAILDIR:i=MAILDIRLEN;break; case ft_MH:i=0;break; case ft_FILE: i=0; /* resolve the ambiguity */ if(!forcedir) { if(paranoid?lstat(buf,&stbuf):stat(buf,&stbuf)) { if(paranoid) { type=ft_NOTYET; goto ret; } goto newfile; } else if(mode=stbuf.st_mode,!S_ISDIR(mode)) goto file; } type=ft_DIR; break; default: /* "this cannot happen" */ nlog("Internal error: improper type ("); ltstr(0,type,buf2);elog(buf2); elog(") passed to foldertype for folder ");logqnl(buf); Terminate(); } chp=strchr(buf,'\0'); if((chp-buf)+UNIQnamelen+1+i>linebuf) { type=ft_TOOLONG; goto ret; } if(type==ft_DIR&&!forcedir) /* we've already checked this case */ goto done; if(paranoid) memcpy(buf2,buf,i=lastdirsep(buf)-buf),buf2[i]='\0'; mode=trymkdir(buf,paranoid!=0,i); if(!S_ISDIR(mode)||(type==ft_MAILDIR&& (forcedir=1,!mkmaildir(buf,chp,paranoid!=0)))) { nlog("Unable to treat as directory");logqnl(buf); /* we can't make it */ if(forcedir) /* fallback or give up? */ { *chp='\0';skipped(buf);type=ft_CANTCREATE; goto ret; } if(!mode) newfile:mode=S_IFREG|NORMperm&~cumask; file:type=ft_FILE; } done: if(paranoid) *paranoid=stbuf; else *modep=mode; ret: return type; } /* lifted out of main() to reduce main()'s size */ int screenmailbox(chp,egid,Deliverymode) char*chp;const gid_t egid;const int Deliverymode; { char ch;struct stat stbuf;int basetype,type; /* * do we need sgidness to access the mail-spool directory/files? */ accspooldir=3; /* assume we can write to the spool directory and */ sgid=gid; /* that we don't need to setgid() to create a lockfile */ strcpy(buf,chp); basetype=folderparse(); /* strip off the type */ if(buf[0]=='\0') /* don't even bother with "" */ return 0; ch= *(chp=lastdirsep(buf)); if(chp>buf) *chp='\0'; /* strip off the filename */ if(!stat(buf,&stbuf)) { unsigned wwsdir; accspooldir=(wwsdir= /* world writable spool dir? */ ((S_IWGRP|S_IXGRP|S_IWOTH|S_IXOTH)&stbuf.st_mode)== (S_IWGRP|S_IXGRP|S_IWOTH|S_IXOTH) <<1| /* note it in bit 1 */ uid==stbuf.st_uid); /* we own the spool dir, note it in bit 0 */ if((CAN_toggle_sgid||accspooldir)&&privileged) privileged=priv_DONTNEED; /* we don't need root to setgid */ if(uid!=stbuf.st_uid&& /* we don't own the spool directory */ (stbuf.st_mode&S_ISGID||!wwsdir)) /* it's not world writable */ { if(stbuf.st_gid==egid) /* but we have setgid privs */ doumask(GROUPW_UMASK); /* make it group-writable */ goto keepgid; } else if(stbuf.st_mode&S_ISGID) keepgid: /* keep the gid from the parent directory */ if((sgid=stbuf.st_gid)!=egid&& /* we were started nosgid, */ setgid(sgid)) /* but we might need it */ checkroot('g',(unsigned long)sgid); } else /* panic, mail-spool directory not available */ { setids();mkdir(buf,NORMdirperm); /* try creating the last member */ } *chp=ch; /* * check if the default-mailbox-lockfile is owned by the * recipient, if not, mark it for further investigation, it * might need to be removed */ chp=strchr(buf,'\0')-1; for(;;) /* what type of folder is this? */ { type=foldertype(basetype,0,0,&stbuf); if(type==ft_NOTYET) { if(errno!=EACCES||(setids(),lstat(buf,&stbuf))) goto nobox; } else if(!ft_checkcloser(type)) { setids(); if(type<0) goto fishy; goto nl; /* no lock needed */ } /* * check if the original/default mailbox of the recipient * exists, if it does, perform some security checks on it * (check if it's a regular file, check if it's owned by * the recipient), if something is wrong try and move the * bogus mailbox out of the way, create the * original/default mailbox file, and chown it to * the recipient */ ;{ int checkiter=1; for(;;) { if(stbuf.st_uid!=uid|| /* recipient not owner */ !(stbuf.st_mode&S_IWUSR)|| /* recipient can write? */ S_ISLNK(stbuf.st_mode)|| /* no symbolic links */ (S_ISDIR(stbuf.st_mode)? /* directories, yes, hardlinks */ !(stbuf.st_mode&S_IXUSR):stbuf.st_nlink!=1)) /* no */ /* * If another procmail is about to create the new * mailbox, and has just made the link, st_nlink==2 */ if(checkiter--) /* maybe it was a race condition */ suspend(); /* close eyes, and hope it improves */ else /* can't deliver to this contraption */ { int i=lastdirsep(buf)-buf; memcpy(buf2,buf,i);buf2[i]='\0'; if(rnmbogus(buf,&stbuf,i,1)) goto fishy; goto nobox; } else break; if(lstat(buf,&stbuf)) goto nobox; } /* SysV type autoforwarding? */ if(Deliverymode&&(stbuf.st_mode&S_ISUID|| !S_ISDIR(stbuf.st_mode)&&stbuf.st_mode&S_ISGID)) { nlog("Autoforwarding mailbox found\n"); exit(EX_NOUSER); } else { if(!(stbuf.st_mode&OVERRIDE_MASK)&& stbuf.st_mode&cumask& (accspooldir?~(mode_t)0:~(S_IRGRP|S_IWGRP))) /* hold back */ { static const char enfperm[]= "Enforcing stricter permissions on"; nlog(enfperm);logqnl(buf); syslog(LOG_NOTICE,slogstr,enfperm,buf);setids(); chmod(buf,stbuf.st_mode&=~cumask); } break; /* everything is just fine */ } } nobox: if(!(accspooldir&1)) /* recipient does not own the spool dir */ { if(!xcreat(buf,NORMperm,(time_t*)0,doCHOWN|doCHECK)) /* create */ break; /* mailbox... yes we could, fine, proceed */ if(!lstat(buf,&stbuf)) /* anything in the way? */ continue; /* check if it could be valid */ } setids(); /* try some magic */ if(!xcreat(buf,NORMperm,(time_t*)0,doCHECK)) /* try again */ break; if(lstat(buf,&stbuf)) /* nothing in the way? */ fishy: { nlog("Couldn't create");logqnl(buf); return 0; } } if(!S_ISDIR(stbuf.st_mode)) { int isgrpwrite=stbuf.st_mode&S_IWGRP; strcpy(chp=strchr(buf,'\0'),lockext); defdeflock=tstrdup(buf); if(!isgrpwrite&&!lstat(defdeflock,&stbuf)&&stbuf.st_uid!=uid&& stbuf.st_uid!=ROOT_uid) { int i=lastdirsep(buf)-buf; memcpy(buf2,buf,i);buf2[i]='\0'; /* try & rename bogus lockfile */ rnmbogus(defdeflock,&stbuf,i,0); /* out of the way */ } *chp='\0'; } else nl: defdeflock=empty; /* no lock needed */ return 1; } procmail-3.22/src/robust.c0100644006717600001440000001224707316763027014745 0ustar guenthersrc/************************************************************************ * The fault-tolerant system-interface * * * * Copyright (c) 1990-1997, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: robust.c,v 1.33 2001/06/23 08:18:50 guenther Exp $"; #endif #include "procmail.h" #include "robust.h" #include "misc.h" #include "pipes.h" #include "common.h" #include "mailfold.h" mode_t cumask; #define nomemretry noresretry #define noforkretry noresretry /* set nextexit to prevent elog() from using malloc() */ void nomemerr(len)const size_t len; { static const char outofmem[]="Out of memory"; nextexit=2;nlog(outofmem);elog("\n"); syslog(LOG_NOTICE,"%s as I tried to allocate %ld bytes\n",outofmem, (long)len); if(!privileged&&buf&&buf2) { buf[linebuf-1]=buf2[linebuf-1]='\0';elog("buffer 0:");logqnl(buf); elog("buffer 1:");logqnl(buf2); } if(retval!=EX_TEMPFAIL) retval=EX_OSERR; Terminate(); } static void heapdefrag P((void)) { register void*p; lcking|=lck_MEMORY; if(p=malloc(1)) free(p); /* works on some systems with latent free */ } void*tmalloc(len)const size_t len; /* this malloc can survive a temporary */ { void*p;int i; /* "out of swap space" condition */ lcking|=lck_ALLOCLIB; if(p=malloc(len)) goto ret; heapdefrag();heapdefrag(); /* try some magic */ for(i=nomemretry;i<0||i--;) { suspend(); /* problems? don't panic, wait a few secs till */ if(p=malloc(len)) /* some other process has paniced (and died 8-) */ ret: { lcking&=~(lck_MEMORY|lck_ALLOCLIB); return p; } } nomemerr(len); } void*trealloc(old,len)void*const old;const size_t len; { void*p;int i; lcking|=lck_ALLOCLIB; if(p=realloc(old,len)) goto ret; /* for comment see tmalloc above */ heapdefrag();heapdefrag(); for(i=nomemretry;i<0||i--;) { suspend(); if(p=realloc(old,len)) ret: { lcking&=~(lck_MEMORY|lck_ALLOCLIB); return p; } } nomemerr(len); } void*fmalloc(len)const size_t len; /* 'fragile' malloc */ { void*p; lcking|=lck_ALLOCLIB;p=malloc(len);lcking&=~lck_ALLOCLIB; return p; } void*frealloc(old,len)void*const old;const size_t len; /* 'fragile' realloc */ { void*p; lcking|=lck_ALLOCLIB;p=realloc(old,len);lcking&=~lck_ALLOCLIB; return p; } void tfree(p)void*const p; { lcking|=lck_ALLOCLIB;free(p);lcking&=~lck_ALLOCLIB; } #include "shell.h" pid_t sfork P((void)) /* this fork can survive a temporary */ { pid_t i;int r; /* "process table full" condition */ zombiecollect();elog(empty);r=noforkretry; /* flush log, just in case */ while((i=fork())==-1) { lcking|=lck_FORK; if(!(r<0||r--)) break; if(waitfor((pid_t)0)==NO_PROCESS) suspend(); } lcking&=~lck_FORK; return i; } void opnlog(file)const char*file; { int i; elog(empty); /* flush stderr */ if(!*file) /* empty LOGFILE? */ file=devnull; /* substitute the bitbucket */ if(0>(i=opena(file))) /* error? keep the old LOGFILE */ writeerr(file),syslog(LOG_NOTICE,slogstr,errwwriting,file); else rclose(STDERR),rdup(i),rclose(i),logopened=1; } int opena(a)const char*const a; { yell("Opening",a); #ifdef O_CREAT return ropen(a,O_WRONLY|O_APPEND|O_CREAT,NORMperm); #else ;{ int fd; return (fd=ropen(a,O_WRONLY,0))<0?creat(a,NORMperm):fd; } #endif } int ropen(name,mode,mask)const char*const name;const int mode; const mode_t mask; { int i,r; /* a SysV secure open */ for(r=noresretry,lcking|=lck_FILDES;0>(i=open(name,mode,mask));) if(errno!=EINTR&&!(errno==ENFILE&&(r<0||r--))) break; /* survives a temporary "file table full" condition */ lcking&=~lck_FILDES; return i; } int rpipe(fd)int fd[2]; { int i,r; /* catch "file table full" */ for(r=noresretry,lcking|=lck_FILDES;0>(i=pipe(fd));) if(!(errno==ENFILE&&(r<0||r--))) { *fd=fd[1]= -1; break; } lcking&=~lck_FILDES; return i; } int rdup(p)const int p; { int i,r; /* catch "file table full" */ for(r=noresretry,lcking|=lck_FILDES;0>(i=dup(p));) if(!(errno==ENFILE&&(r<0||r--))) break; lcking&=~lck_FILDES; return i; } int rclose(fd)const int fd; /* a SysV secure close (signal immune) */ { int i; while((i=close(fd))&&errno==EINTR); return i; } int rread(fd,a,len)const int fd,len;void*const a; /* a SysV secure read */ { int i; while(0>(i=read(fd,a,(size_t)len))&&errno==EINTR); return i; } /* a SysV secure write */ int rwrite(fd,a,len)const int fd,len;const void*const a; { int i; while(0>(i=write(fd,a,(size_t)len))&&errno==EINTR); return i; } void ssleep(seconds)const unsigned seconds; { long t; sleep(seconds); if(alrmtime) if((t=alrmtime-time((time_t*)0))<=1) /* if less than 1s timeout */ ftimeout(); /* activate it by hand now */ else /* set it manually again, to avoid problems with */ alarm((unsigned)t); /* badly implemented sleep library functions */ } void doumask(mask)const mode_t mask; { umask(cumask=mask); } procmail-3.22/src/sublib.c0100644006717600001440000001362407347315117014704 0ustar guenthersrc/************************************************************************ * Collection of standard library substitute routines * * * * Copyright (c) 1990-2001, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: sublib.c,v 1.32 2001/08/31 04:54:15 guenther Exp $"; #endif #include "includes.h" #include "sublib.h" #ifdef NOmemmove void*smemmove(To,From,count)void*To;const void*From;register size_t count; #ifdef NObcopy /* silly compromise, throw */ { register char*to=To;register const char*from=From;/*void*old;*/ /*old=to;*/count++;to--;from--; /* away space to be syntactically correct */ if(to<=from) { goto jiasc; do { *++to= *++from; /* copy from above */ jiasc:; } while(--count); } else { to+=count;from+=count; goto jidesc; do { *--to= *--from; /* copy from below */ jidesc:; } while(--count); } return To/*old*/; #else { bcopy(From,To,count); return To; #endif /* NObcopy */ } #endif /* NOmemmove */ #include "shell.h" #ifdef NOstrpbrk char*sstrpbrk(st,del)const char*const st,*del; { const char*f=0,*t; for(f=0;*del;) if((t=strchr(st,*del++))&&(!f||t0) *p++='\0'; } #endif #ifdef NOstrlcat size_t sstrlcat(dst,src,size)char *dst;const char*src;size_t size; { const char*start=dst; if(size>0) { size--; /* reserve space for the NUL */ while(size>0&&*dst) /* skip to the end */ size--,dst++; while(size>0&&*src) /* copy over characters */ size--,*dst++= *src++; *dst='\0'; /* hasta la vista, baby! */ } return dst-start+strlen(src); } size_t sstrlcpy(dst,src,size)char *dst;const char*src;size_t size; { const char*start=dst; if(size>0) { size--; /* reserve space for the NUL */ while(size>0&&*src) /* copy over characters */ size--,*dst++= *src++; *dst='\0'; /* hasta la vista, baby! */ } return dst-start+strlen(src); } #endif #ifdef NOstrerror char *sstrerror(int err) { #ifndef NOsys_errlist extern int sys_nerr;extern char*sys_errlist[]; if(err>=0&&err=36) goto fault; for(;;str++) /* skip leading whitespace */ { switch(*str) { case '\t':case '\n':case '\v':case '\f':case '\r':case ' ': continue; } break; } switch(*str) /* any signs? */ { case '-':sign=1; case '+':str++; } if(*str=='0') /* leading zero(s)? */ { start++; if((i= *++str)=='x'||i=='X') /* leading 0x or 0X? */ if(!base||base==16) base=16,str++; /* hexadecimal all right */ else goto fault; else if(!base) base=8; /* then it is octal */ } else if(!base) base=10; /* or else decimal */ goto jumpin; do { found=1;result=result*base+i;str++; /* start converting */ jumpin: if((i=(unsigned)*str-'0')<10); else if(i-'A'+'0'<='Z'-'A') i-='A'-10-'0'; /* collating sequence dependency! */ else if(i-'a'+'0'<='z'-'a') i-='a'-10-'0'; /* collating sequence dependency! */ else break; /* not of this world */ } while(ipw_gid)||setgid(p->pw_gid)||setuid(p->pw_uid)) return EX_OSERR; if(fopen(CHECK_FILE,"r")) { struct stat stbuf; if(argc==2) goto nodir; if(stat(argv[2],&stbuf)||(stbuf.st_mode&S_IRWXU)!=S_IRWXU) fprintf(stderr,"Can't access %s, are you sure it's there?\n",argv[2]); else if(stbuf.st_uid!=p->pw_uid) fprintf(stderr,"%s is owned by uid %ld!=%s, please fix this first\n", argv[2],(long)stbuf.st_uid,p->pw_name); else if(stbuf.st_gid!=p->pw_gid) fprintf(stderr,"%s is owned by gid %ld!=%ld, please fix this first\n", argv[2],(long)stbuf.st_gid,(long)p->pw_gid); else nodir: nargv[0]=getenv("SHELL"),nargv[1]=0,execv(nargv[0],nargv); } else fprintf(stderr, "Please make sure %s can read & access the source tree\n",argv[1]); return EX_UNAVAILABLE; } procmail-3.22/src/sublib.h0100644006717600001440000000124207347315132014677 0ustar guenthersrc/*$Id: sublib.h,v 1.13 2001/08/04 06:55:09 guenther Exp $*/ #ifdef NOmemmove void *smemmove Q((void*To,const void*From,size_t count)); #endif #ifdef NOstrpbrk char *sstrpbrk P((const char*const st,const char*del)); #endif #ifdef SLOWstrstr char *sstrstr P((const char*const phaystack,const char*const pneedle)); #endif #ifdef NEEDbbzero void bbzero Q((void *s,size_t n)); #endif #ifdef NOstrlcat size_t sstrlcat Q((char *dst,const char*src,size_t size)), sstrlcpy Q((char *dst,const char*src,size_t size)); #endif #ifdef NOstrerror char *sstrerror P((int err)); #endif #ifdef NOstrtol long sstrtol P((const char*start,const char**const ptr,int base)); #endif procmail-3.22/src/Makefile0100644006717600001440000000054507223546520014713 0ustar guenthersrc#$Id: Makefile,v 1.9 2000/09/28 01:23:11 guenther Exp $ all: init $(MAKE) make $@ # The only real thing that can be made right now is: init: cd ..; $(MAKE) make init .PRECIOUS: Makefile Makefile makefile Makefiles makefiles: init procmail lockfile formail multigram mailstat setid getparams gethome \ ../autoconf.h autoconf.h: init $(MAKE) make $@ procmail-3.22/src/acommon.h0100644006717600001440000000030607347314522015052 0ustar guenthersrc/*$Id: acommon.h,v 1.3 2001/08/04 07:15:00 guenther Exp $*/ const char *hostname P((void)); char *ultoan P((unsigned long val,char*dest)), *ultstr P((int minwidth,unsigned long val,char*dest)); procmail-3.22/src/recommend.c0100644006717600001440000000340407003244561015361 0ustar guenthersrc/************************************************************************ * recommend Analyses the installation, and makes * * recommendations about suid/sgid modes * ************************************************************************/ /*$Id: recommend.c,v 1.16 1999/10/20 04:47:45 guenther Exp $*/ #include "includes.h" #define PERMIS (S_IRWXU|S_IRWXG&~S_IWGRP|S_IRWXO&~S_IWOTH) char mailspooldir[]=MAILSPOOLDIR; const char dirsep[]=DIRSEP, *const checkf[]={"/bin/mail","/bin/lmail","/usr/lib/sendmail", "/usr/lib/smail",0}; int main(argc,argv)const int argc;const char*const argv[]; { struct group*grp;struct stat stbuf;gid_t gid=(gid_t)-1; const char*const*p;mode_t sgid=0;int chmdir=0; if(argc!=3) { fprintf(stderr,"Please run this program via 'make recommend'\n"); return EX_USAGE; } strchr(mailspooldir,'\0')[-1]='\0'; /* strip last character */ for(p=checkf;*p;p++) if(!stat(*p,&stbuf)&&stbuf.st_mode&S_ISGID) { if(stbuf.st_mode&S_ISGID) sgid=S_ISGID,gid=stbuf.st_gid; break; } if(!stat(mailspooldir,&stbuf)&&!(stbuf.st_mode&S_IWOTH)) if(stbuf.st_mode&S_ISVTX) chmdir=2; else { if(!(stbuf.st_mode&S_IWGRP)) chmdir=1; sgid=S_ISGID;gid=stbuf.st_gid; } if(gid!=stbuf.st_gid) sgid=0; printf("chown root %s\n",argv[1]); if(sgid) if(grp=getgrgid(gid)) printf("chgrp %s %s %s\n",grp->gr_name,argv[1],argv[2]); else printf("chgrp %u %s %s\n",(unsigned)gid,argv[1],argv[2]); printf("chmod %lo %s\n",(unsigned long)(sgid|S_ISUID|PERMIS),argv[1]); if(sgid) printf("chmod %lo %s\n",(unsigned long)(sgid|PERMIS),argv[2]); else if(chmdir==1) goto nogchmod; if(chmdir) printf("chmod %c+w %s/.\n",chmdir==1?'g':'a',mailspooldir); nogchmod: return EXIT_SUCCESS; } procmail-3.22/src/common.h0100644006717600001440000000077707316762770014735 0ustar guenthersrc/*$Id: common.h,v 1.14 2001/06/23 08:18:40 guenther Exp $*/ void shexec P((const char*const*argv)) __attribute__((noreturn)), detab P((char*p)); char *skpspace P((const char*chp)); int waitfor Q((const pid_t pid)); #ifdef NOstrcspn int strcspn P((const char*const whole,const char*const sub)); #endif #ifdef NOstrncasecmp int strncasecmp Q((const char*a,const char*b,size_t l)); #endif #define LENoffset (TABWIDTH*LENtSTOP) #define MAXfoldlen (LENoffset-STRLEN(sfolder)-1) #define NO_PROCESS (-256) procmail-3.22/src/formail.h0100644006717600001440000000225606706023704015055 0ustar guenthersrc/*$Id: formail.h,v 1.15 1999/02/14 04:43:31 srb Exp $*/ #define Bsize 128 #define FORMAILN "formail" #define HEAD_DELIMITER ':' #define Re (re+1) #define putssn(a,l) tputssn(a,(size_t)(l)) #define putcs(a) (errout=putc(a,mystdout)) #define lputssn(a,l) ltputssn(a,(size_t)(l)) #define PRDO poutfd[0] #define PWRO poutfd[1] #define FLD_HEADSIZ ((size_t)offsetof(struct field,fld_text[0])) struct saved {const char*const headr;const int lenr;int rexl;char*rexp;}; extern const char binsh[],sfolder[],couldntw[],formailn[]; extern char ffileno[]; extern int errout,oldstdout,quiet,zap,buflast,lenfileno; extern long initfileno; extern pid_t child; extern int childlimit; extern unsigned long rhash; extern FILE*mystdout; extern int nrskip,nrtotal,retval; extern size_t buflen,buffilled; extern long Totallen; extern char*buf,*logsummary; extern struct field { size_t id_len; union {size_t uTot_len;struct field**ufld_ref;} len_fld; struct field*fld_next; char fld_text[255]; }*rdheader,*xheader,*Xheader,*uheader,*Uheader; #define Tot_len len_fld.uTot_len #define fld_ref len_fld.ufld_ref int eqFrom_ P((const char*const a)), breakfield Q((const char*const line,size_t len)); procmail-3.22/src/ecommon.h0100644006717600001440000000023606706023704015055 0ustar guenthersrc/*$Id: ecommon.h,v 1.4 1994/05/26 14:12:33 berg Exp $*/ void *tmalloc Q((const size_t len)), *trealloc Q((void*old,const size_t len)), tfree P((void*a)); procmail-3.22/src/fields.h0100644006717600001440000000074507223546530014675 0ustar guenthersrc/*$Id: fields.h,v 1.8 2000/09/28 01:23:20 guenther Exp $*/ struct field *findf P((const struct field*const p,struct field**ah)), **addfield Q((struct field**pointer,const char*const text, const size_t totlen)), *delfield P((struct field**pointer)); void cleanheader P((void)), clear_uhead P((struct field*hdr)), concatenate P((struct field*const fldp)), flushfield P((struct field**pointer)), dispfield P((const struct field*p)), addbuf P((void)); int readhead P((void)); procmail-3.22/src/formisc.c0100644006717600001440000001443007316762775015076 0ustar guenthersrc/************************************************************************ * Miscellaneous routines used by formail * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: formisc.c,v 1.41 2001/06/27 06:41:27 guenther Exp $"; #endif #include "includes.h" #include "formail.h" #include "sublib.h" #include "shell.h" #include "common.h" #include "acommon.h" #include "ecommon.h" #include "formisc.h" static const char*skipcomment(start)const char*start; { for(;;) switch(*++start) { case '\0':start--; case ')':return start; case '\\':start++; break; /* Prithee, breaking the 11th commandment here: */ case '(':start=skipcomment(start); /* Thou shalt not re-curse! */ } } char*skipwords(start)char*start; /* skips an RFC 822 address */ { int delim,hitspc,machref,group;char*target,*oldstart; group=1;hitspc=machref=0;target=oldstart=start; if(*start=='<') start++,machref=1; for(;;) { switch(*start) { case '<': /* machine reference */ if(machref) /* cannot be nested */ { target=oldstart;hitspc=0; /* so start over */ goto inc; } goto ret; case '(':start=(char*)skipcomment(start); /* comment */ case ' ':case '\t':case '\n':hitspc|=1; /* linear white space */ inc: start++; continue; case ';': if(group==2) start[1]='\0'; /* terminate the group */ case ',': if(group==2) goto special; /* part of the group */ if(machref) /* allow embedded ,; in a machine reference */ { machref=2; goto special; } goto retz; default: if(!machref&&hitspc==3&&target>oldstart) case '\0':case '>': { if(machref==2) /* embedded ,; so you have to encapsulate it */ { *target++='>';tmemmove(oldstart+1,oldstart,target++-oldstart); *oldstart='<'; } retz: *target='\0'; ret: return start; } if(*start=='\\') *target++='\\',start++; hitspc=2; goto normal; /* normal word */ case ':': if(group==1) group=2; /* groupies! */ case '@':case '.': if(group==1) group=0; /* you had your chance, and you blew it, no group */ special: hitspc=0; normal: *target++= *start++; continue; case '[':delim=']'; /* domain-literal */ break; case '"':*target++=delim='"';start++; } ;{ int i; do if((i= *target++= *start++)==delim) /* corresponding delimiter? */ break; else if(i=='\\'&&*start) /* skip quoted character */ *target++= *start++; while(*start); /* anything? */ } hitspc=2; } } void loadsaved(sp)const struct saved*const sp; /* load some saved text */ { switch(*sp->rexp) { default:loadchar(' '); /* make sure it has leading whitespace */ case ' ':case '\t':; } loadbuf(sp->rexp,sp->rexl); } /* append to buf */ void loadbuf(text,len)const char*const text;const size_t len; { if(buffilled+len>buflen) /* buf can't hold the text */ buf=realloc(buf,buflen+=Bsize); tmemmove(buf+buffilled,text,len);buffilled+=len; } void loadchar(c)const int c; /* append one character to buf */ { if(buffilled==buflen) buf=realloc(buf,buflen+=Bsize); buf[buffilled++]=c; } int getline P((void)) /* read a newline-terminated line */ { if(buflast==EOF) /* at the end of our Latin already? */ { loadchar('\n'); /* fake empty line */ return EOF; /* spread the word */ } loadchar(buflast); /* load leftover into the buffer */ if(buflast!='\n') { int ch; while((ch=getchar())!=EOF&&ch!='\n') rhash=rhash*67067L+(uchar)ch,loadchar(ch); /* load rest of the line */ loadchar('\n'); /* make sure (!), it ends with a newline */ } /* (some code in formail.c depends on a terminating newline) */ return buflast=getchar(); /* look ahead, one character */ } void elog(a)const char*const a; /* error output */ { fputs(a,stderr); } void tputssn(a,l)const char*a;size_t l; { while(l--) putcs(*a++); } void ltputssn(a,l)const char*a;size_t l; { if(logsummary) Totallen+=l; else putssn(a,l); } void lputcs(i)const int i; { if(logsummary) Totallen++; else putcs(i); } void startprog(argv)const char*Const*const argv; { if(nrskip) /* should we still skip this mail? */ { nrskip--;opensink();return; /* count it */ } if(nrtotal>0) nrtotal--; /* count it */ dup(oldstdout); if(*argv) /* do we have to start a program at all? */ { int poutfd[2]; static int children; if(lenfileno>=0) { long val=initfileno++;char*chp; chp=ffileno+LEN_FILENO_VAR; if(val<0) *chp++='-'; ultstr(lenfileno-(val<0),val<0?-val:val,chp); while(*chp==' ') *chp++='0'; } pipe(poutfd); ;{ int maxchild=childlimit?childlimit: (unsigned long)children*CHILD_FACTOR,excode; while(children&&waitpid((pid_t)-1,&excode,WNOHANG)>0) if(!WIFSTOPPED(excode)) /* collect any zombies */ { children--; if((excode=WIFEXITED(excode)? WEXITSTATUS(excode):-WTERMSIG(excode))!=EXIT_SUCCESS) retval=excode; } /* reap some children */ while(childlimit&&children>=childlimit||(child=fork())==-1&&children) for(--children;(excode=waitfor((pid_t)0))!=NO_PROCESS;) { if(excode!=EXIT_SUCCESS) retval=excode; if(--children<=maxchild) break; } } if(!child) /* DON'T fclose(stdin), provokes a bug on HP/UX */ { close(STDIN);close(oldstdout);close(PWRO);dup(PRDO);close(PRDO); shexec(argv); } close(STDOUT);close(PRDO); if(STDOUT!=dup(PWRO)) nofild(); close(PWRO); if(-1==child) nlog("Can't fork\n"),exit(EX_OSERR); children++; } if(!(mystdout=Fdopen(STDOUT,"a"))) nofild(); } void nofild P((void)) { nlog("File table full\n");exit(EX_OSERR); } void nlog(a)const char*const a; { elog(formailn);elog(": ");elog(a); } void logqnl(a)const char*const a; { elog(" \"");elog(a);elog("\"\n"); } void closemine P((void)) { if(fclose(mystdout)==EOF||errout==EOF) { if(!quiet) nlog(couldntw),elog("\n"); exit(EX_IOERR); } } void opensink P((void)) { if(!(mystdout=fopen(DevNull,"a"))) nofild(); } procmail-3.22/src/lastdirsep.c0100644006717600001440000000126306706023705015567 0ustar guenthersrc/************************************************************************ * A file just for lastdirsep() * * * * Copyright (c) 1994-1997, S.R. van den Berg, The Netherlands * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: lastdirsep.c,v 1.4 1997/04/03 01:58:44 srb Exp $"; #endif #include "includes.h" #include "lastdirsep.h" extern const char dirsep[]; char*lastdirsep(filename)const char*filename; /* finds the next character */ { const char*p; /* following the last DIRSEP */ while(p=strpbrk(filename,dirsep)) filename=p+1; return (char*)filename; } procmail-3.22/src/lastdirsep.h0100644006717600001440000000015006706023705015566 0ustar guenthersrc/*$Id: lastdirsep.h,v 1.2 1994/08/18 18:12:39 berg Exp $*/ char *lastdirsep P((const char*filename)); procmail-3.22/src/locking.h0100644006717600001440000000077007316763004015053 0ustar guenthersrc/*$Id: locking.h,v 1.8 2001/06/03 21:56:11 guenther Exp $*/ void unlock P((char**const lockp)); int lockit P((char*name,char**const lockp)), lcllock P((const char*const noext,const char*const withext)), xcreat Q((const char*const name,const mode_t mode,time_t*const tim, const chownit)); #ifdef NOfcntl_lock #ifndef USElockf #ifndef USEflock #define fdlock(fd) 0 #define fdunlock() 0 #endif #endif #endif #ifndef fdlock int fdlock P((int fd)), fdunlock P((void)); #endif extern char*globlock; procmail-3.22/src/mcommon.c0100644006717600001440000000140006706023705015053 0ustar guenthersrc/************************************************************************ * Some common routines to all programs but formail * * * * Copyright (c) 1993-1997, S.R. van den Berg, The Netherlands * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: mcommon.c,v 1.5 1997/04/03 01:58:45 srb Exp $"; #endif #include "includes.h" #include "mcommon.h" static volatile int gotsig; static void fakehandler P((void)) { gotsig=1; } void qsignal(sig,action)const int sig;void(*action)P((void)); { gotsig=0; if(SIG_IGN==signal(sig,(void(*)())fakehandler)) signal(sig,SIG_IGN); else { signal(sig,(void(*)())action); if(gotsig) (*action)(); } } procmail-3.22/src/network.h0100644006717600001440000000116006706023705015107 0ustar guenthersrc/*$Id: network.h,v 1.7 1997/04/02 03:15:41 srb Exp $*/ #include /* socket() sendto() AF_INET /* SOCK_DGRAM */ #include /* gethostbyname() getservbyname() /* getprotobyname() */ #include /* htons() struct sockaddr_in */ #ifndef BIFF_serviceport #define BIFF_serviceport COMSATservice #endif #ifndef h_0addr_list #define h_0addr_list h_addr_list[0] /* POSIX struct member */ #endif #ifndef NO_const /* since network.h is outside the autoconf const check */ #ifdef const /* loop, we need this backcheck for some systems */ #undef const #endif #endif procmail-3.22/src/regexp.h0100644006717600001440000000052106706023706014711 0ustar guenthersrc/*$Id: regexp.h,v 1.13 1994/10/07 15:25:09 berg Exp $*/ struct eps { unsigned opc;struct eps*next; union seps {struct eps*awn;int sopc;void*irrelevoid;} sp; }* bregcomp P((const char*const a,const unsigned ign_case)); char* bregexec Q((struct eps*code,const uchar*const text,const uchar*str,size_t len, const unsigned ign_case)); procmail-3.22/src/shell.h0100644006717600001440000000037106706023706014531 0ustar guenthersrc/*$Id: shell.h,v 1.5 1994/05/26 14:13:41 berg Exp $*/ #ifdef malloc #undef malloc #endif #define malloc(n) tmalloc((size_t)(n)) #define realloc(p,n) trealloc(p,(size_t)(n)) #define free(p) tfree(p) #define tmemmove(t,f,n) memmove(t,f,(size_t)(n)) procmail-3.22/src/variables.c0100644006717600001440000003414407347315153015374 0ustar guenthersrc/************************************************************************ * Environment and variable handling routines used by procmail * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 2000-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: variables.c,v 1.22 2001/08/27 08:53:15 guenther Exp $"; #endif #include "procmail.h" #include "acommon.h" /* for hostname() */ #include "common.h" /* for ultstr() */ #include "cstdio.h" #include "robust.h" #include "shell.h" #include "authenticate.h" #include "goodies.h" #include "misc.h" #include "locking.h" /* for lockit() */ #include "comsat.h" #include "sublib.h" #include "variables.h" struct varval strenvvar[]={{"LOCKSLEEP",DEFlocksleep}, {"LOCKTIMEOUT",DEFlocktimeout},{"SUSPEND",DEFsuspend}, {"NORESRETRY",DEFnoresretry},{"TIMEOUT",DEFtimeout},{"VERBOSE",DEFverbose}, {"LOGABSTRACT",DEFlogabstract}}; struct varstr strenstr[]={{"SHELLMETAS",DEFshellmetas},{"LOCKEXT",DEFlockext}, {"MSGPREFIX",DEFmsgprefix},{"TRAP",empty}, {"SHELLFLAGS",DEFshellflags},{"DEFAULT",DEFdefault},{"SENDMAIL",DEFsendmail}, {"SENDMAILFLAGS",DEFflagsendmail},{"PROCMAIL_VERSION",PM_VERSION}}; #define MAXvarvals maxindex(strenvvar) #define MAXvarstrs maxindex(strenstr) const char lastfolder[]="LASTFOLDER",maildir[]="MAILDIR",scomsat[]="COMSAT", offvalue[]="no"; int didchd; long Stdfilled; char*Stdout; static void asenvtext P((const char*const chp)); /* needed by retStdout */ static const char slinebuf[]="LINEBUF",pmoverflow[]="PROCMAIL_OVERFLOW=yes", exitcode[]="EXITCODE"; static int setxit; static struct dynstring*myenv; static char**lastenv; /* smart putenv, the way it was supposed to be */ const char*sputenv(a)const char*const a; { static int alloced;size_t eq,i;int remove;const char*split;char**preenv; struct dynstring*curr,**last; yell("Assigning",a);remove=0; if(!(split=strchr(a,'='))) /* assignment or removal? */ remove=1,split=strchr(a,'\0'); eq=split-a; /* is it */ for(curr= *(last= &myenv);curr;curr= *(last= &curr->enext)) /* one I made */ if(!strncmp(a,curr->ename,eq)&&((char*)curr->ename)[eq]=='=') { split=curr->ename;*last=curr->enext;free(curr); /* earlier? */ for(preenv=environ;*preenv!=split;preenv++); goto wipenv; } for(preenv=environ;*preenv;preenv++) /* is it in the standard */ if(!strncmp(a,*preenv,eq)&&(*preenv)[eq]=='=') /* environment? */ wipenv: { while(*preenv=preenv[1]) /* wipe this entry out of the environment */ preenv++; break; } i=(preenv-environ+2)*sizeof*environ; if(alloced) /* have we ever alloced the environ array before? */ environ=realloc(environ,i); else alloced=1,environ=tmemmove(malloc(i),environ,i-sizeof*environ); if(!remove) /* if not remove, then add it to both environments */ { for(preenv=environ;*preenv;preenv++); preenv[1]=0;*(lastenv=preenv)=(char*)(split=newdynstring(&myenv,a)); return split+eq+1; } return empty; } /* between calling primeStdout() and retStdout() *no* environment */ void primeStdout(varname)const char*const varname; /* changes are allowed! */ { if(!Stdout) sputenv(varname); Stdout=(char*)myenv; Stdfilled=ioffsetof(struct dynstring,ename[0])+strlen(varname); } void retStdout(newmyenv,fail,unset) /* see note on primeStdout() */ char*const newmyenv;const int fail,unset; { char*var,*p; if(fail&&unset) /* on second thought... */ { myenv=((struct dynstring*)newmyenv)->enext; /* pull it back out */ free(newmyenv);*lastenv=Stdout=0; return; } else if(!fail&&newmyenv[Stdfilled-1]=='\n') /* strip one trailing newline */ Stdfilled--; retbStdout(newmyenv); var=newmyenv+ioffsetof(struct dynstring,ename[0]); /* setup to copy */ p=strchr(var,'='); /* the variable name into buf */ tmemmove(buf,var,p-var); /* so that we can check */ buf[p-var]='\0'; /* for magic */ if(fail) asenvtext(p+1); /* we always have to update the pointers for these */ else asenv(p+1); /* invoke any magic */ } void retbStdout(newmyenv)char*const newmyenv; /* see note on primeStdout() */ { newmyenv[Stdfilled]='\0';*lastenv=(myenv=(struct dynstring*)newmyenv)->ename; Stdout=0; } /* Append a space and then `value' to the last variable set */ void appendlastvar(value)const char*const value; { size_t len;char*p; Stdout=(char*)value;primeStdout(empty); len=Stdfilled+strlen(Stdout+Stdfilled); /* Skip over the header */ p=realloc(Stdout,(Stdfilled=len+1+strlen(value))+1); p[len]=' ';strcpy(p+len+1,buf);retbStdout(p); /* WARNING: no magic here! */ } const char*eputenv(src,dst)const char*const src;char*const dst; { sgetcp=src; return readparse(dst,sgetc,2,0)?0:sputenv(buf); } void setdef(name,value)const char*const name,*const value; { char*p; strcpy(buf,name); /* insert the variable name */ p=strchr(buf,'\0'); /* (find the end) */ *p++='='; /* then the = */ eputenv(value,p); /* expand the value and call sputenv */ } const char*tgetenv(a)const char*const a; { const char*b; return (b=getenv(a))?b:empty; } void setoverflow P((void)) { sputenv(pmoverflow); } void cleanupenv(preserve)int preserve; { static const char*const keepenv[]=KEEPENV,*const ld_[]=LDENV; const char**emax=(const char**)environ,**ep,*const*pp; register const char*p; size_t len; if(!preserve) /* drop the environment */ { for(pp=keepenv;*pp;pp++) /* preserve a happy few */ { len=strlen(*pp); for(ep=emax;p= *ep;ep++) /* scan for this keeper */ if(!strncmp(*pp,p,len)&&(p[len]=='='||p[len-1]=='_')) { *ep= *emax; /* it's fine, swap 'em */ *emax++=p; if(p[len-1]!='_') /* if this wasn't a wildcard match */ break; /* then go on to next keepenv entry */ } } *emax=0; /* drop the rest */ } else { while(*emax) /* find the end of the environment */ emax++; } ep=(const char**)environ; while(*ep) /* check for evil entries */ { p=strchr(*ep,'='); if(!p) /* malformed (no '=')? */ drop: { *ep= *--emax;*emax=0; /* copy from the end */ continue; /* check the swapped entry */ } len=p-*ep+1; /* mark how long the actual name is */ for(pp=(const char*const*)environ;pp!=ep;pp++) /* duplicate entry? */ if(!strncmp(*ep,*pp,len)) goto drop; for(pp=ld_;p= *pp;pp++) /* does it start with LD_ or similar? */ if(!strncmp(*ep,p,strlen(p))) goto drop; ep++; } } void initdefenv(pass,fallback,do_presets)auth_identity*pass; const char*fallback;int do_presets; { const char*p; if(pass) { p=auth_username(pass); if(!p||!*p) p=fallback; setdef(lgname,p); p=auth_shell(pass); if(!p||!*p) p=binsh; setdef(shell,p); setdef(home,auth_homedir(pass));setdef(orgmail,auth_mailboxname(pass)); } else { setdef(lgname,fallback);setdef(shell,binsh); setdef(home,ROOT_DIR);setdef(orgmail,DEAD_LETTER); } setlgcs(tgetenv(lgname)); /* make sure sendcomsat has a copy */ if(do_presets) { static const char*const prestenv[]=PRESTENV; const char*const*pp; int i=MAXvarstrs; do /* initialise all non-empty string variables into the environment */ if(*strenstr[i].sval) setdef(strenstr[i].sname,strenstr[i].sval); while(i--); setdef(host,hostname()); /* the other standard presets */ sputenv(lastfolder); sputenv(exitcode); eputenv(defpath,buf); for(pp=prestenv;*pp;pp++) /* non-standard presets */ eputenv(*pp,buf); } } int alphanum(c)const unsigned c; { switch(c) { case '0':case '1':case '2':case '3':case '4': case '5':case '6':case '7':case '8':case '9': return 2; case 'A':case 'B':case 'C':case 'D':case 'E':case 'F':case 'G':case 'H': case 'I':case 'J':case 'K':case 'L':case 'M':case 'N':case 'O':case 'P': case 'Q':case 'R':case 'S':case 'T':case 'U':case 'V':case 'W':case 'X': case 'Y':case 'Z': case 'a':case 'b':case 'c':case 'd':case 'e':case 'f':case 'g':case 'h': case 'i':case 'j':case 'k':case 'l':case 'm':case 'n':case 'o':case 'p': case 'q':case 'r':case 's':case 't':case 'u':case 'v':case 'w':case 'x': case 'y':case 'z': case '_': return 1; default: return 0; } } void setmaildir(newdir)const char*const newdir; /* destroys buf2 */ { char*chp; didchd=1;*(chp=strcpy(buf2,maildir)+STRLEN(maildir))='='; strcpy(++chp,newdir);sputenv(buf2); } void setlastfolder(folder)const char*const folder; { char*chp;size_t len; setlfcs(folder); len=STRLEN(lastfolder)+2+strlen(folder); strcpy(chp=malloc(len),lastfolder); strlcat(chp,"=",len); strlcat(chp,folder,len); sputenv(chp);free(chp); } int setexitcode(trapisset)int trapisset; { char*p;int forceret; if(setxit&&(p=getenv(exitcode))) /* user specified exitcode? */ { if((forceret=renvint(-2L,p))>=0) /* yes, is it positive? */ retval=forceret; /* then override it */ } else { forceret= -1; if(trapisset) /* no EXITCODE set, TRAP found, provide one */ { p=buf2+STRLEN(exitcode); strcpy(buf2,exitcode);*p='='; ultstr(0,(unsigned long)retval,p+1);sputenv(buf2); } } return forceret; } char*gobenv(chp,end)char*chp,*end; { int found,i; found=0;end--; if(alphanum(i=getb())==1) for(found=1;*chp++=i,chp0) { if(i>crestarg) i=crestarg; crestarg-=i;restargv+=i; /* shift away arguments */ } } else if(!strcmp(buf,dropprivs)) /* drop privileges */ { if(renvint(0L,chp)) { if(verbose) nlog("Assuming identity of the recipient, VERBOSE=off\n"); setids(); } } else if(!strcmp(buf,sdelivered)) /* fake delivery */ { if(renvint(0L,chp)) /* is it really? */ { onguard(); if((thepid=sfork())>0) _exit(retvl2); /* parent: do not pass go */ if(!forkerr(thepid,procmailn)) fakedelivery=1; newid();offguard(); } } else if(!strcmp(buf,lockfile)) { if(!lockit(tstrdup((char*)chp),&globlock)) sputenv(lockfile); /* unset it on failure */ } else if(!strcmp(buf,eumask)) doumask((mode_t)strtol(chp,(char**)0,8)); else if(!strcmp(buf,includerc)) { if(rc>=0) /* INCLUDERC and SWITCHRC only work */ pushrc(chp); /* inside rcfiles */ } /* and not on the command line */ else if(!strcmp(buf,switchrc)) { if(rc>=0) changerc(chp); } else if(!strcmp(buf,host)) { const char*name; if(strcmp(chp,name=hostname())) { yell("HOST mismatched",name); if(rc<0) /* if no rcfile opened yet */ retval=EXIT_SUCCESS,Terminate(); /* exit gracefully as well */ closerc(); } } else { int i=MAXvarvals; do /* several numeric assignments */ if(!strcmp(buf,strenvvar[i].name)) strenvvar[i].val=renvint(strenvvar[i].val,chp); while(i--); asenvtext(chp); /* delegate the text assignments */ } } long renvint(i,env)const long i;const char*const env; { const char*p;long t; t=strtol(env,(char**)&p,10); /* parse like a decimal nr */ if(p==env) for(;;p++) { switch(*p) { case ' ':case '\t':case '\n':case '\v':case '\f':case '\r': continue; /* skip leading whitespace */ case 'o':case 'O': if(!strncasecmp(p+1,"n",1)) case 'y':case 'Y':case 't':case 'T':case 'e':case 'E': t=1; else if(!strncasecmp(p+1,"ff",2)) case 'n':case 'N':case 'f':case 'F':case 'd':case 'D': t=0; else default: t=i; break; case 'a':case 'A':t=2; break; } break; } return t; } procmail-3.22/src/foldinfo.h0100644006717600001440000000234407213106161015213 0ustar guenthersrc #define ft_NOTYET (-3) /* spool file doesn't exist yet */ #define ft_CANTCREATE (-2) /* wrong file type and can't change our mind */ #define ft_TOOLONG (-1) /* path + UNIQnamelen > linebuf? */ #define ft_PIPE 0 /* program, stdout, or /dev/null */ #define ft_MAILDIR 1 /* maildir folder */ #define ft_MH 2 /* MH folder */ #define ft_FILE 3 /* real file */ #define ft_DIR 4 /* msg.inode# directory */ #define ft_lock(type) ((type)>ft_MAILDIR) /* kernel lock fd */ #define ft_atime(type) ((type)==ft_FILE) /* force atime < mtime */ #define ft_dotlock(type) ((type)==ft_FILE) /* dotlock $DEFAULT */ #define ft_delim(type) ((type)==ft_FILE) /* add MMDF delim */ #define ft_checkcloser(type) ((type)>ft_MH) #define ft_forceblank(type) ((type)!=ft_MAILDIR) /* force blank line at end */ int foldertype Q((int type,int forcedir,mode_t*const modep, struct stat*const paranoid)), screenmailbox Q((char*chp,const gid_t egid,const int Deliverymode)); extern const char maildirnew[]; extern int accspooldir; #ifdef TESTING static const char*FT2str[]= { "Not-Yet","Can't-Create","Too-Long", "Pipe","Maildir","MH","File","Directory" }; #define ft2str (FT2str-ft_NOTYET) #endif procmail-3.22/src/from.h0100644006717600001440000000044207176357657014405 0ustar guenthersrc/* $Id: from.h,v 1.2 2000/10/27 20:03:59 guenther Exp $ */ int eqFrom_ P((const char*const a)); const char *skipFrom_ P((const char*startchar,long*tobesentp)); void makeFrom P((const char*from,const char*const invoker)), checkprivFrom_ Q((uid_t euid,const char*logname,int override)); procmail-3.22/src/lmtp.c0100644006717600001440000005274107316763003014400 0ustar guenthersrc/************************************************************************ * LMTP (Local Mail Transfer Protocol) routines * * * * Copyright (c) 1997-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: lmtp.c,v 1.13 2001/06/28 21:44:28 guenther Exp $" #endif #include "procmail.h" #ifdef LMTP #include "sublib.h" #include "robust.h" #include "misc.h" #include "shell.h" #include "authenticate.h" #include "cstdio.h" #include "mailfold.h" #include "memblk.h" #include "from.h" #include "lmtp.h" /* lhlo => print stuff mail from: => fork Generate From_ line (just body type) rcpt to: => deduce pass entry, push it somewhere data => process data till end, if we run out of mem, give 552 (or 452?) response bdat => allocate requested size buffer & read it in, or 552/452 reponse. vrfy => just check the user end of data => fork, perform deliveries, giving error codes as you go. rset => if in child, die. Should this simply be a _conformant_ implementation or should it be _strict_? For example, after accepting a BDAT, should a DATA command cause LMTP to not accept anything but a RSET? As it stands we'll be lenient and simply ignore invalid commands. It's undefined behavior, so we can do what we want. */ #define INITIAL_RCPTS 10 #define INCR_RCPTS 20 static int lreaddyn P((void)); int childserverpid; static ctopfd; static char*overread; static size_t overlen; static int nliseol; /* is a plain \n the EOL delimiter? */ static char*bufcur; static const char lhlomsg[]= "250-localhost\r\n\ 250-SIZE\r\n\ 250-8BITMIME\r\n\ 250-PIPELINING\r\n\ 250-CHUNKING\r\n\ 250 ENHANCEDSTATUSCODES\r\n", nomemmsg[]="452 4.3.0 insufficient system storage\r\n", quitmsg[]="221 2.3.0 Goodbye!\r\n", baduser[]="550 5.1.1 mailbox unknown\r\n", protogood[]="250 2.5.0 command succeeded\r\n", protobad[]="503 5.5.1 command unexpected\r\n"; static void bufwrite(buffer,len,flush) const char*buffer;int len;int flush; { int already; if((already=bufcur-buf2)+len>linebuf||flush) { if(already&&already!=rwrite(savstdout,bufcur=buf2,already)|| len&&len!=rwrite(savstdout,buffer,len)) exit(EX_OSERR); } else bufcur=(char*)tmemmove(bufcur,buffer,len)+len; } #define bufinit bufcur=buf2 #define skiptoeol do c=getL(); while(c!='\n'); /* skip to end-o'-line */ static int unexpect(str)const char*str; { char c; while(*str!='\0') { if((c=getL())-'a'<='z'-'a'&&c>='a')c-='a'-'A'; if(c!= *str) { if(c!='\n') skiptoeol; return 1; } str++; } return 0; } #define NOTcommand(command,offset) unexpect((msgcmd=command)+offset) static long slurpnumber P((void)) { int c;long total=0; /* strtol would require buffering the number */ while((c=getL())-'0'>=0&&c-'0'<10) { total*=10; total+=c-'0'; } if(c!=' '&&c!='\n'&&c!='\r') { skiptoeol; return -1; } if(c=='\n') { ungetb(c); } return total; } /* Slurp "<.*?>" with rfc821 quoting rules, strip the trailing angle bracket */ /* This is stricter than it needs to be, but not as strict as the rfcs */ static char *slurpaddress P((void)) { char*p=buf,c,*const last=buf+linebuf-1; /* -1 to leave room for the \0 */ do{*p=getL();}while(*p==' '); if(*p!='<') { if(*p!='\n') skiptoeol; return 0; } while(++p': *p='\0'; /* strip the trailing '>' */ return tstrdup(buf); default: break; } goto syntax_error; } /* given a path from slurpaddress() extract the local-part and strip quoting */ static char *extractaddress(path)char *path; { char *p=path+1,*q=path; /* +1 to skip the leading '<' */ if(*p=='@') /* route address. Perhaps we should assume that */ while(1) /* sendmail will strip these for recipients? */ switch(*++p) { case ':':p++; /* we can take some shortcuts because we know */ goto found; /* the quoting and length to be okay */ case '\\':p++; break; case '"': while(*++p!='"') if(*p=='\\') p++; break; case '>':case '\0': free(path); /* no local part */ return 0; } found: while(1) /* strip quoting */ { switch(*p) { case '\\': p++; break; case '"': while(*++p!='"') if(*p=='\\') *q++= *++p; else *q++= *p; p++; continue; case '\0':case '>': /* no final host part? That's fine with us */ case '@': *q='\0'; return path; } *q++= *p++; } } /* linebuf MUST be at least 256, and should be at least 1024 or so for buffering */ /* LMTP connection states */ #define S_START 0 /* lhlo => S_MAIL */ #define S_MAIL 1 /* mail => S_RCPT */ #define S_RCPT 2 /* data => S_MAIL, bdat => S_BDAT */ #define S_BDAT 3 /* bdat last => S_MAIL */ /* lmtp: run the LTMP protocol. It returns in children, never the parent. The return value is the array of recipients, and into the first argument is stored a pointer to one past the end of that array. the second argument should be the username of the person running procmail (this is used to generate the From_ line) The returning child should handle the deliveries, calling lmtpresponse() with the exitcode of each one, then write 'overlen' bytes from 'overread' to 'ctopfd', and exit. If something unrecoverable goes wrong, and it can't do the necessary calls to lmtpresponse(), then it should exit with some non-zero status. The parent will then syslog it, and exit with EX_SOFTWARE. (See getL() in cstdio.c) */ struct auth_identity **lmtp(lrout,invoker) struct auth_identity***lrout;char*invoker; { static const char cLHLO[]="LHLO ",cMAIL[]="MAIL FROM:",cRCPT[]="RCPT TO:", cDATA[]="DATA",cBDAT[]="BDAT",cRSET[]="RSET",cVRFY[]="VRFY ",cQUIT[]="QUIT", cNOOP[]="NOOP"; const char*msg,*msgcmd;int flush=0,c,lmtp_state=S_START;long size=0; auth_identity**rcpts,**lastrcpt,**currcpt; pushfd(STDIN);overread=0;overlen=0;nliseol=1; bufinit;ctopfd=-1; /* setup our output */ currcpt=rcpts=malloc(INITIAL_RCPTS*sizeof*rcpts); lastrcpt=INITIAL_RCPTS+currcpt; bufwrite("220 ",4,0);bufwrite(procmailn,strlen(procmailn),0); bufwrite(Version,strchr(Version,'\n')-Version,0); bufwrite(" LMTP\r\n",7,1); while(1) { do{c=getL();}while(c==' '); switch(c) { case 'l': case 'L': if(NOTcommand(cLHLO,1)) goto unknown_command; ;{ int sawcrnl=0; /* autodetect \r\n vs plain \n */ while((c=getL())!='\n') if(c=='\r') { c=getL(); /* they lose on \r\r\n */ if(c=='\n') { sawcrnl=1; break; } } flush=1; if(lmtp_state!=S_START) { msg=protobad; goto message; } else { lmtp_state=S_MAIL; msg=lhlomsg;msgcmd=0; if(sawcrnl) nliseol=0; } } goto message; case 'm': case 'M': if(NOTcommand(cMAIL,1)) goto unknown_command; ;{ int pipefds[2];char*from; if(lmtp_state!=S_MAIL) { skiptoeol;msg=protobad; goto message; } if(!(from=slurpaddress())) { msg="553 5.1.7 Unable to parse MAIL address\r\n"; goto message; } size=0; goto jumpin; do { switch(c) { case 's':case 'S': if(unexpect("IZE=")) /* rfc1653 */ goto unknown_param; size=slurpnumber(); if(size<0) /* will be zerod at loop top */ goto unknown_param; break; case 'b':case 'B': if(unexpect("ODY=")) /* rfc1652 */ goto unknown_param; while((c=getL())!='\r') /* just ignore */ switch(c) /* the parameter as we */ { case ' ':goto jumpin; /* can't do anything */ case '\n':goto jumpout; /* useful with it */ } case '\r': if((c=getL())=='\n') continue; default: skiptoeol; unknown_param: msg="504 5.5.4 unknown MAIL parameter or bad value\r\n"; goto message; } jumpin: do c=getL(); while(c==' '); } while(c!='\n'); jumpout: rpipe(pipefds); /* * This is a pipe on which to write back one byte which, * if non-zero, indicates something went wrong and the * parent should act like the MAIL FROM: never happened. * If it was zero then it should be followed by any extra * LMTP commands that the child read past what it needed. */ if(!(childserverpid=sfork())) { char status=0; rclose(pipefds[0]); ctopfd=pipefds[1]; bufwrite(0,0,1); /* free up buf2 for makeFrom() */ makeFrom(from+1,invoker); /* bufinit; only needed if buf2 might be realloced */ free(from); if(size&&!resizeblock(&themail,size+=filled+3,1))/* try for */ { status=1; /* the memory now, +3 for the "." CRLF */ bufwrite(nomemmsg,STRLEN(nomemmsg),1); } if(rwrite(pipefds[1],&status,sizeof(status))!=sizeof(status)) exit(EX_OSERR); if(status) exit(0); lmtp_state=S_RCPT; msg=protogood; goto message; } rclose(pipefds[1]); if(!forkerr(childserverpid,buf)) { char status=1; rread(pipefds[0],&status,sizeof(status)); if(!status) { pushfd(pipefds[0]); /* pick up what the child */ lmtp_state=S_MAIL; /* left lying around */ bufinit; } continue; /* restart loop */ } rclose(pipefds[0]); msg="421 4.3.2 unable to fork for MAIL\r\n"; goto message; } case 'r': case 'R': if((c=getL())=='s'||c=='S') { if(NOTcommand(cRSET,2)) goto unknown_command; skiptoeol; if(lmtp_state!=S_START) lmtp_state=S_MAIL; msg=protogood; goto message; } if((c!='c'&&c!='C')||NOTcommand(cRCPT,2)) goto unknown_command; if(lmtp_state!=S_RCPT) { skiptoeol; msg=protobad; /* don't change lmtp_state */ goto message; } if(currcpt==lastrcpt) /* do I need some space? */ { int num=lastrcpt-rcpts; rcpts=realloc(rcpts,(num+INCR_RCPTS)*sizeof*rcpts); currcpt=rcpts+num;lastrcpt=currcpt+INCR_RCPTS; } ;{ char *path,*mailbox;auth_identity*temp; /* if it errors, extractaddress() will free its argument */ if(!(path=slurpaddress())||!(mailbox=extractaddress(path))) { msg="550 5.1.3 address syntax error\r\n"; goto message; } /* if we were to handle ESMTP params on the RCPT verb, we would do so here */ skiptoeol; if(!(temp=auth_finduser(mailbox,0))) { msg="550 5.1.1 mailbox unknown\r\n"; free(path); goto message; } auth_copyid(*currcpt=auth_newid(),temp); free(path); currcpt++; msg="250 2.1.5 ok\r\n"; goto message; } case 'd': case 'D': flush=1; if(NOTcommand(cDATA,1)) goto unknown_command; skiptoeol; if(lmtp_state!=S_RCPT) { msg=protobad; goto message; } if(currcpt==rcpts) { msg="554 5.5.1 to whom?\r\n"; goto message; } msg="354 Enter DATA terminated with a solo \".\"\r\n"; bufwrite(msg,strlen(msg),1); if(!(lreaddyn())) { /* * At this point we either have more data to read which we * can't fit, or, worse, we've lost part of the command stream. * The (ugly) solution/way out is to send the 452 status code * and then kill both ourselves and out parent. That's the * only solution short of teaching lreaddyn() to take a small * buffer (buf2?) and repeatedly fill it looking for the end * of the data stream, but that's too ugly. If the malloc * failed then the machine is probably hurting enough that * our exit can only help. */ bufwrite(nomemmsg,STRLEN(nomemmsg),1); goto quit; } deliver: readmail(2,0L); /* fix up things */ lastrcpt=rcpts; rcpts=realloc(rcpts,(currcpt-rcpts)*sizeof*rcpts); *lrout=(currcpt-lastrcpt)+rcpts; return rcpts; case 'b': case 'B': /* rfc1830's BDAT */ if(NOTcommand(cBDAT,1)) goto unknown_command; if((c=getL())!=' ') { if(c!='\n') skiptoeol; msg="504 5.5.4 octets count missing\r\n"; goto message; } if(lmtp_statesize) { if(!resizeblock(&themail,size=filled+length+BLKSIZ,1)) { int i; /* eat the BDAT data */ while(length>linebuf) { i=readLe(buf,linebuf); if(i<0) goto quit; length-=i; } if(length&&0>readLe(buf,length)) goto quit; lmtp_state=S_MAIL; msg=nomemmsg; flush=1; goto message; } } while(length>0) { int i=readLe(themail.p+filled,length); if(!i) exit(EX_NOINPUT); else if(i<0) goto quit; length-=i; filled+=i; } if(last) { if(!nliseol) /* change CRNL to NL */ { char*in,*out,*q,*last; last=(in=out=themail.p)+filled; while(inin:!!(q=last)) { if(in!=out) memmove(out,in,q-in); out+=q-in;in=q; } else if(++in==last||*in!='\n') /* keep the CR? */ *out++='\r'; resizeblock(&themail,(filled-=in-out)+1,1); } goto deliver; } msg=protogood; goto message; } case 'v': case 'V': if(NOTcommand(cVRFY,1)) goto unknown_command; flush=1; ;{ char *path,*mailbox; auth_identity *temp; if(!(path=slurpaddress())||!(mailbox=extractaddress(path))) { msg="501 5.1.3 address syntax error\r\n"; goto message; } skiptoeol; if(!(temp=auth_finduser(mailbox,0))) { msg="550 5.1.1 user unknown\r\n"; free(path); goto message; } free(path); msg="252 2.5.0 successful\r\n"; goto message; } case 'q': case 'Q': if(NOTcommand(cQUIT,1)) goto unknown_command; quit: if(ctopfd>=0) /* we're the kid: tell the parent to quit */ { rwrite(ctopfd,cQUIT,STRLEN(cQUIT)); rclose(ctopfd); } else bufwrite(quitmsg,STRLEN(quitmsg),1); exit(0); case 'n': case 'N': if(NOTcommand(cNOOP,1)) goto unknown_command; skiptoeol; flush=1; msg="200 2.0.0 ? Nope\r\n"; goto message; default: skiptoeol; unknown_command: case '\n': msg="500 5.5.1 Unknown command given\r\n";msgcmd=0; flush=1; break; } message: bufwrite(msg,10,0);msg+=10; if(msgcmd) /* insert the command name */ { msg--; bufwrite(msgcmd,4,0); msgcmd=0; } bufwrite(msg,strlen(msg),flush||endoread()); flush=0; } } void flushoverread P(()) /* pass upwards the extra LMTP data */ { int i; while(overlen) { if(0>(i=rwrite(ctopfd,overread,overlen))) return; /* there's nothing to be done */ overlen-=i; overread+=i; } } void freeoverread P(()) /* blow away the extra LMTP data */ { if(overread) { bbzero(overread,overlen); free(overread); overread=0; } } #define X(str) {str,STRLEN(str)} static struct{const char*mess;int len;}ret2LMTP[]= { X("500 5.0.0 usage error\r\n"), /* USAGE */ X("501 5.6.0 data error\r\n"), /* DATAERR */ X("550 5.3.0 input missing\r\n"), /* NOINPUT */ X("550 5.1.1 no such user\r\n"), /* NOUSER */ X("550 5.1.2 no such host\r\n"), /* NOHOST */ X("554 5.0.0 something didn't work\r\n"), /* UNAVAILABLE */ X("554 5.3.0 internal software error\r\n"), /* SOFTWARE */ X("451 4.0.0 OS error\r\n"), /* OSERR */ X("554 5.3.5 system file error\r\n"), /* OSFILE */ X("550 5.0.0 output error\r\n"), /* CANTCREAT */ X("451 4.0.0 I/O error\r\n"), /* IOERR */ X("450 4.0.0 deferred\r\n"), /* TEMPFAIL */ X("554 5.5.0 protocol error\r\n"), /* PROTOCOL */ X("550 5.0.0 insufficient permission\r\n"), /* NOPERM */ X("554 5.3.5 configuration error\r\n"), /* CONFIG */ }; #undef X void lmtpresponse(retcode)int retcode; { const char*message;int len; if(!retcode) message=protogood,len=STRLEN(protogood); else { if(retcode<0) retcode=EX_SOFTWARE; if(0>(retcode-=EX__BASE)||retcode>=(sizeof ret2LMTP/sizeof ret2LMTP[0])) retcode=EX_UNAVAILABLE-EX__BASE; message=ret2LMTP[retcode].mess;len=ret2LMTP[retcode].len; } if(len!=rwrite(savstdout,message,len)) exit(EX_OSERR); } #define IS_READERR (-1) #define IS_NORMAL 0 #define IS_CR 1 #define IS_CRBOL 2 #define IS_CRDOT 3 #define IS_DOTCR 4 #define IS_NLBOL 5 #define IS_NLDOT 6 #define EXIT_LOOP(s) {state=(s);goto loop_exit;} static char*lmtp_read_crnl(char*p,long left,void*statep) { int got,state= *(int*)statep; register char*in,*q,*last; do { if(0>=(got=readL(p,left))) /* read mail */ { state=IS_READERR; return p; } last=(in=p)+got; /* * A state machine to read LMTP data. If 'nliseol' isn't set, * then \r\n is the end-o'-line string, and \n is only special * in it. \r's are stripped from \r\n, but are otherwise preserved. */ switch(state) { case IS_CR: goto is_cr; case IS_CRBOL:goto is_crbol; case IS_CRDOT:goto is_crdot; case IS_DOTCR:goto is_dotcr; case IS_NORMAL:break; case IS_NLBOL:case IS_NLDOT:case IS_READERR: exit(EX_SOFTWARE); } while(inin:!!(q=last)) { if(in!=p) memmove(p,in,q-in); p+=q-in;in=q; } else /* CR */ { found_cr: *p++= *in++; /* tenatively save the \r */ if(in==last) EXIT_LOOP(IS_CR) is_cr: if(*in!='\n') continue; p[-1]= *in++; /* overwrite the \r */ if(in==last) /* CRLF */ EXIT_LOOP(IS_CRBOL) is_crbol: if(*in=='\r') /* CRLF CR? */ goto found_cr; if(*in!='.') { *p++= *in++; continue; } if(++in==last) /* CRLF "." */ EXIT_LOOP(IS_CRDOT) is_crdot: if((*p++= *in++)!='\r') continue; if(in==last) /* CRLF "." CR */ EXIT_LOOP(IS_DOTCR) is_dotcr: if(*in=='\n') /* CRLF "." CRLF */ { p--; /* remove the trailing \r */ if((overlen=last-++in)>0) /* should never be negative */ tmemmove(overread=malloc(overlen),in,overlen); return p; } } state=IS_NORMAL; /* we must have fallen out because in==last */ loop_exit: got-=in-p; /* correct for what disappeared */ } while(left-=got); /* change listed buffer size */ *(long*)statep=state; /* save state */ return 0; } static char*lmtp_read_nl(char*p,long left,void*statep) { int got,state= *(int*)statep; register char*in,*q,*last; do { if(0>=(got=readL(p,left))) /* read mail */ { state=IS_READERR; return p; } last=(in=p)+got; /* * A state machine to read LMTP data. \n is the end-o'-line * character and \r is not special at all. */ switch(state) { case IS_CR:case IS_CRBOL:case IS_CRDOT:case IS_DOTCR: case IS_READERR: exit(EX_SOFTWARE); case IS_NLBOL:goto is_nlbol; case IS_NLDOT:goto is_nldot; case IS_NORMAL:break; } while(inin:!!(q=last)) { if(in!=p) memmove(p,in,q-in); p+=q-in;in=q; } else /* LF */ { do { *p++= *in++; is_nlbol: ; } while(in0) /* should never be negative */ tmemmove(overread=malloc(overlen),in,overlen); return p; } *p++= *in++; } state=IS_NORMAL; /* we must have fallen out because in==last */ loop_exit: got-=in-p; /* correct for what disappeared */ } while(left-=got); /* change listed buffer size */ *(long*)statep=state; /* save state */ return 0; } static int lreaddyn() { int state=nliseol?IS_NLBOL:IS_CRBOL; read2blk(&themail,&filled,nliseol?&lmtp_read_nl:&lmtp_read_crnl, (cleanup_func_type*)0,&state); return state!=IS_READERR; } #else int lmtp_dummy_var; /* to prevent insanity in some linkers */ #endif procmail-3.22/src/mailfold.c0100644006717600001440000003651607347314772015226 0ustar guenthersrc/************************************************************************ * Routines that deal with the mailfolder(format) * * * * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands * * Copyright (c) 1999-2001, Philip Guenther, The United States * * of America * * #include "../README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: mailfold.c,v 1.104 2001/08/04 07:16:26 guenther Exp $"; #endif #include "procmail.h" #include "acommon.h" #include "sublib.h" #include "robust.h" #include "misc.h" #include "memblk.h" #include "pipes.h" #include "common.h" #include "exopen.h" #include "goodies.h" #include "variables.h" #include "locking.h" #include "lastdirsep.h" #include "foldinfo.h" #include "from.h" #include "shell.h" #include "mailfold.h" int logopened,rawnonl; off_t lasttell; static long lastdump; static volatile int mailread; /* if the mail is completely read in already */ static struct dyna_array confield; /* escapes, concatenations */ static const char*realstart,*restbody; static const char from_expr[]=FROM_EXPR; static const char*fifrom(fromw,lbound,ubound) const char*fromw,*const lbound;char*const ubound; { int i; /* terminate & scan block */ i= *ubound;*ubound='\0';fromw=strstr(mx(fromw,lbound),from_expr);*ubound=i; return fromw; } static int doesc; /* inserts escape characters on outgoing mail */ static long getchunk(s,fromw,len)const int s;const char*fromw;const long len; { static const char esc[]=ESCAP,*ffrom,*endp; if(doesc) /* still something to escape since last time? */ doesc=0,rwrite(s,esc,STRLEN(esc)),lastdump++; /* escape it */ ffrom=0; /* start with a clean slate */ if(fromwrestbody) /* nothing yet? but in range? */ { if((endp+=STRLEN(from_expr)-1)>(ffrom=themail.p+filled)) /* add slack */ endp=(char*)ffrom; /* make sure we stay within bounds */ ffrom=fifrom(fromw,restbody,endp); /* scan body block */ } return ffrom?(doesc=1,(ffrom-fromw)+1L):len; /* +1 to write out the '\n' */ } #ifdef sMAILBOX_SEPARATOR #define smboxseparator(fd) (ft_delim(type)&&\ (part=len,rwrite(fd,sMAILBOX_SEPARATOR,STRLEN(sMAILBOX_SEPARATOR)))) #define MAILBOX_SEPARATOR #else #define smboxseparator(fd) #endif /* sMAILBOX_SEPARATOR */ #ifdef eMAILBOX_SEPARATOR #define emboxseparator(fd) \ (ft_delim(type)&&rwrite(fd,eMAILBOX_SEPARATOR,STRLEN(eMAILBOX_SEPARATOR))) #ifndef MAILBOX_SEPARATOR #define MAILBOX_SEPARATOR #endif #else #define emboxseparator(fd) #endif /* eMAILBOX_SEPARATOR */ long dump(s,type,source,len)const int s,type;const char*source; long len; { int i;long part; lasttell=i= -1;SETerrno(EBADF); if(s>=0) { if(ft_lock(type)&&(lseek(s,(off_t)0,SEEK_END),fdlock(s))) nlog("Kernel-lock failed\n"); lastdump=len;doesc=0; if(ft_delim(type)&&!rawnonl) part=getchunk(s,source,len); /* must escape From_ */ else part=len; lasttell=lseek(s,(off_t)0,SEEK_END); if(!rawnonl) { smboxseparator(s); /* optional separator */ #ifndef NO_NFS_ATIME_HACK /* if it is a file, trick NFS into an */ if(part&&ft_atime(type)) /* a_time wasn't a file */ if(ft_lock(type)) { int serrno=errno; /* save any error information */ if(fdunlock()) nlog("Kernel-unlock failed\n"); SETerrno(serrno); } i=rclose(s)||i; } /* return an error even if nothing was to be sent */ return i&&!len?-1:len; } static int dirfile(chp,linkonly,type)char*const chp;const int linkonly,type; { static const char lkingto[]="Linking to";struct stat stbuf; if(type==ft_MH) { long i=0; /* first let us try to prime i with the */ #ifndef NOopendir /* highest MH folder number we can find */ long j;DIR*dirp;struct dirent*dp;char*chp2; if(dirp=opendir(buf)) { while(dp=readdir(dirp)) /* there still are directory entries */ if((j=strtol(dp->d_name,&chp2,10))>i&&!*chp2) i=j; /* yep, we found a higher number */ closedir(dirp); /* aren't we neat today */ } else readerr(buf); #endif /* NOopendir */ if(chp-buf+sizeNUM(i)>linebuf) exlb: { nlog(exceededlb);setoverflow(); goto ret; } ;{ int ok; do ultstr(0,++i,chp); /* find first empty MH folder */ while((ok=linkonly?rlink(buf2,buf,0):hlink(buf2,buf))&&errno==EEXIST); if(linkonly) { yell(lkingto,buf); if(ok) goto nolnk; goto didlnk; } } goto opn; } else if(type==ft_MAILDIR) { if(!unique(buf,chp,linebuf,NORMperm,verbose,doMAILDIR)) goto ret; unlink(buf); /* found a name, remove file in tmp */ memcpy(chp-MAILDIRLEN-1,maildirnew,MAILDIRLEN); /* but link directly */ } /* into new */ else /* ft_DIR */ { size_t mpl=strlen(msgprefix); if(chp-buf+mpl+sizeNUM(stbuf.st_ino)>linebuf) goto exlb; stat(buf2,&stbuf); /* filename with i-node number */ ultoan((unsigned long)stbuf.st_ino,strcpy(chp,msgprefix)+mpl); } if(linkonly) { yell(lkingto,buf); if(rlink(buf2,buf,0)) /* hardlink the new file, it's a directory folder */ nolnk: nlog("Couldn't make link to"),logqnl(buf); else didlnk: appendlastvar(buf); /* lastvar is "LASTFOLDER" here */ goto ret; } if(!rlink(buf2,buf,0)) /* try rename-via-link */ opn: unlink(buf2); /* success; remove the original */ else if(errno=EEXIST||!stat(buf,&stbuf)||errno!=ENOENT||rename(buf2,buf)) ret: return -1; /* rename it, but only if it won't replace an existing file */ setlastfolder(buf); return opena(buf); } int writefolder(boxname,linkfolder,source,len,ignwerr,dolock) char*boxname,*linkfolder;const char*source;long len;const int ignwerr,dolock; { char*chp,*chp2;mode_t mode;int fd,type; if(*boxname=='|'&&(!linkfolder||linkfolder==Tmnate)) { setlastfolder(boxname); fd=rdup(Deliverymode==2?STDOUT:savstdout); type=ft_PIPE; goto dumpc; } if(boxname!=buf) strcpy(buf,boxname); /* boxname can be found back in buf */ if(linkfolder) /* any additional directories specified? */ { size_t blen; if(blen=Tmnate-linkfolder) /* copy the names into safety */ Tmnate=(linkfolder=tmemmove(malloc(blen),linkfolder,blen))+blen; else linkfolder=0; } type=foldertype(0,0,&mode,0); /* the envelope please! */ chp=strchr(buf,'\0'); switch(type) { case ft_FILE: if(linkfolder) /* any leftovers? Now is the time to display them */ concatenate(linkfolder),skipped(linkfolder),free(linkfolder); if(!strcmp(devnull,buf)) type=ft_PIPE,rawnonl=1; /* save the effort on /dev/null */ else if(!(UPDATE_MASK&(mode|cumask))) chmod(boxname,mode|UPDATE_MASK); if(dolock&&type!=ft_PIPE) { strcpy(chp,lockext); if(!globlock||strcmp(buf,globlock)) lockit(tstrdup(buf),&loclock); *chp='\0'; } setlastfolder(boxname); fd=opena(boxname); dumpc: if(dump(fd,type,source,len)&&!ignwerr) dumpf: { switch(errno) { case ENOSPC:nlog("No space left to finish writing"),logqnl(buf); break; #ifdef EDQUOT case EDQUOT:nlog("Quota exceeded while writing"),logqnl(buf); break; #endif default:writeerr(buf); } if(lasttell>=0&&!truncate(boxname,lasttell)&&(logopened||verbose)) nlog("Truncated file to former size\n"); /* undo garbage */ ret0: return 0; } return 1; case ft_TOOLONG: exlb: nlog(exceededlb);setoverflow(); case ft_CANTCREATE: retf: if(linkfolder) free(linkfolder); goto ret0; case ft_MAILDIR: if(source==themail.p) /* skip leading From_? */ source=skipFrom_(source,&len); strcpy(buf2,buf); chp2=buf2+(chp-buf)-MAILDIRLEN; *chp++= *MCDIRSEP_; ;{ int retries=MAILDIRretries; for(;;) { struct stat stbuf; if(0>(fd=unique(buf,chp,linebuf,NORMperm,verbose,doFD|doMAILDIR))) goto nfail; if(dump(fd,ft_MAILDIR,source,len)&&!ignwerr) goto failed; strcpy(chp2,maildirnew); chp2+=MAILDIRLEN; *chp2++= *MCDIRSEP_; strcpy(chp2,chp); if(!rlink(buf,buf2,0)) { unlink(buf); break; } else if(errno!=EEXIST&&lstat(buf2,&stbuf)&&errno==ENOENT&& !rename(buf,buf2)) break; unlink(buf); if(!retries--) goto nfail; } } setlastfolder(buf2); break; case ft_MH: #if 0 if(source==themail.p) source=skipFrom_(source,&len); #endif default: /* case ft_DIR: */ *chp++= *MCDIRSEP_; strcpy(buf2,buf); chp2=buf2+(chp-buf); if(!unique(buf2,chp2,linebuf,NORMperm,verbose,0)|| 0>(fd=dirfile(chp,0,type))) nfail: { nlog("Couldn't create or rename temp file");logqnl(buf); goto retf; } if(dump(fd,type,source,len)&&!ignwerr) { strcpy(buf,buf2); failed: unlink(buf);lasttell= -1; if(linkfolder) free(linkfolder); goto dumpf; } strcpy(buf2,buf); break; } if(!(UPDATE_MASK&(mode|cumask))) { chp[-1]='\0'; /* restore folder name */ chmod(buf,mode|UPDATE_MASK); } if(linkfolder) /* handle secondary folders */ { for(boxname=linkfolder;boxname!=Tmnate;boxname=strchr(boxname,'\0')+1) { strcpy(buf,boxname); switch(type=foldertype(0,1,&mode,0)) { case ft_TOOLONG:goto exlb; case ft_CANTCREATE:continue; /* just skip it */ case ft_DIR:case ft_MH:case ft_MAILDIR: chp=strchr(buf,'\0'); *chp= *MCDIRSEP_; if(dirfile(chp+1,1,type)) /* link it with the original in buf2 */ if(!(UPDATE_MASK&(mode|cumask))) { *chp='\0'; chmod(buf,mode|UPDATE_MASK); } break; } } free(linkfolder); } return 1; } void logabstract(lstfolder)const char*const lstfolder; { if(lgabstract>0||(logopened||verbose)&&lgabstract) /* don't mail it back? */ { char*chp,*chp2;int i;static const char sfolder[]=FOLDER; if(mailread) /* is the mail completely read in? */ { i= *thebody;*thebody='\0'; /* terminate the header, just in case */ if(eqFrom_(chp=themail.p)) /* any "From " header */ { if(chp=strchr(themail.p,'\n')) *chp='\0'; else chp=thebody; /* preserve mailbox format */ elog(themail.p);elog(newline);*chp='\n'; /* (any length) */ } *thebody=i; /* eliminate the terminator again */ if(!nextexit&& /* don't reenter malloc/free */ (chp=egrepin(NSUBJECT,chp,(long)(thebody-chp),0))) { size_t subjlen; for(chp2= --chp;*--chp2!='\n';); if((subjlen=chp-++chp2)>MAXSUBJECTSHOW) subjlen=MAXSUBJECTSHOW; /* keep it within bounds */ ((char*)tmemmove(buf,chp2,subjlen))[subjlen]='\0';detab(buf); elog(" ");elog(buf);elog(newline); } } elog(sfolder);strlcpy(buf,lstfolder,MAXfoldlen);detab(buf);elog(buf); i=strlen(buf)+STRLEN(sfolder);i-=i%TABWIDTH; /* last dump */ do elog(TABCHAR); while((i+=TABWIDTH)dfilled&&isprivate) /* put it in place here */ { tmemmove(themail.p+dfilled,thebody,filled-=tobesent); tmemmove(themail.p,new.p,dfilled); resizeblock(&themail,filled+=dfilled,1); freeblock(&new); } else /* too big or must share -- switch blocks */ { resizeblock(&new,filled-tobesent+dfilled,0); tmemmove(new.p+dfilled,thebody,filled-=tobesent); freeblock(&themail); themail=new;private(1); filled+=dfilled; } } else { if(!mailread||!filled) rhead=1; /* yup, we read in a new header as well as new mail */ mailread=0;dfilled=thebody-themail.p; if(!isprivate) { memblk new; makeblock(&new,filled); if(filled) tmemmove(new.p,themail.p,filled); freeblock(&themail); themail=new;private(1); } readdyn(&themail,&filled,filled+tobesent); } pastend=filled+(thebody=themail.p); while(thebodythemail.p) chp--; if(contlengthoffset) { unsigned places;long cntlen,actcntlen;charNUM(num,cntlen); chp=themail.p+contlengthoffset;cntlen=filled-(thebody-themail.p); if(filled>1&&themail.p[filled-2]=='\n') /* no phantom '\n'? */ cntlen--; /* make sure it points to the last '\n' */ for(actcntlen=places=0;;) { switch(*chp) { default: /* fill'r up, please */ if(places<=sizeof num-2) *chp++='9',places++,actcntlen=(unsigned long)actcntlen*10+9; else *chp++=' '; /* ultra long Content-Length: field */ continue; case '\n':case '\0':; /* ok, end of the line */ } break; } if(cntlen<=0) /* any Content-Length at all? */ cntlen=0; ultstr(places,cntlen,num); /* our preferred size */ if(!num[places]) /* does it fit in the existing space? */ tmemmove(themail.p+contlengthoffset,num,places),actcntlen=cntlen; chp=thebody+actcntlen; /* skip the actual no we specified */ } restbody=chp;mailread=1; } procmail-3.22/src/variables.h0100644006717600001440000000204607347315165015400 0ustar guenthersrc/* $Id: variables.h,v 1.7 2001/08/27 08:44:03 guenther Exp $ */ const char *sputenv P((const char*const a)), *eputenv P((const char*const src,char*const dst)), *tgetenv P((const char*const a)); void primeStdout P((const char*const varname)), retStdout P((char*const newmyenv,const int fail,const int unset)), retbStdout P((char*const newmyenv)), appendlastvar P((const char*const value)), cleanupenv P((int preserve)), initdefenv Q((auth_identity*pass,const char*fallback,int do_presets)), asenv P((const char*const chp)), setdef P((const char*const name,const char*const value)), setlastfolder P((const char*const folder)), allocbuffers Q((size_t lineb,int setenv)), setmaildir P((const char*const newdir)), setoverflow P((void)); int asenvcpy P((char*src)), setexitcode P((int trapisset)), alphanum P((const unsigned c)); char *gobenv P((char*chp,char*end)); long renvint P((const long i,const char*const env)); extern long Stdfilled; extern char*Stdout; extern const char lastfolder[],maildir[],scomsat[],offvalue[];; extern int didchd; procmail-3.22/src/comsat.h0100644006717600001440000000057407314360636014717 0ustar guenthersrc/* $Id: comsat.h,v 1.1 2001/06/21 11:59:26 guenther Exp $ */ #ifndef NO_COMSAT void setlfcs P((const char*folder)), setlgcs P((const char*name)), sendcomsat P((const char*folder)); int setcomsat P((const char*chp)); #else /* If no comsat support, then they all do nothing */ #define setlfcs(x) 0 #define setlgcs(x) 0 #define sendcomsat(x) 0 #define setcomsat(x) 0 #endif procmail-3.22/patchlevel.h0100644006717600001440000000116007347314363014763 0ustar guenthersrc#define VERSION "\ v3.22 2001/09/10\n\ Copyright (c) 1990-2001, Stephen R. van den Berg\t\n\ Copyright (c) 1997-2001, Philip A. Guenther\t\t\n\ \n\ Submit questions/answers to the procmail-related mailinglist by sending to:\n\ \t\n\ \n\ And of course, subscription and information requests for this list to:\n\ \t\n" /* If the formatting or number of newlines of VERSION substantially changes, src/autoconf and src/manconf.c need to be changed as well. And yes, there is supposed to be a leading space */ procmail-3.22/Artistic0100644006717600001440000001373707223546512014201 0ustar guenthersrc The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End procmail-3.22/COPYING0100644006717600001440000004310507223546512013517 0ustar guenthersrc GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. procmail-3.22/FAQ0100644006717600001440000003271707223546512013025 0ustar guenthersrc------------------------------------------------------------------------------ ---------------------- Frequently Asked Questions ---------------------------- ------------------------------------------------------------------------------ 1. How do I go about setting up a mailinglist or a mail-archive server? Look in the SmartList directory, start reading the INTRO file, it describes it in detail and should get you started. 2. I installed procmail (i.e. typed 'make install'), but how am I supposed to use it? When I type procmail on the command line it simply does nothing. *********************************************************************** There exists an excellent newbie FAQ about mailfilters (and procmail in particular), it is being maintained by Nancy McGough and can be obtained via: World Wide Web (the nicest format for online reading!): http://www.faqs.org/faqs/mail/filtering-faq/ Anonymous FTP: ftp://rtfm.mit.edu/pub/usenet/news.answers/mail/filtering-faq E-mail: Send mail to mail-server@rtfm.mit.edu containing the following: send usenet/news.answers/mail/filtering-faq UUCP: uunet!/archive/usenet/news.answers/mail/filtering-faq It is also posted monthly in at least the following newsgroups: comp.mail.misc, comp.answers, news.answers *********************************************************************** You're not supposed to start procmail from the command line. Procmail expects exactly one mail message to be presented to it on its stdin. Usually the mail system feeds it into procmail. If you start it by hand, you have to type the mail. Be sure to have a .forward and a .procmailrc file in your home directory (see the examples subdirectory or the man page). MMDF users should note that they need a .maildelivery file *instead* of a .forward file (see the man page for more detailed information). If however, procmail has been integrated in the maildelivery system (i.e. if your system administrator installed it that way, ask him/her), then you no longer need the .forward files in your home directory, having a .procmailrc file will suffice. On some systems .forward files are not checked. It might be possible that your system supports a command like: mail -F "|/usr/bin/procmail" to set up forwarding to a program. (If procmail is in /usr/local/bin then use that path instead when trying these.) If that doesn't seem to work it might be worth trying to put a line looking like this: Forward to |/usr/bin/procmail or if that doesn't work, try: Pipe to /usr/bin/procmail as the only line in your mail spool file (e.g. /usr/mail/$LOGNAME), as well as doing a "chmod 06660 /usr/mail/$LOGNAME". For more information on such systems, do a "man mail". If all of this doesn't work, procmail can be called on a periodical basis, either via "cron", "at" or whenever you start reading mail (or log in). For a sample script look in the NOTES section of the procmail(1) man page. 3. When I compile everything the compiler complains about invalid or illegal pointer combinations, but it produces the executables anyway. Should I be concerned? Ignore these warnings, they simply indicate that either your compiler or your system include files are not ANSI/POSIX compliant. The compiler will produce correct code regardless of these warnings. 4. The compiler seems to issue warnings about "loop not entered at top", is that a problem? No, no problem at all, it just means I wrote the code :-). That's just about the only uncommon coding technique I use (don't think I don't try to avoid those jumps in loops, it's just that sometimes they are the best way to code it). This warning, as well as "statement not reached", can be ignored -- the compiler will still generate correct code. Use gcc if they really bother you. 5. The compiler complains about unmodifiable lvalues or assignments to const variables. Now what? Well, if the compiler produces the executables anyway everything probably is all right. If it doesn't, you might try inserting a "#define const" in the autoconf.h file by hand. However in any case, your compiler is broken; I would recommend submitting this as a compiler bug to your vendor. In any case, if this should occur, I'd appreciate a mail from you (so I can try to fix the autoconf script to recognise your compiler correctly as well). 6. The compiler refuses to compile regexp.c, what is the problem? Try compiling that module with optimisation turned off. 7. Everything installed just fine, it's just that there are several stale _locktst processes which refuse to die. How do I get rid of those? In order to prevent things like this from happening to procmail, _locktst tries to determine which kernel locking methods are reliable. Sometimes this triggers a bug in the kernel or in your system-supplied lockd; this is good, because _locktst detects this and makes sure that procmail will not make the same mistake. A side effect is that this sometimes leaves behind some stale _locktst processes that seem to be unkillable. This usually is the result of a buggy lockdaemon. In order to get rid of the stale processes, ask your system administrator to kill and restart the (rpc.)lockd (and perhaps the (rpc.)statd) on both the filesystem-client (where you compiled procmail) and the filesystem-server(s) (where the lockingtests took place). Depending on the OS it might help if you send the offending _locktst processes a kill signal before or after restarting the lockd again. In any case, _locktst just uncovered a bug in your operating system. You should contact your system's vendor and ask for a bugfix for your lockd. 8. When I send myself a testmail, the mail bounces with the message: cannot execute binary file. What am I doing wrong? It is very well possible that mail is processed on a different machine from that where you usually read your mail. Therefore you have to make sure that procmail has the right binary format to execute on those machines on which mail could arrive. In order to get this right you might need to do some .forward file tweaking, look at the examples/advanced file for some suggestions. 9. Where do I look for examples about: One home directory, several machine architectures? Procmail as an integrated local mail delivery agent? (generic, sendmail, ZMailer, smail, SysV mailsurr) Changing the mail spool directory to $HOME for all users Security considerations (when installing procmail suid root) Well, this probably is your lucky day :-), all these topics are covered in the examples/advanced file. Other examples (e.g. for autoreplies) are most likely to be found by typing: man procmailex 10. How do I use procmail as a general mail filter inside sendmail? See EXAMPLES section of the procmail(1) man page. 11. Why do I have to insert my login name after the '#' in the .forward or .maildelivery file? Some mailers `optimise' maildelivery and take out duplicates from Cc:, Bcc: and alias lists before delivery. If two or more persons on such a list would have identical .forward files, then the mailer will eliminate all but one. Adding a `#' with your login name following it will make the .forward files unique, and will ensure that the mailer doesn't optimise away some addresses. 12. How do I view the man pages? If the man(1) program on your system understands the MANPATH environment variable, make sure that the installation directory listed in the Makefile for the manpages is included in your MANPATH. If your man program does not support MANPATH, make sure that the man pages are installed in one of the standard man directories, like under /usr/man. If you do not want to install the man pages before viewing them, you can view an individual man file by typing something like: nroff -man procmail.1 | more 13. The leading From_ line on all my arriving mail shows the wrong time. Before putting procmail in the .forward file everything was OK. This is a known bug in sendmail-5.65c+IDA. The real fix would be to upgrade to sendmail 6.x or later. For a quick fix, see the procmailex man page. 14. When sending mail to someone with procmail in his/her .forward I sometimes get back an error saying: "Cannot mail directly to programs." This is a known bug in some older sendmails that mistakenly drop their root privileges when they are given the -t flag. Either make sure that your sendmail always forwards to a mailserver first or upgrade to sendmail 6.x or later. 15. When sending mail to someone with procmail in his/her .forward I sometimes get back an error saying: "User doesn't have a valid shell for mailing to programs." This indicates that the mail arrives on a mailserver which most likely has a different user database (/etc/passwd) where the login shell specified for the recipient is not present in /etc/shells. Contact your administrator to put the name of that shell in /etc/shells. 16. My mailtool sometimes reports that it is confused about the state of the mailbox, or I get "Couldn't unlock" errors from procmail now and then, or sometimes it simply seems to hang just when new mail arrives. This is a known bug in the OpenLook mailtool. It holds on to the kernel lock on the mail-spoolfile most of the time as a signal to other mailtool processes. With newer versions of mailtool, enabling the "Use network aware mail file locking" configuration option may solve the problem, though this option isn't always available. If that doesn't work then recompile procmail with both the fcntl() and lockf() locking method disabled (see config.h). 17. I sometimes get these `Lock failure on "/usr/mail/$LOGNAME.lock"' errors from procmail. What do I do about it? The problem here is that as long as procmail has not read a $HOME/.procmailrc file, it can hang on to the sgid mail permission (which it needs in order to create a lockfile in /usr/mail). I.e. if procmail delivers mail to a user without a $HOME/.procmailrc file, procmail *can* (and does) use the /usr/mail/$LOGNAME.lock file. If, however, it finds a $HOME/.procmailrc file, procmail has to let go of the sgid mail permission because otherwise any ordinary user could abuse that. There are several solutions to this problem: - Some systems support the sticky bit on directories (when set only allows the owner of a file in that directory to rename or remove it). This enables you to make /usr/spool/mail drwxrwxrwt. It is thus effectively world writable, but all the mailboxes in it are protected because only the mailbox owner can remove or rename it. - If your system did not exhibit the !@#$%^&* POSIX semantics for setgid(), procmail would have been able to switch back and forth between group mail and the group the recipient belongs to without creating security holes. - If your system supported setrgid() or setregid() or setresgid() with BSD semantics, procmail would have been able to switch... (see the previous point). - You could simply put the following at the end of your .procmailrc file: LOCKFILE # removes any preexisting lockfile LOG=`lockfile $DEFAULT$LOCKEXT` TRAP="rm -f $DEFAULT$LOCKEXT" :0 $DEFAULT - You could, instead of using /usr/mail/$LOGNAME, use a file below your home directory as your default mailbox. - Or, you could still use /usr/mail/$LOGNAME as the mailbox, but simply instruct procmail to use a different lockfile. This can be achieved by putting following recipe at the bottom of your .procmailrc file: :0:$HOME/.lockmail $DEFAULT You have to make sure that all other programs that update your system mailbox will be using the same lockfile of course. - You can ignore the problem if you know that both your mail reader and procmail use an overlapping kernel locking method. 18. Is procmail Y2K safe/compliant? Both procmail and formail are believed to be Y2K compliant if your system's libraries are Y2K compliant. In particular, they use the time_t type to hold the current time when it is needed and print out the time using the ctime() library routine. However, no actual compliancy tests have been run, so you if you need that you'll need to run them yourself. For those who have examined the code themselves, the casting of a time_t value to unsigned long in formail.c is guaranteed to work according to the current version of the C language standard. Future revisions of that standard may change that, at which time formail will be updated to work with both the new and the old standards. Individual recipes and rcfiles may need to be checked for unsafe date handling. 19. How can I make procmail deliver a message to all local users? E-mail for several people all come into a single mailbox and I'm trying to split them back up. If you are asking this, you are on the wrong track. Procmail cannot route messages like this correctly without special help from the MTA (sendmail, qmail, etc). For a more lengthy discussion about the issues, please refer to http://www.iki.fi/era/procmail/mini-faq.html#advanced 20. None of the above topics cover my problem. Should I panic? Let me ask you a question :-), have you examined the CAVEATS, WARNINGS, BUGS and NOTES sections of the manual pages *closely* ? Have you checked any of the FAQs referenced from the procmail website, http://www.procmail.org, to see if the answer it? If you have, well, then panic. Or, alternatively, you could submit your question to the procmail mailinglist (see the man page for the exact addresses, or try "procmail -v", or look in the patchlevel.h file). procmail-3.22/FEATURES0100644006717600001440000001014507317007461013622 0ustar guenthersrcFeature summary for procmail: + It's less filling (i.e. small) + Very easy to install (rated PG6 :-) + Simple to maintain and configure because all you need is actually only ONE executable (procmail) and ONE configuration file (.procmailrc) + Is event driven (i.e. gets invoked automagically when mail arrives) + Does not use *any* temporary files + Uses standard egrep regular expressions + It poses a very low impact on your system's resources (it's 1.4 times faster than the average /bin/mail in user-cpu time) + Allows for very-easy-to-use yes-no decisions on where the mail should go (can take the size of the mail into consideration) + Also allows for neural-net-type weighted scoring of mails + Filters, delivers and forwards mail *reliably* + Provides a reliable hook (you might even say anchor :-) for any programs or shell scripts you may wish to start upon mail arrival + Performs heroically under even the worst conditions (file system full, out of swap space, process table full, file table full, missing support files, unavailable executables, denied permissions) and tries to deliver the mail somehow anyway + Absolutely undeliverable mail (after trying every trick in the book) will bounce back to the sender (or not, your choice) + Is one of the few mailers to perform reliable mailbox locking across NFS as well (DON'T use NFS mounted mailboxes WITHOUT installing procmail; you may lose valuable mail one day) + Supports five mailfolder standards: single file folders (standard and nonstandard VNIX format), directory folders that contain one file per message, the similar MH directory folders (numbered files), and Maildir directory folders (a multi-directory format that requires no locking) + Native support for /var/spool/mail/b/a/bar type mailspools + Variable assignment and substitution is an extremely complete subset of the standard /bin/sh syntax + Provides a mail log file, which logs all mail arrival, shows in summary whence it came, what it was about, where it went (what folder) and how long (in bytes) it was + Uses this log file to display a wide range of diagnostic and error messages (if something went wrong) + Does not impose *any* limits on line lengths, mail length (as long as memory permits), or the use of any character (any 8-bit character, including '\0' is allowed) in the mail + It has man pages (boy, does it have man pages) + Procmail can be used as a local delivery agent with comsat/biff support (*fully* downwards compatible with /bin/mail); in which case it can heal your system mailbox, if something messes up the permissions + Secure system mailbox handling (contrary to several well known /bin/mail implementations) + Provides for a controlled execution of programs and scripts from the aliases file (i.e. under defined user ids) + Allows you to painlessly shift the system mailboxes into the users' home directories + It runs on virtually all (old and future) operating systems which names start with a 'U' or end in an 'X' :-) (i.e. extremely portable code; POSIX, ANSI C and K&R conforming) + Is clock skew immune (e.g. in the case of NFS mounted mailboxes) + Can be used as a general mailfilter for whole groups of messages (e.g. when called from within sendmail.cf rules) + Can act as an LMTP server for reliable multiple recipient delivery + Works with (among others?) sendmail, ZMailer, smail, MMDF, mailsurr, qmail, and postfix Feature summary for formail: + Can generate auto-reply headers + Can convert mail into standard mailbox format (so that you can process it with standard mail programs) + Can split up mailboxes into the individual messages + Can split up digests into the individual messages + Can split up saved articles into the individual articles + Can do simple header munging/extraction + Can extract messages from mailboxes + Can recognise duplicate messages Feature summary for lockfile: + Provides NFS-secure lockfiles to shell script programmers + Gives normal users the ability to lock their system mailbox, regardless of the permissions on the mail-spool directory procmail-3.22/HISTORY0100644006717600001440000007462507347314261013564 0ustar guenthersrc Only the last entry is complete, the others might have been condensed. 1990/12/07: v1.00 1990/12/12: v1.01 1991/02/04: v1.02 1991/02/13: v1.10 1991/02/21: v1.20 1991/02/22: v1.21 1991/03/01: v1.30 1991/03/15: v1.35 Started using RCS to manage the source 1991/06/04: v1.99 1991/06/10: v2.00 1991/06/11: v2.01 1991/06/12: v2.02 1991/06/20: v2.03 1991/07/04: v2.10 1991/07/12: v2.11 1991/10/02: v2.20 (never released) 1991/10/18: v2.30 Reached the doubtful milestone of having a source file (regexp.c) which provokes a compiler error on an old compiler (if using the optimiser) 1991/10/22: v2.31 1991/12/05: v2.40 1991/12/13: v2.50 1992/01/22: v2.60 1992/01/31: v2.61 1992/04/30: v2.70 1992/07/01: v2.71 Gave procmail, formail, lockfile and mailstat a more verbose command line help (called up by -h or -?) 1993/02/04: v2.80 Started using CVS to manage the source (god's gift to programmers) Changes to the installation scripts: - the autoconf script now performs a reliability test on kernel locking support - reached the doubtful milestone of consistently crashing the kernel on a Convex by running the locktst program 1993/02/19: v2.81 1993/06/02: v2.82 (never really released, was only available as prerelease 4) Worked my way around the !@#$%^&*() POSIX setgid() semantics (if your OS supports setrgid() or setregid()) 1993/07/01: v2.90 Condition lines in recipes can now be started with a leading `*', there is no longer a need to count condition lines, simply set the number to zero, and let procmail find out by itself 1993/07/02: v2.91 Reached the doubtful milestone to sometimes crash an Ultrix machine (due to the lockingtests, not procmail itself) 1994/06/14: v3.00 Changes to procmail: - Changed the semantics of the TRAP keyword. In order to make procmail accept the exitcode it returns, you now have to set EXITCODE="" - It was still occasionally trying to lock /dev/null, which is of course silly, fixed that - Taught it about `nesting recipes'; they allow parts of an rcfile to be grouped hierarchically - Fixed a discrepancy with /bin/sh backquote expansion in environment assignments (preserving all spaces) - Logs its pid and a timestamp when VERBOSE=on - Caused the regular TIMEOUT to break a `hanging' kernel lock - SIGUSR1 and SIGUSR2 can be used to turn on and off verbose logging - Worked around a bug in the `ANSI'-compiler of Domain/OS - Procmail and lockfile now inherit any ignore status of most regular signals (fixes a problem with some buggy shells) - Optionally reads in a global rcfile (/etc/procmailrc) before doing regular delivery (which includes the new keyword: DROPPRIVS) - Can pipe the mail to stdout on request - Moved the "Reiterating kernel lock" diagnostic into the "extended" (i.e. VERBOSE=on) section - Tightened the loop when skipping comments in rcfiles (for a slight speedup) - Added support for filesystems not capable of creating hardlinks - Tightened the security check on initial absolute rcfiles (they sometimes can't be world writable) - Weighted scoring on conditions - Ability to inline parse ${var-text} and ${var:-text} - Ability to inline parse ${var+text} and ${var:+text} - Skipping spaces after "!" and "$" on condition lines - Implicit delivery somehow got broken: fixed - Default umask is always 077 now for deliverymode - Extended ^FROM_DAEMON and ^FROM_MAILER macro regexps again - The -f option became less strict, everyone can use it now, except that unpriviliged users will get an additional >From_ they didn't bargain for (in order to make fakes identifiable) - The date on the From_ line can now be refreshed with -f- - Introduced new recipe flags: E and e (else and error) - Nested blocks clone procmail on a 'c' flag - Introduced the EXITCODE special variable - Implicit delivery mode is now entered if argv[0] doesn't start with the word `procmail' - Fixed the BSD support for kernel-locking only operation - Taught the regexp engine about \< and \> - Fixed bug present on some systems; caused the body to be munged when filtering headers only - Added -o option (makes procmail override the From_ lines, like it used to) - -p and -m together shrink the set of preset variables to the bare minimum - -p is not supported alongside -d anymore - /etc/procmailrcs/ is the place for optional privileged rcfiles in -m mailfilter mode - Switched the meanings of SIGUSR1 and SIGUSR2 - The 'a' flag didn't work correctly after filter recipes - Changed the permissions on the lockfile, writing zero in it - Check the permissions on the existing system mailbox, correct them if necessary - Clean up zombies more often Changes to formail: - Fixed a sender-determination-weight problem, it mixed up the weights when autoreplying and when regenerating the From_ line (and thus didn't always pick the optimal field) - Pays attention to the exitcode of the programs it started - Accepts simultaneous -X and -k options - Fixed a bug introduced in v2.82 in formail when using the -x and the -k options simultaneously - Rearranged the weights for "-rt" (made From: more important) - Parsed return-addresses starting with a \ incorrectly (causing it to coredump on occasion) - Supports the -s option withouth a program argument - Recognise extra UUCP >From_ lines - Introduced the -B option to split up BABYL rmail files - It regards and generates a FILENO variable (for easy numbering) - Moved the idcheck functionality into formail -D (due to popular demand), for eliminating duplicate mails - It terminates early now if it only needs the header - The -n option can now sustain itself by reaping children if it can't fork() immediately - It supports incomplete field specifications which match any field starting similarly - Introduced the -u and -U options - -a Message-ID: and -a Resent-Message-ID: to make it generate new ones - Keep the X-Loop: field when generating autoreplies - Lowered the negative weight for .UUCP reply addresses - Honour Content-Length: fields, also speeds up processing of lengthy messages - Clean up zombies more often - Handle bangpath reconstruction - Made -q the default, use -q- to disable Miscellaneous changes: - Detecting and dodging buggy zshs everywhere - Slightly adjusted autoconf for the new non-standard 386BSD and NeXTStep 3.1 environments - Extended the FAQ - Extended and fixed the procmailex man page - Updated the crontab script recommendation in the procmail man page - Fixed the "procmail"-mailer definition in the procmail man page - Created a new procmailsc man page - Fixed a bug in lockfile, the exitcode was not correct if you used -! with more than one file - Including now, some (old) architectures seem to insist on this - Revamped the library search code - Provided a faster (than most libraries) strstr() routine - Created the setid program (to be used by the SmartList installation) - Checking for fstat() in autoconf - Avoiding i/o-redirection on subshells - Provided for the ability to hotwire the lockingtests - Autoconf asks if you'd like to use the existing autoconf.h - Autoconf determines MAX_argc (for choplist) 1994/06/14: v3.01 No changes, version number bump to keep in sync with SmartList 1994/06/16: v3.02 Made formail quiet (by default) about Content-Length mismatches The version number in patchlevel.h for this version was incorrect and still displayed v3.01 (yes, silly, I know) 1994/06/30: v3.03 Limit the no. of retries on lockfiles if the recipient is over quota (procmail & lockfile) Removed some superfluous "procmail:" prefixes in the middle of an error message Utilise a syslog daemon (if present) to log some critical errors (mostly attempted security violations and errors which are fatal but can't occur (like an unwritable /dev/null)) Reconstruct and respect Content-Length: in procmail (if you need the >From lines, you'll have to take any existing Content-Lenght: field out of the header) Reformatted the source code to match the changed conventions Procmail always defaulting the umask to 077 for deliverymode broke some systems, reverting back to the old method of allowing group access on the system mailbox if necessary 1994/08/02: v3.04 Changes to procmail: - Support some non-BSD compatible syslog() implementations - Even if the Content-Length is zero, write it out (some programs can't deal with the empty field) - Drop the safety margin on Content-Length calculations, some programs can't deal with those - Truncate folders to their former length if delivery was not successful - Fine-tuned the ^FROM_MAILER and ^FROM_DAEMON macros again - The -v option lists the locking strategies employed - Will create the last member of the mail spool directory if found missing Forgot to define closelog() away if syslog support is missing Worked around the old syslog() interface Worked around a compiler bug old HP compilers (pointer-unsigned), caused the Content-Length: field to be mangled on some older HP/UX systems (not on every mail) Worked around compilation problems on SCO and old versions of IRIX Some fixes to the man pages Changes to formail: - Mistakenly turned X-Loop: fields into Old-X-Loop: when autoreplying - Allow wildcard -i when autoreplying - Renaming short fields to longer fields didn't always work - Renaming with a wildcard source/destination is possible now - -rk didn't behave correctly if a Content-Length: field was present Extended the sendmail directions in examples/advanced, it includes a direct example on how to make use of the -a feature Using EXIT_SUCCESS instead of EX_OK Both procmail and formail take the -Y option, for traditional Berkeley format mailboxes (ignoring Content-Length:) Some NCR machines didn't have WNOHANG defined 1994/08/04: v3.05 Formail v3.04 didn't remove the From_ line if given the -I 'From ' option, changed that back, allowing for -a 'From ' Procmail sometimes didn't reliably count the number of matches on a weighted recipe, fixed Some minor manpage adaptations 1994/08/30: v3.06 Groff -mandoc macros managed to display the man pages incorrectly, hacked my way around the .TH dependency to fix it Split up string constant FM_HELP, it exceeded some compiler limits Changes to procmail: - Fixed a bug which was present since v2.30: 'z' was always handled case sensitive (seems like not many people use that letter :-) in regular expression conditions - The ^^ anchor can now also be used to anchor the end of a regular expression - The -m flag will now unset ORGMAIL and will make procmail omit the check for a system mailbox - Allow easy reconfiguration of the default rcfile location - Extend the list of internals displayed with -v - The mail fed to the TRAP command contained some spurious nul characters, fixed Optionally allow the automatic installation of compressed man pages Formail v3.00 and later occasionally seemed to hang if used in a chain of pipes and fed with more text than it needed, fixed Updated the FAQ Updated the man pages (among others: vacation example changed) Sharpened the autoconf const check, AIX 3.2.3 managed to slip past it again Made sure that "make -n" with any make works as expected 1994/10/31: v3.10 Changes to procmail: - Minor corrections to the semantics of the 'a' and 'e' flags - Minor correction to the semantics of the -o option - Slight regular expression engine speedup - Regexp matching of environment variables is possible now - Due to popular demand: LOGABSTRACT=all logs *all* successful delivering-recipes executed - Enforce secure permissions on /etc/procmailrcs if used - Take sgid bit in the system mail spool dir into account even if it is world writable - The regexp engine can return matches now (new token "\/", new variable "MATCH") - New recipe flag 'r', raw mode, so procmail doesn't try to ensure the mail ends in an empty line - Success and failure of a filter recipe is well defined now - Procmail v3.06 prepended a bogus "." to explicit rcfile names searched relative to the home directory, fixed - Carved out two subroutines from main() to get it below the optimisation threshold - Eliminated duplicate error messages when procmailrcless delivery fails - Logging "Quota exceeded" messages when appropriate - Truncate notification suppressed when logfile not opened - Truncating didn't always work when delivering across NFS - The $_ special variable was wrong when wasn't set Changes to formail: - New option: -z (zap whitespace and empty fields) - Reading from stdin doesn't require the silly three EOFs anymore - -D with -r cache reply addresses now - Carved out one subroutine from main() to get it below the optimisation threshold - -R with -x didn't work reliably - -r with -i or -I sometimes had unexpected effects (in v3.06) - The nil-Return-Path-override was broken, fixed Updated the man pages, new subsection to procmailrc(5) summarising procmail regexp syntax Expanded on the sendmail.cf $#local example in the examples/advanced file again Revised detection of hard-link incapable filesystems during the installation Fixed bug in lockfile, the exitcode was not correct if you used -! (I hope this finally fixes this -! problem) Using execv() instead of execve() 1995/05/17: v3.11pre3 Changes to procmail: - varname ?? < nnn conditions didn't have the expected effect - Regression bug since v3.06, procmail -m /etc/procmailrcs didn't allow any arguments to be passed, fixed - Eliminated a superfluous fork() when processing TRAP - "lockfile ignored" warning was generated inappropriately at times - Renamed testb() into testB() to avoid conflict with Solaris - Eliminated spurious extra / in default MAILDIR value - Whole line comments among the conditions are recognised - Embedded empty lines in a recipe are tolerated - $\name regexp safe variable expansion - Delay searching for bogus From_ lines until writeout time (speeds up filtering and writes to /dev/null) - Finally fixed this mess with transparent backup to kernel locking methods when the spool directory is not writable - Avoid the one second NFS_ATIME_HACK under heavy load - The 'r' flag had some undesirable side effects at times - Dotlocks which fail due to permissions are not retried anymore - Made the USER_TO_LOWERCASE_HACK run-time adapting - /usr/spool/mail perm 1777, procmail setgid mail, procmail could not read .procmailrc files in 700 $HOME dirs, fixed - If called with -d option and not running with enough privileges, procmail will bounce the mail (instead of delivering to the invoker, as it used to) - Severe tweaking on ^FROM_MAILER and ^FROM_DAEMON to reduce false matches - Allow for broken From_ lines with a missing sender address Changes to formail: - Slightly extended the number of known header fields - Eliminated the conflict with the 4.4BSD daemon libidentifier - In an MMDF environment formail -b didn't behave correctly - Extracted another function from main() to make it smaller - Process address groups correctly - Process From_ lines with embedded commas correctly Changes to autoconf: - Catch NeXTstep 3.2 missing DIR definition - Detect & work around Ultrix 4.3 "ANSI" C compiler - A defined DEFsendmail or SYSTEM_MBOX caused some "s to be omitted in autoconf.h - Refined preliminary setsid() checks (2.4 x86/sunpro cc managed to break it) - Worked around a HERE document quoting bug in some shells - Fixed the empty argument "shift" problem - Detect & work around BSD 4.4 brain damaged setrgid() New Makefile variable VISIBLE_BASE Added support for a parallelising make Changed manconf.c to cater for broken systems that have a 100 line limit for sed (instead of a 100 command limit) Fixed some portability problems with the Makefiles for the OSF make Worked around old shells not supporting negated classes Extended the FAQ Updated examples/advanced docs for meta-argument setup in a traditional v5.* sendmail setup Fixed potential memory corruption bug for machines that have sizeof(off_t)>sizeof(off_t*) (has been around for ages) The man pages were remade upon every make, fixed 1995/10/29: v3.11pre4 Changes to procmail: - Avoid the NFS delay on directory and MH folders - KEEPENV didn't work reliably for more than one variable - New macro ^TO_, delimits addresses more accurately than ^TO - Don't try to fix the system mailbox permissions too soon, this should put a stop to the numerous confusion reports - SENDMAILFLAGS, new environment variable - Support -y as a substitute kludge for -Y - Fixed parsing of $@' when not doublequoted Changes to formail: - Return failure if the autoreply could not find a proper return address - Multiple -U options sometimes had unfortunate side effects - When splitting and a maximum number of messages was being specified, formail erroneously returned EX_IOERR - Avoid splitting empty messages Changes to autoconf: - If running on a system with good old BSD semantics for setrgid(), use the extra features offered Changed the Mprocmail example, use $g instead of $f 1997/04/28: v3.11pre7 Changes to procmail: - Cater for a race condition that occurs if two procmails try to create an empty system mailbox (bogus BOGUS.* files) - SysV autoforwarding mailboxes didn't work, regression bug in v3.10 - Autocreating the last dirmember of the spooldir didn't (always?) work due to the trailing / - Kernel lockf() method doesn't change the position of the filepointer anymore (results in more accurate lockingtests) - Multiple directory folders are assigned to LASTFOLDER - Don't strip trailing \n in a $MATCH - Refuse to open directories for INCLUDERC files - Syslog failed -o attempts - Don't log non-delivering recipes, even with 'c' flag Changes to formail: - Skip leading spaces when checking for duplicates (will break checks with old id-databases) Worked around an nroff-coredumping problem with IRIX Corrected the last(?) "make -n" glitch Fixed library detection loop for some Solaris 2.[3-5] setups Changes to procmail and lockfile: use the authenticate library for easier integration with custom authentication and mailbox locations 1999/03/02: v3.12 Changes to procmail: - Use BOGUS.$LOGNAME.inode for bogus files to ease recovery - Define RESTRICT_EXEC to restrict execution of programs - Perform continuous checks on heap overflow, everywhere If overflow is occurs then new variable PROCMAIL_OVERFLOW is set - Catch overly long rcfile names - New variable PROCMAIL_VERSION - LOGABSTRACT=all no longer logs filtering or variable capture actions - Don't strip leading \n in a $MATCH - Worked around a compiler bug in Sun C compiler 4.2 (fdefault cached past function calls) - Tempfile names would grow on retry - Open or reopen rcfiles as the user to prevent peeking when not in privileged mailfilter mode - Don't use $HOME/.procmailrc if it's group-writable or in a group-writable directory, unless it's the user's default group and GROUP_PER_USER is set in config.h - hardlink in a an NFS-resistant manner Worked around a compiler bug old HP compilers (pointer-unsigned), caused the Content-Length: field to be mangled on some older HP/UX systems (not on every mail) Changes to formail: - Generated Message-IDs don't contain "s anymore - Fix off-by-one error when zapping whitespace - -z option allows for leading tab instead of space Changes to formail and lockfile: - -v option displays version information Changes to autoconf: - Detect & work around inefficient realloc() implementations Mailstat returns grand totals as well now Update FAQ and docs to reflect default placing of procmail in /usr/bin instead of /usr/local/bin 1999/03/31: v3.13 Mailstat was too loose in its awk syntax Changes to formail: - Formail was ignoring the exitcode of all but the last invocation (or last several, if -n was in effect) Changes to procmail: - Variable expansion of builtin numeric variables in conditions could overwrite the condition (broke SmartList) - weights<1 didn't work if floats changed accuracy when stored Worked around a bug in the Dunix 4.0e compiler (pointer addition not commutative) 1999/11/22: v3.14 Changes to procmail: - Some zero-length extractions using \/ could core dump - Missed a couple possible overflows - Eliminated the conflict with the C9x `restrict' keyword - Support delivery to maildir mailboxes - Support all styles of mailbox for the mail spool - Don't use a locallockfile on $DEFAULT if it's a directory - Set LINEBUF in the environment on startup - Avoid renaming over old messages in directory folders - New variable SWITCHRC performs `tail call' - Refuse to open anything but regular files with INCLUDERC and SWITCHRC - Indicate whether GROUP_PER_USER was defined in the -v output - Stopped depending on parens to stop function macros (they don't under SunOS 4.x cc) - Small heap compilation would fail on nomemerr() in pipes.c - Worked around Tru64 UNIX V4.0E and V4.0F compilers (strcpy() builtin doesn't always return pointer type) - Warn about using 'c' flag with 'f' flag or on variable capture recipes - Warn about using 'h', 'b', 'i', or 'r' flags on nested block recipes. - Test for allowing rcfiles in sticky directories iff chown is restricted was reversed - LASTFOLDER wasn't correctly set when delivering to multiple folders - -f- couldn't find the timestamp if the address contained a space - SENDMAIL and SENDMAILFLAGS are now split in forwarding actions - Variable capture actions now see the variable's current value and restore it if the action fails. Previously unset variables will remain unset. - fsync() mailboxes before closing them - Actually suppress the 'E' and 'a' flags when combined with the 'e' flag instead of just saying so - Avoid some calls to alarm() - Overflows at certain times would confuse procmail - dyna_long code now meets strict ANSI restrictions - 'W' flag changes "Program failure" to "Non-zero exitcode" - Nested blocks must open and close within the same rcfile - Root owned lockfiles aren't bogus - A lone trailing '$' wasn't terminated properly when expanded Changes to formail: - Replies without the -t flag go to the envelope sender - Replies without "-a Resent-" and -t flag ignore the Resent-* headers - Prevent corrupt idcaches by suppressing the -n option when splitting with the -D option - Accept and strip whitespace between the fieldname and colon - Renaming from a wildcard to nothing now works Changes to mailstat: - Work around the detab done on checkin to CVS - Recognize maildir mailboxes - Don't use a tempfile Changes to autoconf: - Don't assume realloc(0,size) works (doesn't under SunOS 4) Stopped using `implicit int' (for C9x) Cache gethostname() and uname() output Changed the form of tempfile names to make them `more' unique and deal with filename length limits more gracefully Updated the FAQ and the list of mirrors in the README Documented the exact behavior of lockfile's -! flag Documented the suggested usage of -r vs -rt 2000/08/25: v3.15 Changes to procmail: - v3.14 broke compilation on systems without lstat() or that didn't declare it - Rewrite folder type parsing: corrects handling of MH and maildir style spools - v3.14 changed '!' actions too much: revert to v3.13 behavior but continue to split SENDMAILFLAGS - Contents of skipped nested blocks could affect 'E', 'e', 'a', and 'A' flags when they shouldn't have - Prevent peeking into buffers on "Out of memory" errors - Unquoted $\var expansions could alter the interpretation of the following whitespace - Prevent attempts to set LINEBUF to really huge values - Optimize SWITCHRC = $_ - Use a secure PATH when processing /etc/procmailrc - Prevent attempts to exercise a Linux kernel security hole - Use 2^31-1 as the maximum score, even if LONG_MAX is larger Changes to formail: - Allow -n with -D and -s again -- corruption couldn't happen after all - Don't strip pre-colon whitespace until header is identified - Properly handle NULs in the body when generating an autoreply that keeps the body (could coredump) Changes to autoconf: - Avoid coredump on systems where sprintf() calls getenv() Documented that $\var expansions are never split on whitespace More manpage tweaks Worked around linkers that don't support compile-time stripping (for MacOS X) Removed ':' and '@' from list of characters that can appear in tempfile names Called nice() when shouldn't have Workaround SunOS 4.x compiler again 2001/06/28: v3.20 Changes to procmail: - SECURITY: don't do unsafe things from signal handlers: - ignore TRAP when terminating because of a signal - resolve the host and protocol of COMSAT when it is set - save the absolute path form of $LASTFOLDER for the comsat message when it is set - only use the log buffer if it's safe - Support LMTP for delivery mode (not enabled by default) - Preliminary support for using mmap() for `large' messages (this doesn't work yet) - SWITCHRC=zero-length-file didn't always abort the current rcfile - A race to create the mailspool would bounce one of the messages due to an internal error - LC_ in KEEPENV would preserve only the first LC_foo variable - Strip runtime linker variables (LD_*) from environment on all platforms - Drop duplicate and malformed environment entries - Multiple -a options will now set $2, $3, etc - Command line assignments to INCLUDERC and SWITCHRC no longer have any effect - Worked around AIX 4.3.3 xlc compiler (incorrect file-scope variable initialization) - When delivering to a maildir, don't force the message to end with an empty line - Be more paranoid about leaking information between recipients - Unset LOCKFILE if we can't actually lock it - Set MAILDIR to '.' if the chdir fails - LASTFOLDER was sometimes set by '?' conditions - Buffer the log more efficiently - Use the `standard' format for maildir filenames and retry on name collision - Rename by linking to prevent lossage - Avoid dangling pointers when variable capture actions fail Changes to autoconf: - Check for enum support - Warn people that non-ISO/ANSI C compilers might not be supported in the future - IRIX compiler (7.3.1) failed the const check from warnings Changes to lockfile: - Include the system mailbox lockfile path in the -v output - Resist attempts to use a setuid lockfile command - Fix infinite loop on -l, -r, or -s flag with no value Documented formail's treatment of >From_ lines as continuations of the From_ line and warned of problems caused by non-RFC822 field names like 'Old-From ' Clarified procmail's treatment of $@ and $# Fixed a man page formatting problem Use long, not off_t, with fseek()/ftell() Increase our paranoia: start to use strlcat() The default MAILDIR is now configurable separately from the default rcfile location Include an RPM spec file in the examples directory for automated builds Include and use mkinstalldirs Called nice() when shouldn't have (this time for sure!) 2001/06/29: v3.21 Changes to procmail: - Incorrect prototype broke compilation on Tru64 UNIX - INCLUDERC was broken by trying to be fancy and not fully succeeding when support for multiple -a options was added 2001/09/10: v3.22 Changes to procmail: - Regression bugs from 3.20: - Broke compilation with K&R compilers - procmail -p -m was overridding PATH - maildir delivery included garbage in filenames - Mismatched HOST in last rcfile didn't discard the message - COMSAT wasn't turned off by an rcfile on the command line - Catch overly long command line variable assignments - If a command expansion is truncated, set PROCMAIL_OVERFLOW and don't trim trailing (really middle) newlines - If the comsat host can't be resolved, set COMSAT to "no" Some fixes to the man pages More paranoia: start to use strlcpy() Generate safe temp and maildir filenames when the hostname contains / or : by mapping them to \ooo procmail-3.22/INSTALL0100644006717600001440000002154007223546513013515 0ustar guenthersrcDiscusses: 1. Getting the lot to compile 2. IMPORTANT info for people with remote filesystems (like NFS) 3. DEBUGGING AID 4. Setting up the environment 5. Extra options if you are a system administrator --- 1. Getting the lot to compile -------------------------- To install procmail, lockfile and formail: edit Makefile & config.h accordingly and type 'make install'. Intended configurable options in Makefile are: the install-destinations (primarily BASENAME) and the LOCKINGTEST directories (you can optionally use something like 'make BASENAME=$HOME install' instead). Intended configurable options in config.h are: MMDF support, standard environment presettings including PATH, trusted userids, whether each user has their own group. If an older version of procmail is currently installed, you should review all the entries in the HISTORY file between that revision and this one. 'make install' will: - implicitly do a 'make init', which will check your basic utilities for POSIX compliance, and generates correcting Makefiles accordingly - execute autoconf (a shell script that repeatedly calls the C compiler to determine if certain features/symbols are supported), which will create a file named autoconf.h - create three stripped binaries, a shell script and five man pages in the new/ subdirectory (all that is needed to install): procmail, lockfile, formail, mailstat, procmail.1, lockfile.1, formail.1, procmailrc.5, procmailsc.5, procmailex.5 - copy these binaries and mailstat to $(BINDIR) - copy the man pages to $(MAN1DIR) and $(MAN5DIR) - BEWARE: the installation obeys the current umask setting. If you want the files to be available to anyone, correct your umask first. 'make deinstall' will: - remove the just installed files in $(BINDIR) - remove the just installed files in $(MAN1DIR) and $(MAN5DIR) Minimal requirements (for regular uses): procmail must be installed. Optional files (depending on your requirements): lockfile only needs to be installed if you plan to read several mailboxes with one of the standard mailers that doesn't support lockfiles. formail only needs to be installed if mail sometimes arrives in nonstandard mailbox format (or if you want to generate auto replies, split up mailboxes/digests etc., see the man page of formail for more info). mailstat is an "example" shell script that can be used as is to produce summaries of the procmail generated logfiles; it is not needed by procmail itself in any way. procmail, lockfile, formail and mailstat are all *standalone* programs, i.e. they do *not* use any compiled-in paths or files that need to be there, they all can be used and installed independently without the need to install the others. If things don't compile automagically, I suggest you take a look at: src/autoconf, autoconf.h, config.h, src/includes.h For autoconf to work as intended, your compiler should either be fully ANSI compliant, or you should NOT turn off all warnings; enabling all warnings shouldn't hurt. In most cases the default options in the Makefile will do. The sources are supposed to be fully ANSI, K&R and POSIX compliant. N.B. If you encounter any difficulty at all in installing procmail (e.g. if you had to change Makefile or config.h in unpredicted ways, or a simple "make install" doesn't work), I'd very much like to hear about it; who knows, next time you might be able to simply "make install" as well. --- 2. IMPORTANT info for people with remote filesystems (like NFS) ------------------------------------------------------------ The autoconf script tries to determine what kernel locking functions are available *and* reliable on your system. In order to do this it exercises all the available locking methods (fcntl(), lockf() and flock()) on some sample files extensively. These tests produce results which depend on the filesystem which is being used to perform the tests. If you haven't changed the definition of LOCKINGTEST in the Makefile (you can include as many directories as you want) autoconf will prompt you to enter any additional directories (preferably on distinct filesystems) you want it to run the tests on. When specifying directories to test, it would probably be advisable to pick exactly those directories which belong to a unique fileserver-fileclient pair. I.e. one local file system will suffice. Only if you have remote filesystems, you might have to specify several. This makes sure that the locking tests will properly reflect the (lowest common denominator) locking capabilities on all filesystems available. If you have a very heterogenous environment (like several OS versions on machines (perhaps even from different vendors) that have NFS mounted file systems all over each other), then it could happen that some tests which are reliable with one remote filesystem, turn out to be unreliable across another (it all depends on the OS versions of clients and servers). If do not want to perform the locking tests on all those filesystems, but if you know what locking methods are unreliable anyway, then you can edit some defines in the config.h file (NO_fcntl_LOCK, NO_lockf_LOCK and NO_flock_LOCK). These can be uncommented by hand to prevent procmail from using them. The most typical case would be if you happen to be using NFS. Autoconf normally is very well capable of finding out if your lockd is reliable enough. In a very heterogenous environment (many different servers, many different lockd's (of perhaps different version and patchlevel)) however, it might be hard to determine if all the lockd's are equally reliable. In such a case (typically on SunOS :-), you might consider uncommenting both NO_fcntl_LOCK and NO_lockf_LOCK (NO_flock_LOCK normally doesn't rely on the infamous lockd, so you can leave it commented out). But, only do so if you think you know it better than autoconf. --- 3. DEBUGGING AID ------------- Since procmail is intended to run completely independent of any terminals, you will typically have difficulties seeing it display error messages on the stderr output. It is recommended, especially during debugging, to specify a LOGFILE (see the procmailrc(5) man page) in the rcfile or on the command line. Procmail will log all serious problems it encounters. Of course, instead of a regular file, one could also specify a terminal as the default logfile. Also, procmail can be persuaded to be a lot more verbose by inserting the following assignment at the top of your rcfile: VERBOSE=on Therefore a suggested command line for your first trial run (no rcfiles needed) would be: procmail VERBOSE=on (now type in a pseudo mail-message) If all else fails, you can try uncommenting the "#define console" entry in the config.h file. This will provide you with the most verbose procmail you can make. It is of course a good idea to comment out this line again after your troubles have been solved. If you run procmail by hand and pipe in some sample mail, then make sure that if you kill procmail, you use "kill pid" and NOT "kill -9 pid". Should procmail seem to hang, check if any lockfiles are still present. If you kill procmail with "kill pid" it will clean up any lockfiles it created itself. --- 4. Setting up the environment -------------------------- Every user that wants to use procmail should have a .forward and a .procmailrc file in his HOME directory. For starters, you can look at the supplied example files in "examples" or the NOTES section in the procmail(1) man page. BTW, be sure to make .forward *world* readable. MMDF users should note that they need a .maildelivery file *instead* of the .forward file (see the procmail(1) man page for more information). --- 5. Extra options if you are a system administrator ----------------------------------------------- If you are a system administrator you can decide to install procmail globally (i.e. as a more robust drop-in replacement for the local- maildelivery-capabilities of /bin/mail, this also gets rid of the known security holes in some /bin/mails), this has the advantage that users do not need to have a .forward file anymore that calls up procmail. Simply having a .procmailrc file in the HOME directory will suffice. Operation is transparent in this case (i.e. if no .procmailrc file is present in the HOME directory, mail will be delivered as usual and procmail behaves like a faster and more reliable /bin/mail substitute). For direct examples on how to do this, look at the examples/advanced file. ******************************************************************************* HIGHLY RECOMMENDED: install "procmail" suid root (and/or sgid maildaemon) install "lockfile" sgid maildaemon To obtain specific instructions on the best installation, type "make recommend" ******************************************************************************* --- For more info about the program, see the man pages or the FAQ list. procmail-3.22/KNOWN_BUGS0100644006717600001440000000435407316762737014142 0ustar guenthersrcTIMEOUT doesn't always work If a shell is invoked then procmail may wait while executing a command for longer than TIMEOUT specifies. regexp matching bug Some regexps may return an incorrect value in the MATCH variable. In particular, this can happen when 'redundant' * or + operators appear on the lefthand side of the \/ token. (The current best guess is that fixing this would require moving the "beginning of match" pointer into the "per-task" regexp structure.) Incorrect usage of -lnsl and -lsocket libsocket and libnsl should be avoided if not needed as they're broken under at least one version of IRIX. If your procmail binary doesn't reliably find user's home directories, or otherwise appears to have problems accessing the passwd file, try removing -lnsl and -lsocket from the SEARCHLIBS variable in the Makefile, then recompile. No "$@" in logged abstract When passing "$@" to a command, the "Folder:" logged does not include any of arguments passed via the "$@" Custom delimiter lossage When using a custom message delimiter (like MMDF's ^A^A^A^A\n) procmail fails to escape the delimiter in incoming messages, resulting in corrupted mailboxes. Best current workaround is to put a recipe in the /etc/procmailrc file that reads something like :0 fw * ^A^A^A^A$ |perl -pe 's:\001\001\001\001$:\002\002\002\002:' The "^A"s in the condition need to be real control-A characters. Lost value on failed chdir() If the user assigns a value to MAILDIR and the chdir() fails, the previous value of the variable (but not the process's cwd) is lost and replaced with "." Control-M isn't whitespace Every so often someone copies an rcfile from a Windows box and it ends up with CRs on the end of every line. They should be treated just like spaces and tabs are. As is, the results are really confusing. Shell Expansion Shell expansion of conditions via the '$' special treats double-quotes weirdly. They should not be considered special at all there. Backslash-newline inconsistencies Backslash-newline removal is almost completely inconsistent and should be straightened out some how, but without breaking anything that's in use. procmail-3.22/Makefile0100644006717600001440000001107707347314265014134 0ustar guenthersrc#$Id: Makefile,v 1.76 2001/07/12 01:27:19 guenther Exp $ # BASENAME should point to where the whole lot will be installed # change BASENAME to your home directory if need be BASENAME = /usr # For display in the man pages VISIBLE_BASENAME= $(BASENAME) # You can predefine ARCHITECTURE to a bin directory suffix ARCHITECTURE = #ARCHITECTURE =.sun4 BINDIR_TAIL = bin$(ARCHITECTURE) MANDIR = $(BASENAME)/man BINDIR = $(BASENAME)/$(BINDIR_TAIL) VISIBLE_BINDIR = $(VISIBLE_BASENAME)/$(BINDIR_TAIL) # MAN1SUFFIX for regular utility manuals MAN1SUFFIX =1 # MAN5SUFFIX for file-format descriptions MAN5SUFFIX =5 MAN1DIR = $(MANDIR)/man$(MAN1SUFFIX) MAN5DIR = $(MANDIR)/man$(MAN5SUFFIX) # Uncomment to install compressed man pages (possibly add extra suffix # to the definitions of MAN?DIR and/or MAN?SUFFIX by hand) #MANCOMPRESS = compress ############################*# # Things that can be made are: # # help (or targets) Displays this list you are looking at # init (or makefiles) Performs some preliminary sanity checks on your system # and generates Makefiles accordingly # bins Preinstalls only the binaries to ./new # mans Preinstalls only the man pages to ./new # all Does both # install.bin Installs the binaries from ./new to $(BINDIR) # install.man Installs the man pages from ./new to $(MAN[15]DIR) # install Does both # recommend Show some recommended suid/sgid modes # install-suid Impose the modes shown by 'make recommend' # clean Attempts to restore the package to pre-make state # realclean Attempts to restore the package to pre-make-init state # deinstall Removes any previously installed binaries and man # pages from your system by careful surgery # autoconf.h Will list your system's anomalies # procmail Preinstalls just all procmail related stuff to ./new # formail Preinstalls just all formail related stuff to ./new # lockfile Preinstalls just all lockfile related stuff to ./new # setid Creates the setid binary needed by the SmartList # installation ######################*# # Makefile.0 - mark, don't (re)move this, a sed script needs it LOCKINGTEST=__defaults__ #LOCKINGTEST=/tmp . # Uncomment and add any directories you see fit. # If LOCKINGTEST is defined, autoconf will NOT # prompt you to enter additional directories. # See INSTALL for more information about the # significance of the locking tests. ######################################################################## # Only edit below this line if you *think* you know what you are doing # ######################################################################## #LOCKINGTEST=100 # Uncomment (and change) if you think you know # it better than the autoconf lockingtests. # This will cause the lockingtests to be hotwired. # 100 to enable fcntl() # 010 to enable lockf() # 001 to enable flock() # Or them together to get the desired combination. # Optional system libraries we search for SEARCHLIBS = -lm -ldir -lx -lsocket -lnet -linet -lnsl_s -lnsl_i -lnsl -lsun \ -lgen -lsockdns -ldl # -lresolv # not really needed, is it? # Informal list of directories where we look for the libraries in SEARCHLIBS LIBPATHS=/lib /usr/lib /usr/local/lib GCC_WARNINGS = -O2 -pedantic -Wreturn-type -Wunused -Wformat -Wtraditional \ -Wpointer-arith -Wconversion -Waggregate-return \ #-Wimplicit -Wshadow -Wid-clash-6 #-Wuninitialized # The place to put your favourite extra cc flag CFLAGS0 = -O #$(GCC_WARNINGS) LDFLAGS0= -s # Read my libs :-) LIBS= CFLAGS1 = $(CFLAGS0) #-posix -Xp LDFLAGS1= $(LDFLAGS0) $(LIBS) #-lcposix ####CC = cc # gcc # object file extension O = o RM = /bin/rm -f MV = mv -f LN = ln BSHELL = /bin/sh INSTALL = cp DEVNULL = /dev/null STRIP = strip MKDIRS = new/mkinstalldirs SUBDIRS = src man BINSS = procmail lockfile formail mailstat MANS1S = procmail formail lockfile MANS5S = procmailrc procmailsc procmailex # Possible locations for the sendmail.cf file SENDMAILCFS = /etc/mail/sendmail.cf /etc/sendmail.cf /usr/lib/sendmail.cf # Makefile - mark, don't (re)move this, a sed script needs it all: init $(MAKE) make $@ make: @$(BSHELL) -c "exit 0" .PRECIOUS: Makefile init: $(BSHELL) ./initmake $(BSHELL) "$(SHELL)" "$(RM)" "$(MV)" "$(LN)" \ "$(SEARCHLIBS)" \ "$(LIBPATHS)" \ $(DEVNULL) "$(MAKE)" $(O) \ "$(CC)" "$(CFLAGS1)" "$(LDFLAGS1)" "$(BINSS)" \ "$(MANS1S)" \ "$(MANS5S)" "$(SUBDIRS)" \ "$(VISIBLE_BINDIR)" \ "$(STRIP)" makefiles makefile Makefiles Makefile: init @$(BSHELL) -c "exit 0" help target targets \ bins mans install.bin install.man install recommend install-suid clean setid \ realclean veryclean clobber deinstall autoconf.h $(BINSS) multigram: init $(MAKE) make $@ procmail-3.22/Makefile.10100644006717600001440000001214607347314265014271 0ustar guenthersrc#$Id: Makefile.1,v 1.52 2001/07/12 01:27:20 guenther Exp $ all: bins mans recommend @echo If you would like to inspect the results before running make \ install: @echo All installable files can be found in the new/ subdirectory. make: @$(SHELL) -c "exit 0" .PRECIOUS: Makefile help target targets: @sed "/^##*\*#$$/,/^##*\*#$$/ !d" $@ @-if $(FGREP) -n -e '`' config.h $(DEVNULL) | $(FGREP) -v EOFName ; \ then \ echo;echo ' ^^^^^^^^^^^^^^^^^^^^ WARNING ^^^^^^^^^^^^^^^^^^^^^';\ echo ' * Having backquotes in there could be unhealthy! *';\ echo;fi;exit 0 recommend: autoconf.h src/Makefile @cd src; $(MAKE) $@ @echo ================================================================\ =============== @if $(FGREP) CF_no_procmail_yet autoconf.h >$(DEVNULL); \ then echo If you are a system administrator you should consider \ integrating procmail; echo into the mail-delivery system -- for advanced \ functionality, speed AND; echo SECURITY "--. For" more information about \ this topic you should look in the; echo examples/advanced file.; elif \ cat $(SENDMAILCFS) 2>$(DEVNULL) | \ grep 'Mlocal.*procmail.*F=[a-zA-Z]*u' >$(DEVNULL) ; then \ echo The recommendation for the sendmail.cf entry of procmail has \ changed.; echo I suggest you remove the '`u'"'"-flag 'like in:'; echo ; \ sed -n 's/.*\(Mlocal.*procmail.*F=[a-zA-Z]*\)u/\1/p' `if test -f \ /etc/sendmail.cf; then echo /etc/sendmail.cf; else \ echo /usr/lib/sendmail.cf; fi`; fi @echo @echo \ "Also, HIGHLY RECOMMENDED (type 'make install-suid' to execute it):" @echo @src/$@ $(BINDIR)/procmail $(BINDIR)/lockfile >suid.sh @src/$@ $(BINDIR)/procmail $(BINDIR)/lockfile @echo ================================================================\ =============== suid.sh: recommend install-suid: suid.sh install.bin @cat suid.sh @$(SHELL) ./suid.sh @cd $(BINDIR); echo Installed in $(BINDIR); ls -l $(BINSS) $(MANS): mans $(BINS): bins $(BASENAME): $(MKDIRS) $(BASENAME) install.man: $(MANS) $(BASENAME) @-$(MKDIRS) $(MANDIR) 2>$(DEVNULL); exit 0 @-test -d $(MAN1DIR) || $(RM) $(MAN1DIR); exit 0 @-$(MKDIRS) $(MAN1DIR) 2>$(DEVNULL); exit 0 @-test -d $(MAN5DIR) || $(RM) $(MAN5DIR); exit 0 @-$(MKDIRS) $(MAN5DIR) 2>$(DEVNULL); exit 0 @chmod 0644 $(MANS) @for a in $(MANS1S); \ do $(INSTALL) new/$$a.1 $(MAN1DIR)/$$a.$(MAN1SUFFIX) || exit 1; \ if test "X$(MANCOMPRESS)" != "X"; \ then $(MANCOMPRESS) -c new/$$a.1 >$(MAN1DIR)/$$a.$(MAN1SUFFIX); \ else :; fi; \ done @for a in $(MANS5S); \ do $(INSTALL) new/$$a.5 $(MAN5DIR)/$$a.$(MAN5SUFFIX) || exit 1; \ if test "X$(MANCOMPRESS)" != "X"; \ then $(MANCOMPRESS) -c new/$$a.5 >$(MAN5DIR)/$$a.$(MAN5SUFFIX); \ else :; fi; \ done echo Housekeeping file >install.man install.bin: $(BINS) $(BASENAME) @-$(MKDIRS) $(BINDIR) 2>$(DEVNULL); exit 0 @chmod 0755 $(BINS) $(INSTALL) $(BINS) $(BINDIR) @-dirname / >$(DEVNULL) || $(INSTALL) examples/dirname $(BINDIR) echo Housekeeping file >install.bin install: @$(MAKE) install.man install.bin @echo @cd $(BINDIR); echo Installed in $(BINDIR); ls -l $(BINSS) @cd $(MAN1DIR); echo Installed in $(MAN1DIR); ls -l $(MANS1) @cd $(MAN5DIR); echo Installed in $(MAN5DIR); ls -l $(MANS5) @$(MAKE) recommend deinstall: @echo ============================= Deinstalling the procmail package. @$(RM) install.man install.bin @echo ============================= Checking if everything was removed: @-cd $(BINDIR); $(RM) $(BINSS); ls -l $(BINSS); exit 0 @-cd $(MAN1DIR); $(RM) $(MANS1); ls -l $(MANS1); exit 0 @-cd $(MAN5DIR); $(RM) $(MANS5); ls -l $(MANS5); exit 0 @echo ============================= Ready. clean: config.check -for a in $(SUBDIRS); do cd $$a; $(MAKE) $@; cd ..; done; exit 0 cd SmartList; $(RM) targetdir.h targetdir.tmp install.list asked.patch $(RM) $(MANS) $(BINS) install.man install.bin suid.sh _Makefile \ *core* autoconf.h.tmp realclean: clean _init $(RM) config.check -for a in $(SUBDIRS); do $(MV) $$a/Makefile.init $$a/Makefile; done; \ exit 0 veryclean clobber: realclean _init: sed -e '/^# Makefile.1 - mark/,$$ d' _Makefile cat Makefile.0 >>_Makefile $(MV) _Makefile Makefile $(RM) Makefile.0 man/Makefile: man/Makefile.0 Makefile src/Makefile: src/Makefile.0 Makefile HIDEMAKE=$(MAKE) man/Makefile src/Makefile Makefile: Makefile.1 initmake sed -e '/^# Makefile.1 - mark/,$$ d' _Makefile cat Makefile.0 >>_Makefile $(MV) _Makefile Makefile $(RM) Makefile.0 $(HIDEMAKE) init init makefiles Makefiles makefile: man/Makefile src/Makefile procmail-3.22/Manifest0100644006717600001440000000472607316762742014173 0ustar guenthersrcMakefile We all know what that is. README Important, read it. INSTALL A description of what has to be done to install procmail. config.h The file to edit if you want to change, yes, the configuration. FAQ Lists the things you are too lazy to figure out yourself. HISTORY Recent and ancient changes, features (or bugs) documented. FEATURES A summary of all the things procmail is particularly good at. Manifest You guessed it. initmake A shell script that performs some preliminary tests on your system and prepares the Makefiles. Makefile.1 Used by initmake to customise the Makefile. src/* The C-sources (in general). src/Makefile.0 Used by initmake to customise the Makefile. src/includes.h System include files are all referenced here. src/autoconf The shell script that seizes your compiler and machine, and then creates a file called autoconf.h describing the kludges that are going to be applied for your installation. new/* After a "make", all the files that will be installed can be found here (for inspection, if you like). man/* Yes, the man pages (made in a labour camp) and two additional files which make these man pages auto-adapting. man/Makefile.0 Used by initmake to customise the Makefile. examples/mailstat A summary generator for procmail generated logfiles. examples/?procmailrc Sample .procmailrc files. examples/?rmail Sample shell scripts that demonstrate how to use lockfiles while reading the mail (to ensure mail integrity as soon as you exit the mail program). examples/dirname Substitute dirname program, for the deprived. examples/forward A sample .forward file (MMDF users should disregard this file and look in the man page). examples/advanced Some extra info for network mounted mailboxes, examples of advanced .procmailrc expressions and using procmail as a local delivery agent. examples/local_procmail_lmtp.m4 A sendmail M4 config file that, when used with "FEATURE(local_procmail_lmtp.m4)", will make sendmail use procmail as its local delivery agent via LMTP. Procmail *MUST* be compiled with LMTP support (see config.h) or this will just bounce all your email. This is not yet ready for general use. examples/procmail-rpm.spec An RPM spec file that allows for automated builds of a procmail RPM package. SmartList/* All you need to start up lots of mailinglists. SmartList/FEATURES A summary of all the SmartList package will take care of. SmartList/INTRO Comprehensive introduction on maintaining a mailinglist. procmail-3.22/README0100644006717600001440000001733307347314265013355 0ustar guenthersrcFor installation instructions see the INSTALL file. ---------------------- Procmail & formail mail processing package. Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands. Copyright (c) 1997-2001, Philip Guenther, The United States of America Some legal stuff: This package is open source software; you can redistribute it and/or modify it under the terms of either: - the GNU General Public License as published by the Free Software Foundation and can be found in the included file called "COPYING"; either version 2, or (at your option) any later version, or - the "Artistic License" which can be found in the included file called "Artistic". This package is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See either the GNU General Public License or the Artistic License for more details. For those of you that choose to use the GNU General Public License, my interpretation of the GNU General Public License is that no procmailrc script falls under the terms of the GPL unless you explicitly put said script under the terms of the GPL yourself. -------------------------- SYSTEM REQUIREMENTS ------------------------------- Any *NIX-alike (or POSIX compliant) system. Sendmail, ZMailer, smail, MMDF, mailsurr or compatible mailers (in effect any mailer that can process RFC-822 compliant mails). For a fairly complete list of all C-library references done in the programs see "src/includes.h". ------------------------------ DESCRIPTION ----------------------------------- The procmail mail processing program. (v3.22 2001/09/10) Can be used to create mail-servers, mailing lists, sort your incoming mail into separate folders/files (real convenient when subscribing to one or more mailing lists or for prioritising your mail), preprocess your mail, start any programs upon mail arrival (e.g. to generate different chimes on your workstation for different types of mail) or selectively forward certain incoming mail automatically to someone. Procmail can be used: - and installed by an unprivileged user (for himself only). - as a drop in replacement for the local delivery agent /bin/mail (with biff/comsat support). - as a general mailfilter for whole groups of messages (e.g. when called from within sendmail.cf rules). The accompanying formail program enables you to generate autoreplies, split up digests/mailboxes into the original messages, do some very simple header-munging/extraction, or force mail into mail-format (with leading From line). ---------------------- We made the utmost effort to make procmail as robust as any program can be (every conceivable system error is caught *and* handled). Since procmail is written entirely in C, it poses a very low impact on your system's resources (under normal conditions, when you don't start other programs/scripts from within it, it is faster and more robust than the average /bin/mail you have on your system now). Procmail was designed to deliver the mail under the worst conditions (file system full, out of swap space, process table full, file table full, missing support files, unavailable executables; it all doesn't matter). Should (in the unlikely event) procmail be unable to deliver your mail somewhere, the mail will bounce back to the sender or reenter the mailqueue (your choice). For a more extensive list of features see the FEATURES file. ---------------------- However, as with any program, bugs cannot be completely ruled out. We tested the program extensively, and believe it should be relatively bug free. Should, however, anyone find any bugs (highly unlikely :-), we would be pleased (well, sort of :-) to hear about it. Please send us the patches or bug report; you can reach us by E-mail at . We'll look at them and will try to fix it in a future release. (BTW, if you should find any spelling or grammar errors in these files, don't hesitate to point them out; we like correct English just as much as you do). ---------------------- I would like to take the opportunity to express my gratitude in particular to these devoted users of the procmail-package. Without their constant feedback procmail would not have looked the same: David W. Tamkin An excellent proofreader and betatester. Josh Laff For stresstesting procmail (and me :-). Dan Jacobson For his many useful suggestions. Rick Troxel Because I crashed his Convex way too often :-). Roman Czyborra For his enthusiastic ideas. Ari Kornfeld The guardian angel of SmartList. Alan K. Stebbens For his endless creativity and suggestions. Philip Guenther Sometimes faster at repairing bugs than I can write them :-) He's also the current main coordinator of procmail/SmartList development. Era Eriksson For maintaining the FAQ. He's also the current coordinator of the procmail volunteer group. Philip Guenther would like to thank the following people: David W. Tamkin For continued duty as procmail's star betatester. Miquel van Smoorenburg For winning an argument with me. Tim Pierce SmartList's current guardian angel. Christopher P. Lindsey For thinking hard about real world usage. ...and all the other members of the procmail-dev mailing list, for suggestions, testing, and being annoying (but in a good way). ---------------------- Please note that this program essentially is supposed to be static; that means no extra features (honouring the VNIX spirit) are supposed to be added (though any useful suggestions will be appreciated and evaluated if time permits). Cheers, Stephen R. van den Berg of Cubic Circle, The Netherlands. Internet E-mail: Snail-Mail: Procmail Foundation P.O.Box 21074 6369 ZG Simpelveld The Netherlands Procmail mailinglist: Procmail updates and patches list (readonly): SmartList mailinglist: SmartList updates and patches list (readonly): Development: Development list for procmail Development list for SmartList Coordinator of development List to coordinate volunteers ---------------------- The most recent version can be obtained via http://www.procmail.org/ or ftp://ftp.procmail.org/pub/procmail/ You'll be able to find instructions there which direct you to suitable mirror sites around the world. The current list of mirror sites include: ftp://ftp.psg.com/pub/unix/procmail/ ftp://ftp.ucsb.edu/pub/mirrors/procmail/ ftp://ftp.informatik.rwth-aachen.de/pub/packages/procmail/ ftp://ftp.fu-berlin.de/pub/unix/mail/procmail/ ftp://ftp.net.ohio-state.edu/pub/networking/mail/procmail/ ftp://ftp.fdt.net/pub/unix/packages/procmail/ ftp://ftp.tamu.edu/pub/mirrors/procmail/ ftp://ftp.kfki.hu/pub/packages/mail/procmail/ ftp://giswitch.sggw.waw.pl/pub/unix/procmail/ ftp://ftp.solarisguide.com/pub/procmail/ ftp://ftp.win.ne.jp/pub/network/mail/procmail/ http://www.ring.gr.jp/archives/net/mail/procmail/ ftp://ftp.ring.gr.jp/pub/net/mail/procmail/ ftp://ftp.ayamura.org/pub/procmail/ ftp://sunsite.cnlab-switch.ch/mirror/procmail/ ftp://ftp.gigabell.net/pub/procmail/ ftp://ftp.linja.net/pub/mirrors/procmail/ ftp://ftp.stealth.net/pub/mirrors/ftp.procmail.org/pub/procmail/ ftp://ftp.mirror.ac.uk/sites/ftp.procmail.org/pub/procmail/ ---------------------- procmail-3.22/config.h0100644006717600001440000004616307347314336014115 0ustar guenthersrc/*$Id: config.h,v 1.101 2001/08/27 08:43:57 guenther Exp $*/ /*#define sMAILBOX_SEPARATOR "\1\1\1\1\n" /* sTART- and eNDing separ. */ /*#define eMAILBOX_SEPARATOR "\1\1\1\1\n" /* uncomment (one or both) if your mail system uses nonstandard mail separators (non sendmail or smail compatible mailers like MMDF), if yours is even different, uncomment and change the value of course */ /* KEEPENV and PRESTENV should be defined as a comma-separated null-terminated list of strings */ /* every environment variable appearing in KEEPENV will not be thrown away * upon startup of procmail, e.g. you could define KEEPENV as follows: * #define KEEPENV {"TZ","LANG",0} * environment variables ending in an _ will designate the whole group starting * with this prefix (e.g. "LC_"). Note that keeping LANG and or the LC_ * variables is not recommended for most installations due to the security * considerations/dependencies present in the use of locales other than * the "C" locale. */ #define KEEPENV {"TZ",0} /* procmail is compiled with two definitions of the PATH variable. The first * definition is used while processing the /etc/procmailrc file and should * only contain trustable (i.e., system) directories. Otherwise the second * definition is used. Note that the /etc/procmailrc file cannot change the * PATH seen by user's rcfiles: the second definition will be applied upon the * completion of the /etc/procmailrc file (future versions of procmail are * expected to provide better runtime configuration control). The autoconf * process attempts to determine reasonable values for these versions of PATH * and sets the defSPATH and defPATH variables accordingly. If you want to * override those settings you should uncomment and possibly change the * DEFSPATH and DEFPATH defines below */ /*#define DEFSPATH "PATH=/bin:/usr/bin" /* */ /*#define DEFPATH "PATH=$HOME/bin:/bin:/usr/bin" /* */ /* every environment variable appearing in PRESTENV will be set or wiped * out of the environment (variables without an '=' sign will be thrown * out), e.g. you could define PRESTENV as follows: * #define PRESTENV {"IFS","ENV","PWD",0} * any side effects (like setting the umask after an assignment to UMASK) will * *not* take place. Do *not* define PATH here -- use the DEFSPATH and * DEFPATH defines above instead */ #define PRESTENV {"IFS","ENV","PWD",0} /*#define GROUP_PER_USER /* uncomment this if each user has his or her own group and procmail can therefore trust a $HOME/.procmailrc that is group writable or contained in a group writable home directory if the group involved is the user's default group. */ /*#define LMTP /* uncomment this if you want to use procmail as an LMTP (rfc2033) server, presumably for invocation by an MTA. The file examples/local_procmail_lmtp.m4 contains info on how to set this up with sendmail. */ /* This file previously allowed you to define SYSTEM_MBOX. This has changed. If you want mail delivery to custom mail-spool-files, edit the src/authenticate.c file and change the content of: auth_mailboxname() (either directly, or through changing the definitions in the same file of MAILSPOOLDIR, MAILSPOOLSUFFIX, MAILSPOOLHASH or MAILSPOOLHOME) */ /************************************************************************ * Only edit below this line if you have viewed/edited this file before * ************************************************************************/ /* every user & group appearing in TRUSTED_IDS is allowed to use the -f option if the list is empty (just a terminating 0), everyone can use it TRUSTED_IDS should be defined as a comma-separated null-terminated list of strings; if unauthorised users use the -f option, an extra >From_ field will be added in the header */ #define TRUSTED_IDS {"root","daemon","uucp","mail","x400","network",\ "list","slist","lists","news",0} /*#define NO_fcntl_LOCK /* uncomment any of these three if you */ /*#define NO_lockf_LOCK /* definitely do not want procmail to make */ /*#define NO_flock_LOCK /* use of those kernel-locking methods */ /* If you set LOCKINGTEST to a binary number than there's no need to set these. These #defines are only useful if you want to disable particular locking styles but are unsure which of the others are safe. Otherwise, don't touch them. */ /*#define RESTRICT_EXEC 100 /* uncomment to prevent users with uids equal or higher than RESTRICT_EXEC from executing programs from within their .procmailrc files (this restriction does not apply to the /etc/procmailrc and /etc/procmailrcs files) */ /*#define NO_NFS_ATIME_HACK /* uncomment if you're definitely not using NFS mounted filesystems and can't afford procmail to sleep for 1 sec. before writing to an empty regular mailbox. This lets programs correctly judge whether there is unread mail present. procmail automatically suppresses this when it isn't needed or under heavy load. */ /*#define DEFsendmail "/usr/sbin/sendmail" /* uncomment and/or change if the autoconfigured default SENDMAIL is not suitable. This program should quack like a sendmail: it should accept the -oi flag (to tell it to _not_ treat a line containing just a period as EOF) and then a list of recipients. If the -t flag is given, it should instead extract the recipients from the To:, Cc:, and Bcc: header fields. If it can't do this, many standard recipes will not work. One reasonable candidate is "/etc/mta/send" on systems that support the MTA configuration switch. */ #define DEFmaildir "$HOME" /* default value for the MAILDIR variable; this must be an absolute path */ #define PROCMAILRC "$HOME/.procmailrc" /* default rcfile for every recipient; if this file is not found, maildelivery will proceed as normal to the default system mailbox. This also must be an absolute path */ #define ETCRC "/etc/procmailrc" /* optional global procmailrc startup file (will only be read if procmail is started with no rcfile on the command line). */ #define ETCRCS "/etc/procmailrcs/" /* optional trusted path prefix for rcfiles which will be executed with the uid of the owner of the rcfile (this only happens if procmail is called with the -m option, without variable assignments on the command line). */ /*#define console "/dev/console" /* uncomment if you want procmail to use the console (or any other terminal or file) to print any error messages that could not be dumped in the "logfile"; only recommended for debugging purposes, if you have trouble creating a "logfile" or suspect that the trouble starts before procmail can interpret any rcfile or arguments. */ /************************************************************************ * Only edit below this line if you *think* you know what you are doing * ************************************************************************/ #define ROOT_uid 0 #define LDENV {"LD_","_RLD","LIBPATH=","ELF_LD_","AOUT_LD_",0} #define UPDATE_MASK S_IXOTH /* bit set on mailboxes when mail arrived */ #define OVERRIDE_MASK (S_IXUSR|S_ISUID|S_ISGID|S_ISVTX) /* if found set */ /* the permissions on the mailbox will be left untouched */ #define INIT_UMASK (S_IRWXG|S_IRWXO) /* == 077 */ #define GROUPW_UMASK (INIT_UMASK&~S_IRWXG) /* == 007 */ #define NORMperm \ (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH|UPDATE_MASK) /* == 0667, normal mode bits used to create files, before umask */ #define READperm (S_IRUSR|S_IRGRP|S_IROTH) /* == 0444 */ #define NORMdirperm (S_IRWXU|S_IRWXG|S_IRWXO) /* == 0777 */ #define LOCKperm READperm /* mode bits used while creating lockfiles */ #define MAX_locksize 16 /* lockfiles are expected not to be longer */ #ifndef SMALLHEAP #define DEFlinebuf 2048 /* default max expanded line length */ #define BLKSIZ 16384 /* blocksize while reading/writing */ #define STDBUF 1024 /* blocksize for emulated stdio */ #else /* and some lower defaults for the unfortunate amongst us */ #define DEFlinebuf 512 #define BLKSIZ 1024 #define STDBUF 128 #undef USE_MMAP /* don't bother on these guys */ #endif /* SMALLHEAP */ #undef USE_MMAP /* UNTIL PROBLEMS ARE FIXED */ #ifdef USE_MMAP #ifndef INEFFICIENTrealloc #define INEFFICIENTrealloc /* don't pussy-foot around */ #endif #define MAXinMEM (1024*1024) /* when to switch to mmap() */ #define MMAP_DIR "/var/spool/procmail/" /* where to put */ #endif /* the files */ #define MINlogbuf 81 /* fit an entire line */ #define MAXlogbuf 1000 /* in case someone abuses LOG */ #define MAILERDAEMON "MAILER-DAEMON" /* From_ address to replace <> */ #define FAKE_FIELD ">From " #define RETRYunique 8 /* # of tries at making a unique filename */ #define BOGUSprefix "BOGUS." /* prepended to bogus mailboxes */ #define DEFsuspend 16 /* multi-purpose 'idle loop' period */ #define DEFlocksleep 8 #define TO_key "^TO_" /* for addresses */ #define TO_substitute "(^((Original-)?(Resent-)?(To|Cc|Bcc)|\ (X-Envelope|Apparently(-Resent)?)-To):(.*[^-a-zA-Z0-9_.])?)" #define TOkey "^TO" /* for words */ #define TOsubstitute "(^((Original-)?(Resent-)?(To|Cc|Bcc)|\ (X-Envelope|Apparently(-Resent)?)-To):(.*[^a-zA-Z])?)" #define FROMDkey "^FROM_DAEMON" /* matches most daemons */ #define FROMDsubstitute "(^(Mailing-List:|Precedence:.*(junk|bulk|list)|\ To: Multiple recipients of |\ (((Resent-)?(From|Sender)|X-Envelope-From):|>?From )([^>]*[^(.%@a-z0-9])?(\ Post(ma?(st(e?r)?|n)|office)|(send)?Mail(er)?|daemon|m(mdf|ajordomo)|n?uucp|\ LIST(SERV|proc)|NETSERV|o(wner|ps)|r(e(quest|sponse)|oot)|b(ounce|bs\\.smtp)|\ echo|mirror|s(erv(ices?|er)|mtp(error)?|ystem)|\ A(dmin(istrator)?|MMGR|utoanswer)\ )(([^).!:a-z0-9][-_a-z0-9]*)?[%@> ][^<)]*(\\(.*\\).*)?)?$([^>]|$)))" #define FROMMkey "^FROM_MAILER" /* matches most mailer-daemons */ #define FROMMsubstitute "(^(((Resent-)?(From|Sender)|X-Envelope-From):|\ >?From )([^>]*[^(.%@a-z0-9])?(\ Post(ma(st(er)?|n)|office)|(send)?Mail(er)?|daemon|mmdf|n?uucp|ops|\ r(esponse|oot)|(bbs\\.)?smtp(error)?|s(erv(ices?|er)|ystem)|A(dmin(istrator)?|\ MMGR)\ )(([^).!:a-z0-9][-_a-z0-9]*)?[%@> ][^<)]*(\\(.*\\).*)?)?$([^>]|$))" #define DEFshellmetas "&|<>~;?*[" /* never put '$' in here */ #define DEFdefault "$ORGMAIL" #define DEFmsgprefix "msg." #define DEFlockext ".lock" #define DEFshellflags "-c" #define DEFlocktimeout 1024 /* defaults to about 17 minutes */ #define DEFtimeout (DEFlocktimeout-64) /* 64 seconds to clean up */ #define DEFnoresretry 4 /* default nr of retries if no resources left */ #define nfsTRY (7+1) /* nr of times+1 to ignore spurious NFS errors */ #define MATCHVAR "MATCH" #define AMATCHVAR "MATCH=" #define DEFlogabstract -1 /* abstract by default, but don't mail it back */ #define COMSAThost "localhost" /* where the biff/comsat daemon lives */ #define COMSATservice "biff" /* the service name of the comsat daemon */ #define COMSATprotocol "udp" /* if you change this, comsat() needs patching */ #define COMSATxtrsep ":" /* mailbox-spec extension separator */ #define SERV_ADDRsep '@' /* when overriding in COMSAT=serv@addr */ #define DEFcomsat offvalue /* when an rcfile has been specified */ /* set to either "offvalue" or "empty" */ #define BinSh "/bin/sh" #define ROOT_DIR "/" #define DEAD_LETTER "/tmp/dead.letter" /* $ORGMAIL if no passwd entry */ #define DevNull "/dev/null" #define NICE_RANGE 39 /* maximal nice difference */ #define chCURDIR '.' /* the current directory */ #define chPARDIR ".." /* the parent directory */ #define DIRSEP "/" /* directory separator symbols, the */ /* last one should be the most common one */ #define MAILDIRtmp "/tmp" /* maildir subdirectories */ #define MAILDIRcur "/cur" #define MAILDIRnew "/new" #define MAILDIRLEN STRLEN(MAILDIRnew) #define MAILDIRretries 5 /* retries on obtaining a unique filename */ #define EOFName " \t\n#`'\");" #define HELPOPT1 'h' /* options to get command line help */ #define HELPOPT2 '?' #define VERSIONOPT 'v' /* option to display version */ #define PRESERVOPT 'p' /* preserve environment */ #define TEMPFAILOPT 't' /* return EX_TEMPFAIL on error */ #define MAILFILTOPT 'm' /* act as a general purpose mail filter */ #define FROMWHOPT 'f' /* set name on From_ line */ #define REFRESH_TIME '-' /* when given as argument to -f */ #define ALTFROMWHOPT 'r' /* alternate and obsolete form of -f */ #define OVERRIDEOPT 'o' /* do not generate >From_ lines */ #define BERKELEYOPT 'Y' /* Berkeley format, disregard Content-Length: */ #define ALTBERKELEYOPT 'y' /* same effect as -Y, kludge */ #define ARGUMENTOPT 'a' /* set $1 */ #define DELIVEROPT 'd' /* deliver mail to named recipient */ #define LMTPOPT 'z' /* talk LTMP on stdin/stdout */ #define PM_USAGE \ "Usage: procmail [-vptoY] [-f fromwhom] [parameter=value | rcfile] ...\ \n Or: procmail [-toY] [-f fromwhom] [-a argument] ... -d recipient ...\ \n\ Or: procmail [-ptY] [-f fromwhom] -m [parameter=value] ... rcfile [arg] ...\ \n Or: procmail [-toY] [-a argument] ... -z\ \n" #define PM_HELP \ "\t-v\t\tdisplay the version number and exit\ \n\t-p\t\tpreserve (most of) the environment upon startup\ \n\t-t\t\tfail softly if mail is undeliverable\ \n\t-f fromwhom\t(re)generate the leading 'From ' line\ \n\t-o\t\toverride the leading 'From ' line if necessary\ \n\t-Y\t\tBerkeley format mailbox, disregard Content-Length:\ \n\t-a argument\twill set $1, $2, etc\ \n\t-d recipient\texplicit delivery mode\ \n\t-z\t\tact as an LMTP server\ \n\t-m\t\tact as a general purpose mail filter\n" #define PM_QREFERENCE \ "Recipe flag quick reference:\ \n\tH egrep header (default)\tB egrep body\ \n\tD distinguish case\ \n\tA also execute this recipe if the common condition matched\ \n\ta same as 'A', but only if the previous recipe was successful\ \n\tE else execute this recipe, if the preceding condition didn't match\ \n\te on error execute this recipe, if the previous recipe failed\ \n\th deliver header (default)\tb deliver body (default)\ \n\tf filter\t\t\ti ignore write errors\ \n\tc carbon copy or clone message\ \n\tw wait for a program\t\tr\ raw mode, mail as is\ \n\tW same as 'w', but suppress 'Program failure' messages\n" #define MINlinebuf 128 /* minimal LINEBUF length (don't change this) */ #define FROM_EXPR "\nFrom " #define FROM "From " #define SHFROM "From" #define NSUBJECT "^Subject:.*$" #define MAXSUBJECTSHOW 78 #define FOLDER " Folder: " #define LENtSTOP 9 /* tab stop at which message length will be logged */ #define TABCHAR "\t" #define TABWIDTH 8 #define RECFLAGS "HBDAahbfcwWiEer" #define HEAD_GREP 0 #define BODY_GREP 1 #define DISTINGUISH_CASE 2 #define ALSO_NEXT_RECIPE 3 #define ALSO_N_IF_SUCC 4 #define PASS_HEAD 5 #define PASS_BODY 6 #define FILTER 7 #define CONTINUE 8 /* carbon copy */ #define WAIT_EXIT 9 #define WAIT_EXIT_QUIET 10 #define IGNORE_WRITERR 11 #define ELSE_DO 12 #define ERROR_DO 13 #define RAW_NONL 14 #define UNIQ_PREFIX '_' /* prepended to temporary unique filenames */ #define ESCAP ">" /* some formail-specific configuration options: */ #define UNKNOWN "foo@bar" /* formail default originator name */ #define OLD_PREFIX "Old-" /* formail field-Old-prefix */ #define RESENT_ "Resent-" /* -a *this* to reply to Resent headers */ #define BABYL_SEP1 '\037' /* BABYL format separator one */ #define BABYL_SEP2 '\f' /* BABYL format separator two */ #define DEFfileno "FILENO=000" /* split counter for formail */ #define LEN_FILENO_VAR 7 /* =strlen("FILENO=") */ #define CHILD_FACTOR 3/4 /* do not parenthesise; average running children */ #define FM_SKIP '+' /* skip the first nnn messages */ #define FM_TOTAL '-' /* only spit out a total of nnn messages */ #define FM_BOGUS 'b' /* leave bogus Froms intact */ #define FM_BERKELEY BERKELEYOPT /* Berkeley format, no Content-Length: */ #define FM_QPREFIX 'p' /* define quotation prefix */ #define FM_CONCATENATE 'c' /* concatenate continued header-fields */ #define FM_ZAPWHITE 'z' /* zap whitespace and empty headers */ #define FM_FORCE 'f' /* force formail to accept an arbitrary format */ #define FM_REPLY 'r' /* generate an auto-reply header */ #define FM_KEEPB 'k' /* keep the header, when replying */ #define FM_TRUST 't' /* reply to the header sender */ #define FM_LOGSUMMARY 'l' /* generate a procmail-compatible log summary */ #define FM_SPLIT 's' /* split it up */ #define FM_NOWAIT 'n' /* don't wait for the programs */ #define FM_EVERY 'e' /* don't require empty lines leading headers */ #define FM_MINFIELDS 'm' /* the number of fields that have to be found */ #define DEFminfields 2 /* before a header is recognised as such */ #define FM_DIGEST 'd' /* split up digests */ #define FM_BABYL 'B' /* split up BABYL format rmail files */ #define FM_QUIET 'q' /* be quiet */ #define FM_DUPLICATE 'D' /* return success on duplicate mails */ #define FM_EXTRACT 'x' /* extract field contents */ #define FM_EXTRC_KEEP 'X' /* extract field */ #define FM_ADD_IFNOT 'a' /* add a field if not already there */ #define FM_ADD_ALWAYS 'A' /* add this field in any case */ #define FM_REN_INSERT 'i' /* rename and insert a field */ #define FM_DEL_INSERT 'I' /* delete and insert a field */ #define FM_FIRST_UNIQ 'u' /* preserve the first occurrence */ #define FM_LAST_UNIQ 'U' /* preserve the last occurrence */ #define FM_ReNAME 'R' /* rename a field */ #define FM_VERSION VERSIONOPT /* option to display version */ #define FM_USAGE "\ Usage: formail [-vbczfrktqY] [-D nnn idcache] [-p prefix] [-l folder]\n\ \t[-xXaAiIuU field] [-R ofield nfield]\n\ Or: formail [+nnn] [-nnn] [-bczfrktedqBY] [-D nnn idcache] [-p prefix]\n\ \t[-n [nnn]] [-m nnn] [-l folder] [-xXaAiIuU field] [-R ofield nfield]\n\ \t-s [prg [arg ...]]\n" /* split up FM_HELP, token too long for some ccs */ #define FM_HELP \ " -v\t\tdisplay the version number and exit\ \n -b\t\tdon't escape bogus mailbox headers\ \n -Y\t\tBerkeley format mailbox, disregard Content-Length:\ \n -c\t\tconcatenate continued header-fields\ \n -z\t\tzap whitespace and empty header-fields\ \n -f\t\tforce formail to pass along any non-mailbox format\ \n -r\t\tgenerate an auto-reply header, preserve fields with -i\ \n -k\t\ton auto-reply keep the body, prevent escaping with -b\ \n -t\t\treply to the header sender instead of the envelope sender\ \n -l folder\tgenerate a procmail-compatible log summary\ \n -D nnn idcache\tdetect duplicates with an idcache of length nnn\ \n -s prg arg\tsplit the mail, startup prg for every message\n" #define FM_HELP2 \ " +nnn\t\tskip the first nnn\t-nnn\toutput at most nnn messages\ \n -n [nnn]\tdon't serialise splits\t-e\tempty lines are optional\ \n -d\t\taccept digest format\t-B\texpect BABYL rmail format\ \n -q\t\tbe quiet\t\t-p prefix\tquotation prefix\ \n -m nnn \tmin fields threshold (default 2) for start of message\ \n -x field\textract contents\t-X field\textract fully\ \n -a field\tadd if not present\t-A field\tadd in any case\ \n -i field\trename and insert\t-I field\tdelete and insert\ \n -u field\tfirst unique\t\t-U field\tlast unique\ \n -R oldfield newfield\trename\n" procmail-3.22/initmake0100755006717600001440000003041107223546513014210 0ustar guenthersrc#! /bin/sh : # Copyright (c) 1992-1999, S.R. van den Berg, The Netherlands #$Id: initmake,v 1.71 2000/09/28 01:23:03 guenther Exp $ if test -z "$IFS" then IFS=" \ \ " export IFS fi PATH=.:$PATH BSHELL=$1 O="cd ." if $O then : else echo "Your BSHELL=$BSHELL seems to be severely handicapped." case "$BSHELL" in *zsh*) ;; *) echo "(If I wouldn't know better, I'd say you're trying to sell" echo "me an incompatible zsh :-)" ;; esac echo "Please edit the Makefile and try setting BSHELL to a shell which" echo "is more backwards compatible with a Bourne, Korn or POSIX shell." exit 1 fi shift; MSHELL=$1 shift; RM="$1" shift; MV="$1" shift; LN="$1" shift; SEARCHLIBS="$1" shift; LIBPATHS="$1" shift; DEVNULL=$1 shift; MAKE="$1" shift; O=$1 shift; CC="$1" shift; CFLAGS1="$1" shift; LDFLAGS1="$1" shift; BINSS="$1" shift; MANS1S="$1" shift; MANS5S="$1" shift; SUBDIRS="$1" shift; VISIBLE_BINDIR="$1" shift; STRIP="$1" test 1 != $# && echo "Don't start this script directly, use \`make init'" && exit 1 test -z "$MSHELL" || SHELL=$MSHELL case "$SHELL" in *sh*) case "$SHELL" in *[zc]sh*) echo "Warning: really perverted make detected"; SHELL="";; esac if test ! -z "$SHELL" then $SHELL <$DEVNULL echo test >src/_autotst echo test >src/_autotst HERE if test $? = 1 then echo "Warning: your $SHELL has noclobber turned on, avoiding it" SHELL="" fi fi $RM src/_autotst;; *) echo "Warning: perverted make detected"; SHELL="";; esac test -z "$SHELL" && SHELL=$BSHELL export SHELL PATH FGREP="fgrep" # POSIX, or not POSIX, that is the question... if test \^hello = "`echo '^hello' | grep -F '^hello' 2>&1`" then FGREP="grep -F" # and POSIX it is! fi echo hi | $FGREP hi >$DEVNULL t=$? echo ho | $FGREP hi >$DEVNULL f=$? if test 0 != $t -o 0 = $f then echo "Your \"$FGREP\" program seems to be incapable of returning a proper" echo "exitvalue depending on the success of the search. This script can" echo "not work without it." exit 2 fi case a in [!a]) classok=no ;; # This shell is *very* old *) classok=yes ;; esac if test ! -z "$LD_LIBRARY_PATH" then echo '***************************** WARNING *********************************' echo '* You seem to have set the LD_LIBRARY_PATH variable, this might cause *' echo '* some trouble during the compilation of this package. If the make *' echo '* does not finish by itself, do a: "make clean", clear *' echo '* LD_LIBRARY_PATH from the environment, and start over. *' echo '***************************** WARNING *********************************' fi cd src # diving into the source directory ###### newln="" $RM _autotst.rrr ../man/core_ln echo hi >_autotst.rrr if $LN ../src/_autotst.rrr ../man/core_ln 2>$DEVNULL then : else $RM ../man/core_ln $LN -s ../src/_autotst.rrr ../man/core_ln && LN="$LN -s" && newln=yes fi cat >_autotst.c <>_autotst.rrr test -z "$cc" -a ! -z "$a" && (exec >>_autotst.rrr 2>&1; $a $CFLAGS1 _autotst.c -o _autotst $LDFLAGS1) && cc="$a" echo "::::" >>_autotst.rrr done if test -z "$cc" then echo 2>&1 "Whoeaaa! There's something fishy going on here." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 "I suggest you take a look at the definition of CFLAGS* and CC" echo 2>&1 "in the Makefile before you try make again." exit 1 fi $RM _autotst.rrr _autotst.$O _autotst echo "$cc seems to work fine, using that as the C-compiler" if test $dostrip = yes then if $cc $CFLAGS1 _autotst.c -o _autotst $LDFLAGS1 -s >$DEVNULL 2>&1 then LDFLAGS1="$LDFLAGS1 -s" dostrip=no STRIP="" else $cc $CFLAGS1 _autotst.c -o _autotst $LDFLAGS1 >$DEVNULL 2>&1 if test ! -z "$STRIP" && $STRIP _autotst >$DEVNULL 2>&1 then : else STRIP="" fi fi $RM _autotst.rrr _autotst.$O _autotst else STRIP="" fi cat >_autotst.c < #include #include main() { struct stat buf;return!&buf; } HERE CFLAGS="" case "$CFLAGS1" in *-D_POSIX_SOURCE*);; *) if $cc -c $CFLAGS1 _autotst.c >$DEVNULL 2>&1 then : else $RM _autotst.$O $cc -c $CFLAGS1 -D_POSIX_SOURCE _autotst.c >$DEVNULL 2>&1 && CFLAGS=" -D_POSIX_SOURCE" fi;; esac LDFLAGSC="" if test -f _autotst.$O || $cc -c $CFLAGS1 $CFLAGS _autotst.c >_autotst.rrr 2>&1 then : else echo 2>&1 "Whoeaaa! There's something fishy going on here." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 "I suggest you take a look at the definition of CC and CFLAGS*" echo 2>&1 "in the Makefile before you try make again." echo 2>&1 "Also: write me a mail showing the errorlog you just generated." echo 2>&1 "The errorlog can still be found in src/_autotst.rrr" echo 2>&1 "It would be helpful if you could mention what machine and OS" echo 2>&1 "you are trying to compile this on (uname -a). Thanks." exit 1 fi $cc $CFLAGS1 $CFLAGS _autotst.$O -o _autotst $LDFLAGS1 -lc >_autotst.rrr 2>&1 \ && if grep "[\"']c['\"]" _autotst.rrr >$DEVNULL || $FGREP libc _autotst.rrr >$DEVNULL then : else LDFLAGSC=" -lc" fi LDFLAGS="$SEARCHLIBS" firstrun=yes checkpath=no runcombination=no while $RM _autotst $cc $CFLAGS1 $CFLAGS _autotst.$O -o _autotst $LDFLAGS1 $LDFLAGS \ $LDFLAGSC >_autotst.rrr 2>&1 || firstrun=yes test $firstrun = yes -o ! -f _autotst do if test $runcombination = yes then LDFLAGS="" read NEWLDFLAGS 0>&9 echo 2>&1 " ...trying $NEWLDFLAGS" else set dummy $LDFLAGS shift echo 2>&1 " ...scanning for $# libraries..." NEWLDFLAGS="" fi for lib in `echo _dummy_ $LDFLAGS | sed -e s/-l//g` do if test _dummy_ != $lib then a=-l$lib ( exec <_autotst.rrr while read b do if test $classok = yes then case ":$b:" in *$cc*_autotst.$O*) ;; *"not used for resolving"*) ;; *lib$lib[!a-z_]*|*$a[!a-z_]*|*[!a-z]$lib[!a-z_]*) exit 1;; esac else case ":$b:" in *$cc*_autotst.$O*) ;; *"not used for resolving"*) ;; *) for c in $b do case ":$c:" in *lib$lib[a-z_]*|*$a[a-z_]*|\ *[a-z]$lib*|*$lib[a-z_]*) ;; *$a*|*$lib*) exit 1;; esac done ;; esac fi done exit 0 ) if test $? = 0 then found=yes if test $checkpath = yes then OLDIFS="$IFS"; IFS=":$IFS" found=no for libpath in $LIBPATHS $LD_LIBRARY_PATH do set $libpath/*lib$lib[A-Z.]* test -f $1 && found=yes done IFS="$OLDIFS" fi test yes = $found && NEWLDFLAGS="$NEWLDFLAGS $a" fi fi done if test a"$LDFLAGS" = a"$NEWLDFLAGS" then if test $checkpath = yes then if test $runcombination = yes then echo 2>&1 "Whoeaaa! There's something fishy going on here." echo 2>&1 "You have a look and see if you detect anything uncanny:" echo 2>&1 "*******************************************************" echo \ $cc $CFLAGS1 $CFLAGS _autotst.$O -o _autotst $LDFLAGS1\ $LDFLAGS >>_autotst.rrr echo " $LDFLAGSC" >>_autotst.rrr cat 2>&1 _autotst.rrr echo 2>&1 "*******************************************************" echo 2>&1 \ "I suggest you take a look at the definition of LDFLAGS* and SEARCHLIBS" echo 2>&1 "in the Makefile before you try make again." echo 2>&1 \ "Also: write me a mail showing the errorlog you just generated." echo 2>&1 "The errorlog can still be found in src/_autotst.rrr" echo 2>&1 \ "It would be helpful if you could mention what machine and OS" echo 2>&1 "you are trying to compile this on (uname -a). Thanks." exit 1 fi echo 2>&1 "Inconsistent libraries found, trying to accomodate..." runcombination=yes cat >_autotst <<\HERE #!/bin/sh exec awk ' { split($0,lib); nf=0; for(a in lib) nf++; p[0]=nf; while(!p[nf-1]) { for(j=i=0;p[i];i++) while(++j=0) p[k]=p[k+1]-1; } exit; }' HERE chmod 755 _autotst echo " $LDFLAGS" | ./_autotst >_autotst.lst echo "" >>_autotst.lst exec 8<&0 <_autotst.lst 9<&0 0<&8 8<&- read NEWLDFLAGS 0<&9 echo 2>&1 " ...trying $NEWLDFLAGS" fi checkpath=yes else checkpath=no fi LDFLAGS="$NEWLDFLAGS" firstrun=no done $RM _autotst.$O _autotst.c _autotst _autotst.rrr _autotst.lst cd .. # returning to the main procmail directory ###### LDFLAGS="$LDFLAGS$LDFLAGSC" test -z "$CFLAGS" || echo "Added CFLAGS=$CFLAGS" test -z "$LDFLAGS" || echo "Added LDFLAGS=$LDFLAGS" if test $dostrip = yes then LDFLAGS="$LDFLAGS1$LDFLAGS" echo "Evaded -s in LDFLAGS1" else LDFLAGS="\$(LDFLAGS1)$LDFLAGS" fi for a in $SUBDIRS do if test ! -f $a/Makefile.init then case "$LN" in ln|*/ln|link|*/link) $LN $a/Makefile $a/Makefile.init $RM $a/Makefile $LN $a/Makefile.init $a/Makefile ;; *) cp $a/Makefile $a/Makefile.init ;; esac fi done test -f Makefile.0 || sed -e '/^# Makefile - mark/,$ !d' Makefile.0 sed -e '/^# Makefile - mark/,$ d' _Makefile echo "# Makefile.1 - mark, don't (re)move this, a sed script needs it " >>_Makefile test a$SHELL != a$MSHELL && echo "SHELL = $SHELL" >>_Makefile echo "FGREP = $FGREP" >>_Makefile echo "STRIP = $STRIP" >>_Makefile test -z "$newln" || echo "LN = $LN" >>_Makefile test -z "$MAKE" && echo "MAKE = make" >>_Makefile test a"$cc" != a"$CC" && echo "CC = $cc" >>_Makefile echo "CFLAGS = \$(CFLAGS1)$CFLAGS" >>_Makefile echo "LDFLAGS = $LDFLAGS" >>_Makefile echo >>_Makefile MANSS="" MANS1="" MANS5="" MANS="" NMANS="" BINS="" NBINS="" for a in $MANS1S do MANSS="$MANSS $a.1" MANS1="$MANS1 $a.\$(MAN1SUFFIX)" done for a in $MANS5S do MANSS="$MANSS $a.5" MANS5="$MANS5 $a.\$(MAN5SUFFIX)" done for a in $MANSS do MANS="$MANS new/$a" NMANS="$NMANS ../new/$a" done for a in $BINSS do BINS="$BINS new/$a" NBINS="$NBINS ../new/$a" done echo "BINS=$BINS" >>_Makefile echo "MANS=$MANS" >>_Makefile echo "MANS1=$MANS1" >>_Makefile echo "MANS5=$MANS5" >>_Makefile echo "MANSS=$MANSS" >>_Makefile echo "NBINS=$NBINS" >>_Makefile echo "NMANS=$NMANS" >>_Makefile echo >>_Makefile for a in $SUBDIRS do sed -e '1,/^# Makefile.0 - mark/ d' <_Makefile >$a/_Makefile cat $a/Makefile.0 >>$a/_Makefile done echo "VISIBLE_BINDIR=$VISIBLE_BINDIR" >>src/_Makefile for a in $BINSS do echo >>src/_Makefile echo "../new/$a: $a ../config.check" >>src/_Makefile echo " @test -d ../new || mkdir ../new" >>src/_Makefile echo " @\$(RM) \$@" >>src/_Makefile echo " \$(LN) ../src/$a \$@" >>src/_Makefile done for a in $MANSS do echo >>man/_Makefile echo "../new/$a: $a ../config.check" >>man/_Makefile echo " @test -d ../new || mkdir ../new" >>man/_Makefile echo " @\$(RM) \$@" >>man/_Makefile echo " \$(LN) ../man/$a \$@" >>man/_Makefile done for a in $MANS1S do echo >>man/_Makefile echo "$a.1: $a.man man.sed mansed" >>man/_Makefile echo \ " \$(SHELL) ./mansed \$(SHELL) $a.man \$@ \"\$(RM)\" \$(DEVNULL)" \ >>man/_Makefile done for a in $MANS5S do echo >>man/_Makefile echo "$a.5: $a.man man.sed mansed" >>man/_Makefile echo \ " \$(SHELL) ./mansed \$(SHELL) $a.man \$@ \"\$(RM)\" \$(DEVNULL)" \ >>man/_Makefile done cat Makefile.1 >>_Makefile $MV _Makefile Makefile sleep 1 # Some machines are just too speedy, make gets confused for a in $SUBDIRS do echo "#" >>$a/_Makefile $MV $a/_Makefile $a/Makefile done procmail-3.22/new/0040755006717600001440000000000007316762763013270 5ustar guenthersrcprocmail-3.22/new/mkinstalldirs0100755006717600001440000000117407315050355016060 0ustar guenthersrc#!/bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Last modified: 1994-03-25 # Public domain # errstatus=0 for file in ${1+"$@"} ; do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d in ${1+"$@"} ; do pathcomp="$pathcomp$d" case "$pathcomp" in -* ) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" 1>&2 mkdir "$pathcomp" || errstatus=$? fi pathcomp="$pathcomp/" done done exit $errstatus # mkinstalldirs ends here