home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-02-04 | 46.6 KB | 1,139 lines |
- Newsgroups: comp.sources.misc
- From: ram@eiffel.com (Raphael Manfredi)
- Subject: v35i033: mailagent - Rule Based Mail Filtering, Patch17
- Message-ID: <1993Feb5.030527.551@sparky.imd.sterling.com>
- X-Md4-Signature: acf4440597484b150fd4adf37a0494b6
- Date: Fri, 5 Feb 1993 03:05:27 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: ram@eiffel.com (Raphael Manfredi)
- Posting-number: Volume 35, Issue 33
- Archive-name: mailagent/patch17
- Environment: Perl, Sendmail, UNIX
- Patch-To: mailagent: Volume 33, Issue 93-109
-
- [The latest patch for mailagent version 2.9 is #19.]
-
- System: mailagent version 2.9
- Patch #: 17
- Priority: MEDIUM
- Subject: now recognizes bogus addresses like '<address> (comment)'
- Subject: copyright extension to year 1993
- Subject: added auto-reply based on keywords rules in the example
- Subject: new optional parameter 'newcmd'
- Subject: both 'compress' and 'newcmd' under same "optional" section
- Subject: configuration variables may now have '-' in them
- Subject: added loading of new commands after initialization
- Subject: two new files are now included: pl/newcmd.pl and pl/q.pl
- Subject: prepare inclusion of new mail hooks package, delayed due to perl bug
- Subject: new configuration variable 'newcmd' for command extensions
- Subject: NOTIFY may now accept a list of addresses instead of just one
- Subject: complete new section documenting extension of filter commands
- Subject: security checks include newcmd file as well
- Subject: added new file pl/q.pl for quotations
- Subject: prepared new mail hook processing, delayed due to a perl bug
- Subject: variable storing perl scripts is now pre-extended
- Subject: random changes and cleanup
- Subject: now recognizes 'mailer-agent' as a special address
- Subject: logging of sender now focuses only on address part
- Subject: file inclusion to load addresses now available with NOTIFY
- Subject: special variables are now initialized by &initvar
- Subject: new &add routine to dynamically build a perl interface
- Subject: special variables may now be initialized within various packages
- Subject: do not abort with fatal but with die (provision for new mailhooks)
- Subject: now takes From: as a Sender if no leading From line
- Subject: new testing directory misc for optional features
- Subject: new actions for compress, mmdf and newcmd tests
- Subject: four new files
- Date: Mon Feb 1 10:25:07 PST 1993
- From: Raphael Manfredi <ram@eiffel.com>
-
- Description:
- Now recognizes bogus addresses like '<address> (comment)' and
- correctly extract the "address" part.
-
- Added auto-reply based on keywords rules in the example. This
- shows how one can send back a FAQ on a specific subject when a
- message with some keywords is received.
-
- New optional parameter 'newcmd' in the configuration file.
- You should update your config file by looking at the new
- standard model in agent/files/mailagent.cf.
-
- Configuration variables may now have '-' in them. Some machines
- do have '-' as part of their hostnames, and not allowing this
- character in variables would mean no way to specify a machine
- specific PATH.
-
- Prepare inclusion of new mail hooks package, delayed due to perl
- bug. The processing of mail hooks should be done directly in the
- mailagent. I have already made the necessary changes, but
- unfortunately there is a nasty bug in perl's cons.c which prevents
- recursion from working properly under some circumstances. There
- is an unofficial patch for this on comp.lang.perl, but I cannot
- assume people would apply it. I am NEVER installing non-standard
- patches to avoid surprises like this.
-
- NOTIFY may now accept a list of addresses instead of just one.
- This means you may use file inclusion to specify a list of people.
-
- Complete new section documenting extension of filter commands. This
- is certainly the biggest change since last patch. The documentation
- explains how you can enhance the mailagent yourself by writing your
- own commands and letting the mailagent see them as builtins. Some
- useful function entry points are also documented to help you writing
- your own commands. You do NOT need to change anything in the
- mailagent program itself. Everything is done externally.
-
- Now recognizes 'mailer-agent' as a special address, and no vacation
- messages will ever be generated from a mail emitted by this user
- (NeXT standard mailer daemon, it would seem).
-
- Logging of sender now focuses only on address part. The routine
- which logs the sender if it is different from the From: field now
- focuses only on the address part to make its comparaison (i.e the
- comment part of the address is irrelevant).
-
- New testing directory misc for optional features, with the two
- empty files misc/compress.t and misc/mmdf.t finally written. There
- is also a regression test for the command extension mechanism. Note
- that if compress is not in your PATH, misc/compress.t will be
- flagged 'untested'...
-
-
- Fix: From rn, say "| patch -p -N -d DIR", where DIR is your mailagent source
- directory. Outside of rn, say "cd DIR; patch -p -N <thisarticle".
- If you don't have the patch program, apply the following by hand,
- or get patch (version 2.0, latest patchlevel).
-
- After patching:
- *** DO NOTHING--INSTALL ALL PATCHES UP THROUGH #18 FIRST ***
-
- If patch indicates that patchlevel is the wrong version, you may need
- to apply one or more previous patches, or the patch may already
- have been applied. See the patchlevel.h file to find out what has or
- has not been applied. In any event, don't continue with the patch.
-
- If you are missing previous patches they can be obtained from me:
-
- Raphael Manfredi <ram@eiffel.com>
-
- If you send a mail message of the following form it will greatly speed
- processing:
-
- Subject: Command
- @SH mailpatch PATH mailagent 2.9 LIST
- ^ note the c
-
- where PATH is a return path FROM ME TO YOU either in Internet notation,
- or in bang notation from some well-known host, and LIST is the number
- of one or more patches you need, separated by spaces, commas, and/or
- hyphens. Saying 35- says everything from 35 to the end.
-
- To get some more detailed instructions, send me the following mail:
-
- Subject: Command
- @SH mailhelp PATH
-
-
- Index: patchlevel.h
- Prereq: 16
- 4c4
- < #define PATCHLEVEL 16
- ---
- > #define PATCHLEVEL 17
-
- Index: agent/man/mailagent.SH
- Prereq: 2.9.1.8
- *** agent/man/mailagent.SH.old Mon Feb 1 10:24:08 1993
- --- agent/man/mailagent.SH Mon Feb 1 10:24:11 1993
- ***************
- *** 18,24 ****
- .TH MAILAGENT $manext "Version $VERSION PL$PATCHLEVEL"
- ''' @(#) Manual page for mailagent's filter -- (c) ram February 1991
- '''
- ! ''' $Id: mailagent.SH,v 2.9.1.8 93/01/12 12:09:46 ram Exp $
- '''
- ''' Copyright (c) 1991, 1992, Raphael Manfredi
- '''
- --- 18,24 ----
- .TH MAILAGENT $manext "Version $VERSION PL$PATCHLEVEL"
- ''' @(#) Manual page for mailagent's filter -- (c) ram February 1991
- '''
- ! ''' $Id: mailagent.SH,v 2.9.1.9 93/02/01 10:05:03 ram Exp $
- '''
- ''' Copyright (c) 1991, 1992, Raphael Manfredi
- '''
- ***************
- *** 26,31 ****
- --- 26,37 ----
- ''' License as specified in the README file that comes with dist.
- '''
- ''' $Log: mailagent.SH,v $
- + ''' Revision 2.9.1.9 93/02/01 10:05:03 ram
- + ''' patch17: new configuration variable 'newcmd' for command extensions
- + ''' patch17: NOTIFY may now accept a list of addresses instead of just one
- + ''' patch17: complete new section documenting extension of filter commands
- + ''' patch17: security checks include newcmd file as well
- + '''
- ''' Revision 2.9.1.8 93/01/12 12:09:46 ram
- ''' patch15: documents new features: compression and MMDF mailboxes
- '''
- ***************
- *** 252,257 ****
- --- 258,268 ----
- First name of the user, used by the mailagent when referring to you. This sets
- the value of the %U macro.
- .TP
- + .I newcmd
- + Name of the file describing new filtering commands. See section \fIExtending
- + Filtering Commands\fR for more details. Leave this optional parameter out
- + unless you are a mailagent expert. (suggested: \$spool/newcmd).
- + .TP
- .I nfslock
- Set it to ON to ensure NFS-secure locks. The difference is that the hostname
- is used in conjunction with the PID to obtain a lock. However, the mailagent
- ***************
- *** 912,918 ****
- .fi
- .in -5
- .sp
- ! we have the following set { From, To Cc, !Subject }. The first tow selectors
- are called \fIdirect\fR selectors, !Subject: is called a \fInegated\fR selector.
- The To Cc: selector is a \fIgroup\fR selector decomposing into two \fIdirect\fR
- selectors, while From: is an \fIatomic\fR selector. Finally, From: is also
- --- 923,929 ----
- .fi
- .in -5
- .sp
- ! we have the following set { From, To Cc, !Subject }. The first two selectors
- are called \fIdirect\fR selectors, !Subject: is called a \fInegated\fR selector.
- The To Cc: selector is a \fIgroup\fR selector decomposing into two \fIdirect\fR
- selectors, while From: is an \fIatomic\fR selector. Finally, From: is also
- ***************
- *** 1317,1325 ****
- No operation. If this seems a bit odd, think of it in terms of a ONCE command.
- (Does not alter existing status)
- .TP
- ! NOTIFY \fIaddress\fR \fIfile\fR
- ! Send a notification message \fIfile\fR to a given address. The text of the
- message is run through the macro substitution mechanism (described later on).
- (Fails if message cannot be sent)
- .TP
- ONCE \fI(name, tag, period) command\fR
- --- 1328,1337 ----
- No operation. If this seems a bit odd, think of it in terms of a ONCE command.
- (Does not alter existing status)
- .TP
- ! NOTIFY \fIaddress(es)\fR \fIfile\fR
- ! Send a notification message \fIfile\fR to a given address list. The text of the
- message is run through the macro substitution mechanism (described later on).
- + As with FORWARD, file inclusion for address specification is possible.
- (Fails if message cannot be sent)
- .TP
- ONCE \fI(name, tag, period) command\fR
- ***************
- *** 1669,1675 ****
- the header of the message. For instance, \fI\$to\fR is really the value of
- \fI\$header{'To'}\fR. The key is specified using a normalized case, i.e.
- the first letter of each word is uppercased, the remaining being lowercased.
- ! This is independant of the actual physical representation in the message
- itself.
- .PP
- The pseudo keys \fIHead\fR, \fIBody\fR and \fIAll\fR respectively gives you
- --- 1681,1687 ----
- the header of the message. For instance, \fI\$to\fR is really the value of
- \fI\$header{'To'}\fR. The key is specified using a normalized case, i.e.
- the first letter of each word is uppercased, the remaining being lowercased.
- ! This is independent of the actual physical representation in the message
- itself.
- .PP
- The pseudo keys \fIHead\fR, \fIBody\fR and \fIAll\fR respectively gives you
- ***************
- *** 1966,1973 ****
- sent back to the user (with macros substitutions) if the user is explicitely
- listed in the \fITo\fR or \fICc\fR field and if the sender is not a special
- user (\fIroot\fR, \fIuucp\fR, \fInews\fR, \fIdaemon\fR, \fIpostmaster\fR,
- ! \fInewsmaster\fR, \fIusenet\fR, \fIMAILER-DAEMON\fR or \fInobody\fR).
- ! Matches are done in a case insentive manner, so \fIMailer-Daemon\fR will also
- be recognized as a special user.
- Furthermore, any message tagged with a \fIPrecedence:\fR field set to
- \fIbulk\fR or \fIjunk\fR will not trigger a vacation message. This built-in
- --- 1978,1986 ----
- sent back to the user (with macros substitutions) if the user is explicitely
- listed in the \fITo\fR or \fICc\fR field and if the sender is not a special
- user (\fIroot\fR, \fIuucp\fR, \fInews\fR, \fIdaemon\fR, \fIpostmaster\fR,
- ! \fInewsmaster\fR, \fIusenet\fR, \fIMailer-Daemon\fR, \fIMailer-Agent\fR or
- ! \fInobody\fR).
- ! Matches are done in a case insentive manner, so \fIMAILER-DAEMON\fR will also
- be recognized as a special user.
- Furthermore, any message tagged with a \fIPrecedence:\fR field set to
- \fIbulk\fR or \fIjunk\fR will not trigger a vacation message. This built-in
- ***************
- *** 2165,2170 ****
- --- 2178,2602 ----
- to that folder will be faced with both a compressed and a plain version of the
- folder, and that will get you a warning in the log file, but delivery will be
- made automatically to the plain file.
- + .SH EXTENDING FILTERING COMMANDS
- + Once you've reached the \fIexpert\fR level, and provided you have a fair
- + knowledge of \fIperl\fR, you may feel the need for more advanced commands
- + which are not part of the standard set. This section explains how you
- + can achieve this dynamically, without the need of diving deep inside the
- + source code.
- + .PP
- + Once you have extended the filtering command set, you may use those commands
- + inside the rule file as if they were built-in. You may even choose to redefine
- + the standard commands if they do not suit you (however, if you wish to do
- + that, you should know exactly what you are doing, or you may start loosing
- + some mail or get an unexpected behaviour -- this also voids your warranty :-).
- + .PP
- + The ability to provide external commands without actually modifying the main
- + source code is, I believe, a strong point in favor of having a program written
- + in an interpreted language like \fIperl\fR. This of course once you have
- + convinced yourself that it is a Good Thing to customize and extend a program
- + in the same language as the one used for the core, meaning usually a fairly
- + low-level language with fewer user-friendly hooks.
- + '''
- + .SS Overview
- + .PP
- + In order to implement a new command, say FOLD, you will need to do the
- + following:
- + .IP \(bu 5
- + Write a perl subroutine to implement the FOLD action and put that into
- + an external file. Say we write the subroutine \fIfold\fR and we store
- + that in a \fIfold.pl\fR file. This is naturally the difficult part, where
- + you need to know some basic things about the mailagent internals.
- + .IP \(bu
- + Choose where you want to store your \fIfold.pl\fR file. Then check the
- + syntax with \fIperl \-c\fR, just to be sure...
- + .IP \(bu
- + Edit the \fInewcmd\fR file (as given by the configuration file) to record
- + your new command. Then make sure this file is tightly protected. You must
- + own it, and it should not be writable by any other individual but you.
- + .IP \(bu
- + Additionally, you may want to specify whether FOLD is to modify the existing
- + execution status and whether or not it will be allowed within the special
- + _SEEN_ mode.
- + .IP \(bu
- + Write some rules using the new FOLD command. This is the \fIeasy\fR part!
- + Note that your command may also be used within perl hooks as if it were
- + a builtin command (this means there is an interface function built for
- + you within the \fImailhook\fR package).
- + .PP
- + In the following sections, we're going to describe the syntax of the
- + \fInewcmd\fR file, and we'll then present some low-level internal variables
- + which may be used when implementing new commands.
- + '''
- + .SS New Command File Format
- + .PP
- + The \fInewcmd\fR file consists of a series of lines, each line describing
- + one command. Blank lines are ignored and shell-style comments introduced by
- + the sharp (#) character are allowed.
- + .PP
- + Each line is formed by 3 principal fields and 2 optional ones; fields are
- + separated by spaces or tabs. Here is a skeleton:
- + .in +5
- + .sp
- + .nf
- + <cmd_name> <path> <function> <status_flag> <seen_flag>
- + .fi
- + .sp
- + .in -5
- + The \fIcmd_name\fR is the name of the command you wish to add. In our
- + previous example, it would be FOLD. The next field, \fIpath\fR, tells
- + the mailagent where the file containing the command implementation is
- + located. Say we store it in \fI~/mail/cmds/fold.pl\fR. The \fIfunction\fR
- + field is the name of the \fIperl\fR function implementing FOLD, which may
- + be found in \fIfold.pl\fR. Here, we named our function \fIfold\fR. Note that
- + if your function has its name within the \fInewcmd\fR package, which is the
- + default behaviour if you do not specify any, then there is no need to prefix
- + the function name with the package. Otherwise, you must use a fully qualified
- + name.
- + .PP
- + The last two fields are optional, and are boolean values which may be
- + specified by \fItrue\fR or \fIyes\fR to express truth, and \fIfalse\fR
- + or \fIno\fR to express falsehood. If \fIstatus_flag\fR is set to
- + true, then the command will modify the last execution status variable.
- + If \fIseen_flag\fR is true, then the command may be used when the filter
- + is in _SEEN_ mode. The default values are respectively \fItrue\fR and
- + \fIfalse\fR.
- + .PP
- + So in our example, we would have written:
- + .sp
- + .in +5
- + .nf
- + FOLD ~/mail/cmds/fold.pl fold no yes
- + .fi
- + .in -5
- + .sp
- + to allow FOLD even in _SEEN_ mode and have it executed without modifying
- + the current value of the \fIlast-command-status\fR variable.
- + '''
- + .SS Writing An Implementation
- + .PP
- + Your perl function will be loaded when needed into the special package
- + \fInewcmd\fR, so that its own namespace is protected and does not accidentally
- + conflict with other mailagent routines or variables. When you need to call the
- + perl interface of some common mailagent functions, you will have to remember
- + to use the fully qualified routine name, for instance \fI&mailhook'leave\fR
- + to actually execute the LEAVE command.
- + .PP
- + (Normally, in PERL hooks, there is no need for this prefixing since the perl
- + script is loaded in the \fImailhook\fR package. When you are extending your
- + mailagent, you should be extra careful however, and it does not really hurt
- + to use this prefixing. You are free to use the perl \fIpackage\fR directive
- + within your function, hence switching to the \fImailhook\fR package in
- + the body of the routine but leaving its name in the \fInewcmd\fR package.)
- + .PP
- + Since the mailagent will dynamically load the implementation of your command
- + the first time it is run, by loading the specified perl script into memory
- + and evaluating it, I suggest you put each command implementation in a separate
- + file, to avoid storing potentially unneeded code in memory.
- + .PP
- + Each command is called with one argument, namely the full command string as
- + read from the filter rules. Additionally, the special \fI@ARGV\fR array is
- + set by performing a shell-style parsing of the command line (which will fail
- + if quotes are mismatched, but then you can do the parsing by yourself since you
- + get the command line).
- + At the end of your routine, you must return a failure status, i.e.
- + \fB0\fR for success and \fB1\fR to signal failure.
- + .PP
- + Those are your only requirements. You are free to do whatever you want inside
- + the routine. To ease your task however, some variables are pre-computed for
- + you, the same ones that are made available within mail hooks, only they are
- + defined within the \fInewcmd\fR package this time. There are also a few
- + special variables which you need to know about, and a set of standard routines
- + you may want to call. Please avoid calling something which is not documented
- + here, since it may change without prior notice. If you would like to use one
- + routine and it is not documented in this manual page, please let me know.
- + .PP
- + Each command is called from within an \fIeval\fR construct, so you may
- + safely use \fIdie\fR or call external library routines that use \fIdie\fR.
- + If you use \fIrequire\fR, be aware that the mailagent is setting up a special
- + \fI@INC\fR array by putting its private library path first, so you may place
- + all your \fImailagent\fR-related library files in this place.
- + '''
- + .SS Special Variables
- + .PP
- + The following special variables (some of them marked read-only, meaning you
- + shouldn't modify them, and indeed you can't) made available directly
- + within the \fInewcmd\fR package, are pre-set by the filter
- + automaton, and are used to control the filtering process:
- + .sp
- + .TP 15
- + .I \$mfile
- + The base name of the mail file being processed. This variable is read-only.
- + It is mainly used in log messages, as in [\$mail] to tag each log, since a
- + single mailagent process may deal with multiple messages.
- + .TP
- + .I \$ever_saved
- + This is a boolean, which should be \fIset\fR to \fB1\fR once a successful
- + saving operation has been completed. If at the end of the filtering, this
- + variable is still \fB0\fR, then the default LEAVE will be executed.
- + .TP
- + .I \$vacation
- + This is a boolean, which when \fIset\fR to \fB1\fR will allow vacation messages.
- + It is mainly used by the VACATION command, but if you wish to re-implement that
- + command you will need access to this variable.
- + .TP
- + .I \$cont
- + This is the continuation status, a variable of the utmost importance when
- + dealing with the control flow. Four constants from the \fImain\fR package
- + can be used to specify whether we should continue with the current rule
- + (\$FT_CONT), abandon current rule (\$FT_REJECT), restart filtering from the
- + beginning (\$FT_RESTART) or simply abort processing (\$FT_ABORT). More on
- + this later.
- + .TP
- + .I \$lastcmd
- + The last failure status recorded by the last command (among those who do
- + modify the execution status). You should not have to update this by yourself
- + unless you are implementing some encapsulation for other commands, like BACK
- + or ONCE, since by default \fI\$lastcmd\fR will be set to the value you return
- + at the end of the command.
- + .TP
- + .I \$wmode
- + This records the current state of the filter automaton (working mode), in a
- + litteral string form, typically modified by the BEGIN command or as a side
- + effect, as in REJECT for instance.
- + .PP
- + Other variables you might have a need for are configuration parameters, held
- + in the \fI~/.mailagent\fR configuration file. Well, the rule is simple. The
- + value of each parameter \fIparam\fR from the configuration file is held in
- + variable \fI\$cf'param\fR. Variable \fI\$main'loglvl\fR is the copy of
- + \fI\$cf'level\fR, since it's always shorter to type in \fI\$'loglvl\fR after
- + each call to the logging routine \fI&add_log\fR.
- + .PP
- + There is one more variable worth knowing about: \$main'FILTER, which is the
- + suitable X-Filter line which should be appended in \fBall\fR the mails you
- + send via the mailagent, in order to avoid loops. Also when you save mails
- + to a folder, it's wise adding this line in case a problem arises: you may
- + then identify the culprit.
- + '''
- + .SS Altering Control Flow
- + .PP
- + When you want to alter control flow to perform a REJECT, a RESTART or an
- + ABORT, you have three choices. If you wish to control that action via an
- + option, the same way the standard UNIQUE does (with \fB\-c\fR, \fB\-r\fR or
- + \fB\-a\fR), you may call \fI&main'alter_execution(option, mode)\fR giving it
- + two parameters: the option letter and the mode you wish to change to before
- + altering the control flow.
- + .PP
- + You may also want to directly alter the \fI\$wmode\fR and \fI\$cont\fR variables,
- + but then you'll have to do your own logging if you want some. Or you may
- + call low-level routines \fI&main'do_reject\fR, \fI&main'do_restart\fR and
- + \fI&main'do_abort\fR to perform the corresponding operation (with logging).
- + .PP
- + Remember that the mode _SEEN_ is special and directly handled at the
- + filter level, and the the filter begins in the INITIAL mode. The default
- + action is to continue with the current rule, which is why there is no
- + routine to perform this task.
- + .PP
- + The preferred way is to invoke the \fImailhook\fR interface functions,
- + \fI&mailhook'begin\fR, \fI&mailhook'reject\fR, etc..., and that will work
- + even if you redefine those functions yourself. Besides, that's the only
- + interface which is likely not to be changed by new versions.
- + '''
- + .SS General Purpose Routines
- + .PP
- + The following is a list of all the general routines you may wish to call when
- + performing some low-level tasks. Note that this information is
- + version-dependent. Since I document them, I'll try to keep them in new
- + versions, but I cannot guarantee I will not have to slightly change some
- + of their semantics. There is a good chance you will never have to worry about
- + that anyway.
- + .sp
- + .TP 10
- + .I &header'format(rfc822-field)
- + Return a formatted RFC822 field to fit in 78 columns, with proper
- + continuations introduced by eight spaces.
- + .TP
- + .I &header'normalize(rfc822-header-name)
- + Normalize case in RFC822 header and return the new header name with every
- + first letter uppercased.
- + .TP
- + .I &header'reset
- + This is part of an RFC822 header validation, mainly used when splitting a
- + digest. This resets the recognition automaton (see &header'valid).
- + .TP
- + .I &header'valid(line)
- + Returns a boolean status, indicating if all the lines given so far to this
- + function since the last &header'reset are part of a valid RFC822 header.
- + The function understands the first From line which is part of UNIX mails.
- + At any time, the variable \fI\$header'maybe\fR may be checked to see if
- + sofar we have found at least one essential mail header field.
- + .TP
- + .I &main'acs_rqst(file)
- + Perform a .lock locking on the file, returning 0 on success and -1 on failure.
- + If an old lock was present, it is removed (time limit set to one hour). Use
- + \fI&main'free_file\fR to release the lock.
- + .TP
- + .I &main'add_log(string)
- + Add the \fIstring\fR to the logfile. The usual idiom is to postfix that call
- + with the \fIif \$'loglvl > value\fR, where \fIvalue\fR is the logging
- + level you wish to have before emitting that kind of log (\fI$'loglvl\fR is
- + a short form for \fI$main'loglvl\fR).
- + .TP
- + .I &main'free_file(file)
- + Remove a .lock on a file, obtained by \fI&main'acs_rqst\fR. It returns 0 if
- + the lock was succefully removed, -1 if it was a stale lock (obtained by someone
- + else).
- + .TP
- + .I &main'header_found(file)
- + Scan the head of a file and try to determine whether there is a mail header
- + at the beginning or not. Return true if a header was found.
- + .TP
- + .I &main'history_record
- + Record the message ID of the current message and return 0 if the message had
- + not been previously seen, 1 if it is a duplicate.
- + .TP
- + .I &main'hostname
- + Return the value of the hostname, lowercased, with possible domain name
- + appended to it.
- + The hostname is cached, since its value must initially be obtained by forking.
- + (see also \fI&main'myhostname\fR)
- + .TP
- + .I &main'internet_info(email-address)
- + Parse an e-mail internet address and return a three-element array containing
- + the host, the domain and the country part of the internet host. For instance,
- + if the address is \fIuser@d.c.b.a\fR, it will return \fI(c, b, a)\fR.
- + .TP
- + .I &main'login_name(email-address)
- + Parse the e-mail internet address and return the login name.
- + .TP
- + .I &main'macros_subst(*line)
- + Perform in-place macro substitution (line passed as a type glob) using
- + the information currently held in the \fI%main'Header\fR array.
- + .TP
- + .I &main'myhostname
- + Returns the hostname of the current machine, without any domain name.
- + The hostname is cached, since its value must initially be obtained by forking.
- + .TP
- + .I &main'run_command(filter-command)
- + Execute the single filter command specified and return the continuation
- + status, which should normally be affected to the \fI\$cont\fR variable. You
- + will need this routine when trying to implement commands which encapsulate
- + other commands, like ONCE or SELECT.
- + .TP
- + .I &main'shell_command(program, input, feedback)
- + Run a shell command and return a failure status (0 for ok). The input parameter
- + may be one of the following constants (defined in the \fImain\fR package):
- + \$NO_INPUT to close standard input, \$BODY_INPUT to pipe the body of the
- + current message, \$MAIL_INPUT to pipe the whole mail and \$HEADER_INPUT to
- + pipe the message header. The feedback parameter may be one of \$FEEDBACK or
- + \$NO_FEEDBACK depending whether or not you wish to use the standard output
- + to alter the corresponding part of the message. If no feedback is wanted, the
- + output of the command is mailed back to the user.
- + .TP
- + .I &main'parse_address(rfc822-address)
- + Parse an RFC822 e-mail address and return a two-elements array containing the
- + internet address and the comment part of that address.
- + .TP
- + .I &main'xeqte(filter-actions)
- + Execute a series of actions separated by the ';' character, calling
- + \fIrun_command\fR to actually perform the job. Return the continuation status.
- + Note that \$FT_ABORT will \fInever\fR be returned, since the mailagent
- + usually stops after having executed one set of actions, only continuing
- + if it saw an RESTART or a REJECT. What ABORT does is skipping the remaining
- + commands on the line and exiting as if all the commands had been run. You
- + could say \fIxeqte\fR is the equivalent of the \fIeval\fR function in perl,
- + since it interprets a little filter script and returns control to the caller
- + once finished, and ABORT is perl's \fIdie\fR.
- + .PP
- + You may also use the three functions from the \fIextern\fR package which
- + manipulate persistent variables (already documented in the section dealing
- + with variables).
- + '''
- + .SS Example
- + .PP
- + Writing your own commands is not easy, since it requires some basic knowledge
- + regarding the mailagent internals. However, once you are familiar with that,
- + then it should be relatively straightforward.
- + .PP
- + Here is a small example. We want to write a command to bounce back a mail
- + message to the original sender, the way sendmail does, with some leading
- + text to explain what happened. The command would have the following syntax:
- + .sp
- + .in +5
- + .nf
- + SENDBACK \fIreason\fR
- + .fi
- + .in -5
- + .sp
- + and we would like that command to modify the existing status, returning
- + a failure if the mail cannot be bounced back. Since this command actually
- + sends something back, we do not want it to be executed in _SEEN_ mode.
- + Here is my implementation (untested):
- + .sp
- + .in +5
- + .nf
- + sub sendback {
- + local(\$cmd_line) = @_;
- + local(\$reason) = join(' ', @ARGV[1..\$#ARGV]);
- + unless (open(MAILER, "|/usr/lib/sendmail -odq -t")) {
- + &'add_log("ERROR cannot run sendmail to send message")
- + if \$'loglvl;
- + return 1;
- + }
- + print MAILER <<EOF;
- + From: mailagent
- + To: \$header{'Sender'}
- + Subject: Returned mail: Mailagent failure
- + \$main'FILTER
- +
- + --- Transcript Of Session
- +
- + \$reason
- +
- + --- Unsent Message Follows
- +
- + \$header{'All'}
- + EOF
- + close MAILER;
- + \$ever_saved = 1; # Don't want it in mailbox
- + \$? == 0 ? 0 : 1; # Failure status
- + }
- + .fi
- + .in -5
- + .sp
- + Assuming this command is put into ~/mail/cmds/sendback.pl, the line
- + describing it in the \fInewcmd\fR file would be:
- + .sp
- + .in +5
- + .nf
- + SENDBACK ~/mail/cmds/sendback.pl sendback yes no
- + .fi
- + .in -5
- + .sp
- + Now this command may be used freely in any rule, and will be logged
- + as a user-defined command by the command dispatcher. Who said it was
- + not easy to do? :-)
- + .PP
- + Note the use of the \$ever_saved variable to mark the mail as saved once
- + it has been bounced. Indeed, should the SENDBACK action be the only one
- + action to be run, we do not want the mailagent to LEAVE the mail in the
- + mailbox because it has never been saved (this default behaviour being
- + a precaution only -- better safe than sorry).
- + '''
- + .SS Conclusion
- + .PP
- + If along the way you imagine some useful commands which could be made
- + part of the standard command set, please e-mail them to me and I'll
- + consider integrating them. In the future, I would also like to provide
- + a standard library of perl scripts to implement some weird commands which
- + could be needed in special cases.
- + .PP
- + Note that you may also use the information presented here inside the
- + perl escape scripts. Via the \fIrequire\fR operator, it is easy to get
- + the new command implementation into your script and perform the same task.
- + You will maybe need to set up @ARGV by yourself if you rely on that
- + feature in your command implementation.
- + .PP
- + Command extension can also be viewed as a way to reuse some other perl
- + code, the mailagent providing a fixed and reliable frame and the external
- + program providing the service. One immediate extension would be mailing
- + list handling, using this mechanism to interface with a mailing list
- + management software like Brent Chapman's \fImajordomo\fR.
- .SH EXAMPLES
- Here are some examples of rule files. First, if you do not specify a rule
- file or if it is empty, the following built-in rule applies:
- ***************
- *** 2251,2256 ****
- --- 2683,2693 ----
- running as you. Via the RUN command, this potential intruder could run any
- command, using your privileges, and could set a trojan horse for later
- perusal. Applying the same logic, the rule file must also be protected tightly.
- + .PP
- + And, no surprise, the same rules apply for your \fInewcmd\fR file, which is
- + used to describe extended filtering commands. Otherwise it would allow someone
- + to quietly redefine a commonly used standard command like LEAVE and later
- + be able to assume your identity.
- .SH FILES
- .PD 0
- .TP 20
-
- Index: agent/pl/actions.pl
- Prereq: 2.9.1.5
- *** agent/pl/actions.pl.old Mon Feb 1 10:24:18 1993
- --- agent/pl/actions.pl Mon Feb 1 10:24:19 1993
- ***************
- *** 1,4 ****
- ! ;# $Id: actions.pl,v 2.9.1.5 93/01/12 12:11:44 ram Exp $
- ;#
- ;# Copyright (c) 1992, Raphael Manfredi
- ;#
- --- 1,4 ----
- ! ;# $Id: actions.pl,v 2.9.1.6 93/02/01 10:08:29 ram Exp $
- ;#
- ;# Copyright (c) 1992, Raphael Manfredi
- ;#
- ***************
- *** 6,11 ****
- --- 6,17 ----
- ;# Licence as specified in the README file that comes with dist.
- ;#
- ;# $Log: actions.pl,v $
- + ;# Revision 2.9.1.6 93/02/01 10:08:29 ram
- + ;# patch17: prepared new mail hook processing, delayed due to a perl bug
- + ;# patch17: NOTIFY now accepts a list of addresses instead of just one
- + ;# patch17: variable storing perl scripts is now pre-extended
- + ;# patch17: random changes and cleanup
- + ;#
- ;# Revision 2.9.1.5 93/01/12 12:11:44 ram
- ;# patch15: saving operation now knows about compression
- ;# patch15: sanity checks performed on saved mail for NFS failure
- ***************
- *** 112,117 ****
- --- 118,124 ----
- $failed |= &mbox_unlock($mailbox); # Will close file
-
- } else {
- + &add_log("SYSERR open: $!") if $loglvl;
- if (-f "$mailbox") {
- do add_log("ERROR cannot append to $mailbox") if $loglvl;
- } else {
- ***************
- *** 124,133 ****
-
- # Called by &save when folder is a hook. This simply calls the mailhook
- # program, which will analyze the hook and perform the necessary actions.
- sub save_hook {
- &add_log("hooking mail on folder") if $loglvl > 15;
- - # Return command failure status (0 means ok)
- &shell_command("$privlib/mailhook $mailbox", $MAIL_INPUT, $NO_FEEDBACK);
- }
-
- # The "PROCESS" command
- --- 131,149 ----
-
- # Called by &save when folder is a hook. This simply calls the mailhook
- # program, which will analyze the hook and perform the necessary actions.
- + # Return command failure status.
- sub save_hook {
- &add_log("hooking mail on folder") if $loglvl > 15;
- &shell_command("$privlib/mailhook $mailbox", $MAIL_INPUT, $NO_FEEDBACK);
- + # The following code to be used with pl/hook.pl.new -- see comment in magent.SH
- + # The idea is to get rid of mailhook and directly process the hooks here, since
- + # most of the time we don't need an extra process (rules/perl hooks).
- + # Unfortunately, perl 4.0 PL35 has a nasty bug in cons.c, which makes the use
- + # of recursion hasardous. Since the fix for this is known but unofficial, I
- + # can't rely on it right now. Sorry--RAM
- + # local($failed) = &hook'process($mailbox);
- + # &add_log("HOOKED [$mfile]") if !$failed && $loglvl > 2;
- + # $failed; # Propagate failure status
- }
-
- # The "PROCESS" command
- ***************
- *** 239,244 ****
- --- 255,261 ----
- $cmdname = $1; # this is the command name
- $trace = "$cf'tmpdir/trace.cmd$$";
- $pid = fork; # We fork here
- + $pid = -1 unless defined $pid;
- if ($pid == 0) {
- open(STDOUT, ">$trace"); # Where output goes
- open(STDERR, ">&STDOUT"); # Make it follow pipe
- ***************
- *** 249,254 ****
- --- 266,272 ----
- # but it is not really important. In fact, this is going to be
- # a feature, not a bug--RAM.
- $error = 1;
- + &add_log("ERROR cannot fork: $!") if $loglvl > 0;
- open(MAILER,"|/usr/lib/sendmail -odq -t");
- print MAILER
- "To: $dest
- ***************
- *** 259,264 ****
- --- 277,283 ----
- Your command was: $fullcmd
-
- It was not executed because I could not fork. Sigh !
- + (Kernel report: $!)
-
- The command has been left in a queue and will be processed again
- as soon as possible, so it is useless to resend it.
- ***************
- *** 269,275 ****
- do add_log("ERROR cannot report failure")
- if ($loglvl > 0);
- }
- - do add_log("ERROR cannot fork") if $loglvl > 0;
- return $error; # Abort processing now--mail remains in queue
- } else {
- wait();
- --- 288,293 ----
- ***************
- *** 349,356 ****
- # The "NOTIFY" command
- sub notify {
- local($msg, $address) = @_;
- ! # Protect all '%' in the address (subject to macro substitution)
- ! $address =~ s/%/%%/g;
- local(@head) = (
- "To: $address",
- "Subject: %s (notification)"
- --- 367,376 ----
- # The "NOTIFY" command
- sub notify {
- local($msg, $address) = @_;
- ! # Any address included withing "" means addresses are stored in a file
- ! $address = &complete_list($address, 'address');
- ! $address =~ s/%/%%/g; # Protect all '%' (subject to macro substitution)
- ! $address =~ s/\s+/, /g; # Addresses separated by ',' on the To: line
- local(@head) = (
- "To: $address",
- "Subject: %s (notification)"
- ***************
- *** 363,377 ****
- sub send_message {
- local($msg, *header) = @_; # Message to send, header of message
- unless (-f "$msg") {
- ! do add_log("cannot find message $msg") if $loglvl > 0;
- return 1;
- }
- unless (open(MSG, "$msg")) {
- ! do add_log("cannot open message $msg") if $loglvl > 0;
- return 1;
- }
- unless (open(MAILER,"|/usr/lib/sendmail -odq -t")) {
- ! do add_log("cannot run sendmail to send message") if $loglvl > 0;
- return 1;
- }
-
- --- 383,397 ----
- sub send_message {
- local($msg, *header) = @_; # Message to send, header of message
- unless (-f "$msg") {
- ! &add_log("ERROR cannot find message $msg") if $loglvl > 0;
- return 1;
- }
- unless (open(MSG, "$msg")) {
- ! &add_log("ERROR cannot open message $msg") if $loglvl > 0;
- return 1;
- }
- unless (open(MAILER,"|/usr/lib/sendmail -odq -t")) {
- ! &add_log("ERROR cannot run sendmail to send message") if $loglvl > 0;
- return 1;
- }
-
- ***************
- *** 448,454 ****
- $addresses =
- &complete_list($addresses, 'address'); # Process "include-requests"
- unless (open(MAILER,"|/usr/lib/sendmail -odq $addresses")) {
- ! do add_log("cannot run sendmail to forward message") if $loglvl > 0;
- return 1;
- }
- local(@addr) = split(' ', $addresses);
- --- 468,474 ----
- $addresses =
- &complete_list($addresses, 'address'); # Process "include-requests"
- unless (open(MAILER,"|/usr/lib/sendmail -odq $addresses")) {
- ! &add_log("ERROR cannot run sendmail to forward message") if $loglvl > 0;
- return 1;
- }
- local(@addr) = split(' ', $addresses);
- ***************
- *** 481,487 ****
- $addresses =
- &complete_list($addresses, 'address'); # Process "include-requests"
- unless (open(MAILER,"|/usr/lib/sendmail -odq $addresses")) {
- ! do add_log("cannot run sendmail to bounce message") if $loglvl > 0;
- return 1;
- }
- # Protect Sender: lines in the original message
- --- 501,507 ----
- $addresses =
- &complete_list($addresses, 'address'); # Process "include-requests"
- unless (open(MAILER,"|/usr/lib/sendmail -odq $addresses")) {
- ! &add_log("ERROR cannot run sendmail to bounce message") if $loglvl > 0;
- return 1;
- }
- # Protect Sender: lines in the original message
- ***************
- *** 509,515 ****
- local($newsgroups) = @_; # Newsgroup(s) mail should be posted to
- local($address) = &email_addr; # Address of user
- unless (open(NEWS,"| $inews -h")) {
- ! do add_log("cannot run $inews to post message") if $loglvl > 0;
- return 1;
- }
- do add_log("distribution of posting is local")
- --- 529,535 ----
- local($newsgroups) = @_; # Newsgroup(s) mail should be posted to
- local($address) = &email_addr; # Address of user
- unless (open(NEWS,"| $inews -h")) {
- ! &add_log("ERROR cannot run $inews to post message") if $loglvl > 0;
- return 1;
- }
- do add_log("distribution of posting is local")
- ***************
- *** 710,716 ****
- sub shell_command {
- local($program, $input, $feedback) = @_;
- unless (chdir $cf'home) {
- ! &add_log("WARNING: cannot chdir to $cf'home: $!") if $loglvl > 5;
- }
- $program =~ s/^\s*~/$cf'home/; # ~ substitution
- $program =~ s/\b~/$cf'home/g; # ~ substitution as first letter in word
- --- 730,736 ----
- sub shell_command {
- local($program, $input, $feedback) = @_;
- unless (chdir $cf'home) {
- ! &add_log("WARNING cannot chdir to $cf'home: $!") if $loglvl > 5;
- }
- $program =~ s/^\s*~/$cf'home/; # ~ substitution
- $program =~ s/\b~/$cf'home/g; # ~ substitution as first letter in word
- ***************
- *** 786,791 ****
- --- 806,812 ----
- pipe(READ, WRITE); # Open a pipe
- local($ppid) = $$; # Pid of parent process
- local($pid) = fork; # We fork here
- + $pid = -1 unless defined $pid;
- if ($pid == 0) { # Child process
- alarm 0;
- close WRITE; # The child reads from pipe
- ***************
- *** 1120,1126 ****
-
- # Fetch the perl script in memory
- local($/) = undef;
- ! local($body) = <PERL>; # Slurp whole file
- close(PERL);
- local(@saved) = @INC; # Save INC array (perl library location path)
- local(%saved) = %INC; # Save already required files
- --- 1141,1148 ----
-
- # Fetch the perl script in memory
- local($/) = undef;
- ! local($body) = ' ' x (-s PERL);
- ! $body = <PERL>; # Slurp whole file into pre-extended variable
- close(PERL);
- local(@saved) = @INC; # Save INC array (perl library location path)
- local(%saved) = %INC; # Save already required files
- ***************
- *** 1129,1135 ****
- unshift(@INC, $privlib); # Files first searched for in mailagent's lib
- package mailhook; # -- entering in mailhook --
- &interface'new; # Signal new script being loaded
- ! &hook'initialize; # Initialize convenience variables
- eval $'body; # Load, compile and execute within mailhook
- &interface'reset; # Clear the mailhook package if no more pending
- package main; # -- reverting to main --
- --- 1151,1157 ----
- unshift(@INC, $privlib); # Files first searched for in mailagent's lib
- package mailhook; # -- entering in mailhook --
- &interface'new; # Signal new script being loaded
- ! &hook'initvar('mailhook'); # Initialize convenience variables
- eval $'body; # Load, compile and execute within mailhook
- &interface'reset; # Clear the mailhook package if no more pending
- package main; # -- reverting to main --
-
- Index: agent/magent.SH
- Prereq: 2.9.1.4
- *** agent/magent.SH.old Mon Feb 1 10:24:00 1993
- --- agent/magent.SH Mon Feb 1 10:24:01 1993
- ***************
- *** 22,28 ****
- # via the filter. Mine looks like this:
- # "|exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"
-
- ! # $Id: magent.SH,v 2.9.1.4 93/01/12 12:08:31 ram Exp $
- #
- # Copyright (c) 1991, 1992, Raphael Manfredi
- #
- --- 22,28 ----
- # via the filter. Mine looks like this:
- # "|exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"
-
- ! # $Id: magent.SH,v 2.9.1.5 93/02/01 09:57:14 ram Exp $
- #
- # Copyright (c) 1991, 1992, Raphael Manfredi
- #
- ***************
- *** 30,35 ****
- --- 30,40 ----
- # Licence as specified in the README file that comes with dist.
- #
- # $Log: magent.SH,v $
- + # Revision 2.9.1.5 93/02/01 09:57:14 ram
- + # patch17: added loading of new commands after initialization
- + # patch17: two new files are now included: pl/newcmd.pl and pl/q.pl
- + # patch17: prepare inclusion of new mail hooks package, delayed due to perl bug
- + #
- # Revision 2.9.1.4 93/01/12 12:08:31 ram
- # patch15: can now deal with compression
- # patch15: knows about MMDF-style mailboxes
- ***************
- *** 253,259 ****
- # Suppress statistics when mailagent invoked manually (in not in test mode)
- &no_stats if $nolock && !$test_mode;
-
- ! &read_stats; # Get statistics, so that we may update them in memory
-
- if (!$run_queue) { # Do not enter here if -q
- if (0 != &analyze_mail($file_name)) { # Analyze the mail
- --- 258,265 ----
- # Suppress statistics when mailagent invoked manually (in not in test mode)
- &no_stats if $nolock && !$test_mode;
-
- ! &read_stats; # Load statistics into memory for fast update
- ! &newcmd'load if $cf'newcmd; # Load user-defined command definitions
-
- if (!$run_queue) { # Do not enter here if -q
- if (0 != &analyze_mail($file_name)) { # Analyze the mail
- ***************
- *** 336,350 ****
-
- # Start-up initializations
- sub init_all {
- ! do init_signals(); # Trap common signals
- ! do init_constants(); # Constants definitions
- ! do init_interpreter(); # Initialize tables %Priority, %Function, ...
- ! do init_env(); # Initialize the %XENV array
- ! do init_matcher(); # Initialize special matching functions
- ! do init_pseudokey(); # Initialize the pseudo header keys for H table
- ! do init_builtins(); # Initialize built-in commands like @RR
- ! do init_filter(); # Initialize filter commands
- ! do init_special(); # Initialize special user table %Special
- }
-
- # Protect ourselves (trap common signals)
- --- 342,356 ----
-
- # Start-up initializations
- sub init_all {
- ! &init_signals; # Trap common signals
- ! &init_constants; # Constants definitions
- ! &init_interpreter; # Initialize tables %Priority, %Function, ...
- ! &init_env; # Initialize the %XENV array
- ! &init_matcher; # Initialize special matching functions
- ! &init_pseudokey; # Initialize the pseudo header keys for H table
- ! &init_builtins; # Initialize built-in commands like @RR
- ! &init_filter; # Initialize filter commands
- ! &init_special; # Initialize special user table %Special
- }
-
- # Protect ourselves (trap common signals)
- ***************
- *** 585,589 ****
- --- 591,603 ----
- $grep -v '^;#' pl/hostname.pl >>magent
- $grep -v '^;#' pl/mmdf.pl >>magent
- $grep -v '^;#' pl/compress.pl >>magent
- + $grep -v '^;#' pl/newcmd.pl >>magent
- + $grep -v '^;#' pl/q.pl >>magent
- + # The following will be added (using pl/hook.pl.new) when Larry releases
- + # perl 5.0. Right now, due to a bug in cons.c, optimizing hooks is not
- + # possible (the optimization being getting rid of mailhook and processing
- + # commands directly within the mailagent). But recursion through SAVE is
- + # not allowed and will make perl 4.0 PL35 die with a segementation violation.
- + #$grep -v '^;#' pl/hook.pl >>magent
- chmod 755 magent
- $eunicefix magent
-
- Index: agent/pl/parse.pl
- Prereq: 2.9.1.2
- *** agent/pl/parse.pl.old Mon Feb 1 10:24:41 1993
- --- agent/pl/parse.pl Mon Feb 1 10:24:42 1993
- ***************
- *** 1,4 ****
- ! ;# $Id: parse.pl,v 2.9.1.2 92/12/01 09:26:19 ram Exp $
- ;#
- ;# Copyright (c) 1992, Raphael Manfredi
- ;#
- --- 1,4 ----
- ! ;# $Id: parse.pl,v 2.9.1.3 93/02/01 10:21:34 ram Exp $
- ;#
- ;# Copyright (c) 1992, Raphael Manfredi
- ;#
- ***************
- *** 6,11 ****
- --- 6,14 ----
- ;# Licence as specified in the README file that comes with dist.
- ;#
- ;# $Log: parse.pl,v $
- + ;# Revision 2.9.1.3 93/02/01 10:21:34 ram
- + ;# patch17: now takes From: as a Sender if no leading From line
- + ;#
- ;# Revision 2.9.1.2 92/12/01 09:26:19 ram
- ;# patch13: now also understands multiple To and Cc lines in headers
- ;#
- ***************
- *** 161,166 ****
- --- 164,170 ----
- # Unless there is already a sender line, fake one using From field
- if (!$Header{'Sender'}) {
- $Header{'Sender'} = $first_from;
- + $Header{'Sender'} = $Header{'From'} unless $first_from;
- }
- }
-
-
- *** End of Patch 17 ***
-
- exit 0 # Just in case...
-