You can subscribe to this list here.
| 2000 |
Jan
|
Feb
(1) |
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2001 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(9) |
Nov
|
Dec
|
|
From: <mai...@ra...> - 2001-10-26 18:01:33
|
On Thu, Oct 11, 2001 at 09:43:10PM -0400, Stuart D. Gathman wrote: > I am already doing this in my current (non mailchecker) milters. Howev= er, > When you find an attachment named foo.doc.pif (and you don't know this = until > you are processing the body), you *know* it is a virus > and can reject the message to avoid downloading the 2Meg .doc with viru= s > attached. So I still think that rejecting the entire message should > be at least optionally signalled by throwing a RejectMessage exception. Agreed. > > There are a lot of things that needs to be fixed with mailchecker: > > * the name :-) >=20 > I suggest "maildefang". I think this is too simular too mimedefang. > > * packaging >=20 > Read about distutils in the python2 documentation (it is available for > python 1.5 also, but not installed by default). Look at setup.py in > the milter package for an example. Look at other packages. Will do. > > I'm unsure if the best thing would be to fix one thing at the time, o= r > > to rewrite from scratch with this issues in mind. >=20 > Step 1 - write some test cases. I have attached a test case for > mfilter.py and revised mfilter.py as an example. (mfilter fails the te= st. =20 > Testing errormsg to see whether the body should be replaced didn't work= . =20 > This is why testCheck2 fails. >=20 > I/you need to add a tool to mailcheck to replace virus attachments with= a > text attachment containing a warning message. This is why testCheck1 f= ails. >=20 > Step 2 - get all the current code to pass the tests. >=20 > Step 3 - move check and friends to a subdirectory - this will make them > a python "package". I suggest "mailcheck" as the package name. Leave > main.py, mda.py, mfilter.py as top level modules. Move the test > modules to a test subdirectory. Get everything to pass tests again. >=20 > Step 4 - make a setup.py (I can help) and install on a production syste= m. > (I have dozens - both AIX 4.1.5 and RedHat 6.2 :-) Add any failures > or shortcomings to the test cases and get everything to pass all tests. >=20 > Step 5 - Make announcement of maildefang-0.2.0 and milter-0.4.0 on vari= ous > forums. >=20 > Step 6 - Make test cases for bugs reported and release again. Sounds good. Unfortenately I don't have time right now, but sooner or later I'll get around to it. --=20 Ragnar Kj=F8rstad Zet.no |
|
From: <mai...@ra...> - 2001-10-15 11:48:55
|
On Sun, Oct 14, 2001 at 09:01:00PM -0400, Stuart D. Gathman wrote: > The unconditional logging to syslog gets in the way of setting up a tes= t > framework. Could we add an indirection? I'm not following. Could you be more specific? --=20 Ragnar Kj=F8rstad Zet.no |
|
From: Stuart D. G. <st...@bm...> - 2001-10-15 01:10:07
|
The unconditional logging to syslog gets in the way of setting up a test
framework. Could we add an indirection?
--
Stuart D. Gathman
Business Management Systems Inc. Phone: 703 591-0911 Fax: 703 591-6154
"Confutatis maledictis, flamis acribus addictis" - background song for
a Microsoft sponsored "Where do you want to go from here?" commercial.
|
|
From: Stuart D. G. <st...@bm...> - 2001-10-12 03:11:31
|
mailchecker is invoking the 'file' command with a -b option. AIX file
does not understand this.
--
Stuart D. Gathman <st...@bm...>
Business Management Systems Inc. Phone: 703 591-0911 Fax: 703 591-6154
"Microsoft is the QWERTY of Operating Systems" - SDG
"Confutatis maledictis, flamis acribus addictis" - background song for
a Microsoft sponsored "Where do you want to go from here?" commercial.
(HINT: The song is "Dies Irae" from the Mozart Requiem.)
|
|
From: Stuart D. G. <st...@bm...> - 2001-10-12 01:43:20
|
On Fri, 12 Oct 2001, Ragnar Kj=F8rstad wrote:
> > Because once you know the entire message will be rejected, the milter=
can
> > return REJECT to sendmail and sendmail will issue a 5xx error and ter=
minate
> > the connection. This saves downloading stuff you are going to throw =
away
> > - important for a busy mail server.
>=20
> I think I have a possible solution:
>=20
> What about making the milter interface call check() twice? once for jus=
t
> the headers and once for the whole mail? (It's not enough to send just
> the body the second time, because to solve problems in the body the
> header may have to be changed.
>=20
> This will enable the performance enhancement you're looking for while
> moving policy issues away from the interface-code.
>=20
> The overhead of checking the headers twice is probably not important.
I am already doing this in my current (non mailchecker) milters. However=
,
When you find an attachment named foo.doc.pif (and you don't know this un=
til
you are processing the body), you *know* it is a virus
and can reject the message to avoid downloading the 2Meg .doc with virus
attached. So I still think that rejecting the entire message should
be at least optionally signalled by throwing a RejectMessage exception.
> > 4) the check() module and all its helpers should be in their own pack=
age.
> > Only main.py, mda.py, mfilter.py should be in the main directory. =20
> > The check package should be installed with distutils. When this is d=
one,
> > the mfilter.py could be part of the milter package - and require that
> > the mailchecker package is installed to use it.
> >=20
> > Another packaging possibility is to make mailcheck.py =3D check.py a
> > toplevel module and put all the variations in a package.
>=20
> I'm sure you're right, but could you explain in more detail?
> I'm not familiar with distutils.
> There are a lot of things that needs to be fixed with mailchecker:
> * the name :-)
I suggest "maildefang".
> * packaging
Read about distutils in the python2 documentation (it is available for
python 1.5 also, but not installed by default). Look at setup.py in
the milter package for an example. Look at other packages.
> I'm unsure if the best thing would be to fix one thing at the time, or
> to rewrite from scratch with this issues in mind.
Step 1 - write some test cases. I have attached a test case for
mfilter.py and revised mfilter.py as an example. (mfilter fails the test=
. =20
Testing errormsg to see whether the body should be replaced didn't work. =
=20
This is why testCheck2 fails.
I/you need to add a tool to mailcheck to replace virus attachments with a
text attachment containing a warning message. This is why testCheck1 fai=
ls.
Step 2 - get all the current code to pass the tests.
Step 3 - move check and friends to a subdirectory - this will make them
a python "package". I suggest "mailcheck" as the package name. Leave
main.py, mda.py, mfilter.py as top level modules. Move the test
modules to a test subdirectory. Get everything to pass tests again.
Step 4 - make a setup.py (I can help) and install on a production system.
(I have dozens - both AIX 4.1.5 and RedHat 6.2 :-) Add any failures
or shortcomings to the test cases and get everything to pass all tests.
Step 5 - Make announcement of maildefang-0.2.0 and milter-0.4.0 on variou=
s
forums.
Step 6 - Make test cases for bugs reported and release again.
--=20
Stuart D. Gathman <st...@bm...>
Business Management Systems Inc. Phone: 703 591-0911 Fax: 703 591-=
6154
"Microsoft is the QWERTY of Operating Systems" - SDG
"Confutatis maledictis, flamis acribus addictis" - background song =
for
a Microsoft sponsored "Where do you want to go from here?" commerci=
al.
(HINT: The song is "Dies Irae" from the Mozart Requiem.)
|
|
From: <mai...@ra...> - 2001-10-12 00:52:24
|
On Thu, Oct 11, 2001 at 08:35:40PM -0400, Stuart D. Gathman wrote: > > > 2) If the entire message is to be rejected, this should be done ASA= P. > > > Rejecting the entire message should be signalled via an exception. > >=20 > > Why treat a bad message differently from a bad message-part? >=20 > Because once you know the entire message will be rejected, the milter c= an > return REJECT to sendmail and sendmail will issue a 5xx error and termi= nate > the connection. This saves downloading stuff you are going to throw aw= ay > - important for a busy mail server. I think I have a possible solution: What about making the milter interface call check() twice? once for just the headers and once for the whole mail? (It's not enough to send just the body the second time, because to solve problems in the body the header may have to be changed. This will enable the performance enhancement you're looking for while moving policy issues away from the interface-code. The overhead of checking the headers twice is probably not important. > > The whole "filetypes" structure got kind of messy when we added suppo= rt > > for checking on filenames. I think we should redo it so that: > > * only data-types that should be accepted or denied directly > > should be specified. The settings for doing recursive checking (e.g. > > doing gunzip on gziped files) should default to the "correct"=20 > > setting. > > * The configuration should specify if the regexp is to be matched > > against filenames, mime-types, file-magic or content. > > Not like now, when the regexp is checked against everything. > > * Implement a way for the user to specify multiple "chained" rules. > > If [ "mime-type": "text/plain" ] > > is the rule to match all text/plain parts, then > > [[ "mime-type": "application/msword" ], ["content", "macro"] ] > > would match word-documents that include the word "macro". > > (OK, the example may not be the best, but I think this will have us= es) > >=20 > > It doesn't really solve the problem with envelope-to and character-se= t > > checking. I don't worry to much about envelope-to, because it sounds > > plain wrong to check on it. If the to address is postmaster then it w= ill > > be delivered to postmaster - stopping it has the same effect as > > forwarding postmaster email to /dev/null - you don't need a special > > filter for that. (or am I wrong?) >=20 > If you REJECT it, you save the bandwidth. DISCARDing it is equivalent > to /dev/null. So I guess the framework needs to take this into account somehow... > 4) the check() module and all its helpers should be in their own packag= e. > Only main.py, mda.py, mfilter.py should be in the main directory. =20 > The check package should be installed with distutils. When this is don= e, > the mfilter.py could be part of the milter package - and require that > the mailchecker package is installed to use it. >=20 > Another packaging possibility is to make mailcheck.py =3D check.py a > toplevel module and put all the variations in a package. I'm sure you're right, but could you explain in more detail? I'm not familiar with distutils. There are a lot of things that needs to be fixed with mailchecker: * the name :-) * packaging * configuration - according to previous mails * passing data back to the caller - e.g. so we can distinguish between reject and discard. and probably a lot of other things. I'm unsure if the best thing would be to fix one thing at the time, or to rewrite from scratch with this issues in mind. Maybe the approach doesn't matter to much - the tricky part is getting the design right - the actual implementation is not too much work. --=20 Ragnar Kj=F8rstad Zet.no |
|
From: Stuart D. G. <st...@bm...> - 2001-10-12 00:35:44
|
On Fri, 12 Oct 2001, Ragnar Kj=F8rstad wrote: > On Thu, Oct 11, 2001 at 04:09:56PM -0400, Stuart D. Gathman wrote: > The check function returns multiple arguments, and one of them is a > "comment" about what has been changed. If this is empty, nothing has > changed. Great. > > 2) If the entire message is to be rejected, this should be done ASAP. > > Rejecting the entire message should be signalled via an exception. >=20 > Why treat a bad message differently from a bad message-part? Because once you know the entire message will be rejected, the milter can return REJECT to sendmail and sendmail will issue a 5xx error and termina= te the connection. This saves downloading stuff you are going to throw away - important for a busy mail server. > > 3) libmilter wants us to communicate header changes via addhdr/chghdr= /delhdr > > with headername and index (for multiple headers with same name). We = could > > handle this with a diff against a saved original.=20 >=20 > Right now you don't modify the headers at all, right?=20 > Can you modify headers in eom? Yes. It was tricky to do on a first cut. Yes. > I'm not to happy about the "policy" part in the interface, e.g.: > * Skipping email to mailer-deamon > That doesn't work well for those of us that are postmasters... > * Skipping email with big5 character set > That may very well be a good choice for many of us, but it doesn't > sound like something that should be hard-coded. >=20 > Did you put this in the milter interface code for performance reasons, > or because there is no way to do it inside mailchecker right now? As examples of stuff that I am doing now that a generalized package perhaps ought to be able to do. We could handle this by leaving it out of the generic mailcheckMilter, and a mail admin could derive from it or simply customize it. > I think mailchecker should be improved to make it possible to make rule= s > like this - allthough I'm not 100% how. Exactly. > The whole "filetypes" structure got kind of messy when we added support > for checking on filenames. I think we should redo it so that: > * only data-types that should be accepted or denied directly > should be specified. The settings for doing recursive checking (e.g. > doing gunzip on gziped files) should default to the "correct"=20 > setting. > * The configuration should specify if the regexp is to be matched > against filenames, mime-types, file-magic or content. > Not like now, when the regexp is checked against everything. > * Implement a way for the user to specify multiple "chained" rules. > If [ "mime-type": "text/plain" ] > is the rule to match all text/plain parts, then > [[ "mime-type": "application/msword" ], ["content", "macro"] ] > would match word-documents that include the word "macro". > (OK, the example may not be the best, but I think this will have uses= ) >=20 > It doesn't really solve the problem with envelope-to and character-set > checking. I don't worry to much about envelope-to, because it sounds > plain wrong to check on it. If the to address is postmaster then it wil= l > be delivered to postmaster - stopping it has the same effect as > forwarding postmaster email to /dev/null - you don't need a special > filter for that. (or am I wrong?) If you REJECT it, you save the bandwidth. DISCARDing it is equivalent to /dev/null. 4) the check() module and all its helpers should be in their own package. Only main.py, mda.py, mfilter.py should be in the main directory. =20 The check package should be installed with distutils. When this is done, the mfilter.py could be part of the milter package - and require that the mailchecker package is installed to use it. Another packaging possibility is to make mailcheck.py =3D check.py a toplevel module and put all the variations in a package. |
|
From: <mai...@ra...> - 2001-10-11 23:26:44
|
On Thu, Oct 11, 2001 at 04:09:56PM -0400, Stuart D. Gathman wrote:
> I have started work on integrating mailchecker-0.1 with milter-0.3.4
> libmilter interface.
>
> 1) We don't want to replace the body unnecessarily. The check function
> needs to return whether any parts were replaced.
Agreed.
The check function returns multiple arguments, and one of them is a
"comment" about what has been changed. If this is empty, nothing has
changed.
> 2) If the entire message is to be rejected, this should be done ASAP.
> Rejecting the entire message should be signalled via an exception.
Why treat a bad message differently from a bad message-part?
> 3) libmilter wants us to communicate header changes via addhdr/chghdr/delhdr
> with headername and index (for multiple headers with same name). We could
> handle this with a diff against a saved original.
Right now you don't modify the headers at all, right?
Can you modify headers in eom?
> I have attached a first untested cut for review.
Great!
I'm not to happy about the "policy" part in the interface, e.g.:
* Skipping email to mailer-deamon
That doesn't work well for those of us that are postmasters...
* Skipping email with big5 character set
That may very well be a good choice for many of us, but it doesn't
sound like something that should be hard-coded.
Did you put this in the milter interface code for performance reasons,
or because there is no way to do it inside mailchecker right now?
I think mailchecker should be improved to make it possible to make rules
like this - allthough I'm not 100% how.
The whole "filetypes" structure got kind of messy when we added support
for checking on filenames. I think we should redo it so that:
* only data-types that should be accepted or denied directly
should be specified. The settings for doing recursive checking (e.g.
doing gunzip on gziped files) should default to the "correct"
setting.
* The configuration should specify if the regexp is to be matched
against filenames, mime-types, file-magic or content.
Not like now, when the regexp is checked against everything.
* Implement a way for the user to specify multiple "chained" rules.
If [ "mime-type": "text/plain" ]
is the rule to match all text/plain parts, then
[[ "mime-type": "application/msword" ], ["content", "macro"] ]
would match word-documents that include the word "macro".
(OK, the example may not be the best, but I think this will have uses)
It doesn't really solve the problem with envelope-to and character-set
checking. I don't worry to much about envelope-to, because it sounds
plain wrong to check on it. If the to address is postmaster then it will
be delivered to postmaster - stopping it has the same effect as
forwarding postmaster email to /dev/null - you don't need a special
filter for that. (or am I wrong?)
We can make the rfc822 module run check on both the mail headers and the
body seperately. Then it will be possible to specify a rule like:
[[ "auto-type": "rfc822_subject"], [ "content", "Subject: =big5"]]: reject
I think this will be much cleaner than the current implementation (both
the mailchecker configuration, and the part done in the milter
interface). The only disadvantage is performance.
This is just from the top of my head, so I haven't given it much thought
yet. I'm open to suggestions - let me know what you think.
> # libmilter interface to mailcheck
>
> # Author: Stuart D. Gathman <st...@bm...>
> # Copyright 2001 Business Management Systems, Inc.
> # This code is under GPL. See COPYING for details.
>
> import sys
> import os
> import StringIO
> import rfc822
> import mime
> import mimetools
> import Milter
> import tempfile
> from config import *
> from syslog import syslog
>
> class mailcheckMilter(Milter.Milter):
> "Milter to check attachments with the mailcheck package."
>
> def __init__(self):
> self.tempname = None
> self.fp = None
>
> # multiple messages can be received on a single connection
> # envfrom (MAIL FROM in the SMTP protocol) seems to mark the start
> # of each message.
> def envfrom(self,f,*str):
> if debug:
> syslog("mail from "+f+' '+repr(str))
> self.tempname = fname = tempfile.mktemp(".defang")
> self.fp = open(fname,"w+b")
> return Milter.CONTINUE
>
> def envrcpt(self,to,*str):
> # mail to MAILER-DAEMON is generally spam that bounced
> if mime.startsWith(to,'<MAILER-DAEMON@'):
> syslog('DISCARD: RCPT TO:'+to+' '+repr(str))
> return Milter.DISCARD
> if debug:
> syslog("rcpt to "+to+' '+repr(str))
> return Milter.CONTINUE
>
> def header(self,name,val):
> # even if we wanted the Taiwanese spam, we can't read Chinese
> if name.lower() == 'subject' and mime.startsWith(val,'=?big5'):
> syslog('REJECT: %s: %s' % (name,val))
> #self.setreply('550','','Go away spammer')
> return Milter.REJECT
> if self.fp:
> self.fp.write("%s: %s\n" % (name,val)) # add header to buffer
> return Milter.CONTINUE
>
> def eoh(self):
> if not self.fp: return Milter.ACCEPT # not seen by envfrom
> self.fp.write("\n") # terminate headers
> return Milter.CONTINUE
>
> def close(self):
> sys.stdout.flush() # make log messages visible
> if self.tempname:
> os.remove(self.tempname) # remove in case session aborted
> return Milter.CONTINUE
>
> def body(self,chunk): # copy body to temp file
> if self.fp:
> self.fp.write(chunk) # IOError causes TEMPFAIL in milter
> return Milter.CONTINUE
>
> # FIXME: We aren't handling the case where the main message is a virus.
> # This would involve telling sendmail about headers changed in the
> # MimeMessage somehow - including index of said header.
> def eom(self):
> mail = self.fp;
> if not mail: return Milter.ACCEPT
> mail.seek(0)
> (outmail, types, errormsg) = check(mail, [('smtp mail','')])
>
> if errormsg:
> syslog(errormsg)
> mail.seek(0)
> m=Message(mail)
> dict=m.dict
> m_from=''
> m_subject=''
> m_to=''
> if dict.has_key('to'):
> m_to=dict['to']
> if dict.has_key('return-path'):
> m_from=dict['return-path']
> elif dict.has_key('from'):
> m_from=dict['from']
> if dict.has_key('subject'):
> m_subject=dict['subject']
> dict['error']=errormsg
> if notify_sender:
> msg2=replace_variables(message2, dict)
> sendmail(m_from, "Mail regarding "+m_subject+" blocked", msg2)
> if notify_recipient:
> msg=replace_variables(message, dict)
> sendmail(m_to, "Mail regarding "+m_subject+" blocked", msg)
> if send_backup:
> mail.seek(0)
> command=procmail + ' -Y -d '+send_backup
> procmail_fp=popen(command, "w")
> procmail_fp.write(mail.read())
> procmail_fp.close()
>
> # FIXME: replaces body even if no changes!
> if (outmail): # replace body
> while 1:
> buf = outmail.read(8192)
> if len(buf) == 0: break
> self.replacebody(buf) # feed modified message to sendmail
> return Milter.ACCEPT # ACCEPT modified message
>
> return Milter.REJECT # ???
>
> if __name__ == "__main__":
> tempfile.tempdir = "/var/log/milter"
> socketname = "/var/log/milter/pythonsock"
> Milter.factory = bmsMilter
> Milter.set_flags(Milter.CHGBODY)
> print "bms milter startup"
> sys.stdout.flush()
> Milter.runmilter("pythonfilter",socketname,120)
> print "bms milter shutdown"
|
|
From: <mai...@ra...> - 2001-10-01 09:41:07
|
Hi I just read your announcement of Python Milter on freshmeat. I assume your goal is to be able to filter out attachments from emails? We have the same goal, but a different approach. We started with being able to split up mime-attachments, decode / decompress data and then take "appropriate" action based on the content (determined by filename,=20 mime-type or file magic) Our code is at http://sourceforge.net/projects/mailchecker. Mailchecker can be used as an MDA (instead of procmail), or as a filter started from e.g. .procmailrc - however, using it as a milter gives added functionality in some cases (checking outgoing email). I think it's wise to keep the two projects separate. Python milters can be used for a lot of different things than virus-checking, and mailchecker should be able to start as mda or from .procmailrc and interface with milter-like features in other MTAs. However, some coordination is probably a good idea. I'd like to have a milter.py in mailchecker (just like mda.py and main.py) to have mailchecker start up as a milter. Most likely I will not get around to this very soon, so maybe you will do it first? :-) If you'd like to join the developement of mailchecker, I'll give you CVS access to the code. (let me know your sourceforge username). Mailchecker probably doesn't conform to the python coding standards, and it uses the native python mime-support, so there is definitively work to be done. Neither does it interface with regular anti-virus scanners yet, allthough this should be relatively easy to add.=20 Let me know what you think. --=20 Ragnar Kj=F8rstad Zet.no |
|
From: <sou...@ra...> - 2000-03-21 02:07:00
|
On Mon, Mar 20, 2000 at 02:32:46PM -0700, Shawn Button wrote: > > We're writing Mailchecker to do exactly this. It's not "done" yet, but > > it can be used if you're willing to fiddle a little bit. > > > > It's a set of pyhton scripts that parses, decodes and decompresses > > attachments - and then filters attachment according to a set of rules. > > > > It's available at http://sourceforge.net/project/?group_id=2306 > > Any idea when this product will be available? We are definitely > interested in it. We run SendMail 8.9.3. Thank you very much, Well, it is "available" - it's just not "done"/"complete". What it does: * parses mail * removes attachments based on type (file magic is used to determine type) * Puts mail back together Can be used as a filter (for testing) (main.py), or as a MDA (instad of procmail for delivery) (mda.py). The mda-version will pipe it's output to procmail, and write errors to syslog. What is not done yet: * Better error-reporting (now errors are just written to syslog - both the sender and the recipient should be notified, and the original mail should be stored, sent to system administrator or something. Some care has to be taken to avoid loops) * Integrate with wv (new name of mswordview - this version can convert Microsoft Word documents versions 6,7,8 and 9 and should be very useful. Unfortenately it sometimes crashes on Norwegian documents so I havent worked on it yet. First step is to make file-magic recognize Word Documents - the offical version just call them "Microsoft Office Document"). * Write filters to decompress/decode more formats. The only tricky part is extending the interfaces to handle files that include multiple files - like tar and zip. It shouldn't be too hard though. * Improve integration with other utilities (Now Mailchecker runs other programs seperately and pipes data to and from. This is not good for performance, and error-detection would be easier if we used a better API) * mda.py should change it's effective user to the user recieving the mail for better security * Instead of mda.py I would like to use Sendmail 8.10's libMilter API. This would allow us to filter both incomming and outgoing mail, and we can avoid interfering with the MDA (procmail) * Making tar's and rpms for easier installation (now it's only available via CVS - there should be instructions on how to use it on sourceforge - if you have any problems: please let me know) If you look at what we have done, you'll see that it's an extremely small project (just a few hundred lines of code), and the issues remaining is also easily solved. Unfortenately other projects have higher priority right now, so progress is slow. If you have some time to work with this, I'll grant you write access to our CVS repository. If you don't have the time I'll still like feedback (bug rapports, feature requests, questions, whatever)! -- Ragnar Kjørstad |
|
From: <mai...@ra...> - 2000-02-07 13:13:29
|
I've updated the CVS with a couple of minor changes * Some more comments * check_mail now understands 7BIT and 8BIT encoding (earlier just 7bit and 8bit) * new file: mda.py mda.py can be used to replace procmail as the local mailer of the system. It will check the content of the mail, and then fire up procmail with the result. Currently the only error-handling is a syslog-entry when some content has been removed or converted. In the future it will send mail to the recipient, the author and the system administrator instead. -- Ragnar Kjørstad |