diff -urN exim-4.44-orig/OS/Makefile-Base exim-4.44/OS/Makefile-Base
--- exim-4.44-orig/OS/Makefile-Base	Tue Jan 11 13:54:54 2005
+++ exim-4.44/OS/Makefile-Base	Fri Jan 14 20:35:02 2005
@@ -285,14 +285,14 @@
 # Targets for final binaries; the main one has a build number which is
 # updated each time. We don't bother with that for the auxiliaries.
 
-OBJ_EXIM = acl.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o \
+OBJ_EXIM = acl.o bmi_spam.o child.o crypt16.o daemon.o dbfn.o debug.o deliver.o demime.o \
         directory.o dns.o drtables.o enq.o exim.o expand.o filter.o \
         filtertest.o globals.o \
-        header.o host.o ip.o log.o lss.o match.o moan.o \
+        header.o host.o ip.o log.o lss.o malware.o match.o mime.o moan.o \
         os.o parse.o queue.o \
-        rda.o readconf.o receive.o retry.o rewrite.o rfc2047.o \
-        route.o search.o sieve.o smtp_in.o smtp_out.o spool_in.o spool_out.o \
-        store.o string.o tls.o tod.o transport.o tree.o verify.o \
+        rda.o readconf.o receive.o regex.o retry.o rewrite.o rfc2047.o \
+        route.o search.o sieve.o smtp_in.o smtp_out.o spam.o spf.o spool_in.o spool_mbox.o spool_out.o \
+        srs.o store.o string.o tls.o tnef.o tod.o transport.o tree.o verify.o \
         local_scan.o $(EXIM_PERL)
 
 exim:   pcre/libpcre.a lookups/lookups.a auths/auths.a \
@@ -499,12 +499,14 @@
 # Dependencies for the "ordinary" exim modules
 
 acl.o:           $(HDRS) acl.c
+bmi_spam.o:      $(HDRS) bmi_spam.c
 child.o:         $(HDRS) child.c
 crypt16.o:       $(HDRS) crypt16.c
 daemon.o:        $(HDRS) daemon.c
 dbfn.o:          $(HDRS) dbfn.c
 debug.o:         $(HDRS) debug.c
 deliver.o:       $(HDRS) deliver.c
+demime.o:        $(HDRS) demime.c
 directory.o:     $(HDRS) directory.c
 dns.o:           $(HDRS) dns.c
 enq.o:           $(HDRS) enq.c
@@ -518,7 +520,9 @@
 ip.o:            $(HDRS) ip.c
 log.o:           $(HDRS) log.c
 lss.o:           $(HDRS) lss.c
+malware.o:       $(HDRS) malware.c
 match.o:         $(HDRS) match.c
+mime.o:          $(HDRS) mime.c
 moan.o:          $(HDRS) moan.c
 os.o:            $(HDRS) os.c
 parse.o:         $(HDRS) parse.c
@@ -526,6 +530,7 @@
 rda.o:           $(HDRS) rda.c
 readconf.o:      $(HDRS) readconf.c
 receive.o:       $(HDRS) receive.c
+regex.o:         $(HDRS) regex.c
 retry.o:         $(HDRS) retry.c
 rewrite.o:       $(HDRS) rewrite.c
 rfc2047.o:       $(HDRS) rfc2047.c
@@ -534,11 +539,16 @@
 sieve.o:         $(HDRS) sieve.c
 smtp_in.o:       $(HDRS) smtp_in.c
 smtp_out.o:      $(HDRS) smtp_out.c
+spam.o:          $(HDRS) spam.c
+spf.o:           $(HDRS) spf.c
 spool_in.o:      $(HDRS) spool_in.c
+spool_mbox.o:    $(HDRS) spool_mbox.c
 spool_out.o:     $(HDRS) spool_out.c
+srs.o:           $(HDRS) srs.c
 store.o:         $(HDRS) store.c
 string.o:        $(HDRS) string.c
 tls.o:           $(HDRS) tls.c tls-gnu.c tls-openssl.c
+tnef.o:          $(HDRS) tnef.c
 tod.o:           $(HDRS) tod.c
 transport.o:     $(HDRS) transport.c
 tree.o:          $(HDRS) tree.c
diff -urN exim-4.44-orig/OS/os.h-Linux exim-4.44/OS/os.h-Linux
--- exim-4.44-orig/OS/os.h-Linux	Tue Jan 11 21:05:08 2005
+++ exim-4.44/OS/os.h-Linux	Fri Jan 14 20:35:02 2005
@@ -35,3 +35,8 @@
 extern struct ip_address_item *os_find_running_interfaces_linux(void);
 
 /* End */
+
+/* Fudge added because this Linux doesn't appear to have a definition
+for ip_options in /usr/include/linux/ip.h. */
+
+#define ip_options options
diff -urN exim-4.44-orig/README.EXISCAN exim-4.44/README.EXISCAN
--- exim-4.44-orig/README.EXISCAN	Thu Jan  1 01:00:00 1970
+++ exim-4.44/README.EXISCAN	Fri Jan 14 20:35:02 2005
@@ -0,0 +1 @@
+Please refer to doc/exiscan-acl-spec.txt
diff -urN exim-4.44-orig/doc/exiscan-acl-examples.txt exim-4.44/doc/exiscan-acl-examples.txt
--- exim-4.44-orig/doc/exiscan-acl-examples.txt	Thu Jan  1 01:00:00 1970
+++ exim-4.44/doc/exiscan-acl-examples.txt	Fri Jan 14 20:35:02 2005
@@ -0,0 +1,440 @@
+--------------------------------------------------------------
+exiscan-acl example configurations / FAQ
+--------------------------------------------------------------
+
+Author: Tom Kistner <tom@duncanthrax.net>
+
+The exiscan website is at http://duncanthrax.net/exiscan/. You
+will find the latest patch  versions, as well as links  to the
+mailing list and its archives there.
+
+This document shows some example configuration snippets:
+
+1. Basic  sitewide  virus  and  spam  filtering  by  rejecting
+   matching messages after DATA.
+2. Adding  a  cryptographic  "checks  done"  header  that will
+   prevent re-scanning when the message re-visits one of  your
+   mail servers, and the body size did not change.
+3. Marking spam-suspicious messages  with extra headers  and a
+   tag in the subject.
+4. Having more than one spam threshold to act on.
+5. Redirecting  matching  messages to  special  accounts while
+   preserving envelope recipient information.
+6. A  multi-profile  configuration for  sites  where different
+   "customers"  (or  users)  have  different  content scanning
+   preferences.
+
+These examples serve as a  guideline and should give you  some
+pointers that can help  you to create your  own configuration.
+Please do not copy these examples verbatim. You really need to
+know what you are doing. The content scanning topic is  really
+complex and you can screw up your mail server easily if you do
+not get it "right".
+
+I recommend  to read  the exiscan  documentation on  the above
+mentioned website before trying to make sense of the following
+examples.
+
+Each  example  shows part  of  a DATA  ACL  definition, unless
+otherwise noted. 
+
+--------------------------------------------------------------
+1. Basic setup for simple site-wide filtering
+--------------------------------------------------------------
+The following  example only  shows the  most basic  use of the
+exiscan content  filtering features.  You should  see it  as a
+base that you can  build on. However, it  may be all you  need
+for smaller systems with only a few users.
+
+/* -----------------
+# Do not scan messages submitted from our own hosts
+# and locally submitted messages. Since the DATA ACL
+# is not called for messages not submitted via SMTP
+# protocols, we do not need to check for an empty
+# host field.
+accept  hosts = 127.0.0.1:+relay_from_hosts
+
+# Unpack MIME containers and reject file extensions
+# used by worms. Note that the extension list may be
+# incomplete.
+deny  message = $found_extension files are not accepted here
+      demime = com:vbs:bat:pif:scr
+
+# Reject messages that have serious MIME errors.
+# This calls the demime condition again, but it
+# will return cached results.
+deny  message = Serious MIME defect detected ($demime_reason)
+      demime = *
+      condition = ${if >{$demime_errorlevel}{2}{1}{0}}
+
+# Reject messages containing malware.
+deny message = This message contains malware ($malware_name)
+     malware = *
+     
+# Reject spam messages. Remember to tweak your
+# site-wide SA profile. Do not spam-scan messages
+# larger than eighty kilobytes.
+deny message = Classified as spam (score $spam_score)
+     condition = ${if <{$message_size}{80k}{1}{0}}
+     spam = nobody
+     
+# Finally accept all other messages that have
+# made it to this point
+accept
+------------------ */
+
+
+
+--------------------------------------------------------------
+2. Adding a cryptographic "scanning done" header
+--------------------------------------------------------------
+
+If you have a mail setup where the same message may pass  your
+server  twice  (redirects  from other  servers),  or  you have
+multiple mail  servers, you  may want  to make  sure that each
+message is only checked once, to save processing time. Here is
+how to do it:
+
+At the very beginning of your DATA ACL, put this:
+
+/* -----------------
+# Check our crytographic header. If it matches, accept
+# the message.
+accept condition = ${if eq {${hmac{md5}\
+                                  {mysecret}\
+                                  {$body_linecount}}}\
+                           {$h_X-Scan-Signature:} {1}{0}}
+------------------ */
+
+At the end, just before the final "accept" verb, put this:
+
+/* -----------------
+# Add the cryptographic header.
+warn message = X-Scan-Signature: ${hmac{md5}{mysecret}\
+                                       {$body_linecount}}
+------------------ */
+
+Notice the two "mysecret" strings? Replace them with your  own
+secret, and don't  tell anyone :)  The hash also  includes the
+number  of  lines  in the  message  body,  to protect  against
+message "modifications".
+
+
+--------------------------------------------------------------
+3. Marking Spam messages with extra headers and subject tag
+--------------------------------------------------------------
+
+Since  the  false positive  rate  with spam  scanning  is high
+compared to virus scanning, it  is wise to implement a  scheme
+with  two  thresholds,  where you  reject  messages  with high
+scores and just mark messages with lower scores. End users can
+then set up  filters in their  Mail User Agents  (MUAs). Since
+many  MUAs  can  not  filter  on  custom  headers,  it  can be
+necessary to put a "spam tag" in the subject line. Since it is
+not (yet)  possible to  remove headers  in Exims  DATA ACL, we
+must do this in a system  filter. Please see the Exim docs  on
+how to set up a system filter.
+
+The  following  example  will  unconditionally  put  two  spam
+information headers  in each  message, if  it is  smaller than
+eighty kilobytes:
+
+/* -----------------
+# Always put X-Spam-Score header in the message.
+# It looks like this:
+# X-Spam-Score: 6.6 (++++++)
+# When a MUA cannot match numbers, it can match for an
+# equivalent number of '+' signs.
+# The 'true' makes sure that the header is always put
+# in, no matter what the score.
+warn message = X-Spam-Score: $spam_score ($spam_bar)
+     condition = ${if <{$message_size}{80k}{1}{0}}
+     spam = nobody:true
+     
+# Always put X-Spam-Report header in the message.
+# This is a multiline header that informs the user
+# which tests a message has "hit", and how much a
+# test has contributed to the score.
+warn message = X-Spam-Report: $spam_report
+     condition = ${if <{$message_size}{80k}{1}{0}}
+     spam = nobody:true
+------------------ */
+
+For the subject  tag, we prepare  a new subject  header in the
+ACL, then  swap it  with the  original Subject  in the  system
+filter.
+
+In the DATA ACL, put this:
+/* -----------------
+warn message = X-New-Subject: *SPAM* $h_subject:
+     spam = nobody
+------------------ */
+
+In the system filter, put this:
+/* -----------------
+if "${if def:header_X-New-Subject: {there}}" is there
+then
+   headers remove Subject
+   headers add "Subject: $h_X-New-Subject:"
+   headers remove X-New-Subject
+endif
+------------------ */
+
+
+--------------------------------------------------------------
+4. Defining multiple spam thresholds with different actions
+--------------------------------------------------------------
+If you want  to mark messages  if they exceed  your threshold,
+but also  have a  higher "cutoff"  threshold where  you reject
+messages, use the example above, plus this part:
+
+/* -----------------
+deny message = Spam score too high ($spam_score)
+     condition = ${if <{$message_size}{80k}{1}{0}}
+     spam = nobody:true
+     condition = ${if >{$spam_score_int}{100}{1}{0}}
+------------------ */
+
+The last condition is only true if the spam score exceeds 10.0
+points  (Keep in  mind that  $spam_score_int is  the messages
+score multiplied by ten).
+
+
+
+--------------------------------------------------------------
+5. Redirect infected or spam messages to special accounts
+--------------------------------------------------------------
+Sometimes it is desirable not to reject messages, but to  stop
+them for inspection, and then decide wether to delete,  bounce
+or pass them.
+
+There are multiple ways to  achieve this. The simplest way  is
+to freeze suspicious  messages, and then  thaw or bounce  them
+after a review. Here is a simple example that will freeze spam
+suspicious messages when they exceed the SA threshold:
+
+/* -----------------
+warn log_message = frozen by spam scanner, score $spam_score
+     spam = nobody
+     control = freeze
+------------------ */
+
+Another  way is  to redirect  suspicious messages  to special
+postmaster accounts, where they can be reviewed. This involves
+setting up a router for these special accounts that acts on  a
+header set in the DATA ACL.
+
+This is the DATA ACL entry:
+
+/* -----------------
+warn message = X-Redirect-To: spambox@mycompany.com
+     spam = nobody
+------------------ */
+
+This puts the target address in a special header, which can in
+turn be read with this router:
+
+/* -----------------
+scan_redirect:
+     driver = redirect
+     condition = ${if def:h_X-Redirect-To: {1}{0}}
+     headers_add = X-Original-Recipient: $local_part@$domain
+     data = $h_X-Redirect-To:
+     headers_remove = X-Redirect-To
+     redirect_router = my_second_router
+------------------ */
+
+This router should  probably be your  very first one,  and you
+need to  edit the  last line  (redirect_router =  ) to replace
+"my_second_router"  with  the  name  of  your  original  first
+router. Note that the  original message recipient is  saved in
+the  "X-Original-Recipient"  header,  and  the   X-Redirect-To
+header line is removed.
+
+
+--------------------------------------------------------------
+6. Having multiple content scanning profiles for several
+   users or domains.
+--------------------------------------------------------------
+This is one of the most often asked questions, and it also has
+the most complicated  answer. To understand  the difficulties,
+you should first remember that the exiscan facilities are  run
+in the DATA  ACL. This ACL  is called ONCE  per message, after
+the  sending server  has transmitted  the end-of-data  marker.
+This gives  us the  very cool  possibility to  reject unwanted
+messages with a 5xx error  code in response. The big  drawback
+is that a  message can have  multiple recipients, and  you can
+only  reject  or  accept a  message  for  ALL recipients,  not
+individual ones.
+
+I will first sum up the possible solutions to this dilemma:
+
+  a. Make sure that  each incoming message  can have only  one
+     envelope  recipient. This  is brutal,  but effective  and
+     reliably solves  the problem  on your  end. :)  Drawback:
+     Incoming mail to multiple recipients is slowed down.  The
+     exact time depends on the retry strategies of the sending
+     hosts.
+  
+  b. Offer a limited number of "profiles" that your  customers
+     can  subscribe  to.  Then, similar  to  a.),  only accept
+     recipients with the same profile in a single "batch", and
+     defer the others.  This does improve  on the drawback  of
+     a.) a bit.
+  
+  c. Do scanning as  usual, but never  reject messages in  the
+     DATA ACL.  Instead put  appropriate information  in extra
+     headers and query those  in routers or transports  later.
+     Drawback: You'll have to send bounces yourself, and  your
+     queue will fill up with frozen bounces. Advantage:  clean
+     solution, protocol-wise.
+  
+As you see, you can't have your cake and eat it too. Now  lets
+get into the details of each possible solution.
+
+a.) Making sure  each incoming  message that  will be  scanned
+    only has one recipient.
+    
+    To use this scheme, you must make sure that you do not use
+    it on  your +relay_from_hosts  and authenticated  senders.
+    Both of  these may  be MUAs  who cannot  cope with  such a
+    thing.
+    
+    Here is a RCPT ACL that implements the behaviour
+    (shortened, do not copy 1:1!):
+    
+    /* ------------
+    acl_check_rcpt:
+
+      # accept local, relay-allowed
+      # and authenticated sources
+      
+      accept  hosts         = :
+      deny    local_parts   = ^.*[@%!/|]
+      accept  hosts         = 127.0.0.1:+relay_from_hosts
+      accept  authenticated = *
+    
+      # the following treat non-local,
+      # non-authenticated sources
+      
+      defer   message       = only one recipient at a time
+              condition     = ${if def:acl_m0 {1}{0}}
+    
+      # [ .. ]
+      # put RBLs etc. here
+      # [ .. ]
+      
+      accept  domains       = +local_domains
+              endpass
+              message       = unknown user
+              verify        = recipient
+              set acl_m0    = $local_part@$domain
+    
+      accept  domains       = +relay_to_domains
+              endpass
+              message       = unrouteable address
+              verify        = recipient
+              set acl_m0    = $domain
+    
+      deny    message       = relay not permitted
+    ------------ */
+    
+    The lines which contain acl_m0 are the important ones. The
+    $acl_m0   variable   gets  set   when   a  remote   server
+    successfully sends one RCPT. Subsequent RCPT commands  are
+    deferred if this variable is set. The $acl_m0 variable now
+    contains the single recipient domain, which you can use in
+    the DATA ACL to determine the scanning profile. 
+    
+    This scheme is only  recommended for small servers  with a
+    low number of possible recipients, where recipients do not
+    belong to  the same  organization. An  example would  be a
+    multiuser shell server.
+    
+
+b.) Having  several  scanning  profiles  that  "customers" can
+    choose from.
+    
+    Suppose you want to  offer three profiles. Lets  call them
+    "reject-aggressive",   "reject-conservative",   and  "warn
+    -only". Customers can select one of the profiles for  each
+    of their domains. So you end up with a mapping like this:
+    
+    domain-a.com:   reject-aggressive
+    domain-b.org:   warn-only
+    domain-c.net:   reject-aggressive
+    domain-d.com:   reject-conservative
+    [ .. ]
+    
+    Suppose you put that in a file called /etc/exim/scanprefs
+    
+    Now we make a scheme similar to a.), but we do allow  more
+    than one recipient if they have the same scanning  profile
+    than the first recipient.
+    
+    Here is a RCPT ACL that implements the behaviour
+    (shortened, do not copy 1:1!):
+    
+    /* ------------
+    acl_check_rcpt:
+
+      # accept local, relay-allowed and authenticated sources
+      
+      accept  hosts         = :
+      deny    local_parts   = ^.*[@%!/|]
+      accept  hosts         = 127.0.0.1:+relay_from_hosts
+      accept  authenticated = *
+    
+      # the following treat non-local, non-authenticated sources
+      
+      defer   message       = try this address in the next batch
+              condition     = ${if eq {${acl_m0}}\
+                              {${lookup{$domain}\
+                              lsearch{/etc/exim/scanprefs}}}\
+                              {0}{1}}
+    
+      # [ .. ]
+      # put RBLs etc. here
+      # [ .. ]
+      
+      accept  domains       = +local_domains
+              endpass
+              message       = unknown user
+              verify        = recipient
+              set acl_m0    = $local_part@$domain
+    
+      accept  domains       = +relay_to_domains
+              endpass
+              message       = unrouteable address
+              verify        = recipient
+              set acl_m0    = ${lookup{$domain}\
+                              lsearch{/etc/exim/scanprefs}}
+    
+      deny    message       = relay not permitted
+    ------------ */
+    
+    Now a recipient address  get deferred if its  scan profile
+    does  not match  the current  batch profile.  The $acl_m0
+    variable contains  the name  of the  profile, that  can be
+    used for processing in the DATA ACL.
+    
+    This scheme works  pretty well if  you keep the  number of
+    possible   profiles   low,   since   that   will   prevent
+    fragmentation of RCPT blocks.
+    
+    
+c.) Classic  content  scanning  without  the  possibility   of
+    rejects after DATA.
+    
+    This emulates  the "classic"  content scanning  in routers
+    and transports.  The difference  is that  we still  do the
+    scan in the DATA ACL, but put the outcome of each facility
+    in message headers, that  can the be evaluated  in special
+    routers, individually for each recipient.
+    
+    A special approach can  be taken for spam  scanning, since
+    the $spam_score_int variable is also available in  routers
+    and transports (it  gets written to  the spool files),  so
+    you do not need to  put that information in a  header, but
+    rather act on $spam_score_int directly.
+    
diff -urN exim-4.44-orig/doc/exiscan-acl-spec.txt exim-4.44/doc/exiscan-acl-spec.txt
--- exim-4.44-orig/doc/exiscan-acl-spec.txt	Thu Jan  1 01:00:00 1970
+++ exim-4.44/doc/exiscan-acl-spec.txt	Fri Jan 14 20:35:02 2005
@@ -0,0 +1,1282 @@
+--------------------------------------------------------------
+The exiscan-acl patch for exim4 - Documentation
+--------------------------------------------------------------
+(c) Tom Kistner <tom@duncanthrax.net> 2003-????
+License: GPL
+
+The exiscan-acl patch adds  content scanning to the  exim4 ACL
+system. It supports the following scanning features:
+
+ - MIME ACL that is called for all MIME parts in
+   incoming MIME messages.
+ - Antivirus using 3rd party scanners.
+ - Anti-spam using SpamAssassin.
+ - Anti-spam using Brightmail Antispam.
+ - SPF support via the libspf2 library
+ - SRS support via the libsrs_alt library
+ - Regular expression match against headers, bodies, raw
+   MIME parts and decoded MIME parts.
+ 
+These features are hooked  into exim by extending  exim's  ACL
+system. The patch adds expansion variables and ACL conditions.
+These conditions are designed to be used in the  acl_smtp_data
+ACL. It is  run when the  sending host has  completed the DATA
+phase and is waiting for our final response to his end-of-data
+marker.  This  allows   us  to   reject  messages   containing
+unwanted content at that stage.
+
+Support for Brightmail AntiSpam requires special  compile-time
+flags. Please refer to chapter 7 for details.
+
+The   default   exim   configure   file   contains   commented
+configuration examples for some features of exiscan-acl.
+
+
+0. Overall concept / Overview
+--------------------------------------------------------------
+
+The   exiscan-acl   patch    extends Exims with  mechanisms to
+deal with the  message body content.  Most of these  additions
+affect the ACL system. The exiscan patch adds
+
+- A new ACL, called 'acl_smtp_mime' (Please see detailed
+  chapter on this one below).
+- ACL conditions and modifiers
+  o malware (attach 3rd party virus/malware scanner)
+  o spam (attach SpamAssassin)
+  o regex (match regex against message, linewise)
+  o decode (decode MIME part to disk)
+  o mime_regex (match regex against decoded MIME part)
+  o control = fakereject (reject but really accept a message)
+  o spf (do SPF checks)
+- expansion variables
+  (see chapters below for names and explanations)
+- configuration options in section 1 of Exim's configure file.
+  o av_scanner (type and options of the AV scanner)
+  o spamd_address (network address / socket of spamd daemon).
+
+All  facilites work  on a  MBOX copy  of the  message that  is
+temporarily spooled up in a file called:
+
+  <spool_directory>/scan/<message_id>/<message_id>.eml
+
+The .eml extension is a  friendly hint to virus scanners  that
+they can expect an  MBOX-like structure inside that  file. The
+file is only spooled up once, when the first exiscan  facility
+is  called. Subsequent  calls to  exiscan conditions will just
+open  the file again.   The directory is  recursively  removed
+when the  acl_smtp_data has  finished running.  When  the MIME
+ACL decodes files, they will  be put into that same  folder by
+default.
+
+
+1. The acl_smtp_mime MIME ACL
+--------------------------------------------------------------
+
+Note: if you are not familiar with exims ACL system, please go
+read the documentation on it, otherwise this chapter will  not
+make much sense to you.
+
+Here are the facts on acl_smtp_mime:
+
+  - It  is  called  once  for each  MIME  part  of  a message,
+    including  multipart  types,  in  the  sequence  of  their
+    position in the message.
+  
+  - It is called just before the acl_smtp_data ACL. They share
+    a result code  (the one assed  to the remote  system after
+    DATA).  When  a  call  to  acl_smtp_mime  does  not  yield
+    "accept",  ACL processing  is aborted  and the  respective
+    result code is sent to the remote mailer. This means  that
+    the acl_smtp_data is NOT called any more.
+    
+  - It is ONLY called if the message has a MIME-Version header.
+
+  - MIME parts will NOT be dumped to disk by default, you have
+    to call  the "decode"  condition to  do that  (see further
+    below).
+
+  - For  RFC822 attachments  (these are  messages attached  to
+    messages,  with   a   content-type   of 'message/rfc822'),
+    the ACL   is  called   again  in    the  same   manner  as
+    for  the "primary" message, only  that the $mime_is_rfc822
+    expansion variable  is  set  (see below).  These  messages
+    are always  decoded  to  disk  before  being  checked, but
+    the files  are unlinked once the check is done.
+    
+To activate acl_smtp_mime, you need to add assign it the  name
+of an  ACL entry  in section  1 of  the config  file, and then
+write that ACL in the ACL section, like:
+
+  /* ---------------
+  
+  # -- section 1 ----
+  [ ... ]
+  acl_smtp_mime = my_mime_acl
+  [ ... ]
+  
+  # -- acl section ----
+  begin acl
+  
+  [ ... ]
+  
+  my_mime_acl:
+  
+    < ACL logic >
+    
+  [ ... ]
+  
+  ---------------- */
+
+The following list describes all expansion variables that are
+available in the MIME ACL:
+
+  $mime_content_type
+  ------------------
+  A very important variable. If  the MIME part has a  "Content
+  -Type:"  header,  this  variable  will  contain  its  value,
+  lowercased,  and  WITHOUT   any  options  (like   "name"  or
+  "charset", see below for  these). Here are some  examples of
+  popular MIME types, as they may appear in this variable:
+  
+  text/plain
+  text/html
+  application/octet-stream
+  image/jpeg
+  audio/midi
+
+  If  the  MIME  part  has  no  "Content-Type:"  header,  this
+  variable is the empty string.
+
+
+  $mime_filename
+  --------------
+  Another important variable, possibly the most important one.
+  It contains a  proposed filename for  an attachment, if  one
+  was  found  in   either  the  "Content-Type:"   or  "Content
+  -Disposition"   headers.   The  filename   will   be RFC2047
+  decoded,  however NO  additional sanity checks are done. See
+  instructions on "decode" further  below. If no filename  was
+  found, this variable is the  empty string.
+  
+  
+  $mime_charset
+  -------------
+  Contains the  charset identifier,  if one  was found  in the
+  "Content-Type:" header. Examples for charset identifiers are
+  
+  us-ascii
+  gb2312 (Chinese)
+  iso-8859-1
+  
+  Please note that this value  will NOT be normalized, so  you
+  should do matches case-insensitively.
+  
+  
+  $mime_boundary
+  --------------
+  If the current part is a multipart (see  $mime_is_multipart)
+  below, it  SHOULD have  a boundary  string. It  is stored in
+  this variable. If the current part has no boundary parameter
+  in the  "Content-Type:" header,  this variable  contains the
+  empty string. 
+  
+  
+  $mime_content_disposition
+  -------------------------
+  Contains   the   normalized   content   of   the    "Content
+  -Disposition:"   header.   You  can   expect   strings  like
+  "attachment" or "inline" here.
+  
+  
+  $mime_content_transfer_encoding
+  -------------------------------
+  Contains   the   normalized   content   of   the    "Content
+  -transfer-encoding:"   header. This  is a symbolic  name for
+  an encoding  type. Typical  values are  "base64" and "quoted
+  -printable".
+
+
+  $mime_content_id
+  ----------------
+  Contains   the   normalized   content   of   the    "Content
+  -ID:"   header.  This is  a unique  ID that  can be  used to
+  reference a part from another part.
+  
+  
+  $mime_content_description
+  -------------------------
+  Contains   the   normalized   content   of   the    "Content
+  -Description:"    header.  It can  contain  a human-readable
+  description of the parts content. Some implementations  will
+  repeat  the  filename  for attachments  here,  but  they are
+  usually only used for display purposes.
+  
+  
+  $mime_part_count
+  ----------------
+  This is  a counter  that is  raised for  each processed MIME
+  part. It starts  at zero for  the very first  part (which is
+  usually a multipart). The  counter is per-message, so  it is
+  reset    when    processing    RFC822    attachments    (see
+  $mime_is_rfc822). The counter stays set after  acl_smtp_mime
+  is complete, so you can use it in the DATA ACL to  determine
+  the  number  of  MIME  parts  of  a  message.  For  non-MIME
+  messages, this variable will contain the value -1.
+  
+  
+  $mime_is_multipart
+  ------------------
+  A  "helper"  flag   that  is  true  (1)  when  the   current
+  part   has   the   main   type  "multipart",    for  example
+  "multipart/alternative"    or     "multipart/mixed".   Since
+  multipart entities only serve as containers for other parts,
+  you may not want to carry out specific actions on them.
+ 
+ 
+  $mime_is_coverletter
+  --------------------
+  This flag attempts to differentiate the "cover letter" of an
+  e-mail from attached data.  It can be used to clamp down  on
+  "flashy"   or   unneccessarily   encoded   content   in  the
+  coverletter, while not restricting attachments at all.
+
+  It returns 1 (true) for a  MIME part believed to be part  of
+  the  coverletter,  0   (false)  for   an  attachment.     At
+  present,  the algorithm is as follows:
+
+  1. The outermost MIME part of a message always coverletter.
+  2. If a multipart/alternative or multipart/related MIME part
+     is coverletter, so are all MIME subparts within that
+     multipart.
+  3. If any other multipart is coverletter, the first subpart
+     is coverletter, the rest are attachments.
+  4. All parts contained within an attachment multipart are
+     attachments.
+
+  As an example, the following will ban "HTML mail" (including
+  that sent with alternative plain text), while allowing  HTML
+  files to be attached:
+  
+    /* ----------------------
+    deny message = HTML mail is not accepted here
+         condition = $mime_is_coverletter
+         condition = ${if eq{$mime_content_type}{text/html}{1}{0}}
+    ----------------------- */
+  
+  
+  $mime_is_rfc822
+  ---------------
+  This flag is true (1) if  the current part is NOT a  part of
+  the checked message itself, but part of an attached message.
+  Attached message decoding is fully recursive.
+
+
+  $mime_decoded_filename
+  ----------------------
+  This variable is only set after the "decode" condition  (see
+  below) has been successfully run. It contains the full  path
+  and file name of the file containing the decoded data.
+  
+  
+  $mime_content_size
+  ------------------
+  This variable is only set after the "decode" condition  (see
+  below) has been  successfully run. It  contains the size  of
+  the  decoded   part  in  kilobytes(!).  The  size  is always
+  rounded up  to full  kilobytes, so  only a  completely empty
+  part will have a mime_content_size of 0.
+
+
+The expansion variables only  reflect the content of  the MIME
+headers for each  part. To actually  decode the part  to disk,
+you can use the "decode" condition. The general syntax is
+
+decode = [/<PATH>/]<FILENAME>
+
+The right hand side is expanded before use. After expansion,
+the value can
+
+  - be '0' or 'false', in which case no decoding is done.
+  - be the string 'default'. In that case, the file will be
+    put in the temporary "default" directory
+    <spool_directory>/scan/<message_id>/
+    with a sequential file name, consisting of the message  id
+    and a sequence number. The full path and name is available
+    in $mime_decoded_filename after decoding.
+  - start  with  a slash.  If  the full  name  is an  existing
+    directory,  it  will  be used  as  a  replacement for  the
+    "default"  directory.  The  filename  will  then  also  be
+    sequentially assigned. If the name does not exist, it will
+    be used as the full path and file name.
+  - not  start with  a slash.  It will  then be  used as  the
+    filename, and the default path will be used.
+    
+You  can  easily decode  a  file with  its  original, proposed
+filename using "decode = $mime_filename". However, you  should
+keep in  mind that  $mime_filename might  contain anything. If
+you place files outside of the default path, they will not  be
+automatically unlinked.
+
+The  MIME  ACL  also  supports  the  regex=  and   mime_regex=
+conditions. You  can use  those to  match regular  expressions
+against raw  and decoded  MIME parts,  respectively. Read  the
+next section for more information on these conditions.
+
+
+
+2. Match message or MIME parts against regular expressions
+--------------------------------------------------------------
+
+The "regex" condition takes one or more regular expressions as
+arguments and  matches them   against the  full message  (when
+called in the DATA ACL) or a raw MIME part (when called in the
+MIME  ACL). The  "regex" condition  matches linewise,  with a
+maximum line length  of 32k characters.  That means you  can't
+have multiline matches with the "regex" condition.
+
+The  "mime_regex"  can only  be  called in  the  MIME ACL.  It
+matches up  to 32k  of decoded  content (the  whole content at
+once, not linewise). If the part has not been decoded with the
+"decode"  condition  earlier  in  the  ACL,  it   is   decoded
+automatically  when "mime_regex"  is  executed (using  default
+path and  filename values).   If the  decoded data   is larger
+than   32k,   only   the   first   32k   characters   will  be
+matched.
+
+The regular expressions are passed as a colon-separated  list.
+To include  a literal  colon, you  must double  it. Since  the
+whole right-hand  side string  is expanded  before being used,
+you must also escape dollar ($) signs with backslashes.
+
+Here is a simple example:
+
+/* ----------------------
+deny message = contains blacklisted regex ($regex_match_string)
+     regex = [Mm]ortgage : URGENT BUSINESS PROPOSAL
+----------------------- */
+
+The  conditions   returns  true    if  one   of  the   regular
+expressions  has matched.  The  $regex_match_string  expansion
+variable  is then  set up  and contains  the matching  regular
+expression.
+
+Warning: With large messages,  these conditions can be  fairly
+CPU-intensive.
+
+
+
+3. Antispam measures with SpamAssassin
+--------------------------------------------------------------
+
+The "spam" ACL  condition calls SpamAssassin's  "spamd" daemon
+to get a spam-score  and a  report for  the message.  You must
+first install     SpamAssassin.     You     can     get     it
+at http://www.spamassassin.org, or,   if you  have  a  working
+Perl installation, you can use CPAN by calling
+
+perl -MCPAN -e 'install Mail::SpamAssassin'
+
+SpamAssassin has its own  set of configuration files.  Please
+review its  documentation to  see how  you can  tweak it.  The
+default installation should work nicely, however.
+
+After having installed and configured SpamAssassin, start  the
+"spamd" daemon. By default, it  listens on 127.0.0.1, TCP port
+783. If you use another host  or port for spamd, you must  set
+the   spamd_address  option   in  Section   1  of   the  exim
+configuration as follows (example):
+
+spamd_address = 127.0.0.1 783
+
+As of version 2.60, spamd also supports communication over UNIX
+sockets. If you want to use these, supply spamd_address with
+an absolute file name instead of a address/port pair, like:
+
+spamd_address = /var/run/spamd_socket
+
+If you use the above mentioned default, you do NOT need to set
+this option.
+
+You  can   also  have   multiple  spamd   servers  to  improve
+scalability. These can also reside on other hardware reachable
+over the network. To specify multiple spamd servers, just  put
+multiple  address/port  pairs  in  the  spamd_address  option,
+separated with colons:
+
+spamd_address = 192.168.2.10 783 : 192.168.2.11 783 : 192.168.2.12 783
+
+Up to  32 spamd  servers are  supported. The  servers will  be
+queried in a random fashion.   When a server fails to  respond
+to the  connection attempt,   all  other  servers  are   tried
+until  one succeeds.   If  no  server   responds,  the  "spam"
+condition will  return DEFER.  IMPORTANT:  It  is not possible
+to    use  the    UNIX   socket    connection   method    with
+multiple  spamd servers.
+
+To use the  antispam facility, put  the "spam" condition  in a
+DATA ACL block. Here is a very simple example:
+
+/* ---------------
+deny message = This message was classified as SPAM
+        spam = joe
+---------------- */
+
+On the right-hand side of the spam condition, you can put  the
+username that SpamAssassin should scan for. That allows you to
+use per-domain or  per-user antispam profiles.  The right-hand
+side is expanded before being used, so you can put lookups  or
+conditions there. When the right-hand side evaluates to "0" or
+"false", no scanning will be done and the condition will  fail
+immediately.
+
+If you do not want to  scan for a particular user, but  rather
+use the SpamAssassin system-wide default profile, you can scan
+for an unknown user, or simply use "nobody".
+
+The  "spam"  condition  will  return  true  if  the  threshold
+specified in the user's SpamAssassin profile has been  matched
+or exceeded. If  you want to  use the spam  condition for its
+side effects (see the variables below), you can make it always
+return "true" by appending ":true" to the username. 
+
+When the condition is run, it sets up the following  expansion
+variables:
+
+  $spam_score       The spam score of the message, for example
+                    "3.4"  or  "30.5".  This  is  useful   for
+                    inclusion in log or reject messages.
+                  
+  $spam_score_int   The spam score of the message,  multiplied
+                    by ten, as  an integer value.  For example
+                    "34" or "305". This is useful for  numeric
+                    comparisons  in  conditions.  See  further
+                    below for a more complicated example. This
+                    variable is special,  since it is  written
+                    to  the  spool  file, so  it  can  be used
+                    during the  whole life  of the  message on
+                    your exim system, even in routers
+                    or transports.
+                    
+  $spam_bar         A string consisting of a number of '+'  or
+                    '-'    characters,    representing     the
+                    spam_score value.  A spam  score of  "4.4"
+                    would have a  spam_bar of '++++'.  This is
+                    useful for  inclusion in  warning headers,
+                    since MUAs can match on such strings.
+  
+  $spam_report      A  multiline  text  table,  containing the
+                    full SpamAssassin report for the  message.
+                    Useful for inclusion in headers or  reject
+                    messages.
+            
+The spam condition  caches its results.  If you call  it again
+with the same  user name, it  will not really  scan again, but
+rather return the same values as before.
+
+The spam  condition will  return DEFER  if there  is any error
+while running the message through SpamAssassin. If you want to
+treat DEFER  as FAIL  (to pass  on to  the next  ACL statement
+block), append '/defer_ok' to the right hand side of the  spam
+condition, like this:
+
+/* ---------------
+deny message = This message was classified as SPAM
+        spam = joe/defer_ok
+---------------- */
+
+This will  cause messages  to be  accepted even  if there is a
+problem with spamd.
+
+Finally, here is  a commented example  on how to  use the spam
+condition:
+
+/* ----------------
+# put headers in all messages (no matter if spam or not)
+warn  message = X-Spam-Score: $spam_score ($spam_bar)
+      spam = nobody:true
+warn  message = X-Spam-Report: $spam_report
+      spam = nobody:true
+      
+# add second subject line with *SPAM* marker when message
+# is over threshold
+warn  message = Subject: *SPAM* $h_Subject
+      spam = nobody
+
+# reject spam at high scores (> 12)
+deny   message = This message scored $spam_score spam points.
+       spam = nobody:true
+       condition = ${if >{$spam_score_int}{120}{1}{0}}
+----------------- */
+
+
+
+4. The "malware" facility
+   Scan messages for viruses using an external virus scanner
+--------------------------------------------------------------
+
+This facility lets you connect virus scanner software to exim.
+It supports a "generic"  interface to scanners called  via the
+shell,  and  specialized interfaces  for  "daemon" type  virus
+scanners, who are resident in memory and thus are much faster.
+
+To use this facility, you MUST set the "av_scanner" option  in
+section 1 of  the exim config  file. It specifies  the scanner
+type to use, and any  additional options it needs to  run. The
+basic syntax is as follows:
+
+  av_scanner = <scanner-type>:<option1>:<option2>:[...]
+  
+The following scanner-types are supported in this release:
+
+  sophie      Sophie  is a  daemon that  uses Sophos'  libsavi
+              library to scan for viruses. You can get  Sophie
+              at http://www.vanja.com/tools/sophie/. The  only
+              option for this scanner type is the path to  the
+              UNIX  socket   that  Sophie   uses  for   client
+              communication.    The     default    path     is
+              /var/run/sophie, so if  you are using  this, you
+              can omit the option. Example:
+              
+              av_scanner = sophie:/tmp/sophie
+
+  aveserver   This is the scanner daemon of Kaspersky  Version
+              5.   You   can   get   a   trial   version    at
+              http://www.kaspersky.com.   This   scanner  type
+              takes  one  option,  which is  the  path  to the
+              daemon's   UNIX    socket.   The    default   is
+              "/var/run/aveserver". Example:
+              
+              av_scanner = aveserver:/var/run/aveserver
+
+
+  kavdaemon   This is the scanner daemon of Kaspersky  Version
+              4.  This  version of  the  Kaspersky scanner  is
+              outdated.   Please   upgrade   (see  "aveserver"
+              above).  This  scanner  type  takes  one option,
+              which is the path  to the daemon's UNIX  socket.
+              The default is "/var/run/AvpCtl". Example:
+              
+              av_scanner = kavdaemon:/opt/AVP/AvpCtl
+              
+              
+  clamd       Another daemon type scanner, this one is GPL and
+              free. Get  it at  http://clamav.elektrapro.com/.
+              Clamd does not  seem to unpack  MIME containers,
+              so it is recommended to use the demime  facility
+              with it.  It takes  one option:  either the path
+              and  name   of  a   UNIX  socket   file,  or   a
+              hostname/port  pair,  separated  by  space.   If
+              unset, the default is "/tmp/clamd". Example:
+              
+              av_scanner = clamd:192.168.2.100 1234
+              or
+              av_scanner = clamd:/opt/clamd/socket
+  
+  
+  fsecure     This  is  for  the  F-Secure  (www.f-secure.com)
+              scanner. It takes   one argument  which  is  the
+              path   to  a   UNIX  socket.   If  omitted,   it
+              defaults to
+              
+              /var/run/.fsav
+              
+              Example:
+              
+              av_scanner = fsecure:/path/to/.fsav
+       
+              Thanks  to   Johan  Thelmen   <jth@home.se>  for
+              contributing the code for this scanner.     
+                
+                                       
+  drweb       This one is for the DrWeb (http://www.sald.com/)
+              daemon.  It takes  one argument,  either a  full
+              path to a UNIX socket, or an IP address and port
+              separated  by  whitespace.   If  you  omit   the
+              argument, the default
+              
+              /usr/local/drweb/run/drwebd.sock
+              
+              is used. Example:
+              
+              av_scanner = drweb:192.168.2.20 31337
+              or
+              av_scanner = drweb:/var/run/drwebd.sock
+              
+              Thanks  to  Alex  Miller  <asm@abbyy.com.ua> for
+              contributing the code for this scanner.              
+              
+              
+  mksd        Yet another daemon type scanner, aimed mainly at
+              Polish users, though some parts of documentation
+              are now avaliable in English.  You can get it at
+              http://linux.mks.com.pl/.  The only  option  for
+              this  scanner  type  is the  maximum  number  of
+              processes   used   simultaneously  to  scan  the
+              attachments, provided  that the  demime facility
+              is  employed  and also  mksd has been  run  with
+              at least  the same  number of  child  processes.
+              You can  safely  omit this  option,  the default
+              value is 1. Example:
+              
+              av_scanner = mksd:2
+
+
+  cmdline     This is the keyword for the generic command line
+              scanner  interface.  It can  be  used to  attach
+              virus scanners  that are  invoked on  the shell.
+              This scanner type takes 3 mantadory options:
+              
+              - full path and name of the scanner binary, with
+                all  command  line options  and  a placeholder
+                (%s) for the directory to scan.
+                
+              - A  regular  expression  to  match  against the
+                STDOUT and STDERR output of the virus scanner.
+                If the expression matches, a virus was  found.
+                You  must  make  absolutely  sure  that   this
+                expression only matches on "virus found". This
+                is called the "trigger" expression.
+                
+              - Another regular expression, containing exactly
+                ONE pair of braces,  to match the name  of the
+                virus found  in the  scanners output.  This is
+                called the "name" expression.
+                
+              Example:
+              
+              Sophos  Sweep reports  a virus  on a  line  like
+              this:
+
+              Virus 'W32/Magistr-B' found in file ./those.bat
+
+              For the  "trigger" expression,  we just  use the
+              "found" word. For the "name" expression, we want
+              to get the W32/Magistr-B string, so we can match
+              for  the single  quotes left  and right  of it,
+              resulting in the regex '(.*)' (WITH the quotes!)
+
+              Altogether,   this   makes   the   configuration
+              setting:
+              
+              av_scanner = cmdline:\
+              /path/to/sweep -all -rec -archive %s:\
+              found:'(.+)'
+
+When av_scanner  is correcly  set, you  can use  the "malware"
+condition in the  DATA ACL. The  condition takes a  right-hand
+argument that is expanded before use. It can then be one of
+
+  - "true", "*", or "1", in which case the message is  scanned
+    for viruses.  The condition  will succeed  if a  virus was
+    found, or fail otherwise. This is the recommended usage.
+    
+  - "false" or "0", in which case no scanning is done and  the
+    condition will fail immediately.
+    
+  - a regular expression, in which case the message is scanned
+    for viruses. The condition  will succeed if a  virus found
+    found and  its name  matches the  regular expression. This
+    allows you  to take  special actions  on certain  types of
+    viruses.
+    
+When a  virus was  found, the  condition sets  up an expansion
+variable called  $malware_name that  contains the  name of the
+virus found. You  should use it  in a "message"  modifier that
+contains the error returned to the sender.
+
+The malware condition caches its  results, so when you use  it
+multiple times,  the actual  scanning process  is only carried
+out once.
+
+If your virus scanner  cannot unpack MIME and  TNEF containers
+itself,  you  should use  the  demime condition  prior  to the
+malware condition.
+
+Here is a simple example:
+
+/* ----------------------
+deny message = This message contains malware ($malware_name)
+     demime = *
+     malware = *
+---------------------- */
+
+Like with the "spam" condition, you can append '/defer_ok'  to
+the malware condition  to accept messages  even if there  is a
+problem with the virus scanner, like this:
+
+/* ----------------------
+deny message = This message contains malware ($malware_name)
+     demime = *
+     malware = */defer_ok
+---------------------- */
+
+ADVANCED TIP: When the av_scanner option starts with a  dollar
+($) sign, it is expanded before being used. This is useful  if
+you want to use multiple scanners. You can then set
+
+/* ----------------------
+av_scanner = $acl_m0
+---------------------- */
+
+and  use  these  ACL  blocks  to  scan  with  both  sophie and
+aveserver scanners:
+
+/* ----------------------
+deny message = This message contains malware ($malware_name)
+     set acl_m0 = sophie
+     malware = *
+     
+deny message = This message contains malware ($malware_name)
+     set acl_m0 = aveserver
+     malware = *
+---------------------- */
+
+However, when  av_scanner is  expanded, the  result caching of
+the malware condition is  not used, so each  malware condition
+call results in a new scan of the message.
+
+
+
+5. The "demime" facility
+   MIME unpacking, sanity checking and file extension blocking
+--------------------------------------------------------------
+
+* This facility provides a simpler interface to MIME decoding
+* than the MIME ACL functionality. It is kept in exiscan for
+* backward compatability.
+
+The demime facility unpacks MIME containers in the message. It
+detects  errors  in  MIME   containers  and  can  match   file
+extensions found  in the  message against  a list.  Using this
+facility will produce additional  files in the temporary  scan
+directory that contain the unpacked MIME parts of the message.
+If you  do antivirus  scanning, it  is recommened  to use  the
+"demime" condition before the antivirus ("malware") condition.
+
+The condition name of this facility is "demime". On the  right
+hand  side,  you  can  pass  a  colon-separated  list  of file
+extensions that it  should match against.  If one of  the file
+extensions  is  found,  the  condition  will  return  "OK" (or
+"true"), otherwise it will return FAIL (or "false"). If  there
+was any TEMPORARY error while demimeing (mostly "disk  full"),
+the  condition  will return  DEFER,  and the  message  will be
+temporarily rejected.
+
+The right-hand side gets "expanded" before being treated as  a
+list, so  you can  have conditions  and lookups  there. If  it
+expands  to  an  empty  string,  "false",  or  zero  ("0"), no
+demimeing is done and the conditions returns FALSE.
+
+A short example:
+
+/* ------------
+deny message = Found blacklisted file attachment
+     demime = vbs:com:bat:pif:prf:lnk
+--------------- */
+
+When the condition is run, it sets up the following  expansion
+variables:
+
+ $demime_errorlevel   When  an error  was detected  in a  MIME
+                      container, this   variable contains  the
+                      "severity"  of the error,  as an integer
+                      number.  The   higher  the   value,  the
+                      more  severe    the  error.    If   this
+                      variable is unset or zero, no error  has
+                      occured.
+
+ $demime_reason       When $demime_errorlevel is greater  than
+                      zero,  this  variable  contains  a human
+                      -readable  text  string  describing  the
+                      MIME error that occured.
+                          
+ $found_extension     When  the  "demime"  condition   returns
+                      "true", this variable contains the  file
+                      extension it has found.
+                          
+Both $demime_errorlevel  and $demime_reason  are set  with the
+first call of the "demime"  condition, and are not changed  on
+subsequent calls.
+
+If do not  want to check  for any file  extensions, but rather
+use  the  demime  facility  for  unpacking  or  error checking
+purposes, just pass "*" as the right-hand side value.
+
+Here is a more elaborate example on how to use this facility:
+
+/* -----------------
+# Reject messages with serious MIME container errors
+deny  message = Found MIME error ($demime_reason).
+      demime = *
+      condition = ${if >{$demime_errorlevel}{2}{1}{0}}
+
+# Reject known virus spreading file extensions.
+# Accepting these is pretty much braindead.
+deny  message = contains $found_extension file (blacklisted).
+      demime = com:vbs:bat:pif:scr
+
+# Freeze .exe and .doc files. Postmaster can
+# examine them and eventually thaw them up.
+deny  log_message = Another $found_extension file.
+      demime = exe:doc
+      control = freeze
+--------------------- */
+
+
+
+6. The "fakereject" control statement
+   Reject a message while really accepting it.
+--------------------------------------------------------------
+
+When you put "control =  fakereject" in an ACL statement,  the
+following  will  happen:  If  exim  would  have  accepted  the
+message, it will tell the remote host that it did not, with  a
+message of:
+
+550-FAKE_REJECT id=xxxxxx-xxxxxx-xx
+550-Your message has been rejected but is being kept for evaluation.
+550 If it was a legit message, it may still be delivered to the target recipient(s).
+
+But exim will go on to treat the message as if it had accepted
+it. This should be used with extreme caution, please look into
+the examples document for possible usage.
+
+
+
+7. Brighmail AntiSpam (BMI) suppport
+--------------------------------------------------------------
+
+Brightmail  AntiSpam  is  a  commercial  package.  Please  see
+http://www.brightmail.com    for    more    information     on
+the product. For  the sake of  clarity, we'll refer  to it  as
+"BMI" from now on.
+
+
+0) BMI concept and implementation overview
+
+In  contrast  to   how  spam-scanning  with   SpamAssassin  is
+implemented  in  exiscan-acl,  BMI  is  more  suited  for  per
+-recipient  scanning of  messages. However,  each messages  is
+scanned  only  once,  but  multiple  "verdicts"  for  multiple
+recipients can be  returned from the  BMI server. The  exiscan
+implementation  passes  the  message to  the  BMI  server just
+before accepting it.  It then adds  the retrieved verdicts  to
+the messages header file in the spool. These verdicts can then
+be  queried  in  routers,  where  operation  is  per-recipient
+instead  of per-message.  To use  BMI, you  need to  take the
+following steps:
+
+  1) Compile Exim with BMI support
+  2) Set up main BMI options (top section of exim config file)
+  3) Set up ACL control statement (ACL section of the config
+     file)
+  4) Set up your routers to use BMI verdicts (routers section
+     of the config file).
+  5) (Optional) Set up per-recipient opt-in information.
+
+These four steps are explained in more details below. 
+
+1) Adding support for BMI at compile time
+
+  To compile with BMI support,  you need to link Exim  against
+  the   Brighmail  client   SDK,  consisting   of  a   library
+  (libbmiclient_single.so)  and  a  header  file  (bmi_api.h).
+  You'll also need to explicitly set a flag in the Makefile to
+  include BMI support in the Exim binary. Both can be achieved
+  with  these 2 lines in Local/Makefile:
+  
+  CFLAGS=-DBRIGHTMAIL -I/path/to/the/dir/with/the/includefile
+  EXTRALIBS_EXIM=-L/path/to/the/dir/with/the/library -lbmiclient_single
+  
+  If  you use  other CFLAGS  or EXTRALIBS_EXIM  settings then
+  merge the content of these lines with them.
+
+  Note for BMI6.x users: You'll also have to add -lxml2_single
+  to the EXTRALIBS_EXIM line. Users of 5.5x do not need to  do
+  this.
+  
+  You    should     also    include     the    location     of
+  libbmiclient_single.so in your dynamic linker  configuration
+  file   (usually   /etc/ld.so.conf)   and   run    "ldconfig"
+  afterwards, or  else the  produced Exim  binary will  not be
+  able to find the library file.
+
+
+2) Setting up BMI support in the exim main configuration
+
+  To enable BMI  support in the  main exim configuration,  you
+  should set the path to the main BMI configuration file  with
+  the "bmi_config_file" option, like this:
+  
+  bmi_config_file = /opt/brightmail/etc/brightmail.cfg
+  
+  This must go into section 1 of exims configuration file (You
+  can  put it  right on  top). If  you omit  this option,  it
+  defaults to /opt/brightmail/etc/brightmail.cfg.
+
+  Note for BMI6.x users: This  file is in XML format  in V6.xx
+  and its  name is  /opt/brightmail/etc/bmiconfig.xml. So  BMI
+  6.x users MUST set the bmi_config_file option.
+  
+
+3) Set up ACL control statement
+
+  To  optimize performance,  it makes  sense only  to process
+  messages coming from remote, untrusted sources with the  BMI
+  server.  To set  up a  messages for  processing by  the BMI
+  server, you MUST set the "bmi_run" control statement in  any
+  ACL for an incoming message.  You will typically do this  in
+  an "accept"  block in  the "acl_check_rcpt"  ACL. You should
+  use the "accept" block(s)  that accept messages from  remote
+  servers for your own domain(s). Here is an example that uses
+  the "accept" blocks from exims default configuration file:
+  
+
+  accept  domains       = +local_domains
+          endpass
+          verify        = recipient
+          control       = bmi_run
+
+  accept  domains       = +relay_to_domains
+          endpass
+          verify        = recipient
+          control       = bmi_run
+  
+  If bmi_run  is not  set in  any ACL  during reception of the
+  message, it will NOT be passed to the BMI server.
+
+
+4) Setting up routers to use BMI verdicts
+
+  When a message has been  run through the BMI server,  one or
+  more "verdicts" are  present. Different recipients  can have
+  different verdicts. Each  recipient is treated  individually
+  during routing, so you  can query the verdicts  by recipient
+  at  that stage.  From Exims  view, a  verdict can  have the
+  following outcomes:
+  
+  o deliver the message normally
+  o deliver the message to an alternate location
+  o do not deliver the message
+  
+  To query  the verdict  for a  recipient, the  implementation
+  offers the following tools:
+  
+  
+  - Boolean router  preconditions. These  can be  used in  any
+    router. For a simple  implementation of BMI, these  may be
+    all  that  you  need.  The  following  preconditions   are
+    available:
+    
+    o bmi_deliver_default
+    
+      This  precondition  is  TRUE  if  the  verdict  for  the
+      recipient is  to deliver  the message  normally. If  the
+      message has not been  processed by the BMI  server, this
+      variable defaults to TRUE.
+      
+    o bmi_deliver_alternate
+    
+      This  precondition  is  TRUE  if  the  verdict  for  the
+      recipient  is to  deliver the  message to  an alternate
+      location.  You  can  get the  location  string  from the
+      $bmi_alt_location expansion variable if you need it. See
+      further below. If the message has not been processed  by
+      the BMI server, this variable defaults to FALSE.
+      
+    o bmi_dont_deliver
+    
+      This  precondition  is  TRUE  if  the  verdict  for  the
+      recipient  is  NOT  to   deliver  the  message  to   the
+      recipient. You will typically use this precondition in a
+      top-level blackhole router, like this:
+      
+        # don't deliver messages handled by the BMI server
+        bmi_blackhole:
+          driver = redirect
+          bmi_dont_deliver
+          data = :blackhole:
+      
+      This router should be on top of all others, so  messages
+      that should not be delivered do not reach other  routers
+      at all. If   the  message  has  not  been  processed  by
+      the  BMI server, this variable defaults to FALSE.
+      
+      
+  - A list router  precondition to query  if rules "fired"  on
+    the message for the recipient. Its name is "bmi_rule". You
+    use  it  by  passing it  a  colon-separated  list of  rule
+    numbers. You can use this condition to route messages that
+    matched specific rules. Here is an example:
+    
+      # special router for BMI rule #5, #8 and #11
+      bmi_rule_redirect:
+        driver = redirect
+        bmi_rule = 5:8:11
+        data = postmaster@mydomain.com
+      
+  
+  - Expansion variables. Several  expansion variables are  set
+    during  routing.  You  can  use  them  in  custom   router
+    conditions,  for  example.  The  following  variables  are
+    available:
+    
+    o $bmi_base64_verdict
+    
+      This variable  will contain  the BASE64  encoded verdict
+      for the recipient being routed. You can use it to add  a
+      header to messages for tracking purposes, for example:
+      
+      localuser:
+        driver = accept
+        check_local_user
+        headers_add = X-Brightmail-Verdict: $bmi_base64_verdict
+        transport = local_delivery
+      
+      If there is no verdict available for the recipient being
+      routed, this variable contains the empty string.
+    
+    o $bmi_base64_tracker_verdict
+    
+      This variable  will contain  a BASE64  encoded subset of
+      the  verdict  information  concerning  the  "rules" that
+      fired  on the  message. You  can add  this string  to a
+      header, commonly named "X-Brightmail-Tracker". Example:
+      
+      localuser:
+        driver = accept
+        check_local_user
+        headers_add = X-Brightmail-Tracker: $bmi_base64_tracker_verdict
+        transport = local_delivery
+        
+      If there is no verdict available for the recipient being
+      routed, this variable contains the empty string.
+      
+    o $bmi_alt_location
+    
+      If  the  verdict  is  to  redirect  the  message  to  an
+      alternate  location,  this  variable  will  contain  the
+      alternate location string returned by the BMI server. In
+      its default configuration, this is a header-like  string
+      that can be added to the message with "headers_add".  If
+      there is  no verdict  available for  the recipient being
+      routed, or if the  message is to be  delivered normally,
+      this variable contains the empty string.
+      
+    o $bmi_deliver
+    
+      This is an additional integer variable that can be  used
+      to query if the message should be delivered at all.  You
+      should use router preconditions instead if possible.
+      
+      $bmi_deliver is '0': the message should NOT be delivered.
+      $bmi_deliver is '1': the message should be delivered.
+      
+   
+  IMPORTANT NOTE: Verdict inheritance.
+  The  message  is passed  to  the BMI  server  during message
+  reception,  using the  target addresses  from the  RCPT TO:
+  commands in the SMTP transaction. If recipients get expanded
+  or re-written (for example by aliasing), the new address(es)
+  inherit the  verdict from  the original  address. This means
+  that verdicts also apply to all "child" addresses  generated
+  from top-level addresses that were sent to the BMI server.
+  
+  
+5) Using per-recipient opt-in information (Optional)
+
+  The  BMI server  features multiple  scanning "profiles"  for
+  individual recipients.  These are  usually stored  in a LDAP
+  server and are  queried by the  BMI server itself.  However,
+  you can also  pass opt-in data  for each recipient  from the
+  MTA to the  BMI server. This  is particularly useful  if you
+  already look  up recipient  data in  exim anyway  (which can
+  also be  stored in  a SQL  database or  other source).  This
+  implementation enables you  to pass opt-in  data to the  BMI
+  server  in  the  RCPT   ACL.  This  works  by   setting  the
+  'bmi_optin' modifier in  a block of  that ACL. If  should be
+  set to a list  of comma-separated strings that  identify the
+  features which the BMI server should use for that particular
+  recipient. Ideally, you  would use the  'bmi_optin' modifier
+  in the same  ACL block where  you set the  'bmi_run' control
+  flag. Here is an example that will pull opt-in data for each
+  recipient      from       a      flat       file      called
+  '/etc/exim/bmi_optin_data'.
+  
+  The file format:
+  
+    user1@mydomain.com: <OPTIN STRING1>:<OPTIN STRING2>
+    user2@thatdomain.com: <OPTIN STRING3>
+    
+    
+  The example:
+  
+    accept  domains       = +relay_to_domains
+            endpass
+            verify        = recipient
+            bmi_optin     = ${lookup{$local_part@$domain}lsearch{/etc/exim/bmi_optin_data}}
+            control       = bmi_run  
+  
+  Of course,  you can  also use  any other  lookup method that
+  exim supports, including LDAP, Postgres, MySQL, Oracle etc.,
+  as long as  the result is  a list of  colon-separated opt-in
+  strings.
+  
+  For a list of available opt-in strings, please contact  your
+  Brightmail representative.
+  
+  
+
+  
+8. Sender Policy Framework (SPF) support
+--------------------------------------------------------------
+
+To learn  more  about  SPF, visit   http://spf.pobox.com. This
+document does   not explain  the SPF  fundamentals, you should
+read and understand the implications of deploying SPF on  your
+system before doing so.
+
+SPF support is added via the libspf2 library. Visit 
+
+  http://www.libspf2.org/
+  
+to obtain  a copy,  then compile  and install  it. By default,
+this will  put headers  in /usr/local/include  and the  static
+library in /usr/local/lib.
+
+To compile exim with SPF support, set these additional flags in
+Local/Makefile:
+
+CFLAGS=-DSPF -I/usr/local/include
+EXTRALIBS_EXIM=-L/usr/local/lib -lspf2
+
+This assumes   that the   libspf2 files   are installed  in
+their default locations.
+
+You can now run SPF checks in incoming SMTP by using the "spf"
+ACL condition  in either  the MAIL,  RCPT or  DATA ACLs.  When
+using it in the RCPT ACL, you can make the checks dependend on
+the RCPT  address (or  domain), so  you can  check SPF records
+only  for   certain  target   domains.  This   gives  you  the
+possibility  to opt-out  certain customers  that do  not want
+their mail to be subject to SPF checking.
+
+The spf condition  takes a list  of strings on  its right-hand
+side. These strings describe the outcome of the SPF check  for
+which the spf condition should succeed. Valid strings are:
+
+  o pass      The SPF check passed, the sending host
+              is positively verified by SPF.
+  o fail      The SPF check failed, the sending host
+              is NOT allowed to send mail for the domain
+              in the envelope-from address.
+  o softfail  The SPF check failed, but the queried
+              domain can't absolutely confirm that this
+              is a forgery.
+  o none      The queried domain does not publish SPF
+              records.
+  o neutral   The SPF check returned a "neutral" state.
+              This means the queried domain has published
+              a SPF record, but wants to allow outside
+              servers to send mail under its domain as well.
+  o err_perm  This indicates a syntax error in the SPF
+              record of the queried domain. This should be
+              treated like "none".
+  o err_temp  This indicates a temporary error during all
+              processing, including exim's SPF processing.
+              You may defer messages when this occurs.
+              
+You can prefix each string with an exclamation mark to  invert
+is meaning,  for example  "!fail" will  match all  results but
+"fail".  The  string  list is  evaluated  left-to-right,  in a
+short-circuit fashion.  When a  string matches  the outcome of
+the SPF check, the condition  succeeds. If none of the  listed
+strings matches the  outcome of the  SPF check, the  condition
+fails.
+
+Here is a simple example to fail forgery attempts from domains
+that publish SPF records:
+
+/* -----------------
+deny message = $sender_host_address is not allowed to send mail from $sender_address_domain
+     spf = fail
+--------------------- */
+
+You can also give special treatment to specific domains:
+
+/* -----------------
+deny message = AOL sender, but not from AOL-approved relay.
+     sender_domains = aol.com
+     spf = fail:neutral
+--------------------- */
+
+Explanation: AOL  publishes SPF  records, but  is liberal  and
+still allows  non-approved relays  to send  mail from aol.com.
+This will result in a "neutral" state, while mail from genuine
+AOL servers  will result  in "pass".  The example  above takes
+this into account and  treats "neutral" like "fail",  but only
+for aol.com. Please note that this violates the SPF draft.
+
+When the spf condition has run, it sets up several expansion
+variables.
+
+  $spf_header_comment
+  This contains a human-readable string describing the outcome
+  of the SPF check. You can add it to a custom header or use
+  it for logging purposes.
+  
+  $spf_received
+  This contains a complete SPF-Received: header that can be
+  added to the message. Please note that according to the SPF
+  draft, this header must be added at the top of the header
+  list. Please see section 10 on how you can do this.
+  
+  $spf_result
+  This contains the outcome of the SPF check in string form,
+  one of pass, fail, softfail, none, neutral, err_perm or
+  err_temp.
+  
+  $spf_smtp_comment
+  This contains a string that can be used in a SMTP response
+  to the calling party. Useful for "fail".
+  
+  
+
+9. SRS (Sender Rewriting Scheme) Support
+--------------------------------------------------------------
+
+Exiscan  currently  includes SRS  support  via Miles  Wilton's
+libsrs_alt  library.  His patch  to  link to  that  library is
+included  in exiscan.  The current  version of  the supported
+library is 0.5.
+
+In order to  use SRS, you  must get a  copy of libsrs_alt from
+
+http://srs.mirtol.com/
+
+Unpack the tarball, then refer to MTAs/README.EXIM
+to proceed. 
+
+The SRS support is subject to change and should be  considered
+experimental. SRS itself is still subject to changes.
+
+
+
+10. Selecting header positions when adding headers in ACLs
+--------------------------------------------------------------\
+
+Exim introduced selectable  header positions in  Version 4.43.
+One option allows you to  add your header after all  Received:
+headers. For optimal SPF draft/RFC2822 compliance, exiscan-acl
+adds one  other option,  called "spf-received".  It adds  your
+header  before  the first  header  which is  NOT  Received: or
+Resent-*:, seen from the top of the header stack.
+
+Example:
+
+warn message = :spf_received:$spf_received
+
+--------------------------------------------------------------
+End of file
+--------------------------------------------------------------
diff -urN exim-4.44-orig/exim_monitor/em_globals.c exim-4.44/exim_monitor/em_globals.c
--- exim-4.44-orig/exim_monitor/em_globals.c	Tue Jan 11 13:54:54 2005
+++ exim-4.44/exim_monitor/em_globals.c	Fri Jan 14 20:35:02 2005
@@ -42,6 +42,10 @@
 uschar *action_required;
 uschar *alternate_config = NULL;
 
+#ifdef BRIGHTMAIL
+int     bmi_run                = 0;
+uschar *bmi_verdicts           = NULL;
+#endif
 int     body_max = 20000;
 
 uschar *exim_path              = US BIN_DIRECTORY "/exim"
@@ -130,6 +134,8 @@
 BOOL    deliver_manual_thaw    = FALSE;
 BOOL    dont_deliver           = FALSE;
 
+BOOL	fake_reject            = FALSE;
+
 header_line *header_last       = NULL;
 header_line *header_list       = NULL;
 
@@ -139,6 +145,7 @@
 
 BOOL    local_error_message    = FALSE;
 uschar *local_scan_data        = NULL;
+uschar *spam_score_int         = NULL;
 BOOL    log_timezone           = FALSE;
 int     message_age            = 0;
 uschar *message_id;
diff -urN exim-4.44-orig/scripts/MakeLinks exim-4.44/scripts/MakeLinks
--- exim-4.44-orig/scripts/MakeLinks	Tue Jan 11 13:54:54 2005
+++ exim-4.44/scripts/MakeLinks	Fri Jan 14 20:35:02 2005
@@ -173,19 +173,27 @@
 # but local_scan.c does not, because its location is taken from the build-time
 # configuration. Likewise for the os.c file, which gets build dynamically.
 
+ln -s ../src/bmi_spam.h        bmi_spam.h
 ln -s ../src/dbfunctions.h     dbfunctions.h
 ln -s ../src/dbstuff.h         dbstuff.h
+ln -s ../src/demime.h          demime.h
 ln -s ../src/exim.h            exim.h
 ln -s ../src/functions.h       functions.h
 ln -s ../src/globals.h         globals.h
 ln -s ../src/local_scan.h      local_scan.h
 ln -s ../src/macros.h          macros.h
+ln -s ../src/mime.h            mime.h
 ln -s ../src/mytypes.h         mytypes.h
 ln -s ../src/osfunctions.h     osfunctions.h
+ln -s ../src/spam.h            spam.h
+ln -s ../src/spf.h             spf.h
+ln -s ../src/srs.h		srs.h
 ln -s ../src/store.h           store.h
 ln -s ../src/structs.h         structs.h
+ln -s ../src/tnef.h            tnef.h
 
 ln -s ../src/acl.c             acl.c
+ln -s ../src/bmi_spam.c        bmi_spam.c
 ln -s ../src/buildconfig.c     buildconfig.c
 ln -s ../src/child.c           child.c
 ln -s ../src/crypt16.c         crypt16.c
@@ -193,6 +201,7 @@
 ln -s ../src/dbfn.c            dbfn.c
 ln -s ../src/debug.c           debug.c
 ln -s ../src/deliver.c         deliver.c
+ln -s ../src/demime.c          demime.c
 ln -s ../src/directory.c       directory.c
 ln -s ../src/dns.c             dns.c
 ln -s ../src/drtables.c        drtables.c
@@ -211,7 +220,9 @@
 ln -s ../src/ip.c              ip.c
 ln -s ../src/log.c             log.c
 ln -s ../src/lss.c             lss.c
+ln -s ../src/malware.c         malware.c
 ln -s ../src/match.c           match.c
+ln -s ../src/mime.c            mime.c
 ln -s ../src/moan.c            moan.c
 ln -s ../src/parse.c           parse.c
 ln -s ../src/perl.c            perl.c
@@ -219,6 +230,7 @@
 ln -s ../src/rda.c             rda.c
 ln -s ../src/readconf.c        readconf.c
 ln -s ../src/receive.c         receive.c
+ln -s ../src/regex.c           regex.c
 ln -s ../src/retry.c           retry.c
 ln -s ../src/rewrite.c         rewrite.c
 ln -s ../src/rfc2047.c         rfc2047.c
@@ -227,13 +239,18 @@
 ln -s ../src/sieve.c           sieve.c
 ln -s ../src/smtp_in.c         smtp_in.c
 ln -s ../src/smtp_out.c        smtp_out.c
+ln -s ../src/spam.c            spam.c
+ln -s ../src/spf.c             spf.c
 ln -s ../src/spool_in.c        spool_in.c
+ln -s ../src/spool_mbox.c      spool_mbox.c
 ln -s ../src/spool_out.c       spool_out.c
+ln -s ../src/srs.c             srs.c
 ln -s ../src/store.c           store.c
 ln -s ../src/string.c          string.c
 ln -s ../src/tls.c             tls.c
 ln -s ../src/tls-gnu.c         tls-gnu.c
 ln -s ../src/tls-openssl.c     tls-openssl.c
+ln -s ../src/tnef.c            tnef.c
 ln -s ../src/tod.c             tod.c
 ln -s ../src/transport.c       transport.c
 ln -s ../src/tree.c            tree.c
diff -urN exim-4.44-orig/src/acl.c exim-4.44/src/acl.c
--- exim-4.44-orig/src/acl.c	Tue Jan 11 14:43:27 2005
+++ exim-4.44/src/acl.c	Fri Jan 14 20:43:55 2005
@@ -7,8 +7,13 @@
 
 /* Code for handling Access Control Lists (ACLs) */
 
+/* This file has been modified by the exiscan-acl patch. */
+
 #include "exim.h"
 
+#ifdef BRIGHTMAIL
+#include "bmi_spam.h"
+#endif
 
 /* Default callout timeout */
 
@@ -32,19 +37,33 @@
 /* ACL condition and modifier codes - keep in step with the table that
 follows. */
 
-enum { ACLC_ACL, ACLC_AUTHENTICATED, ACLC_CONDITION, ACLC_CONTROL, ACLC_DELAY,
+enum { ACLC_ACL, ACLC_AUTHENTICATED,
+#ifdef BRIGHTMAIL
+  ACLC_BMI_OPTIN,
+#endif
+  ACLC_CONDITION, ACLC_CONTROL, ACLC_DECODE, ACLC_DELAY, ACLC_DEMIME,
   ACLC_DNSLISTS, ACLC_DOMAINS, ACLC_ENCRYPTED, ACLC_ENDPASS, ACLC_HOSTS,
-  ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_LOGWRITE, ACLC_MESSAGE,
-  ACLC_RECIPIENTS, ACLC_SENDER_DOMAINS, ACLC_SENDERS, ACLC_SET, ACLC_VERIFY };
+  ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_LOGWRITE, ACLC_MALWARE, ACLC_MESSAGE, ACLC_MIME_REGEX,
+  ACLC_RECIPIENTS, ACLC_REGEX, ACLC_SENDER_DOMAINS, ACLC_SENDERS, ACLC_SET, ACLC_SPAM,
+#ifdef SPF
+  ACLC_SPF,
+#endif
+  ACLC_VERIFY };
 
 /* ACL conditions/modifiers: "delay", "control", "endpass", "message",
 "log_message", "logwrite", and "set" are modifiers that look like conditions
 but always return TRUE. They are used for their side effects. */
 
-static uschar *conditions[] = { US"acl", US"authenticated", US"condition",
-  US"control", US"delay", US"dnslists", US"domains", US"encrypted",
-  US"endpass", US"hosts", US"local_parts", US"log_message", US"logwrite",
-  US"message", US"recipients", US"sender_domains", US"senders", US"set",
+static uschar *conditions[] = { US"acl", US"authenticated", 
+#ifdef BRIGHTMAIL
+  US"bmi_optin",
+#endif
+  US"condition", US"control", US"decode", US"delay", US"demime", US"dnslists", US"domains", US"encrypted",
+  US"endpass", US"hosts", US"local_parts", US"log_message", US"logwrite", US"malware",
+  US"message", US"mime_regex", US"recipients", US"regex", US"sender_domains", US"senders", US"set", US"spam", 
+#ifdef SPF
+  US"spf",
+#endif
   US"verify" };
   
 /* ACL control names */
@@ -60,9 +79,14 @@
 static uschar cond_expand_at_top[] = {
   TRUE,    /* acl */
   FALSE,   /* authenticated */
+#ifdef BRIGHTMAIL
+  TRUE,    /* bmi_optin */
+#endif
   TRUE,    /* condition */
   TRUE,    /* control */
+  TRUE,    /* decode */
   TRUE,    /* delay */
+  TRUE,    /* demime */
   TRUE,    /* dnslists */
   FALSE,   /* domains */
   FALSE,   /* encrypted */
@@ -71,11 +95,18 @@
   FALSE,   /* local_parts */
   TRUE,    /* log_message */
   TRUE,    /* logwrite */
+  TRUE,    /* malware */
   TRUE,    /* message */
+  TRUE,    /* mime_regex */
   FALSE,   /* recipients */
+  TRUE,    /* regex */
   FALSE,   /* sender_domains */
   FALSE,   /* senders */
   TRUE,    /* set */
+  TRUE,    /* spam */
+#ifdef SPF
+  TRUE,    /* spf */
+#endif
   TRUE     /* verify */
 };
 
@@ -84,9 +115,14 @@
 static uschar cond_modifiers[] = {
   FALSE,   /* acl */
   FALSE,   /* authenticated */
+#ifdef BRIGHTMAIL
+  TRUE,    /* bmi_optin */
+#endif
   FALSE,   /* condition */
   TRUE,    /* control */
+  FALSE,   /* decode */
   TRUE,    /* delay */
+  FALSE,   /* demime */
   FALSE,   /* dnslists */
   FALSE,   /* domains */
   FALSE,   /* encrypted */
@@ -95,11 +131,18 @@
   FALSE,   /* local_parts */
   TRUE,    /* log_message */
   TRUE,    /* log_write */
+  FALSE,   /* malware */
   TRUE,    /* message */
+  FALSE,   /* mime_regex */
   FALSE,   /* recipients */
+  FALSE,   /* regex */
   FALSE,   /* sender_domains */
   FALSE,   /* senders */
   TRUE,    /* set */
+  FALSE,   /* spam */
+#ifdef SPF
+  FALSE,   /* spf */
+#endif
   FALSE    /* verify */
 };
 
@@ -108,15 +151,45 @@
 
 static unsigned int cond_forbids[] = {
   0,                                               /* acl */
+  
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_CONNECT)|   /* authenticated */
     (1<<ACL_WHERE_HELO),
+    
+#ifdef BRIGHTMAIL
+  (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* bmi_optin */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_MIME)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA),                                                 
+#endif
+    
   0,                                               /* condition */
 
   /* Certain types of control are always allowed, so we let it through
   always and check in the control processing itself */
 
   0,                                               /* control */
+
+  (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* decode */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_RCPT)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA),
+
   0,                                               /* delay */
+
+  (1<<ACL_WHERE_AUTH)|                             /* demime */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_RCPT)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA),
+
   (1<<ACL_WHERE_NOTSMTP),                          /* dnslists */
 
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* domains */
@@ -125,7 +198,7 @@
     (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
     (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
     (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY),
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_MIME),
 
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_CONNECT)|   /* encrypted */
     (1<<ACL_WHERE_HELO),
@@ -138,19 +211,43 @@
     (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
     (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
     (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY),
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_MIME),
 
   0,                                               /* log_message */
   0,                                               /* logwrite */
+
+  (1<<ACL_WHERE_AUTH)|                             /* malware */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_RCPT)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA),
+
   0,                                               /* message */
 
+  (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* mime_regex */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_RCPT)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA),
+
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* recipients */
     (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
     (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_PREDATA)|
     (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
     (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_QUIT)|
     (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
-    (1<<ACL_WHERE_VRFY),
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_MIME),
+
+  (1<<ACL_WHERE_AUTH)|                             /* regex */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|(1<<ACL_WHERE_RCPT)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY)|(1<<ACL_WHERE_PREDATA),
 
   (1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)|      /* sender_domains */
     (1<<ACL_WHERE_HELO)|
@@ -166,6 +263,22 @@
 
   0,                                               /* set */
 
+  (1<<ACL_WHERE_AUTH)|                             /* spam */
+    (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_RCPT)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
+    (1<<ACL_WHERE_VRFY),
+
+#ifdef SPF
+  (1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)|      /* spf */
+    (1<<ACL_WHERE_HELO)|
+    (1<<ACL_WHERE_MAILAUTH)|
+    (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
+    (1<<ACL_WHERE_STARTTLS)|(1<<ACL_WHERE_VRFY),
+#endif
+
   /* Certain types of verify are always allowed, so we let it through
   always and check in the verify function itself */
 
@@ -175,20 +288,28 @@
 
 /* Return values from decode_control() */
 
-enum { CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART,
-  CONTROL_ENFORCE_SYNC, CONTROL_NO_ENFORCE_SYNC, CONTROL_FREEZE,
-  CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, CONTROL_NO_MULTILINE };
+enum { 
+#ifdef BRIGHTMAIL
+  CONTROL_BMI_RUN,
+#endif 
+  CONTROL_ERROR, CONTROL_CASEFUL_LOCAL_PART, CONTROL_CASELOWER_LOCAL_PART,
+  CONTROL_ENFORCE_SYNC, CONTROL_NO_ENFORCE_SYNC, CONTROL_FAKEREJECT, CONTROL_FREEZE,
+  CONTROL_QUEUE_ONLY, CONTROL_SUBMISSION, CONTROL_NO_MBOX_UNSPOOL, CONTROL_NO_MULTILINE };
 
 /* Bit map vector of which controls are not allowed at certain times. For
 each control, there's a bitmap of dis-allowed times. For some, it is easier to
 specify the negation of a small number of allowed times. */
 
 static unsigned int control_forbids[] = {
+#ifdef BRIGHTMAIL
+  0,
+#endif
   0,                                               /* error */
   ~(1<<ACL_WHERE_RCPT),                            /* caseful_local_part */
   ~(1<<ACL_WHERE_RCPT),                            /* caselower_local_part */
   (1<<ACL_WHERE_NOTSMTP),                          /* enforce_sync */
   (1<<ACL_WHERE_NOTSMTP),                          /* no_enforce_sync */
+  (1<<ACL_WHERE_NOTSMTP),                          /* fakereject */
    
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* freeze */
     (1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
@@ -200,6 +321,8 @@
      
   ~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)|       /* submission */
     (1<<ACL_WHERE_PREDATA)),                       
+
+  (1<<ACL_WHERE_NOTSMTP),                          /* no_mbox_unspool */
      
   (1<<ACL_WHERE_NOTSMTP)                           /* no_multiline */
 };
@@ -213,11 +336,16 @@
 } control_def;
 
 static control_def controls_list[] = {
+#ifdef BRIGHTMAIL
+  { US"bmi_run",                CONTROL_BMI_RUN, FALSE},
+#endif
   { US"caseful_local_part",     CONTROL_CASEFUL_LOCAL_PART, FALSE},
   { US"caselower_local_part",   CONTROL_CASELOWER_LOCAL_PART, FALSE},
   { US"enforce_sync",           CONTROL_ENFORCE_SYNC, FALSE},
+  { US"fakereject",             CONTROL_FAKEREJECT, FALSE},
   { US"freeze",                 CONTROL_FREEZE, FALSE},
   { US"no_enforce_sync",        CONTROL_NO_ENFORCE_SYNC, FALSE},
+  { US"no_mbox_unspool",        CONTROL_NO_MBOX_UNSPOOL, FALSE},
   { US"no_multiline_responses", CONTROL_NO_MULTILINE, FALSE},
   { US"queue_only",             CONTROL_QUEUE_ONLY, FALSE},
   { US"submission",             CONTROL_SUBMISSION, TRUE}
@@ -545,6 +673,11 @@
         newtype = htype_add_rec;
         p += 16;
         }
+      else if (strncmpic(p, US":spf_received:", 14) == 0)
+        {
+        newtype = htype_add_spf;
+        p += 14;
+        }
       else if (strncmpic(p, US":at_start:", 10) == 0)
         {
         newtype = htype_add_top;
@@ -1226,6 +1359,7 @@
 uschar *log_message = NULL;
 uschar *p;
 int rc = OK;
+int sep = '/';
 
 for (; cb != NULL; cb = cb->next)
   {
@@ -1359,6 +1493,12 @@
 
     switch(control_type)
       {
+#ifdef BRIGHTMAIL
+      case CONTROL_BMI_RUN:
+      bmi_run = 1;
+      break;
+#endif
+            
       case CONTROL_ERROR:
       return ERROR;
 
@@ -1374,10 +1514,18 @@
       smtp_enforce_sync = TRUE;
       break;
 
+      case CONTROL_FAKEREJECT:
+      fake_reject = TRUE;
+      break;
+
       case CONTROL_NO_ENFORCE_SYNC:
       smtp_enforce_sync = FALSE;
       break;
 
+      case CONTROL_NO_MBOX_UNSPOOL:
+      no_mbox_unspool = TRUE;
+      break;
+
       case CONTROL_NO_MULTILINE:
       no_multiline_responses = TRUE;
       break;
@@ -1431,7 +1579,76 @@
     break;
 
     case ACLC_DNSLISTS:
-    rc = verify_check_dnsbl(&arg);
+      rc = verify_check_dnsbl(&arg);
+    break;
+
+#ifdef BRIGHTMAIL
+    case ACLC_BMI_OPTIN:
+      {
+      int old_pool = store_pool;
+      store_pool = POOL_PERM;
+      bmi_current_optin = string_copy(arg);
+      store_pool = old_pool;
+      }
+    break;
+#endif
+
+    case ACLC_DECODE:
+      rc = mime_decode(&arg);
+    break;
+
+    case ACLC_MIME_REGEX:
+      rc = mime_regex(&arg);
+    break;
+
+    case ACLC_DEMIME:
+      rc = demime(&arg);
+    break;
+
+#ifdef SPF
+    case ACLC_SPF:
+      rc = spf_process(&arg, sender_address);
+    break;
+#endif
+
+    case ACLC_MALWARE:
+      {
+      /* Seperate the regular expression and any optional parameters. */
+      uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
+      /* Run the malware backend. */
+      rc = malware(&ss);
+      /* Modify return code based upon the existance of options. */
+      while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
+            != NULL) {
+        if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
+          {
+          /* FAIL so that the message is passed to the next ACL */
+          rc = FAIL;
+          }
+        }
+      }
+    break;
+
+    case ACLC_SPAM:
+      {
+      /* Seperate the regular expression and any optional parameters. */
+      uschar *ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size);
+      /* Run the spam backend. */
+      rc = spam(&ss);
+      /* Modify return code based upon the existance of options. */
+      while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
+            != NULL) {
+        if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER)
+          {
+          /* FAIL so that the message is passed to the next ACL */
+          rc = FAIL;
+          }
+        }
+      }
+    break;
+
+    case ACLC_REGEX:
+      rc = regex(&arg);
     break;
 
     case ACLC_DOMAINS:
diff -urN exim-4.44-orig/src/auths/pwcheck.c exim-4.44/src/auths/pwcheck.c
--- exim-4.44-orig/src/auths/pwcheck.c	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/auths/pwcheck.c	Fri Jan 14 20:35:02 2005
@@ -1,7 +1,7 @@
 /* SASL server API implementation
  * Rob Siemborski
  * Tim Martin
- * $Id: checkpw.c,v 1.49 2002/03/07 19:14:04 ken3 Exp $
+ * $Id: pwcheck.c,v 1.1.1.1 2004/06/24 07:46:30 tkistner Exp $
  */
 /*
  * Copyright (c) 2001 Carnegie Mellon University.  All rights reserved.
diff -urN exim-4.44-orig/src/bmi_spam.c exim-4.44/src/bmi_spam.c
--- exim-4.44-orig/src/bmi_spam.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/bmi_spam.c	Fri Jan 14 20:35:02 2005
@@ -0,0 +1,474 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+   patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* Code for calling Brightmail AntiSpam. */
+
+#include "exim.h"
+#include "bmi_spam.h"
+
+#ifdef BRIGHTMAIL
+
+uschar *bmi_current_optin = NULL;
+
+uschar *bmi_process_message(header_line *header_list, int data_fd) {
+  BmiSystem *system = NULL;
+  BmiMessage *message = NULL;
+  BmiError err;
+  BmiErrorLocation err_loc;
+  BmiErrorType err_type;
+  const BmiVerdict *verdict = NULL;
+  FILE *data_file;
+  uschar data_buffer[4096];
+  uschar localhost[] = "127.0.0.1";
+  uschar *host_address;
+  uschar *verdicts = NULL;
+  int i,j;
+  
+  err = bmiInitSystem(BMI_VERSION, (char *)bmi_config_file, &system);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: could not initialize Brightmail system.", (int)err_loc, (int)err_type);
+    return NULL;
+  }
+
+  err = bmiInitMessage(system, &message);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: could not initialize Brightmail message.", (int)err_loc, (int)err_type);
+    bmiFreeSystem(system);
+    return NULL;
+  }
+
+  /* Send IP address of sending host */
+  if (sender_host_address == NULL)
+    host_address = localhost;
+  else
+    host_address = sender_host_address;
+  err = bmiProcessConnection((char *)host_address, message);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiProcessConnection() failed (IP %s).", (int)err_loc, (int)err_type, (char *)host_address);
+    bmiFreeMessage(message);
+    bmiFreeSystem(system);
+    return NULL;
+  };
+
+  /* Send envelope sender address */
+  err = bmiProcessFROM((char *)sender_address, message);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiProcessFROM() failed (address %s).", (int)err_loc, (int)err_type, (char *)sender_address);
+    bmiFreeMessage(message);
+    bmiFreeSystem(system);
+    return NULL;
+  };
+
+  /* Send envelope recipients */
+  for(i=0;i<recipients_count;i++) {
+    recipient_item *r = recipients_list + i;
+    BmiOptin *optin = NULL;
+    
+    /* create optin object if optin string is given */
+    if ((r->bmi_optin != NULL) && (Ustrlen(r->bmi_optin) > 1)) {
+      debug_printf("passing bmiOptin string: %s\n", r->bmi_optin);
+      bmiOptinInit(&optin);
+      err = bmiOptinMset(optin, r->bmi_optin, ':');
+      if (bmiErrorIsFatal(err) == BMI_TRUE) {
+        log_write(0, LOG_PANIC|LOG_MAIN,
+                   "bmi warning: [loc %d type %d]: bmiOptinMSet() failed (address '%s', string '%s').", (int)err_loc, (int)err_type, (char *)r->address, (char *)r->bmi_optin);
+        if (optin != NULL)
+          bmiOptinFree(optin);
+        optin = NULL;
+      };
+    };
+    
+    err = bmiAccumulateTO((char *)r->address, optin, message);
+    
+    if (optin != NULL)
+      bmiOptinFree(optin);
+    
+    if (bmiErrorIsFatal(err) == BMI_TRUE) {
+      err_loc = bmiErrorGetLocation(err);
+      err_type = bmiErrorGetType(err);
+      log_write(0, LOG_PANIC,
+                 "bmi error [loc %d type %d]: bmiAccumulateTO() failed (address %s).", (int)err_loc, (int)err_type, (char *)r->address);
+      bmiFreeMessage(message);
+      bmiFreeSystem(system);
+      return NULL;
+    };
+  };
+  err = bmiEndTO(message);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiEndTO() failed.", (int)err_loc, (int)err_type);
+    bmiFreeMessage(message);
+    bmiFreeSystem(system);
+    return NULL;
+  };
+  
+  /* Send message headers */
+  while (header_list != NULL) {
+    /* skip deleted headers */
+    if (header_list->type == '*') {
+      header_list = header_list->next;
+      continue;
+    };
+    err = bmiAccumulateHeaders((const char *)header_list->text, header_list->slen, message);
+    if (bmiErrorIsFatal(err) == BMI_TRUE) {
+      err_loc = bmiErrorGetLocation(err);
+      err_type = bmiErrorGetType(err);
+      log_write(0, LOG_PANIC,
+                 "bmi error [loc %d type %d]: bmiAccumulateHeaders() failed.", (int)err_loc, (int)err_type);
+      bmiFreeMessage(message);
+      bmiFreeSystem(system);
+      return NULL;
+    };
+    header_list = header_list->next;
+  };
+  err = bmiEndHeaders(message);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiEndHeaders() failed.", (int)err_loc, (int)err_type);
+    bmiFreeMessage(message);
+    bmiFreeSystem(system);
+    return NULL;
+  };
+  
+  /* Send body */
+  data_file = fdopen(data_fd,"r");
+  do {
+    j = fread(data_buffer, 1, sizeof(data_buffer), data_file);
+    if (j > 0) {
+      err = bmiAccumulateBody((const char *)data_buffer, j, message);
+      if (bmiErrorIsFatal(err) == BMI_TRUE) {
+        err_loc = bmiErrorGetLocation(err);
+        err_type = bmiErrorGetType(err);
+        log_write(0, LOG_PANIC,
+                   "bmi error [loc %d type %d]: bmiAccumulateBody() failed.", (int)err_loc, (int)err_type);
+        bmiFreeMessage(message);
+        bmiFreeSystem(system);
+        return NULL;
+      };
+    };
+  } while (j > 0);
+  err = bmiEndBody(message);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiEndBody() failed.", (int)err_loc, (int)err_type);
+    bmiFreeMessage(message);
+    bmiFreeSystem(system);
+    return NULL;
+  };
+  
+  
+  /* End message */
+  err = bmiEndMessage(message);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiEndMessage() failed.", (int)err_loc, (int)err_type);
+    bmiFreeMessage(message);
+    bmiFreeSystem(system);
+    return NULL;
+  };
+  
+  /* get store for the verdict string */
+  verdicts = store_get(1);
+  *verdicts = '\0';
+  
+  for ( err = bmiAccessFirstVerdict(message, &verdict);
+        verdict != NULL;
+        err = bmiAccessNextVerdict(message, verdict, &verdict) ) {
+    char *verdict_str;
+
+    err = bmiCreateStrFromVerdict(verdict,&verdict_str);
+    if (!store_extend(verdicts, Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) {
+      /* can't allocate more store */
+      return NULL;
+    };
+    if (*verdicts != '\0')
+      Ustrcat(verdicts, US ":");
+    Ustrcat(verdicts, US verdict_str);
+    bmiFreeStr(verdict_str);
+  };
+
+  DEBUG(D_receive) debug_printf("bmi verdicts: %s\n", verdicts);
+
+  if (Ustrlen(verdicts) == 0)
+    return NULL;
+  else
+    return verdicts;
+}
+
+
+int bmi_get_delivery_status(uschar *base64_verdict) {
+  BmiError err;
+  BmiErrorLocation err_loc;
+  BmiErrorType err_type;
+  BmiVerdict *verdict = NULL;
+  int rc = 1;   /* deliver by default */
+  
+  /* always deliver when there is no verdict */
+  if (base64_verdict == NULL)
+    return 1;
+
+  /* create verdict from base64 string */
+  err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
+    return 1;
+  };
+
+  err = bmiVerdictError(verdict);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    /* deliver normally due to error */
+    rc = 1;
+  }
+  else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
+    /* deliver normally */
+    rc = 1;    
+  }
+  else if (bmiVerdictAccessDestination(verdict) == NULL) {
+    /* do not deliver */
+    rc = 0;
+  }
+  else {
+    /* deliver to alternate location */
+    rc = 1;
+  };
+  
+  bmiFreeVerdict(verdict);
+  return rc;
+}
+
+
+uschar *bmi_get_alt_location(uschar *base64_verdict) {
+  BmiError err;
+  BmiErrorLocation err_loc;
+  BmiErrorType err_type;
+  BmiVerdict *verdict = NULL;
+  uschar *rc = NULL;
+  
+  /* always deliver when there is no verdict */
+  if (base64_verdict == NULL)
+    return NULL;
+
+  /* create verdict from base64 string */
+  err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
+    return NULL;
+  };
+  
+  err = bmiVerdictError(verdict);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    /* deliver normally due to error */
+    rc = NULL;
+  }
+  else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
+    /* deliver normally */
+    rc = NULL; 
+  }
+  else if (bmiVerdictAccessDestination(verdict) == NULL) {
+    /* do not deliver */
+    rc = NULL;
+  }
+  else {
+    /* deliver to alternate location */
+    rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1);
+    Ustrcpy(rc, bmiVerdictAccessDestination(verdict));
+    rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0';
+  };
+  
+  bmiFreeVerdict(verdict);
+  return rc;
+}
+
+uschar *bmi_get_base64_verdict(uschar *bmi_local_part, uschar *bmi_domain) {
+  BmiError err;
+  BmiErrorLocation err_loc;
+  BmiErrorType err_type;
+  BmiVerdict *verdict = NULL;
+  const BmiRecipient *recipient = NULL;
+  const char *verdict_str = NULL;
+  uschar *verdict_ptr;
+  uschar *verdict_buffer = NULL;
+  int sep = 0;
+  
+  /* return nothing if there are no verdicts available */
+  if (bmi_verdicts == NULL)
+    return NULL;
+  
+  /* allocate room for the b64 verdict string */
+  verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1);
+  
+  /* loop through verdicts */
+  verdict_ptr = bmi_verdicts;
+  while ((verdict_str = (const char *)string_nextinlist(&verdict_ptr, &sep,
+                                          verdict_buffer,
+                                          Ustrlen(bmi_verdicts)+1)) != NULL) {
+    
+    /* create verdict from base64 string */
+    err = bmiCreateVerdictFromStr(verdict_str, &verdict);
+    if (bmiErrorIsFatal(err) == BMI_TRUE) {
+      err_loc = bmiErrorGetLocation(err);
+      err_type = bmiErrorGetType(err);
+      log_write(0, LOG_PANIC,
+                 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, verdict_str);
+      return NULL;
+    };
+    
+    /* loop through rcpts for this verdict */
+    for ( recipient = bmiVerdictAccessFirstRecipient(verdict);
+          recipient != NULL;
+          recipient = bmiVerdictAccessNextRecipient(verdict, recipient)) {
+      uschar *rcpt_local_part;
+      uschar *rcpt_domain;
+      
+      /* compare address against our subject */
+      rcpt_local_part = (unsigned char *)bmiRecipientAccessAddress(recipient);
+      rcpt_domain = Ustrchr(rcpt_local_part,'@');
+      if (rcpt_domain == NULL) {
+        rcpt_domain = US"";
+      }
+      else {
+        *rcpt_domain = '\0';
+        rcpt_domain++;
+      };
+
+      if ( (strcmpic(rcpt_local_part, bmi_local_part) == 0) &&
+           (strcmpic(rcpt_domain, bmi_domain) == 0) ) {
+        /* found verdict */
+        bmiFreeVerdict(verdict);
+        return (uschar *)verdict_str;
+      };   
+    };
+  
+    bmiFreeVerdict(verdict);
+  };
+  
+  return NULL;
+}
+
+
+uschar *bmi_get_base64_tracker_verdict(uschar *base64_verdict) {
+  BmiError err;
+  BmiErrorLocation err_loc;
+  BmiErrorType err_type;
+  BmiVerdict *verdict = NULL;
+  uschar *rc = NULL;
+  
+  /* always deliver when there is no verdict */
+  if (base64_verdict == NULL)
+    return NULL;
+
+  /* create verdict from base64 string */
+  err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
+    return NULL;
+  };
+  
+  /* create old tracker string from verdict */
+  err = bmiCreateOldStrFromVerdict(verdict, &rc);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiCreateOldStrFromVerdict() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
+    return NULL;
+  };
+  
+  bmiFreeVerdict(verdict);
+  return rc;
+}
+
+
+int bmi_check_rule(uschar *base64_verdict, uschar *option_list) {
+  BmiError err;
+  BmiErrorLocation err_loc;
+  BmiErrorType err_type;
+  BmiVerdict *verdict = NULL;
+  int rc = 0;
+  uschar *rule_num;
+  uschar *rule_ptr;
+  uschar rule_buffer[32];
+  int sep = 0;
+  
+  
+  /* no verdict -> no rule fired */
+  if (base64_verdict == NULL)
+    return 0;
+  
+  /* create verdict from base64 string */
+  err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    err_loc = bmiErrorGetLocation(err);
+    err_type = bmiErrorGetType(err);
+    log_write(0, LOG_PANIC,
+               "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
+    return 0;
+  };
+  
+  err = bmiVerdictError(verdict);
+  if (bmiErrorIsFatal(err) == BMI_TRUE) {
+    /* error -> no rule fired */
+    bmiFreeVerdict(verdict);
+    return 0;
+  }
+  
+  /* loop through numbers */
+  rule_ptr = option_list;
+  while ((rule_num = string_nextinlist(&rule_ptr, &sep,
+                                       rule_buffer, 32)) != NULL) {
+    int rule_int = -1;
+    
+    /* try to translate to int */
+    sscanf(rule_num, "%d", &rule_int);
+    if (rule_int > 0) {
+      debug_printf("checking rule #%d\n", rule_int);
+      /* check if rule fired on the message */
+      if (bmiVerdictRuleFired(verdict, rule_int) == BMI_TRUE) {
+        debug_printf("rule #%d fired\n", rule_int);
+        rc = 1;
+        break;
+      };
+    };
+  };
+
+  bmiFreeVerdict(verdict);
+  return rc;
+};
+
+#endif
diff -urN exim-4.44-orig/src/bmi_spam.h exim-4.44/src/bmi_spam.h
--- exim-4.44-orig/src/bmi_spam.h	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/bmi_spam.h	Fri Jan 14 20:35:02 2005
@@ -0,0 +1,26 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+   patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* Code for calling Brightmail AntiSpam. */
+
+#ifdef BRIGHTMAIL
+
+#include <bmi_api.h>
+
+extern uschar *bmi_process_message(header_line *, int);
+extern uschar *bmi_get_base64_verdict(uschar *, uschar *);
+extern uschar *bmi_get_base64_tracker_verdict(uschar *);
+extern int bmi_get_delivery_status(uschar *);
+extern uschar *bmi_get_alt_location(uschar *);
+extern int bmi_check_rule(uschar *,uschar *);
+
+extern uschar *bmi_current_optin;
+
+#endif
diff -urN exim-4.44-orig/src/config.h.defaults exim-4.44/src/config.h.defaults
--- exim-4.44-orig/src/config.h.defaults	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/config.h.defaults	Fri Jan 14 20:35:02 2005
@@ -50,7 +50,7 @@
 #define HAVE_CRYPT16
 #define HAVE_SA_LEN
 #define HEADERS_CHARSET           "ISO-8859-1"
-#define HEADER_ADD_BUFFER_SIZE     8192
+#define HEADER_ADD_BUFFER_SIZE    (8192 * 4)
 #define HEADER_MAXSIZE            (1024*1024)
 
 #define INPUT_DIRECTORY_MODE       0750
@@ -104,7 +104,7 @@
 #define SPOOL_DIRECTORY
 #define SPOOL_DIRECTORY_MODE       0750
 #define SPOOL_MODE                 0640
-#define STRING_SPRINTF_BUFFER_SIZE 8192
+#define STRING_SPRINTF_BUFFER_SIZE (8192 * 4)
 
 #define SUPPORT_A6
 #define SUPPORT_CRYPTEQ
diff -urN exim-4.44-orig/src/configure.default exim-4.44/src/configure.default
--- exim-4.44-orig/src/configure.default	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/configure.default	Fri Jan 14 20:35:02 2005
@@ -108,6 +108,26 @@
 
 # You should not change that setting until you understand how ACLs work.
 
+# The following ACL entries are used if you want to do content scanning with
+# the exiscan-acl patch. When you uncomment one of these lines, you must also
+# review the respective entries in the ACL section further below.
+
+# acl_smtp_mime = acl_check_mime
+# acl_smtp_data = acl_check_content
+
+# This configuration variable defines the virus scanner that is used with
+# the 'malware' ACL condition of the exiscan acl-patch. If you do not use
+# virus scanning, leave it commented. Please read doc/exiscan-acl-readme.txt
+# for a list of supported scanners.
+
+# av_scanner = sophie:/var/run/sophie
+
+# The following setting is only needed if you use the 'spam' ACL condition
+# of the exiscan-acl patch. It specifies on which host and port the SpamAssassin
+# "spamd" daemon is listening. If you do not use this condition, or you use
+# the default of "127.0.0.1 783", you can omit this option.
+
+# spamd_address = 127.0.0.1 783
 
 # Specify the domain you want to be added to all unqualified addresses
 # here. An unqualified address is one that does not contain an "@" character
@@ -342,6 +362,56 @@
   deny    message       = relay not permitted
 
 
+# These access control lists are used for content scanning with the exiscan-acl
+# patch. You must also uncomment the entries for acl_smtp_data and acl_smtp_mime
+# (scroll up), otherwise the ACLs will not be used. IMPORTANT: the default entries here
+# should be treated as EXAMPLES. You MUST read the file doc/exiscan-acl-spec.txt
+# to fully understand what you are doing ...
+
+acl_check_mime:
+
+  # Decode MIME parts to disk. This will support virus scanners later.
+  warn decode = default
+
+  # File extension filtering.
+  deny message = Blacklisted file extension detected
+       condition = ${if match \
+                        {${lc:$mime_filename}} \
+                        {\N(\.exe|\.pif|\.bat|\.scr|\.lnk|\.com)$\N} \
+                     {1}{0}}
+  
+  # Reject messages that carry chinese character sets.
+  # WARNING: This is an EXAMPLE.
+  deny message = Sorry, noone speaks chinese here
+       condition = ${if eq{$mime_charset}{gb2312}{1}{0}}
+
+  accept
+
+acl_check_content:
+
+  # Reject virus infested messages.
+  deny  message = This message contains malware ($malware_name)
+        malware = *
+
+  # Always add X-Spam-Score and X-Spam-Report headers, using SA system-wide settings
+  # (user "nobody"), no matter if over threshold or not.
+  warn  message = X-Spam-Score: $spam_score ($spam_bar)
+        spam = nobody:true
+  warn  message = X-Spam-Report: $spam_report
+        spam = nobody:true
+
+  # Add X-Spam-Flag if spam is over system-wide threshold
+  warn message = X-Spam-Flag: YES
+       spam = nobody
+
+  # Reject spam messages with score over 10, using an extra condition.
+  deny  message = This message scored $spam_score points. Congratulations!
+        spam = nobody:true
+        condition = ${if >{$spam_score_int}{100}{1}{0}}
+
+  # finally accept all the rest
+  accept
+  
 
 ######################################################################
 #                      ROUTERS CONFIGURATION                         #
diff -urN exim-4.44-orig/src/deliver.c exim-4.44/src/deliver.c
--- exim-4.44-orig/src/deliver.c	Tue Jan 11 22:27:26 2005
+++ exim-4.44/src/deliver.c	Fri Jan 14 20:35:02 2005
@@ -10,6 +10,9 @@
 
 #include "exim.h"
 
+#ifdef BRIGHTMAIL
+#include "bmi_spam.h"
+#endif
 
 /* Data block for keeping track of subprocesses for parallel remote
 delivery. */
@@ -154,6 +157,13 @@
 deliver_domain = addr->domain;
 self_hostname = addr->self_hostname;
 
+#ifdef BRIGHTMAIL
+bmi_deliver = 1;    /* deliver by default */
+bmi_alt_location = NULL;
+bmi_base64_verdict = NULL;
+bmi_base64_tracker_verdict = NULL;
+#endif
+
 /* If there's only one address we can set everything. */
 
 if (addr->next == NULL)
@@ -203,6 +213,19 @@
       deliver_localpart_suffix = addr->parent->suffix;
       }
     }
+  
+#ifdef BRIGHTMAIL
+    /* Set expansion variables related to Brightmail AntiSpam */
+    bmi_base64_verdict = bmi_get_base64_verdict(deliver_localpart_orig, deliver_domain_orig);
+    bmi_base64_tracker_verdict = bmi_get_base64_tracker_verdict(bmi_base64_verdict);
+    /* get message delivery status (0 - don't deliver | 1 - deliver) */
+    bmi_deliver = bmi_get_delivery_status(bmi_base64_verdict);
+    /* if message is to be delivered, get eventual alternate location */
+    if (bmi_deliver == 1) {
+      bmi_alt_location = bmi_get_alt_location(bmi_base64_verdict);
+    };
+#endif
+  
   }
 
 /* For multiple addresses, don't set local part, and leave the domain and
diff -urN exim-4.44-orig/src/demime.c exim-4.44/src/demime.c
--- exim-4.44-orig/src/demime.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/demime.c	Fri Jan 14 20:35:02 2005
@@ -0,0 +1,1276 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* Code for unpacking MIME containers. Called from acl.c. */
+
+#include "exim.h"
+#include "demime.h"
+
+uschar demime_reason_buffer[1024];
+struct file_extension *file_extensions = NULL;
+
+int demime(uschar **listptr) {
+  int sep = 0;
+  uschar *list = *listptr;
+  uschar *option;
+  uschar option_buffer[64];
+  unsigned long long mbox_size;
+  FILE *mbox_file;
+  uschar defer_error_buffer[1024];
+  int demime_rc = 0;
+  
+  /* reset found_extension variable */
+  found_extension = NULL;
+  
+  /* try to find 1st option */
+  if ((option = string_nextinlist(&list, &sep,
+                                  option_buffer,
+                                  sizeof(option_buffer))) != NULL) {
+    
+    /* parse 1st option */
+    if ( (Ustrcmp(option,"false") == 0) || (Ustrcmp(option,"0") == 0) ) {
+      /* explicitly no demimeing */
+      return FAIL;
+    };
+  }
+  else {
+    /* no options -> no demimeing */
+    return FAIL;
+  };
+  
+  /* make sure the eml mbox file is spooled up */
+  mbox_file = spool_mbox(&mbox_size);
+  
+  if (mbox_file == NULL) {
+    /* error while spooling */
+    log_write(0, LOG_MAIN|LOG_PANIC,
+           "demime acl condition: error while creating mbox spool file");
+    return DEFER;  
+  };
+  
+  /* call demimer if not already done earlier */
+  if (!demime_ok)
+    demime_rc = mime_demux(mbox_file, defer_error_buffer);
+  
+  fclose(mbox_file);
+  
+  if (demime_rc == DEFER) {
+    /* temporary failure (DEFER => DEFER) */
+    log_write(0, LOG_MAIN,
+        "demime acl condition: %s", defer_error_buffer);
+    return DEFER;
+  };
+  
+  /* set demime_ok to avoid unpacking again */
+  demime_ok = 1;
+  
+  /* check for file extensions, if there */
+  while (option != NULL) {
+    struct file_extension *this_extension = file_extensions;
+    
+    /* Look for the wildcard. If it is found, we always return true.
+    The user must then use a custom condition to evaluate demime_errorlevel */
+    if (Ustrcmp(option,"*") == 0) {
+      found_extension = NULL;
+      return OK;
+    };
+
+    /* loop thru extension list */
+    while (this_extension != NULL) {   
+      if (strcmpic(option, this_extension->file_extension_string) == 0) {
+        /* found one */
+        found_extension = this_extension->file_extension_string;
+        return OK;
+      };
+      this_extension = this_extension->next;
+    };
+    
+    /* grab next extension from option list */
+    option = string_nextinlist(&list, &sep,
+                               option_buffer,
+                               sizeof(option_buffer));
+  };
+  
+  /* nothing found */
+  return FAIL;
+}
+
+
+/*************************************************
+*   unpack TNEF in given directory               *
+*************************************************/
+
+int mime_unpack_tnef(uschar *directory) {
+  uschar filepath[1024];
+  int n;
+  struct dirent *entry;
+  DIR *tempdir;
+  
+	/* open the dir */
+	tempdir = opendir(CS directory);
+	if (tempdir == NULL) {
+	  return -2;
+	};
+	
+	/* loop thru dir */
+	n = 0;
+	do {
+	  entry = readdir(tempdir);
+	  /* break on end of list */
+	  if (entry == NULL) break;
+	  snprintf(CS filepath,1024,"%s/%s",directory,entry->d_name);
+	  if ( (Ustrcmp(entry->d_name,"..") != 0) && (Ustrcmp(entry->d_name,".") != 0) && (Ustrcmp(entry->d_name,"winmail.dat") == 0) ) {    
+	    TNEF_set_path(CS directory);
+      n = TNEF_main(CS filepath);
+    };
+	} while (1);
+	
+	closedir(tempdir);
+  return 0;
+}
+
+
+/*************************************************
+* small hex_str -> integer conversion function   *
+*************************************************/
+
+/* needed for quoted-printable
+*/
+
+unsigned int mime_hstr_i(uschar *cptr) {
+  unsigned int i, j = 0;
+  
+  while (cptr && *cptr && isxdigit(*cptr)) {
+    i = *cptr++ - '0';
+    if (9 < i) i -= 7;
+    j <<= 4;
+    j |= (i & 0x0f);
+  }
+  
+  return(j);
+}
+
+
+/*************************************************
+* decode quoted-printable chars                  *
+*************************************************/
+
+/* gets called when we hit a =
+   returns: new pointer position
+   result code in c:
+          -2 - decode error
+          -1 - soft line break, no char
+           0-255 - char to write
+*/
+
+uschar *mime_decode_qp(uschar *qp_p,int *c) {
+  uschar hex[] = {0,0,0};
+  int nan = 0;
+  uschar *initial_pos = qp_p;
+  
+  /* advance one char */
+  qp_p++;
+  
+  REPEAT_FIRST:
+  if ( (*qp_p == '\t') || (*qp_p == ' ') || (*qp_p == '\r') )  {
+    /* tab or whitespace may follow
+       just ignore it, but remember
+       that this is not a valid hex
+       encoding any more */
+    nan = 1;
+    qp_p++;
+    goto REPEAT_FIRST;
+  }
+  else if ( (('0' <= *qp_p) && (*qp_p <= '9')) || (('A' <= *qp_p) && (*qp_p <= 'F'))  || (('a' <= *qp_p) && (*qp_p <= 'f')) ) {
+    /* this is a valid hex char, if nan is unset */
+    if (nan) {
+      /* this is illegal */
+      *c = -2;
+      return initial_pos;
+    }
+    else {
+      hex[0] = *qp_p;
+      qp_p++;
+    };
+  }
+  else if (*qp_p == '\n') {    
+    /* hit soft line break already, continue */
+    *c = -1;
+    return qp_p;
+  }
+  else {
+    /* illegal char here */
+    *c = -2;
+    return initial_pos;
+  };
+  
+  if ( (('0' <= *qp_p) && (*qp_p <= '9')) || (('A' <= *qp_p) && (*qp_p <= 'F')) || (('a' <= *qp_p) && (*qp_p <= 'f')) ) {
+    if (hex[0] > 0) {
+      hex[1] = *qp_p;
+      /* do hex conversion */
+      *c = mime_hstr_i(hex);
+      qp_p++;
+      return qp_p;
+    }
+    else {
+      /* huh ? */
+      *c = -2;
+      return initial_pos;  
+    };
+  }
+  else {
+    /* illegal char */
+    *c = -2;
+    return initial_pos;  
+  };
+  
+}
+
+
+/*************************************************
+* open new dump file                             *
+*************************************************/
+
+/* open new dump file
+   returns: -2 soft error
+            or file #, FILE * in f
+*/
+
+int mime_get_dump_file(uschar *extension, FILE **f, uschar *info) {
+  uschar file_name[1024];
+  int result;
+  unsigned int file_nr;
+  uschar default_extension[] = ".com";
+  uschar *p;
+  
+  if (extension == NULL)
+    extension = default_extension;
+  
+  /* scan the proposed extension.
+     if it is longer than 4 chars, or
+     contains exotic chars, use the default extension */
+  
+/*  if (Ustrlen(extension) > 4) {
+    extension = default_extension;
+  };
+*/  
+  
+  p = extension+1;
+  
+  while (*p != 0) {
+    *p = (uschar)tolower((uschar)*p);
+    if ( (*p < 97) || (*p > 122) ) {
+      extension = default_extension;
+      break;  
+    };
+    p++;
+  };
+  
+  /* find a new file to write to */
+  file_nr = 0;
+  do {
+    struct stat mystat;
+    
+    snprintf(CS file_name,1024,"%s/scan/%s/%s-%05u%s",spool_directory,message_id,message_id,file_nr,extension);
+    file_nr++;
+    if (file_nr >= MIME_SANITY_MAX_DUMP_FILES) {
+      /* max parts reached */
+      mime_trigger_error(MIME_ERRORLEVEL_TOO_MANY_PARTS);
+      break;
+    };
+    result = stat(CS file_name,&mystat);
+  }
+  while(result != -1);
+  
+  *f = fopen(CS file_name,"w+");
+  if (*f == NULL) {
+    /* cannot open new dump file, disk full ? -> soft error */
+    snprintf(CS info, 1024,"unable to open dump file");
+    return -2;
+  };
+ 
+  return file_nr;
+}
+
+
+/*************************************************
+* Find a string in a mime header                 *
+*************************************************/
+
+/* Find a string in a mime header, and optionally fill in
+   the value associated with it into *value
+ 
+   returns: 0 - nothing found
+            1 - found param
+            2 - found param + value
+*/
+
+int mime_header_find(uschar *header, uschar *param, uschar **value) {
+  uschar *needle;
+  
+  needle = strstric(header,param,FALSE);
+  if (needle != NULL) {
+    if (value != NULL) {
+      needle += Ustrlen(param);
+      if (*needle == '=') {
+        uschar *value_start;
+        uschar *value_end;
+        
+        value_start = needle + 1;
+        value_end = strstric(value_start,US";",FALSE);
+        if (value_end != NULL) {
+          /* allocate mem for value */
+          *value = (uschar *)malloc((value_end - value_start)+1);
+          if (*value == NULL)
+            return 0;
+          
+          Ustrncpy(*value,value_start,(value_end - value_start));
+          (*value)[(value_end - value_start)] = '\0';
+          return 2;
+        };
+      };
+    };
+    return 1;
+  };
+  return 0;
+}
+
+
+/*************************************************
+* Read a line of MIME input                      *
+*************************************************/
+/* returns status code, one of
+   MIME_READ_LINE_EOF 0
+   MIME_READ_LINE_OK 1
+   MIME_READ_LINE_OVERFLOW 2
+
+   In header mode, the line will be "cooked".
+*/
+
+int mime_read_line(FILE *f, int mime_demux_mode, uschar *buffer, long *num_copied) {
+  int c = EOF;
+  int done = 0;
+  int header_value_mode = 0;
+  int header_open_brackets = 0;
+  
+  *num_copied = 0;
+  
+  while(!done) {
+    
+    c = fgetc(f);
+    if (c == EOF) break;
+   
+    /* --------- header mode -------------- */
+    if (mime_demux_mode == MIME_DEMUX_MODE_MIME_HEADERS) {
+      
+      /* always skip CRs */
+      if (c == '\r') continue;
+      
+      if (c == '\n') {
+        if ((*num_copied) > 0) {
+          /* look if next char is '\t' or ' ' */
+          c = fgetc(f);
+          if (c == EOF) break;
+          if ( (c == '\t') || (c == ' ') ) continue;
+          ungetc(c,f);
+        };
+        /* end of the header, terminate with ';' */
+        c = ';';
+        done = 1;
+      };
+    
+      /* skip control characters */
+      if (c < 32) continue;
+    
+      /* skip whitespace + tabs */
+      if ( (c == ' ') || (c == '\t') )
+        continue;
+
+      if (header_value_mode) {
+        /* --------- value mode ----------- */
+        /* skip quotes */
+        if (c == '"') continue;
+        
+        /* leave value mode on ';' */
+        if (c == ';') {
+          header_value_mode = 0;
+        };
+        /* -------------------------------- */
+      }
+      else {
+        /* -------- non-value mode -------- */
+        if (c == '\\') {
+          /* quote next char. can be used
+          to escape brackets. */
+          c = fgetc(f);
+          if (c == EOF) break;
+        }
+        else if (c == '(') {
+          header_open_brackets++;
+          continue;
+        }
+        else if ((c == ')') && header_open_brackets) {
+          header_open_brackets--;
+          continue;
+        }
+        else if ( (c == '=') && !header_open_brackets ) {
+          /* enter value mode */
+          header_value_mode = 1;          
+        };
+        
+        /* skip chars while we are in a comment */
+        if (header_open_brackets > 0)
+          continue;
+        /* -------------------------------- */
+      };
+    }
+    /* ------------------------------------ */
+    else {
+    /* ----------- non-header mode -------- */
+      /* break on '\n' */
+      if (c == '\n')
+        done = 1;
+    /* ------------------------------------ */
+    };
+    
+    /* copy the char to the buffer */
+    buffer[*num_copied] = (uschar)c;
+    /* raise counter */
+    (*num_copied)++;
+    
+    /* break if buffer is full */
+    if (*num_copied > MIME_SANITY_MAX_LINE_LENGTH-1) {
+      done = 1;
+    };
+  }
+  
+  /* 0-terminate */
+  buffer[*num_copied] = '\0';
+  
+  if (*num_copied > MIME_SANITY_MAX_LINE_LENGTH-1)
+    return MIME_READ_LINE_OVERFLOW;
+  else
+    if (c == EOF)
+      return MIME_READ_LINE_EOF;
+    else
+      return MIME_READ_LINE_OK;
+}
+
+
+/*************************************************
+* Check for a MIME boundary                      *
+*************************************************/
+
+/* returns: 0 - no boundary found
+            1 - start boundary found
+            2 - end boundary found
+*/
+
+int mime_check_boundary(uschar *line, struct boundary *boundaries) {
+  struct boundary *thisboundary = boundaries;
+  uschar workbuf[MIME_SANITY_MAX_LINE_LENGTH+1];
+  unsigned int i,j=0;
+  
+  /* check for '--' first */
+  if (Ustrncmp(line,"--",2) == 0) {
+    
+    /* strip tab and space */
+    for (i = 2; i < Ustrlen(line); i++) {
+      if ((line[i] != ' ') && (line[i] != '\t')) {
+        workbuf[j] = line[i];
+	      j++;
+      };
+    };
+    workbuf[j+1]='\0';
+
+    while(thisboundary != NULL) {
+      if (Ustrncmp(workbuf,thisboundary->boundary_string,Ustrlen(thisboundary->boundary_string)) == 0) {
+        if (Ustrncmp(&workbuf[Ustrlen(thisboundary->boundary_string)],"--",2) == 0) {
+          /* final boundary found */
+          return 2;
+        };      
+        return 1;
+      };
+      thisboundary = thisboundary->next;
+    };
+  };
+  
+  return 0;  
+}
+
+
+/*************************************************
+* Check for start of a UUENCODE block            *
+*************************************************/
+
+/* returns 0 for no hit,
+           >0 for hit
+*/
+
+int mime_check_uu_start(uschar *line, uschar *uu_file_extension, int *has_tnef) {
+  
+  if ( (strncmpic(line,US"begin ",6) == 0)) {
+    uschar *uu_filename = &line[6];
+    
+    /* skip perms, if present */
+    Ustrtoul(&line[6],&uu_filename,10);
+      
+    /* advance one char */
+    uu_filename++;
+    
+    /* This should be the filename.
+    Check if winmail.dat is present,
+    which indicates TNEF. */
+    if (strncmpic(uu_filename,US"winmail.dat",11) == 0) {
+      *has_tnef = 1;  
+    };
+    
+    /* reverse to dot if present,
+    copy up to 4 chars for the extension */
+    if (Ustrrchr(uu_filename,'.') != NULL)
+      uu_filename = Ustrrchr(uu_filename,'.');
+ 
+    return sscanf(CS uu_filename, "%4[.0-9A-Za-z]",CS uu_file_extension);
+  }
+  else {
+    /* nothing found */
+    return 0;
+  };
+}
+
+
+/*************************************************
+* Decode a uu line                               *
+*************************************************/
+
+/* returns number of decoded bytes
+         -2 for soft errors
+*/
+
+int warned_about_uudec_line_sanity_1 = 0;
+int warned_about_uudec_line_sanity_2 = 0;
+long uu_decode_line(uschar *line, uschar **data, long line_len, uschar *info) {
+  uschar *p;
+  long num_decoded = 0;
+  uschar tmp_c;
+  uschar *work;
+  int uu_decoded_line_len, uu_encoded_line_len;
+  
+  /* allocate memory for data and work buffer */
+  *data = (uschar *)malloc(line_len);
+  if (*data == NULL) {
+    snprintf(CS info, 1024,"unable to allocate %lu bytes",line_len);
+    return -2;
+  };
+
+  work = (uschar *)malloc(line_len);
+  if (work == NULL) {
+    snprintf(CS info, 1024,"unable to allocate %lu bytes",line_len);
+    return -2;
+  };
+  
+  memcpy(work,line,line_len);
+  
+  /* First char is line length
+  This is microsofts way of getting it. Scary. */
+  if (work[0] < 32) {
+    /* ignore this line */
+    return 0;
+  }
+  else {
+    uu_decoded_line_len = uudec[work[0]];
+  };
+  
+  p = &work[1];
+
+  while (*p > 32) {
+    *p = uudec[*p];
+    p++;
+  };
+
+  uu_encoded_line_len = (p - &work[1]);
+  p = &work[1];
+          
+  /* check that resulting line length is a multiple of 4 */
+  if ( ( uu_encoded_line_len % 4 ) != 0) {
+    if (!warned_about_uudec_line_sanity_1) {
+      mime_trigger_error(MIME_ERRORLEVEL_UU_MISALIGNED);
+      warned_about_uudec_line_sanity_1 = 1;
+    };
+    return -1;
+  };
+
+  /* check that the line length matches */
+  if ( ( (((uu_encoded_line_len/4)*3)-2) > uu_decoded_line_len ) || (((uu_encoded_line_len/4)*3) < uu_decoded_line_len) ) {
+    if (!warned_about_uudec_line_sanity_2) {
+      mime_trigger_error(MIME_ERRORLEVEL_UU_LINE_LENGTH);
+      warned_about_uudec_line_sanity_2 = 1;
+    };
+    return -1;
+  };
+
+  while ( ((p - &work[1]) < uu_encoded_line_len) && (num_decoded < uu_decoded_line_len)) {
+           
+    /* byte 0 ---------------------- */
+    if ((p - &work[1] + 1) >= uu_encoded_line_len) {
+      return 0;
+    }
+    
+    (*data)[num_decoded] = *p;
+    (*data)[num_decoded] <<= 2;
+    
+    tmp_c = *(p+1);
+    tmp_c >>= 4;
+    (*data)[num_decoded] |= tmp_c;
+    
+    num_decoded++;
+    p++;
+     
+    /* byte 1 ---------------------- */
+    if ((p - &work[1] + 1) >= uu_encoded_line_len) {
+      return 0;
+    }
+    
+    (*data)[num_decoded] = *p;
+    (*data)[num_decoded] <<= 4;
+    
+    tmp_c = *(p+1);
+    tmp_c >>= 2;
+    (*data)[num_decoded] |= tmp_c;
+    
+    num_decoded++;
+    p++;
+   
+    /* byte 2 ---------------------- */
+    if ((p - &work[1] + 1) >= uu_encoded_line_len) {
+      return 0;
+    }
+    
+    (*data)[num_decoded] = *p;
+    (*data)[num_decoded] <<= 6;
+    
+    (*data)[num_decoded] |= *(p+1);
+    
+    num_decoded++;
+    p+=2;
+   
+  };
+  
+  return uu_decoded_line_len;
+}
+
+
+/*************************************************
+* Decode a b64 or qp line                        *
+*************************************************/
+
+/* returns number of decoded bytes
+         -1 for hard errors
+         -2 for soft errors
+*/
+
+int warned_about_b64_line_length = 0;
+int warned_about_b64_line_sanity = 0;
+int warned_about_b64_illegal_char = 0;
+int warned_about_qp_line_sanity = 0;
+long mime_decode_line(int mime_demux_mode,uschar *line, uschar **data, long max_data_len, uschar *info) {
+  uschar *p;
+  long num_decoded = 0;
+  int offset = 0;
+  uschar tmp_c;
+  
+  /* allocate memory for data */
+  *data = (uschar *)malloc(max_data_len);
+  if (*data == NULL) {
+    snprintf(CS info, 1024,"unable to allocate %lu bytes",max_data_len);
+    return -2;
+  };
+  
+  if (mime_demux_mode == MIME_DEMUX_MODE_BASE64) {
+    /* ---------------------------------------------- */
+    
+    /* NULL out trailing '\r' and '\n' chars */
+    while (Ustrrchr(line,'\r') != NULL) {
+      *(Ustrrchr(line,'\r')) = '\0';
+    };
+    while (Ustrrchr(line,'\n') != NULL) {
+      *(Ustrrchr(line,'\n')) = '\0';
+    };
+    
+    /* check maximum base 64 line length */
+    if (Ustrlen(line) > MIME_SANITY_MAX_B64_LINE_LENGTH ) {
+      if (!warned_about_b64_line_length) {
+        mime_trigger_error(MIME_ERRORLEVEL_B64_LINE_LENGTH);
+        warned_about_b64_line_length = 1;
+      };
+    };
+
+    p = line;
+    offset = 0;
+    while (*(p+offset) != '\0') {
+      /* hit illegal char ? */
+      if (b64[*(p+offset)] == 128) {
+        if (!warned_about_b64_illegal_char) {
+          mime_trigger_error(MIME_ERRORLEVEL_B64_ILLEGAL_CHAR);
+          warned_about_b64_illegal_char = 1;
+        };
+        offset++;
+      }
+      else {
+        *p = b64[*(p+offset)];
+        p++;
+      };
+    };
+    *p = 255;
+   
+    /* check that resulting line length is a multiple of 4 */
+    if ( ( (p - &line[0]) % 4 ) != 0) {
+      if (!warned_about_b64_line_sanity) {
+        mime_trigger_error(MIME_ERRORLEVEL_B64_MISALIGNED);
+        warned_about_b64_line_sanity = 1;
+      };
+    };
+          
+    /* line is translated, start bit shifting */
+    p = line;
+    num_decoded = 0;
+          
+    while(*p != 255) {
+    
+      /* byte 0 ---------------------- */
+      if (*(p+1) == 255) {
+        break;
+      }
+      
+      (*data)[num_decoded] = *p;
+      (*data)[num_decoded] <<= 2;
+      
+      tmp_c = *(p+1);
+      tmp_c >>= 4;
+      (*data)[num_decoded] |= tmp_c;
+      
+      num_decoded++;
+      p++;
+       
+      /* byte 1 ---------------------- */
+      if (*(p+1) == 255) {
+        break;
+      }
+      
+      (*data)[num_decoded] = *p;
+      (*data)[num_decoded] <<= 4;
+      
+      tmp_c = *(p+1);
+      tmp_c >>= 2;
+      (*data)[num_decoded] |= tmp_c;
+      
+      num_decoded++;
+      p++;
+      
+      /* byte 2 ---------------------- */
+      if (*(p+1) == 255) {
+        break;
+      }
+      
+      (*data)[num_decoded] = *p;
+      (*data)[num_decoded] <<= 6;
+      
+      (*data)[num_decoded] |= *(p+1);
+      
+      num_decoded++;
+      p+=2;
+      
+    };
+    return num_decoded;   
+    /* ---------------------------------------------- */
+  }
+  else if (mime_demux_mode == MIME_DEMUX_MODE_QP) {
+    /* ---------------------------------------------- */
+    p = line;
+         
+    while (*p != 0) {
+      if (*p == '=') {
+        int decode_qp_result;
+        
+        p = mime_decode_qp(p,&decode_qp_result);
+              
+        if (decode_qp_result == -2) {
+          /* Error from decoder. p is unchanged. */
+          if (!warned_about_qp_line_sanity) {
+            mime_trigger_error(MIME_ERRORLEVEL_QP_ILLEGAL_CHAR);
+            warned_about_qp_line_sanity = 1;
+          };
+          (*data)[num_decoded] = '=';
+          num_decoded++;
+          p++;
+        }
+        else if (decode_qp_result == -1) {
+          /* End of the line with soft line break. 
+          Bail out. */
+          goto QP_RETURN;
+        }
+        else if (decode_qp_result >= 0) {
+          (*data)[num_decoded] = decode_qp_result;
+          num_decoded++;
+        };
+      }
+      else {
+        (*data)[num_decoded] = *p;
+        num_decoded++;
+        p++;
+      };
+    };
+    QP_RETURN:
+    return num_decoded;
+    /* ---------------------------------------------- */
+  };
+  
+  return 0;
+}
+
+
+
+/*************************************************
+* Log demime errors and set mime error level     *
+*************************************************/
+
+/* This sets the global demime_reason expansion
+variable and the demime_errorlevel gauge. */
+
+void mime_trigger_error(int level, uschar *format, ...) {
+  char *f;
+  va_list ap;
+
+  if( (f = malloc(16384+23)) != NULL ) {
+    /* first log the incident */
+    sprintf(f,"demime acl condition: ");
+    f+=22;
+    va_start(ap, format);
+    vsnprintf(f, 16383,(char *)format, ap);
+    va_end(ap);
+    f-=22;
+    log_write(0, LOG_MAIN, f);
+    /* then copy to demime_reason_buffer if new
+    level is greater than old level */
+    if (level > demime_errorlevel) {
+      demime_errorlevel = level;
+      Ustrcpy(demime_reason_buffer, US f);
+      demime_reason = demime_reason_buffer;
+    };
+    free(f);
+  };
+}
+
+/*************************************************
+* Demultiplex MIME stream.                       *
+*************************************************/
+
+/* We can handle BASE64, QUOTED-PRINTABLE, and UUENCODE.
+ UUENCODE does not need to have a proper
+ transfer-encoding header, we detect it with "begin"
+
+ This function will report human parsable errors in
+ *info.
+
+ returns DEFER -> soft error (see *info)
+         OK    -> EOF hit, all ok
+*/
+
+int mime_demux(FILE *f, uschar *info) {
+  int mime_demux_mode = MIME_DEMUX_MODE_MIME_HEADERS;
+  int uu_mode = MIME_UU_MODE_OFF;
+  FILE *mime_dump_file = NULL;
+  FILE *uu_dump_file = NULL;
+  uschar *line;
+  int mime_read_line_status = MIME_READ_LINE_OK;
+  long line_len;
+  struct boundary *boundaries = NULL;
+  struct mime_part mime_part_p;
+  int has_tnef = 0;
+  int has_rfc822 = 0;
+  
+  /* allocate room for our linebuffer */
+  line = (uschar *)malloc(MIME_SANITY_MAX_LINE_LENGTH);
+  if (line == NULL) {
+    snprintf(CS info, 1024,"unable to allocate %u bytes",MIME_SANITY_MAX_LINE_LENGTH);
+    return DEFER;
+  };
+  
+  /* clear MIME header structure */
+  memset(&mime_part_p,0,sizeof(mime_part));
+  
+  /* ----------------------- start demux loop --------------------- */
+  while (mime_read_line_status == MIME_READ_LINE_OK) {
+  
+    /* read a line of input. Depending on the mode we are in,
+    the returned format will differ. */
+    mime_read_line_status = mime_read_line(f,mime_demux_mode,line,&line_len);
+    
+    if (mime_read_line_status == MIME_READ_LINE_OVERFLOW) {
+      mime_trigger_error(MIME_ERRORLEVEL_LONG_LINE);
+      /* despite the error, continue  .. */
+      mime_read_line_status = MIME_READ_LINE_OK;
+      continue;
+    }
+    else if (mime_read_line_status == MIME_READ_LINE_EOF) {
+      break;
+    };
+
+    if (mime_demux_mode == MIME_DEMUX_MODE_MIME_HEADERS) {
+      /* -------------- header mode --------------------- */
+      
+      /* Check for an empty line, which is the end of the headers.
+       In HEADER mode, the line is returned "cooked", with the
+       final '\n' replaced by a ';' */
+      if (line_len == 1) {
+        int tmp;
+        
+        /* We have reached the end of the headers. Start decoding
+        with the collected settings. */
+        if (mime_part_p.seen_content_transfer_encoding > 1) {
+          mime_demux_mode = mime_part_p.seen_content_transfer_encoding;
+        }
+        else {
+          /* default to plain mode if no specific encoding type found */
+          mime_demux_mode = MIME_DEMUX_MODE_PLAIN;
+        };
+        
+        /* open new dump file */
+        tmp = mime_get_dump_file(mime_part_p.extension, &mime_dump_file, info);
+        if (tmp < 0) {
+          return DEFER;
+        };
+
+        /* clear out mime_part */
+        memset(&mime_part_p,0,sizeof(mime_part));
+      }
+      else {
+        /* Another header to check for file extensions,
+        encoding type and boundaries */
+        if (strncmpic(US"content-type:",line,Ustrlen("content-type:")) == 0) {
+          /* ---------------------------- Content-Type header ------------------------------- */
+          uschar *value = line;
+          
+          /* check for message/partial MIME type and reject it */
+          if (mime_header_find(line,US"message/partial",NULL) > 0)
+            mime_trigger_error(MIME_ERRORLEVEL_MESSAGE_PARTIAL);
+
+          /* check for TNEF content type, remember to unpack TNEF later. */
+          if (mime_header_find(line,US"application/ms-tnef",NULL) > 0)
+            has_tnef = 1;
+          
+          /* check for message/rfcxxx attachments */
+          if (mime_header_find(line,US"message/rfc822",NULL) > 0)
+            has_rfc822 = 1;
+          
+          /* find the file extension, but do not fill it in
+          it is already set, since content-disposition has
+          precedence. */
+          if (mime_part_p.extension == NULL) {
+            if (mime_header_find(line,US"name",&value) == 2) {
+              if (Ustrlen(value) > MIME_SANITY_MAX_FILENAME)
+                mime_trigger_error(MIME_ERRORLEVEL_FILENAME_LENGTH);
+              mime_part_p.extension = value;   
+              mime_part_p.extension = Ustrrchr(value,'.');
+              if (mime_part_p.extension == NULL) {
+                /* file without extension, setting
+                NULL will use the default extension later */
+                mime_part_p.extension = NULL;
+              }
+              else {
+                struct file_extension *this_extension =
+                  (struct file_extension *)malloc(sizeof(file_extension));
+                
+                this_extension->file_extension_string = 
+                  (uschar *)malloc(Ustrlen(mime_part_p.extension)+1);
+                Ustrcpy(this_extension->file_extension_string,
+                        mime_part_p.extension+1);
+                this_extension->next = file_extensions;
+                file_extensions = this_extension;
+              };
+            };
+          };
+        
+          /* find a boundary and add it to the list, if present */
+          value = line;
+          if (mime_header_find(line,US"boundary",&value) == 2) {
+            struct boundary *thisboundary;
+
+            if (Ustrlen(value) > MIME_SANITY_MAX_BOUNDARY_LENGTH) {
+              mime_trigger_error(MIME_ERRORLEVEL_BOUNDARY_LENGTH); 
+            }
+            else {
+              thisboundary = (struct boundary*)malloc(sizeof(boundary));
+              thisboundary->next = boundaries;
+              thisboundary->boundary_string = value;
+              boundaries = thisboundary;
+            };
+          };
+        
+          if (mime_part_p.seen_content_type == 0) {
+            mime_part_p.seen_content_type = 1;
+          }
+          else {
+            mime_trigger_error(MIME_ERRORLEVEL_DOUBLE_HEADERS);
+          };
+          /* ---------------------------------------------------------------------------- */
+        }
+        else if (strncmpic(US"content-transfer-encoding:",line,Ustrlen("content-transfer-encoding:")) == 0) {
+          /* ---------------------------- Content-Transfer-Encoding header -------------- */
+ 
+         if (mime_part_p.seen_content_transfer_encoding == 0) {
+            if (mime_header_find(line,US"base64",NULL) > 0) {
+              mime_part_p.seen_content_transfer_encoding = MIME_DEMUX_MODE_BASE64;
+            }
+            else if (mime_header_find(line,US"quoted-printable",NULL) > 0) {
+              mime_part_p.seen_content_transfer_encoding = MIME_DEMUX_MODE_QP;
+            }
+            else {
+              mime_part_p.seen_content_transfer_encoding = MIME_DEMUX_MODE_PLAIN;
+            };
+          }
+          else {
+            mime_trigger_error(MIME_ERRORLEVEL_DOUBLE_HEADERS);
+          };
+          /* ---------------------------------------------------------------------------- */
+        }
+        else if (strncmpic(US"content-disposition:",line,Ustrlen("content-disposition:")) == 0) {
+          /* ---------------------------- Content-Disposition header -------------------- */
+          uschar *value = line;
+          
+          if (mime_part_p.seen_content_disposition == 0) {
+            mime_part_p.seen_content_disposition = 1;
+          
+            if (mime_header_find(line,US"filename",&value) == 2) {
+              if (Ustrlen(value) > MIME_SANITY_MAX_FILENAME)
+                mime_trigger_error(MIME_ERRORLEVEL_FILENAME_LENGTH);
+              mime_part_p.extension = value;
+              mime_part_p.extension = Ustrrchr(value,'.');
+              if (mime_part_p.extension == NULL) {
+                /* file without extension, setting
+                NULL will use the default extension later */
+                mime_part_p.extension = NULL;
+              }
+              else {
+                struct file_extension *this_extension =
+                  (struct file_extension *)malloc(sizeof(file_extension));
+                
+                this_extension->file_extension_string = 
+                  (uschar *)malloc(Ustrlen(mime_part_p.extension)+1);
+                Ustrcpy(this_extension->file_extension_string,
+                        mime_part_p.extension+1);
+                this_extension->next = file_extensions;
+                file_extensions = this_extension;
+              };
+            };
+          }
+          else {
+            mime_trigger_error(MIME_ERRORLEVEL_DOUBLE_HEADERS);
+          };
+          /* ---------------------------------------------------------------------------- */
+        };
+      };    /* End of header checks */
+      /* ------------------------------------------------ */
+    }
+    else {
+      /* -------------- non-header mode ----------------- */
+      int tmp;
+
+      if (uu_mode == MIME_UU_MODE_OFF) {
+        uschar uu_file_extension[5];
+        /* We are not currently decoding UUENCODE
+        Check for possible UUENCODE start tag. */
+        if (mime_check_uu_start(line,uu_file_extension,&has_tnef)) {
+          /* possible UUENCODING start detected.
+          Set unconfirmed mode first. */
+          uu_mode = MIME_UU_MODE_UNCONFIRMED;
+          /* open new uu dump file */
+          tmp = mime_get_dump_file(uu_file_extension, &uu_dump_file, info);
+          if (tmp < 0) {
+            free(line);
+            return DEFER;
+          };
+        };
+      }
+      else {
+        uschar *data;
+        long data_len = 0;
+
+        if (uu_mode == MIME_UU_MODE_UNCONFIRMED) {
+         /* We are in unconfirmed UUENCODE mode. */
+         
+         data_len = uu_decode_line(line,&data,line_len,info);
+         
+         if (data_len == -2) {
+           /* temp error, turn off uudecode mode */
+           if (uu_dump_file != NULL) {
+            fclose(uu_dump_file); uu_dump_file = NULL;
+           };
+           uu_mode = MIME_UU_MODE_OFF;
+           return DEFER;
+         }
+         else if (data_len == -1) {
+           if (uu_dump_file != NULL) {
+            fclose(uu_dump_file); uu_dump_file = NULL;
+           };
+           uu_mode = MIME_UU_MODE_OFF;
+           data_len = 0;
+         }
+         else if (data_len > 0) {
+           /* we have at least decoded a valid byte
+           turn on confirmed mode */
+           uu_mode = MIME_UU_MODE_CONFIRMED;
+         };
+        }
+        else if (uu_mode == MIME_UU_MODE_CONFIRMED) {
+          /* If we are in confirmed UU mode,
+          check for single "end" tag on line */
+          if ((strncmpic(line,US"end",3) == 0) && (line[3] < 32)) {
+            if (uu_dump_file != NULL) {
+              fclose(uu_dump_file); uu_dump_file = NULL;
+            };
+            uu_mode = MIME_UU_MODE_OFF;
+          }
+          else {
+            data_len = uu_decode_line(line,&data,line_len,info);
+            if (data_len == -2) {
+               /* temp error, turn off uudecode mode */
+               if (uu_dump_file != NULL) {
+                 fclose(uu_dump_file); uu_dump_file = NULL;
+               };
+               uu_mode = MIME_UU_MODE_OFF;
+               return DEFER;
+             }
+             else if (data_len == -1) {
+               /* skip this line */
+               data_len = 0;
+             };
+          };
+        };
+       
+        /* write data to dump file, if available */
+        if (data_len > 0) {
+          if (fwrite(data,1,data_len,uu_dump_file) < data_len) {
+            /* short write */
+            snprintf(CS info, 1024,"short write on uudecode dump file");
+            free(line);
+            return DEFER;            
+          };
+        };
+      };
+      
+      if (mime_demux_mode != MIME_DEMUX_MODE_SCANNING) {
+        /* Non-scanning and Non-header mode. That means
+        we are currently decoding data to the dump
+        file. */
+
+        /* Check for a known boundary. */
+        tmp = mime_check_boundary(line,boundaries);
+        if (tmp == 1) {
+          /* We have hit a known start boundary.
+          That will put us back in header mode. */
+          mime_demux_mode = MIME_DEMUX_MODE_MIME_HEADERS;
+          if (mime_dump_file != NULL) {
+            /* if the attachment was a RFC822 message, recurse into it */
+            if (has_rfc822) {
+              has_rfc822 = 0;
+              rewind(mime_dump_file);
+              mime_demux(mime_dump_file,info);
+            };
+            
+            fclose(mime_dump_file); mime_dump_file = NULL;
+          };
+        }
+        else if (tmp == 2) {
+          /* We have hit a known end boundary.
+          That puts us into scanning mode, which will end when we hit another known start boundary */
+          mime_demux_mode = MIME_DEMUX_MODE_SCANNING;
+          if (mime_dump_file != NULL) {
+            /* if the attachment was a RFC822 message, recurse into it */
+            if (has_rfc822) {
+              has_rfc822 = 0;
+              rewind(mime_dump_file);
+              mime_demux(mime_dump_file,info);
+            };
+            
+            fclose(mime_dump_file); mime_dump_file = NULL;
+          };
+        }
+        else {
+          uschar *data;
+          long data_len = 0;
+          
+          /* decode the line with the appropriate method */
+          if (mime_demux_mode == MIME_DEMUX_MODE_PLAIN) {
+            /* in plain mode, just dump the line */
+            data = line;
+            data_len = line_len;
+          }
+          else if ( (mime_demux_mode == MIME_DEMUX_MODE_QP) || (mime_demux_mode == MIME_DEMUX_MODE_BASE64) ) {
+            data_len = mime_decode_line(mime_demux_mode,line,&data,line_len,info);
+            if (data_len < 0) {
+              /* Error reported from the line decoder. */
+              data_len = 0;
+            };
+          };
+          
+          /* write data to dump file */
+          if (data_len > 0) {
+            if (fwrite(data,1,data_len,mime_dump_file) < data_len) {
+              /* short write */
+              snprintf(CS info, 1024,"short write on dump file");
+              free(line);
+              return DEFER;            
+            };
+          };
+          
+        };
+      }
+      else {
+        /* Scanning mode. We end up here after a end boundary.
+        This will usually be at the end of a message or at
+        the end of a MIME container.
+        We need to look for another start boundary to get
+        back into header mode. */
+        if (mime_check_boundary(line,boundaries) == 1) {
+          mime_demux_mode = MIME_DEMUX_MODE_MIME_HEADERS;
+        };
+        
+      };
+      /* ------------------------------------------------ */
+    };
+  };
+  /* ----------------------- end demux loop ----------------------- */
+  
+  /* close files, they could still be open */
+  if (mime_dump_file != NULL)
+    fclose(mime_dump_file);
+  if (uu_dump_file != NULL)
+    fclose(uu_dump_file);
+  
+  /* release line buffer */
+  free(line);
+  
+  /* FIXME: release boundary buffers.
+  Not too much of a problem since
+  this instance of exim is not resident. */
+  
+  if (has_tnef) {
+    uschar file_name[1024];
+    /* at least one file could be TNEF encoded.
+    attempt to send all decoded files thru the TNEF decoder */
+    
+    snprintf(CS file_name,1024,"%s/scan/%s",spool_directory,message_id);
+    mime_unpack_tnef(file_name);
+  };
+
+  return 0;
+}
+
diff -urN exim-4.44-orig/src/demime.h exim-4.44/src/demime.h
--- exim-4.44-orig/src/demime.h	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/demime.h	Fri Jan 14 20:35:02 2005
@@ -0,0 +1,146 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* demime defines */
+
+#define MIME_DEMUX_MODE_SCANNING     0
+#define MIME_DEMUX_MODE_MIME_HEADERS 1
+#define MIME_DEMUX_MODE_BASE64       2
+#define MIME_DEMUX_MODE_QP           3
+#define MIME_DEMUX_MODE_PLAIN        4
+
+#define MIME_UU_MODE_OFF             0
+#define MIME_UU_MODE_UNCONFIRMED     1
+#define MIME_UU_MODE_CONFIRMED       2
+
+#define MIME_MAX_EXTENSION 128
+
+#define MIME_READ_LINE_EOF 0
+#define MIME_READ_LINE_OK 1
+#define MIME_READ_LINE_OVERFLOW 2
+
+#define MIME_SANITY_MAX_LINE_LENGTH 131071
+#define MIME_SANITY_MAX_FILENAME 512
+#define MIME_SANITY_MAX_HEADER_OPTION_VALUE 1024
+#define MIME_SANITY_MAX_B64_LINE_LENGTH 76
+#define MIME_SANITY_MAX_BOUNDARY_LENGTH 1024
+#define MIME_SANITY_MAX_DUMP_FILES 1024
+
+
+
+/* MIME errorlevel settings */
+
+#define MIME_ERRORLEVEL_LONG_LINE        3,US"line length in message or single header size exceeds %u bytes",MIME_SANITY_MAX_LINE_LENGTH
+#define MIME_ERRORLEVEL_TOO_MANY_PARTS   3,US"too many MIME parts (max %u)",MIME_SANITY_MAX_DUMP_FILES
+#define MIME_ERRORLEVEL_MESSAGE_PARTIAL  3,US"'message/partial' MIME type"
+#define MIME_ERRORLEVEL_FILENAME_LENGTH  3,US"proposed filename exceeds %u characters",MIME_SANITY_MAX_FILENAME
+#define MIME_ERRORLEVEL_BOUNDARY_LENGTH  3,US"boundary length exceeds %u characters",MIME_SANITY_MAX_BOUNDARY_LENGTH
+#define MIME_ERRORLEVEL_DOUBLE_HEADERS   2,US"double headers (content-type, content-disposition or content-transfer-encoding)"
+#define MIME_ERRORLEVEL_UU_MISALIGNED    1,US"uuencoded line length is not a multiple of 4 characters"
+#define MIME_ERRORLEVEL_UU_LINE_LENGTH   1,US"uuencoded line length does not match advertised number of bytes"
+#define MIME_ERRORLEVEL_B64_LINE_LENGTH  1,US"base64 line length exceeds %u characters",MIME_SANITY_MAX_B64_LINE_LENGTH
+#define MIME_ERRORLEVEL_B64_ILLEGAL_CHAR 2,US"base64 line contains illegal character"
+#define MIME_ERRORLEVEL_B64_MISALIGNED   1,US"base64 line length is not a multiple of 4 characters"
+#define MIME_ERRORLEVEL_QP_ILLEGAL_CHAR  1,US"quoted-printable encoding contains illegal character"
+
+
+/* demime structures */
+
+typedef struct mime_part {
+  /* true if there was a content-type header */
+  int seen_content_type;
+  /* true if there was a content-transfer-encoding header
+     contains the encoding type */
+  int seen_content_transfer_encoding;
+  /* true if there was a content-disposition header */
+  int seen_content_disposition;
+  /* pointer to a buffer with the proposed file extension */
+  uschar *extension;
+} mime_part;
+
+typedef struct boundary {
+  struct boundary *next;
+  uschar *boundary_string;
+} boundary;
+
+typedef struct file_extension {
+  struct file_extension *next;
+  uschar *file_extension_string;
+} file_extension;
+
+/* available functions for the TNEF library (tnef.c & tnef.h) */
+
+extern int TNEF_main( char *filename );
+extern int TNEF_set_verbosity( int level );
+extern int TNEF_set_debug( int level );
+extern int TNEF_set_syslogging( int level );
+extern int TNEF_set_stderrlogging( int level );
+extern int TNEF_set_path( char *path );
+
+
+
+/* demime.c prototypes */
+
+int          mime_unpack_tnef(uschar *);
+unsigned int mime_hstr_i(uschar *);
+uschar      *mime_decode_qp(uschar *, int *);
+int          mime_get_dump_file(uschar *, FILE **, uschar *);
+int          mime_header_find(uschar *, uschar *, uschar **);
+int          mime_read_line(FILE *, int, uschar *, long *);
+int          mime_check_boundary(uschar *, struct boundary *);
+int          mime_check_uu_start(uschar *, uschar *, int *);
+long         uu_decode_line(uschar *, uschar **, long, uschar *);
+long         mime_decode_line(int ,uschar *, uschar **, long, uschar *);
+void         mime_trigger_error(int, uschar *, ...);
+int          mime_demux(FILE *, uschar *);
+
+
+
+/* BASE64 decoder matrix */
+static unsigned char b64[256]={
+/*   0 */ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/*  16 */ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128, 
+/*  32 */ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,   62,  128,  128,  128,   63,
+/*  48 */  52,   53,   54,   55,   56,   57,   58,   59,   60,   61,  128,  128,  128,  255,  128,  128,
+/*  64 */ 128,    0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,
+/*  80 */  15,   16,   17,   18,   19,   20,   21,   22,   23,   24,   25,  128,  128,  128,  128,  128,
+/*  96 */ 128,   26,   27,   28,   29,   30,   31,   32,   33,   34,   35,   36,   37,   38,   39,   40,
+  41,   42,   43,   44,   45,   46,   47,   48,   49,   50,   51,  128,  128,  128,  128,  128,
+ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+ 128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128 
+};
+
+
+/* Microsoft-Style uudecode matrix */
+static unsigned char uudec[256]={
+/*   0 */   0,   33,   34,   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,   45,   46,   47,  
+/*  16 */  48,   49,   50,   51,   52,   53,   54,   55,   56,   57,   58,   59,   60,   61,   62,   63,
+/*  32 */   0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,   15,
+/*  48 */  16,   17,   18,   19,   20,   21,   22,   23,   24,   25,   26,   27,   28,   29,   30,   31,
+/*  64 */  32,   33,   34,   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,   45,   46,   47,
+/*  80 */  48,   49,   50,   51,   52,   53,   54,   55,   56,   57,   58,   59,   60,   61,   62,   63,
+/*  96 */   0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,   15,
+/* 112 */  16,   17,   18,   19,   20,   21,   22,   23,   24,   25,   26,   27,   28,   29,   30,   31,
+/* 128 */  32,   33,   34,   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,   45,   46,   47,
+/* 144 */  48,   49,   50,   51,   52,   53,   54,   55,   56,   57,   58,   59,   60,   61,   62,   63,
+/* 160 */   0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,   15,
+/* 176 */  16,   17,   18,   19,   20,   21,   22,   23,   24,   25,   26,   27,   28,   29,   30,   31,
+/* 192 */  32,   33,   34,   35,   36,   37,   38,   39,   40,   41,   42,   43,   44,   45,   46,   47,
+/* 208 */  48,   49,   50,   51,   52,   53,   54,   55,   56,   57,   58,   59,   60,   61,   62,   63,
+/* 224 */   0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,   15,
+/* 240 */  16,   17,   18,   19,   20,   21,   22,   23,   24,   25,   26,   27,   28,   29,   30,   31 
+};
+
diff -urN exim-4.44-orig/src/exim.c exim-4.44/src/exim.c
--- exim-4.44-orig/src/exim.c	Thu Jan 13 12:49:18 2005
+++ exim-4.44/src/exim.c	Fri Jan 14 20:35:02 2005
@@ -1744,6 +1744,10 @@
       printf("%s\n", CS version_copyright);
       version_printed = TRUE;
       show_whats_supported(stdout);
+      printf("Contains exiscan-acl patch revision %s (c) Tom Kistner [http://duncanthrax.net/exiscan/]\n", exiscan_version_string);
+#ifdef BRIGHTMAIL
+      printf("Contains Brightmail AntiSpam support via the exiscan-acl patch.\n");
+#endif      
       }
 
     else badarg = TRUE;
diff -urN exim-4.44-orig/src/expand.c exim-4.44/src/expand.c
--- exim-4.44-orig/src/expand.c	Tue Jan 11 21:16:48 2005
+++ exim-4.44/src/expand.c	Fri Jan 14 20:35:02 2005
@@ -314,6 +314,12 @@
   { "authenticated_id",    vtype_stringptr,   &authenticated_id },
   { "authenticated_sender",vtype_stringptr,   &authenticated_sender },
   { "authentication_failed",vtype_int,        &authentication_failed },
+#ifdef BRIGHTMAIL
+  { "bmi_alt_location",    vtype_stringptr,   &bmi_alt_location },
+  { "bmi_base64_tracker_verdict", vtype_stringptr, &bmi_base64_tracker_verdict },
+  { "bmi_base64_verdict",  vtype_stringptr,   &bmi_base64_verdict },
+  { "bmi_deliver",         vtype_int,         &bmi_deliver },
+#endif
   { "body_linecount",      vtype_int,         &body_linecount },
   { "body_zerocount",      vtype_int,         &body_zerocount },
   { "bounce_recipient",    vtype_stringptr,   &bounce_recipient },
@@ -322,6 +328,8 @@
   { "caller_uid",          vtype_uid,         &real_uid },
   { "compile_date",        vtype_stringptr,   &version_date },
   { "compile_number",      vtype_stringptr,   &version_cnumber },
+  { "demime_errorlevel",   vtype_int,         &demime_errorlevel },
+  { "demime_reason",       vtype_stringptr,   &demime_reason },
   { "dnslist_domain",      vtype_stringptr,   &dnslist_domain },
   { "dnslist_text",        vtype_stringptr,   &dnslist_text },
   { "dnslist_value",       vtype_stringptr,   &dnslist_value },
@@ -330,6 +338,7 @@
   { "exim_gid",            vtype_gid,         &exim_gid },
   { "exim_path",           vtype_stringptr,   &exim_path },
   { "exim_uid",            vtype_uid,         &exim_uid },
+  { "found_extension",     vtype_stringptr,   &found_extension },
   { "home",                vtype_stringptr,   &deliver_home },
   { "host",                vtype_stringptr,   &deliver_host },
   { "host_address",        vtype_stringptr,   &deliver_host_address },
@@ -351,6 +360,7 @@
   { "local_user_uid",      vtype_uid,         &local_user_uid },
   { "localhost_number",    vtype_int,         &host_number },
   { "mailstore_basename",  vtype_stringptr,   &mailstore_basename },
+  { "malware_name",        vtype_stringptr,   &malware_name },
   { "message_age",         vtype_int,         &message_age },
   { "message_body",        vtype_msgbody,     &message_body },
   { "message_body_end",    vtype_msgbody_end, &message_body_end },
@@ -358,6 +368,22 @@
   { "message_headers",     vtype_msgheaders,  NULL },
   { "message_id",          vtype_stringptr,   &message_id },
   { "message_size",        vtype_int,         &message_size },
+  { "mime_anomaly_level",  vtype_int,         &mime_anomaly_level },
+  { "mime_anomaly_text",   vtype_stringptr,   &mime_anomaly_text },
+  { "mime_boundary",       vtype_stringptr,   &mime_boundary },
+  { "mime_charset",        vtype_stringptr,   &mime_charset },
+  { "mime_content_description", vtype_stringptr, &mime_content_description },
+  { "mime_content_disposition", vtype_stringptr, &mime_content_disposition },
+  { "mime_content_id",     vtype_stringptr,   &mime_content_id },
+  { "mime_content_size",   vtype_int,         &mime_content_size },
+  { "mime_content_transfer_encoding",vtype_stringptr, &mime_content_transfer_encoding },
+  { "mime_content_type",   vtype_stringptr,   &mime_content_type },
+  { "mime_decoded_filename", vtype_stringptr, &mime_decoded_filename },
+  { "mime_filename",       vtype_stringptr,   &mime_filename },
+  { "mime_is_coverletter", vtype_int,         &mime_is_coverletter },
+  { "mime_is_multipart",   vtype_int,         &mime_is_multipart },
+  { "mime_is_rfc822",      vtype_int,         &mime_is_rfc822 },
+  { "mime_part_count",     vtype_int,         &mime_part_count },
   { "n0",                  vtype_filter_int,  &filter_n[0] },
   { "n1",                  vtype_filter_int,  &filter_n[1] },
   { "n2",                  vtype_filter_int,  &filter_n[2] },
@@ -387,6 +413,7 @@
   { "recipient_data",      vtype_stringptr,   &recipient_data },
   { "recipients",          vtype_recipients,  NULL },
   { "recipients_count",    vtype_int,         &recipients_count },
+  { "regex_match_string",  vtype_stringptr,   &regex_match_string },
   { "reply_address",       vtype_reply,       NULL },
   { "return_path",         vtype_stringptr,   &return_path },
   { "return_size_limit",   vtype_int,         &bounce_return_size_limit },
@@ -416,7 +443,25 @@
   { "sn7",                 vtype_filter_int,  &filter_sn[7] },
   { "sn8",                 vtype_filter_int,  &filter_sn[8] },
   { "sn9",                 vtype_filter_int,  &filter_sn[9] },
+  { "spam_bar",            vtype_stringptr,   &spam_bar },
+  { "spam_report",         vtype_stringptr,   &spam_report },
+  { "spam_score",          vtype_stringptr,   &spam_score },
+  { "spam_score_int",      vtype_stringptr,   &spam_score_int },
+#ifdef SPF
+  { "spf_header_comment",  vtype_stringptr,   &spf_header_comment },
+  { "spf_received",        vtype_stringptr,   &spf_received },
+  { "spf_result",          vtype_stringptr,   &spf_result },
+  { "spf_smtp_comment",    vtype_stringptr,   &spf_smtp_comment },
+#endif
   { "spool_directory",     vtype_stringptr,   &spool_directory },
+#ifdef SRS
+  { "srs_db_address",      vtype_stringptr,   &srs_db_address },
+  { "srs_db_key",          vtype_stringptr,   &srs_db_key },
+  { "srs_orig_recipient",  vtype_stringptr,   &srs_orig_recipient },
+  { "srs_orig_sender",     vtype_stringptr,   &srs_orig_sender },
+  { "srs_recipient",       vtype_stringptr,   &srs_recipient },
+  { "srs_status",          vtype_stringptr,   &srs_status },
+#endif
   { "thisaddress",         vtype_stringptr,   &filter_thisaddress },
   { "tls_certificate_verified", vtype_int,    &tls_certificate_verified },
   { "tls_cipher",          vtype_stringptr,   &tls_cipher },
diff -urN exim-4.44-orig/src/functions.h exim-4.44/src/functions.h
--- exim-4.44-orig/src/functions.h	Tue Jan 11 21:16:48 2005
+++ exim-4.44/src/functions.h	Fri Jan 14 20:35:02 2005
@@ -66,6 +66,7 @@
 extern void    deliver_set_expansions(address_item *);
 extern int     deliver_split_address(address_item *);
 extern void    deliver_succeeded(address_item *);
+extern int     demime(uschar **);
 extern BOOL    directory_make(uschar *, uschar *, int, BOOL);
 extern dns_address *dns_address_from_rr(dns_answer *, dns_record *);
 extern void    dns_build_reverse(uschar *, uschar *);
@@ -123,6 +124,7 @@
 extern uschar *local_part_quote(uschar *lpart);
 extern void    log_close_all(void);
 
+extern int     malware(uschar **);
 extern int     match_address_list(uschar *, BOOL, BOOL, uschar **,
                  unsigned int *, int, int, uschar **);
 extern int     match_check_list(uschar **, int, tree_node **, unsigned int **,
@@ -136,6 +138,11 @@
 extern void    md5_mid(md5 *, const uschar *);
 extern void    md5_start(md5 *);
 extern void    millisleep(int);
+struct mime_boundary_context;
+extern int     mime_acl_check(FILE *f, struct mime_boundary_context *,
+                              uschar **, uschar **);
+extern int     mime_decode(uschar **);
+extern int     mime_regex(uschar **);
 extern uschar *moan_check_errorcopy(uschar *);
 extern BOOL    moan_skipped_syntax_errors(uschar *, error_block *, uschar *,
                  BOOL, uschar *);
@@ -160,6 +167,7 @@
 extern void    queue_run(uschar *, uschar *, BOOL);
 
 extern int     random_number(int);
+extern int     recv_line(int, uschar *, int);
 extern int     rda_interpret(redirect_block *, int, uschar *, uschar *, ugid_block *,
                  address_item **, uschar **, error_block **, int *, uschar *);
 extern int     rda_is_filter(const uschar *);
@@ -179,6 +187,7 @@
 extern BOOL    receive_check_set_sender(uschar *);
 extern BOOL    receive_msg(BOOL);
 extern void    receive_swallow_smtp(void);
+extern int     regex(uschar **);
 extern BOOL    regex_match_and_setup(const pcre *, uschar *, int, int);
 extern const pcre *regex_must_compile(uschar *, BOOL, BOOL);
 extern void    retry_add_item(address_item *, uschar *, int);
@@ -239,6 +248,8 @@
 extern BOOL    smtp_start_session(void);
 extern int     smtp_ungetc(int);
 extern int     smtp_write_command(smtp_outblock *, BOOL, char *, ...);
+extern int     spam(uschar **);
+extern FILE   *spool_mbox(unsigned long long *);
 extern BOOL    spool_move_message(uschar *, uschar *, uschar *, uschar *);
 extern BOOL    spool_open_datafile(uschar *);
 extern int     spool_open_temp(uschar *);
@@ -290,6 +301,8 @@
 extern tree_node *tree_search(tree_node *, uschar *);
 extern void    tree_write(tree_node *, FILE *);
 
+extern void    unspool_mbox(void);
+
 extern int     verify_address(address_item *, FILE *, int, int, int, uschar *,
                  uschar *, BOOL *);
 extern int     verify_check_dnsbl(uschar **);
diff -urN exim-4.44-orig/src/globals.c exim-4.44/src/globals.c
--- exim-4.44-orig/src/globals.c	Tue Jan 11 14:43:27 2005
+++ exim-4.44/src/globals.c	Fri Jan 14 20:35:02 2005
@@ -153,6 +153,7 @@
 /* General global variables */
 
 tree_node *acl_anchor          = NULL;
+int     acl_header_pos         = 0;
 uschar *acl_not_smtp           = NULL;
 uschar *acl_smtp_auth          = NULL;
 uschar *acl_smtp_connect       = NULL;
@@ -162,6 +163,7 @@
 uschar *acl_smtp_helo          = NULL;
 uschar *acl_smtp_mail          = NULL;
 uschar *acl_smtp_mailauth      = NULL;
+uschar *acl_smtp_mime          = NULL;
 uschar *acl_smtp_predata       = NULL;
 uschar *acl_smtp_quit          = NULL;
 uschar *acl_smtp_rcpt          = NULL;
@@ -179,6 +181,7 @@
 uschar *acl_wherenames[]       = { US"RCPT",
                                    US"MAIL",
                                    US"PREDATA",
+                                   US"MIME",
                                    US"DATA",
                                    US"non-SMTP",
                                    US"AUTH",
@@ -195,6 +198,7 @@
 int     acl_wherecodes[]       = { 550,     /* RCPT */
                                    550,     /* MAIL */
                                    550,     /* PREDATA */
+                                   550,     /* MIME */
                                    550,     /* DATA */
                                    0,       /* not SMTP; not relevant */
                                    503,     /* AUTH */
@@ -305,6 +309,7 @@
 uschar *auth_defer_msg         = US"reason not recorded";
 uschar *auth_defer_user_msg    = US"";
 int     auto_thaw              = 0;
+uschar *av_scanner             = US"sophie:/var/run/sophie";
 
 BOOL    background_daemon      = TRUE;
 uschar *base62_chars=
@@ -312,6 +317,15 @@
 uschar *bi_command             = NULL;
 uschar *big_buffer             = NULL;
 int     big_buffer_size        = BIG_BUFFER_SIZE;
+#ifdef BRIGHTMAIL
+uschar *bmi_alt_location       = NULL;
+uschar *bmi_base64_tracker_verdict = NULL;
+uschar *bmi_base64_verdict     = NULL;
+uschar *bmi_config_file        = US"/opt/brightmail/etc/brightmail.cfg";
+int     bmi_deliver            = 1;
+int     bmi_run                = 0;
+uschar *bmi_verdicts           = NULL;
+#endif
 int     body_linecount         = 0;
 int     body_zerocount         = 0;
 uschar *bounce_message_file    = NULL;
@@ -427,6 +441,9 @@
 BOOL    deliver_selectstring_regex = FALSE;
 uschar *deliver_selectstring_sender = NULL;
 BOOL    deliver_selectstring_sender_regex = FALSE;
+int     demime_errorlevel      = 0;
+int     demime_ok              = 0;
+uschar *demime_reason          = NULL;
 BOOL    disable_logging        = FALSE;
 
 uschar *dns_again_means_nonexist = NULL;
@@ -456,6 +473,7 @@
                         "\0<---------------Space to patch exim_path->";
 uid_t   exim_uid               = EXIM_UID;
 BOOL    exim_uid_set           = TRUE;          /* This uid is always set */
+uschar *exiscan_version_string = US"??";
 int     expand_forbid          = 0;
 int     expand_nlength[EXPAND_MAXN+1];
 int     expand_nmax            = -1;
@@ -465,12 +483,14 @@
 BOOL    extract_addresses_remove_arguments = TRUE;
 uschar *extra_local_interfaces = NULL;
 
+BOOL    fake_reject            = FALSE;
 int     filter_n[FILTER_VARIABLE_COUNT];
 BOOL    filter_running         = FALSE;
 int     filter_sn[FILTER_VARIABLE_COUNT];
 uschar *filter_test            = NULL;
 uschar *filter_thisaddress     = NULL;
 int     finduser_retries       = 0;
+uschar *found_extension        = NULL;
 uid_t   fixed_never_users[]    = { FIXED_NEVER_USERS };
 uschar *freeze_tell            = NULL;
 uschar *fudged_queue_times     = US"";
@@ -625,6 +645,7 @@
 
 macro_item  *macros            = NULL;
 uschar *mailstore_basename     = NULL;
+uschar *malware_name           = NULL;
 int     max_username_length    = 0;
 int     message_age            = 0;
 uschar *message_body           = NULL;
@@ -645,9 +666,26 @@
 uschar *message_size_limit     = US"50M";
 uschar  message_subdir[2]      = { 0, 0 };
 uschar *message_reference      = NULL;
+uschar *mime_anomaly_level     = NULL;
+uschar *mime_anomaly_text      = NULL;
+uschar *mime_boundary          = NULL;
+uschar *mime_charset           = NULL;
+uschar *mime_content_description = NULL;
+uschar *mime_content_disposition = NULL;
+uschar *mime_content_id        = NULL;
+unsigned int mime_content_size = 0;
+uschar *mime_content_transfer_encoding = NULL;
+uschar *mime_content_type      = NULL;
+uschar *mime_decoded_filename  = NULL;
+uschar *mime_filename          = NULL;
+int     mime_is_multipart      = 0;
+int     mime_is_coverletter    = 0;
+int     mime_is_rfc822         = 0;
+int     mime_part_count        = -1;
 BOOL    mua_wrapper            = FALSE;
 
 uid_t  *never_users            = NULL;
+BOOL    no_mbox_unspool        = FALSE;
 BOOL    no_multiline_responses = FALSE;
 
 uid_t   original_euid;
@@ -747,6 +785,7 @@
 const pcre *regex_PIPELINING   = NULL;
 const pcre *regex_SIZE         = NULL;
 const pcre *regex_ismsgid      = NULL;
+uschar *regex_match_string     = NULL;
 int     remote_delivery_count  = 0;
 int     remote_max_parallel    = 2;
 uschar *remote_sort_domains    = NULL;
@@ -771,6 +810,9 @@
     NULL,                      /* driver name */
 
     NULL,                      /* address_data */
+#ifdef BRIGHTMAIL
+    NULL,                      /* bmi_rule */
+#endif
     NULL,                      /* cannot_route_message */
     NULL,                      /* condition */
     NULL,                      /* current_directory */
@@ -799,6 +841,11 @@
     NULL,                      /* transport_name */
 
     TRUE,                      /* address_test */
+#ifdef BRIGHTMAIL
+    FALSE,                     /* bmi_deliver_alternate */
+    FALSE,                     /* bmi_deliver_default */
+    FALSE,                     /* bmi_dont_deliver */
+#endif
     TRUE,                      /* expn */
     FALSE,                     /* caseful_local_part */
     FALSE,                     /* check_local_user */
@@ -926,9 +973,29 @@
 int     smtp_rlr_threshold     = INT_MAX;
 BOOL    smtp_use_pipelining    = FALSE;
 BOOL    smtp_use_size          = FALSE;
+uschar *spamd_address          = US"127.0.0.1 783";
+uschar *spam_bar               = NULL;
+uschar *spam_report            = NULL;
+uschar *spam_score             = NULL;
+uschar *spam_score_int         = NULL;
+#ifdef SPF
+uschar *spf_header_comment     = NULL;
+uschar *spf_received           = NULL;
+uschar *spf_result             = NULL;
+uschar *spf_smtp_comment       = NULL;
+#endif
 BOOL    split_spool_directory  = FALSE;
 uschar *spool_directory        = US SPOOL_DIRECTORY
                            "\0<--------------Space to patch spool_directory->";
+#ifdef SRS
+uschar *srs_config             = NULL;
+uschar *srs_db_address         = NULL;
+uschar *srs_db_key             = NULL;
+uschar *srs_orig_recipient     = NULL;
+uschar *srs_orig_sender        = NULL;
+uschar *srs_recipient          = NULL;
+uschar *srs_status             = NULL;
+#endif
 int     string_datestamp_offset= -1;
 BOOL    strip_excess_angle_brackets = FALSE;
 BOOL    strip_trailing_dot     = FALSE;
diff -urN exim-4.44-orig/src/globals.h exim-4.44/src/globals.h
--- exim-4.44-orig/src/globals.h	Tue Jan 11 14:43:27 2005
+++ exim-4.44/src/globals.h	Fri Jan 14 20:35:02 2005
@@ -95,6 +95,7 @@
 
 extern BOOL    accept_8bitmime;        /* Allow *BITMIME incoming */
 extern tree_node *acl_anchor;          /* Tree of named ACLs */
+extern int     acl_header_pos;         /* Where to put headers in MAIL, RCPT, DATA, MIME and non-SMTP ACLs */
 extern uschar *acl_not_smtp;           /* ACL run for non-SMTP messages */
 extern uschar *acl_smtp_auth;          /* ACL run for AUTH */
 extern uschar *acl_smtp_connect;       /* ACL run on SMTP connection */
@@ -104,6 +105,7 @@
 extern uschar *acl_smtp_helo;          /* ACL run for HELO/EHLO */
 extern uschar *acl_smtp_mail;          /* ACL run for MAIL */
 extern uschar *acl_smtp_mailauth;      /* ACL run for MAIL AUTH */
+extern uschar *acl_smtp_mime;          /* ACL run after DATA, before acl_smtp_data, for each MIME part */
 extern uschar *acl_smtp_predata;       /* ACL run for DATA command */
 extern uschar *acl_smtp_quit;          /* ACL run for QUIT */
 extern uschar *acl_smtp_rcpt;          /* ACL run for RCPT */
@@ -140,12 +142,22 @@
 extern uschar *auth_defer_msg;         /* Error message for log */
 extern uschar *auth_defer_user_msg;    /* Error message for user */
 extern int     auto_thaw;              /* Auto-thaw interval */
+extern uschar *av_scanner;             /* AntiVirus scanner to use for the malware condition */
 
 extern BOOL    background_daemon;      /* Set FALSE to keep in foreground */
 extern uschar *base62_chars;           /* Table of base-62 characters */
 extern uschar *bi_command;             /* Command for -bi option */
 extern uschar *big_buffer;             /* Used for various temp things */
 extern int     big_buffer_size;        /* Current size (can expand) */
+#ifdef BRIGHTMAIL
+extern uschar *bmi_alt_location;       /* expansion variable that contains the alternate location for the rcpt (available during routing) */
+extern uschar *bmi_base64_tracker_verdict; /* expansion variable with base-64 encoded OLD verdict string (available during routing) */
+extern uschar *bmi_base64_verdict;     /* expansion variable with base-64 encoded verdict string (available during routing) */
+extern uschar *bmi_config_file;        /* Brightmail config file */
+extern int     bmi_deliver;            /* Flag that determines if the message should be delivered to the rcpt (available during routing) */
+extern int     bmi_run;                /* Flag that determines if message should be run through Brightmail server */
+extern uschar *bmi_verdicts;           /* BASE64-encoded verdicts with recipient lists */
+#endif
 extern int     body_linecount;         /* Line count in body */
 extern int     body_zerocount;         /* Binary zero count in body */
 extern uschar *bounce_message_file;    /* Template file */
@@ -227,6 +239,9 @@
 extern BOOL    deliver_selectstring_regex; /* String is regex */
 extern uschar *deliver_selectstring_sender; /* For selecting by sender */
 extern BOOL    deliver_selectstring_sender_regex; /* String is regex */
+extern int     demime_errorlevel;      /* Severity of MIME error */
+extern int     demime_ok;              /* Nonzero if message has been demimed */
+extern uschar *demime_reason;          /* Reason for broken MIME container */
 extern BOOL    disable_logging;        /* Disables log writing when TRUE */
 
 extern uschar *dns_again_means_nonexist; /* Domains that are badly set up */
@@ -256,6 +271,7 @@
 extern uschar *exim_path;              /* Path to exec exim */
 extern uid_t   exim_uid;               /* Non-root uid for exim */
 extern BOOL    exim_uid_set;           /* TRUE if exim_uid set */
+extern uschar *exiscan_version_string; /* Exiscan version string */
 extern int     expand_forbid;          /* RDO flags for forbidding things */
 extern int     expand_nlength[];       /* Lengths of numbered strings */
 extern int     expand_nmax;            /* Max numerical value */
@@ -264,6 +280,7 @@
 extern BOOL    extract_addresses_remove_arguments; /* Controls -t behaviour */
 extern uschar *extra_local_interfaces; /* Local, non-listen interfaces */
 
+extern BOOL    fake_reject;            /* TRUE if fake reject is to be given */
 extern int     filter_n[FILTER_VARIABLE_COUNT]; /* filter variables */
 extern BOOL    filter_running;         /* TRUE while running a filter */
 extern int     filter_sn[FILTER_VARIABLE_COUNT]; /* variables set by system filter */
@@ -271,6 +288,7 @@
 extern uschar *filter_thisaddress;     /* For address looping */
 extern int     finduser_retries;       /* Retry count for getpwnam() */
 extern uid_t   fixed_never_users[];    /* Can't be overridden */
+extern uschar *found_extension;        /* demime acl condition: file extension found */
 extern uschar *freeze_tell;            /* Message on (some) freezings */
 extern uschar *fudged_queue_times;     /* For use in test harness */
 
@@ -350,6 +368,7 @@
 
 extern macro_item *macros;             /* Configuration macros */
 extern uschar *mailstore_basename;     /* For mailstore deliveries */
+extern uschar *malware_name;           /* Name of virus or malware ("W32/Klez-H") */
 extern int     max_username_length;    /* For systems with broken getpwnam() */
 extern int     message_age;            /* In seconds */
 extern uschar *message_body;           /* Start of message body for filter */
@@ -369,9 +388,26 @@
 extern uschar *message_size_limit;     /* As it says */
 extern uschar  message_subdir[];       /* Subdirectory for messages */
 extern uschar *message_reference;      /* Reference for error messages */
+extern uschar *mime_anomaly_level;
+extern uschar *mime_anomaly_text;
+extern uschar *mime_boundary;
+extern uschar *mime_charset;
+extern uschar *mime_content_description;
+extern uschar *mime_content_disposition;
+extern uschar *mime_content_id;
+extern unsigned int mime_content_size;
+extern uschar *mime_content_transfer_encoding;
+extern uschar *mime_content_type;
+extern uschar *mime_decoded_filename;
+extern uschar *mime_filename;
+extern int     mime_is_multipart;
+extern int     mime_is_coverletter;
+extern int     mime_is_rfc822;
+extern int     mime_part_count;
 extern BOOL    mua_wrapper;            /* TRUE when Exim is wrapping an MUA */
 
 extern uid_t  *never_users;            /* List of uids never to be used */
+extern BOOL    no_mbox_unspool;        /* don't unspool exiscan files */
 extern BOOL    no_multiline_responses; /* For broken clients */
 
 extern optionlist optionlist_auths[];      /* These option lists are made */
@@ -456,6 +492,7 @@
 extern const pcre  *regex_PIPELINING;  /* For recognizing PIPELINING */
 extern const pcre  *regex_SIZE;        /* For recognizing SIZE settings */
 extern const pcre  *regex_ismsgid;     /* Compiled r.e. for message it */
+extern uschar *regex_match_string;     /* regex that matched a line (regex ACL condition) */
 extern int     remote_delivery_count;  /* Number of remote addresses */
 extern int     remote_max_parallel;    /* Maximum parallel delivery */
 extern uschar *remote_sort_domains;    /* Remote domain sorting order */
@@ -547,7 +584,27 @@
 extern BOOL    smtp_use_pipelining;    /* Global for passed connections */
 extern BOOL    smtp_use_size;          /* Global for passed connections */
 extern BOOL    split_spool_directory;  /* TRUE to use multiple subdirs */
+extern uschar *spamd_address;          /* address for the spamassassin daemon */
+extern uschar *spam_bar;               /* the spam "bar" (textual representation of spam_score) */
+extern uschar *spam_report;            /* the spamd report (multiline) */
+extern uschar *spam_score;             /* the spam score (float) */
+extern uschar *spam_score_int;         /* spam_score * 10 (int) */
+#ifdef SPF
+extern uschar *spf_header_comment;     /* spf header comment */
+extern uschar *spf_received;           /* SPF-Received: header */
+extern uschar *spf_result;             /* spf result in string form */
+extern uschar *spf_smtp_comment;       /* spf comment to include in SMTP reply */
+#endif
 extern uschar *spool_directory;        /* Name of spool directory */
+#ifdef SRS
+extern uschar *srs_config;            /* SRS config secret:max age:hash length:use timestamp:use hash */
+extern uschar *srs_db_address;        /* SRS db address */
+extern uschar *srs_db_key;            /* SRS db key */
+extern uschar *srs_orig_sender;       /* SRS original sender */
+extern uschar *srs_orig_recipient;    /* SRS original recipient */
+extern uschar *srs_recipient;         /* SRS recipient */
+extern uschar *srs_status;            /* SRS staus */
+#endif
 extern int     string_datestamp_offset;/* After insertion by string_format */
 extern BOOL    strip_excess_angle_brackets; /* Surrounding route-addrs */
 extern BOOL    strip_trailing_dot;     /* Remove dots at ends of domains */
diff -urN exim-4.44-orig/src/header.c exim-4.44/src/header.c
--- exim-4.44-orig/src/header.c	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/header.c	Fri Jan 14 20:35:02 2005
@@ -38,6 +38,15 @@
 return *tt == ':';
 }
 
+/* Same as above, only the passed header name does not need to be complete */
+BOOL
+header_testname_incomplete(header_line *h, uschar *name, int len, BOOL notdel)
+{
+uschar *tt;
+if (h->type == '*' && notdel) return FALSE;
+if (h->text == NULL || strncmpic(h->text, name, len) != 0) return FALSE;
+return TRUE;
+}
 
 
 /*************************************************
diff -urN exim-4.44-orig/src/local_scan.h exim-4.44/src/local_scan.h
--- exim-4.44-orig/src/local_scan.h	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/local_scan.h	Fri Jan 14 20:35:02 2005
@@ -16,7 +16,6 @@
 #include "mytypes.h"
 #include "store.h"
 
-
 /* The function and its return codes. */
 
 extern int local_scan(int, uschar **);
@@ -107,6 +106,9 @@
   uschar *address;              /* the recipient address */
   int     pno;                  /* parent number for "one_time" alias, or -1 */
   uschar *errors_to;            /* the errors_to address or NULL */
+#ifdef BRIGHTMAIL
+  uschar *bmi_optin;
+#endif
 } recipient_item;
 
 
@@ -145,6 +147,7 @@
 extern void    header_add_at_position(BOOL, uschar *, BOOL, int, char *, ...);
 extern void    header_remove(int, uschar *);
 extern BOOL    header_testname(header_line *, uschar *, int, BOOL);
+extern BOOL    header_testname_incomplete(header_line *, uschar *, int, BOOL);
 extern void    log_write(unsigned int, int, char *format, ...);
 extern int     lss_b64decode(uschar *, uschar **);
 extern uschar *lss_b64encode(uschar *, int);
diff -urN exim-4.44-orig/src/lookups/cdb.h exim-4.44/src/lookups/cdb.h
--- exim-4.44-orig/src/lookups/cdb.h	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/lookups/cdb.h	Fri Jan 14 20:35:02 2005
@@ -3,7 +3,7 @@
 *************************************************/
 
 /*
- * $Id: cdb.h,v 1.2.2.1 1998/05/29 16:21:36 cvs Exp $
+ * $Id: cdb.h,v 1.1.1.1 2004/06/24 07:46:30 tkistner Exp $
  *
  * Exim - CDB database lookup module
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff -urN exim-4.44-orig/src/macros.h exim-4.44/src/macros.h
--- exim-4.44-orig/src/macros.h	Wed Jan 12 14:12:10 2005
+++ exim-4.44/src/macros.h	Fri Jan 14 20:46:00 2005
@@ -541,6 +541,7 @@
 #define htype_add_top       'a'
 #define htype_add_rec       'r'
 #define htype_add_bot       'z'
+#define htype_add_spf       'f'
 
 /* Types of item in options lists. These are the bottom 8 bits of the "type"
 field, which is an int. The opt_void value is used for entries in tables that
@@ -733,6 +734,7 @@
 enum { ACL_WHERE_RCPT,       /* Some controls are for RCPT only */
        ACL_WHERE_MAIL,       /* )                                           */
        ACL_WHERE_PREDATA,    /* ) There are several tests for "in message", */
+       ACL_WHERE_MIME,
        ACL_WHERE_DATA,       /* ) implemented by <= WHERE_NOTSMTP           */
        ACL_WHERE_NOTSMTP,    /* )                                           */
 
diff -urN exim-4.44-orig/src/malware.c exim-4.44/src/malware.c
--- exim-4.44-orig/src/malware.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/malware.c	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,1415 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* Code for calling virus (malware) scanners. Called from acl.c. */
+
+#include "exim.h"
+
+/* declaration of private routines */
+int mksd_scan_packed(int sock);
+int mksd_scan_unpacked(int sock, int maxproc);
+
+/* SHUT_WR seems to be undefined on Unixware ? */
+#ifndef SHUT_WR
+#define SHUT_WR 1
+#endif
+
+#define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
+#define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
+#define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
+
+/* Routine to check whether a system is big- or litte-endian. 
+   Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
+   Needed for proper kavdaemon implementation. Sigh. */
+#define BIG_MY_ENDIAN      0
+#define LITTLE_MY_ENDIAN   1
+int test_byte_order(void);
+int test_byte_order() {
+      short int word = 0x0001;
+      char *byte = (char *) &word;
+      return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
+}
+
+uschar malware_name_buffer[256];
+int malware_ok = 0;
+
+int malware(uschar **listptr) {
+  int sep = 0;
+  uschar *list = *listptr;
+  uschar *av_scanner_work = av_scanner;
+  uschar *scanner_name;
+  uschar scanner_name_buffer[16];
+  uschar *malware_regex;
+  uschar malware_regex_buffer[64];
+  uschar malware_regex_default[] = ".+";
+  unsigned long long mbox_size;
+  FILE *mbox_file;
+  int roffset;
+  const pcre *re;
+  const uschar *rerror;
+  
+  /* make sure the eml mbox file is spooled up */
+  mbox_file = spool_mbox(&mbox_size);
+  if (mbox_file == NULL) {
+    /* error while spooling */
+    log_write(0, LOG_MAIN|LOG_PANIC,
+           "malware acl condition: error while creating mbox spool file");
+    return DEFER;  
+  };
+  /* none of our current scanners need the mbox
+     file as a stream, so we can close it right away */
+  fclose(mbox_file);
+  
+  /* extract the malware regex to match against from the option list */
+  if ((malware_regex = string_nextinlist(&list, &sep,
+                                         malware_regex_buffer,
+                                         sizeof(malware_regex_buffer))) != NULL) {
+    
+    /* parse 1st option */
+    if ( (strcmpic(malware_regex,US"false") == 0) || 
+         (Ustrcmp(malware_regex,"0") == 0) ) {
+      /* explicitly no matching */
+      return FAIL;
+    };
+    
+    /* special cases (match anything except empty) */
+    if ( (strcmpic(malware_regex,US"true") == 0) || 
+         (Ustrcmp(malware_regex,"*") == 0) || 
+         (Ustrcmp(malware_regex,"1") == 0) ) {
+      malware_regex = malware_regex_default;
+    };
+  }
+  else {
+    /* empty means "don't match anything" */
+    return FAIL;
+  };
+
+  /* compile the regex, see if it works */
+  re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+  if (re == NULL) {
+    log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
+    return DEFER;
+  };
+
+  /* if av_scanner starts with a dollar, expand it first */
+  if (*av_scanner == '$') {
+    av_scanner_work = expand_string(av_scanner);
+    if (av_scanner_work == NULL) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+           "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
+      return DEFER;
+    }
+    else {
+      debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
+      /* disable result caching in this case */
+      malware_name = NULL;
+      malware_ok = 0;
+    };
+  }
+
+  /* Do not scan twice. */
+  if (malware_ok == 0) {
+
+    /* find the scanner type from the av_scanner option */
+    if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
+                                          scanner_name_buffer,
+                                          sizeof(scanner_name_buffer))) == NULL) {
+      /* no scanner given */
+      log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: av_scanner configuration variable is empty");
+      return DEFER;
+    };
+    
+  	/* "drweb" scanner type ----------------------------------------------- */
+  	/* v0.1 - added support for tcp sockets					*/
+  	/* v0.0 - initial release -- support for unix sockets			*/
+  	if (strcmpic(scanner_name,US"drweb") == 0) {
+  		uschar *drweb_options;
+  		uschar drweb_options_buffer[1024];
+  		uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
+  		struct sockaddr_un server;
+  		int sock, port, result, ovector[30];
+  		unsigned int fsize;
+  		uschar tmpbuf[1024], *drweb_fbuf;
+  		uschar scanrequest[1024];
+  		uschar drweb_match_string[128];
+  		int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
+  		    drweb_vnum, drweb_slen, drweb_fin = 0x0000;
+  		unsigned long bread;
+  		uschar hostname[256];
+  		struct hostent *he;
+  		struct in_addr in;
+  		pcre *drweb_re;
+      
+  		if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
+  			drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
+  			/* no options supplied, use default options */
+  			drweb_options = drweb_options_default;
+  		};
+  
+  		if (*drweb_options != '/') {
+      
+  			/* extract host and port part */
+  			if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: invalid socket '%s'", drweb_options);
+  				return DEFER;
+      			}
+      
+  			/* Lookup the host */
+  			if((he = gethostbyname(CS hostname)) == 0) {
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: failed to lookup host '%s'", hostname);
+  				return DEFER;
+  			}
+      
+  			in = *(struct in_addr *) he->h_addr_list[0];
+      
+  			/* Open the drwebd TCP socket */
+  			if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: unable to acquire socket (%s)",
+  					strerror(errno));
+  				return DEFER;
+  			}
+      
+  			if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
+  				close(sock); 
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: connection to %s, port %u failed (%s)",
+  					inet_ntoa(in), port, strerror(errno));
+  				return DEFER;
+  			}
+  
+  			/* prepare variables */
+  			drweb_cmd = htonl(DRWEBD_SCAN_CMD);
+  			drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
+  			snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml", 
+  				    spool_directory, message_id, message_id);
+  
+  			/* calc file size */
+  			drweb_fd = open(CS scanrequest, O_RDONLY);
+  			if (drweb_fd == -1) {
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+              				"malware acl condition: drweb: can't open spool file %s: %s", 
+  					scanrequest, strerror(errno));
+  				return DEFER; 
+  			}
+  			fsize = lseek(drweb_fd, 0, SEEK_END);
+  			if (fsize == -1) {
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+              				"malware acl condition: drweb: can't seek spool file %s: %s", 
+  					scanrequest, strerror(errno));
+  				return DEFER; 
+  			}
+  			drweb_slen = htonl(fsize);
+  			lseek(drweb_fd, 0, SEEK_SET);
+  
+  			/* send scan request */
+  			if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || 
+  			    (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
+  			    (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
+  			    (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
+  				close(sock); 
+  				close(drweb_fd);
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
+  				return DEFER;
+  			}
+  
+  			drweb_fbuf = (uschar *) malloc (fsize);
+  			if (!drweb_fbuf) {
+  				close(sock);
+  				close(drweb_fd);
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: unable to allocate memory %u for file (%s)", 
+  					fsize, scanrequest);
+  				return DEFER;
+  			}
+  
+  			result = read (drweb_fd, drweb_fbuf, fsize);
+  			if (result == -1) {
+  				close(sock);
+  				close(drweb_fd);
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+              				"malware acl condition: drweb: can't read spool file %s: %s",
+  					scanrequest, strerror(errno));
+  				return DEFER; 
+  			}
+  			
+  			/* send file body to socket */
+  			if (send(sock, drweb_fbuf, fsize, 0) < 0) {
+  				close(sock);
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
+  				return DEFER;
+  			}
+  			close(drweb_fd);
+  			free(drweb_fbuf);
+  		}
+      		else {
+  			/* open the drwebd UNIX socket */
+  			sock = socket(AF_UNIX, SOCK_STREAM, 0);
+  			if (sock < 0) {
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+              				"malware acl condition: drweb: can't open UNIX socket");
+  				return DEFER; 
+  			}
+  			server.sun_family = AF_UNIX;
+  			Ustrcpy(server.sun_path, drweb_options);
+  			if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+  				close(sock);
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
+  				return DEFER;
+  			}
+        
+  			/* prepare variables */
+  			drweb_cmd = htonl(DRWEBD_SCAN_CMD);
+  			drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
+  			snprintf(CS scanrequest, 1024,CS"%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
+  			drweb_slen = htonl(Ustrlen(scanrequest));
+  
+  			/* send scan request */
+  			if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) || 
+  			    (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
+  			    (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
+  			    (send(sock, scanrequest, Ustrlen(scanrequest), 0) < 0) ||
+  			    (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
+  				close(sock);
+  				log_write(0, LOG_MAIN|LOG_PANIC,
+  					"malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
+  				return DEFER;
+  			}
+  		}
+  
+  		/* wait for result */
+  		if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
+  			close(sock);
+  			log_write(0, LOG_MAIN|LOG_PANIC,
+  				"malware acl condition: drweb: unable to read return code");
+  			return DEFER;
+  		}
+  		drweb_rc = ntohl(drweb_rc);
+      
+  		if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
+  			close(sock);
+  			log_write(0, LOG_MAIN|LOG_PANIC,
+  				"malware acl condition: drweb: unable to read the number of viruses");
+  			return DEFER;
+  		}
+  		drweb_vnum = ntohl(drweb_vnum);
+  		
+  		/* "virus(es) found" if virus number is > 0 */
+  		if (drweb_vnum)
+  		{
+  			int i;
+  			uschar pre_malware_nb[256];
+  			
+  			malware_name = malware_name_buffer;
+  			
+  			/* setup default virus name */
+  			Ustrcpy(malware_name_buffer,"unknown");
+  			
+  			/* read and concatenate virus names into one string */
+  			for (i=0;i<drweb_vnum;i++)
+  			{
+  				/* read the size of report */
+  				if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
+  					close(sock);
+  					log_write(0, LOG_MAIN|LOG_PANIC,
+  						"malware acl condition: drweb: cannot read report size");
+  					return DEFER;
+  				};
+  				drweb_slen = ntohl(drweb_slen);
+  			
+  				/* read report body */
+  				if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
+  					close(sock);
+  					log_write(0, LOG_MAIN|LOG_PANIC,
+  						"malware acl condition: drweb: cannot read report string");
+  					return DEFER;
+  				};
+  				tmpbuf[drweb_slen] = '\0';
+  
+  				/* set up match regex, depends on retcode */
+  				Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
+  
+  				drweb_re = pcre_compile( CS drweb_match_string,
+                          		PCRE_COPT,
+                                  	(const char **)&rerror,
+                                  	&roffset,
+                                  	NULL );
+              
+  				/* try matcher on the line, grab substring */
+  				result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
+  				if (result >= 2) {
+  					pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
+  				}
+  				/* the first name we just copy to malware_name */
+  				if (i==0)
+  					Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
+  				else {
+  					/* concatenate each new virus name to previous */
+  					int slen = Ustrlen(malware_name_buffer);
+  					if (slen < (slen+Ustrlen(pre_malware_nb))) {
+  						Ustrcat(malware_name_buffer, "/");
+  						Ustrcat(malware_name_buffer, pre_malware_nb);
+  					}
+  				}
+  			}
+  		}
+  		else {
+  			/* no virus found */
+  			malware_name = NULL;
+  		};
+  		close(sock);
+  	}
+  	/* ----------------------------------------------------------------------- */
+    else if (strcmpic(scanner_name,US"aveserver") == 0) {
+      uschar *kav_options;
+      uschar kav_options_buffer[1024];
+      uschar kav_options_default[] = "/var/run/aveserver";
+      uschar buf[32768];
+      uschar *p;
+      struct sockaddr_un server;
+      int sock;
+    
+      if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
+                                           kav_options_buffer,
+                                           sizeof(kav_options_buffer))) == NULL) {
+        /* no options supplied, use default options */
+        kav_options = kav_options_default;
+      };
+    
+      /* open the aveserver socket */
+      sock = socket(AF_UNIX, SOCK_STREAM, 0);
+      if (sock < 0) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: can't open UNIX socket.");
+        return DEFER; 
+      }
+      server.sun_family = AF_UNIX;
+      Ustrcpy(server.sun_path, kav_options);
+      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
+        return DEFER;
+      }
+    
+      /* read aveserver's greeting and see if it is ready (2xx greeting) */
+      recv_line(sock, buf, 32768);
+
+      if (buf[0] != '2') {
+        /* aveserver is having problems */
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
+        return DEFER;
+      };
+      
+      /* prepare our command */
+      snprintf(CS buf, 32768, "SCAN bPQRSTUW %s/scan/%s/%s.eml\r\n", spool_directory, message_id, message_id);
+      
+      /* and send it */
+      if (send(sock, buf, Ustrlen(buf), 0) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
+        return DEFER;
+      }
+      
+      malware_name = NULL;
+      /* read response lines, find malware name and final response */
+      while (recv_line(sock, buf, 32768) > 0) {
+        debug_printf("aveserver: %s\n", buf);
+        if (buf[0] == '2') break;
+        if (Ustrncmp(buf,"322",3) == 0) {
+          uschar *p = Ustrchr(&buf[4],' ');
+          *p = '\0';
+          Ustrcpy(malware_name_buffer,&buf[4]);
+          malware_name = malware_name_buffer;
+        };
+      }
+
+      close(sock);
+    }
+    /* "fsecure" scanner type ------------------------------------------------- */
+    else if (strcmpic(scanner_name,US"fsecure") == 0) {
+      uschar *fsecure_options;
+      uschar fsecure_options_buffer[1024];
+      uschar fsecure_options_default[] = "/var/run/.fsav";
+      struct sockaddr_un server;
+      int sock, i, j, bread = 0;
+      uschar file_name[1024];
+      uschar av_buffer[1024];
+      pcre *fs_inf;
+      static uschar *cmdoptions[] = { "CONFIGURE\tARCHIVE\t1\n","CONFIGURE\tTIMEOUT\t0\n","CONFIGURE\tMAXARCH\t5\n","CONFIGURE\tMIME\t1\n" };
+      
+      malware_name = NULL;
+      if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
+                                               fsecure_options_buffer,
+                                               sizeof(fsecure_options_buffer))) == NULL) { 
+         /* no options supplied, use default options */
+         fsecure_options = fsecure_options_default;
+      };
+     
+      /* open the fsecure socket */
+      sock = socket(AF_UNIX, SOCK_STREAM, 0);
+      if (sock < 0) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: unable to open fsecure socket %s (%s)",
+                  fsecure_options, strerror(errno));
+        return DEFER;
+      }
+      server.sun_family = AF_UNIX;
+      Ustrcpy(server.sun_path, fsecure_options);
+      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: unable to connect to fsecure socket %s (%s)",
+                  fsecure_options, strerror(errno));
+        return DEFER;
+      }
+       
+      /* pass options */
+      memset(av_buffer, 0, sizeof(av_buffer));
+      for (i=0; i != 4; i++) {
+        /* debug_printf("send option \"%s\"",cmdoptions[i]); */
+        if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
+          close(sock);
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: unable to write fsecure option %d to %s (%s)",
+                    i, fsecure_options, strerror(errno));
+          return DEFER; 
+        };
+       
+        bread = read(sock, av_buffer, sizeof(av_buffer));
+        if (bread >0) av_buffer[bread]='\0';
+        if (bread < 0) {
+          close(sock);
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
+          return DEFER;
+        };
+        for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
+        /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
+        /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
+      };
+ 
+      /* pass the mailfile to fsecure */
+      snprintf(CS file_name,1024,"SCAN\t%s/scan/%s/%s.eml\n", spool_directory, message_id, message_id);
+      /* debug_printf("send scan %s",file_name); */
+      if (write(sock, file_name, Ustrlen(file_name)) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: unable to write fsecure scan to %s (%s)",
+                  fsecure_options, strerror(errno));
+        return DEFER;
+      };
+       
+      /* set up match */
+      /* todo also SUSPICION\t */
+      fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+ 
+      /* read report, linewise */
+      do {
+        int ovector[30];
+        i = 0;
+        memset(av_buffer, 0, sizeof(av_buffer));
+        do {
+          bread=read(sock, &av_buffer[i], 1);
+          if (bread < 0) {
+            close(sock);
+            log_write(0, LOG_MAIN|LOG_PANIC,
+                      "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
+            return DEFER;
+          };
+          i++;
+        }
+        while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
+        av_buffer[i-1] = '\0';
+        /* debug_printf("got line \"%s\"\n",av_buffer); */
+         
+        /* Really search for virus again? */
+        if (malware_name == NULL) {
+          /* try matcher on the line, grab substring */
+          i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
+          if (i >= 2) {
+            /* Got it */
+            pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
+            malware_name = malware_name_buffer;
+          };
+        };
+      }
+      while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
+      close(sock);      
+    }
+    /* ----------------------------------------------------------------------- */
+
+    /* "kavdaemon" scanner type ------------------------------------------------ */
+    else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
+      uschar *kav_options;
+      uschar kav_options_buffer[1024];
+      uschar kav_options_default[] = "/var/run/AvpCtl";
+      struct sockaddr_un server;
+      int sock;
+      time_t t;
+      uschar tmpbuf[1024];
+      uschar scanrequest[1024];
+      uschar kav_match_string[128];
+      int kav_rc;
+      unsigned long kav_reportlen, bread;
+      pcre *kav_re;
+    
+      if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
+                                           kav_options_buffer,
+                                           sizeof(kav_options_buffer))) == NULL) {
+        /* no options supplied, use default options */
+        kav_options = kav_options_default;
+      };
+    
+      /* open the kavdaemon socket */
+      sock = socket(AF_UNIX, SOCK_STREAM, 0);
+      if (sock < 0) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: can't open UNIX socket.");
+        return DEFER; 
+      }
+      server.sun_family = AF_UNIX;
+      Ustrcpy(server.sun_path, kav_options);
+      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
+        return DEFER;
+      }
+      
+      /* get current date and time, build scan request */
+      time(&t);
+      strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s/scan/%%s", localtime(&t));
+      snprintf(CS scanrequest, 1024,CS tmpbuf, spool_directory, message_id);
+      
+      /* send scan request */
+      if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
+        return DEFER;
+      }
+      
+      /* wait for result */
+      if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
+        return DEFER;
+      }
+    
+      /* get errorcode from one nibble */
+      if (test_byte_order() == LITTLE_MY_ENDIAN) {
+        kav_rc = tmpbuf[0] & 0x0F;
+      }
+      else {
+        kav_rc = tmpbuf[1] & 0x0F;
+      };
+    
+      /* improper kavdaemon configuration */
+      if ( (kav_rc == 5) || (kav_rc == 6) ) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
+        return DEFER;
+      };
+      
+      if (kav_rc == 1) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
+        return DEFER;
+      };
+    
+      if (kav_rc == 7) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
+        return DEFER;
+      };
+    
+      /* code 8 is not handled, since it is ambigous. It appears mostly on
+      bounces where part of a file has been cut off */
+    
+      /* "virus found" return codes (2-4) */
+      if ((kav_rc > 1) && (kav_rc < 5)) {
+        int report_flag = 0;
+        
+        /* setup default virus name */
+        Ustrcpy(malware_name_buffer,"unknown");
+        malware_name = malware_name_buffer;
+      
+        if (test_byte_order() == LITTLE_MY_ENDIAN) {
+          report_flag = tmpbuf[1];
+        }
+        else {
+          report_flag = tmpbuf[0];
+        };
+      
+        /* read the report, if available */
+        if( report_flag == 1 ) {
+          /* read report size */
+          if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
+            close(sock);
+            log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: cannot read report size from kavdaemon");
+            return DEFER;
+          };
+  
+          /* it's possible that avp returns av_buffer[1] == 1 but the
+          reportsize is 0 (!?) */
+          if (kav_reportlen > 0) {
+            /* set up match regex, depends on retcode */
+            if( kav_rc == 3 )
+              Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
+            else
+              Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
+  
+            kav_re = pcre_compile( CS kav_match_string,
+                                   PCRE_COPT,
+                                   (const char **)&rerror,
+                                   &roffset,
+                                   NULL );
+            
+            /* read report, linewise */  
+            while (kav_reportlen > 0) {
+              int result = 0;
+              int ovector[30];
+              
+              bread = 0;
+              while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
+                kav_reportlen--;
+                if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
+                bread++;
+              };
+              bread++;
+              tmpbuf[bread] = '\0';
+              
+              /* try matcher on the line, grab substring */
+              result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
+              if (result >= 2) {
+                pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
+                break;
+              };
+            };
+          };
+        };
+      }
+      else {
+        /* no virus found */
+        malware_name = NULL;
+      };
+    
+      close(sock);
+    }
+    /* ----------------------------------------------------------------------- */
+    
+    
+    /* "cmdline" scanner type ------------------------------------------------ */
+    else if (strcmpic(scanner_name,US"cmdline") == 0) {
+      uschar *cmdline_scanner;
+      uschar cmdline_scanner_buffer[1024];
+      uschar *cmdline_trigger;
+      uschar cmdline_trigger_buffer[1024];
+      const pcre *cmdline_trigger_re;
+      uschar *cmdline_regex;
+      uschar cmdline_regex_buffer[1024];
+      const pcre *cmdline_regex_re;
+      uschar file_name[1024];
+      uschar commandline[1024];
+      void (*eximsigchld)(int);
+      void (*eximsigpipe)(int);
+      FILE *scanner_out = NULL;
+      FILE *scanner_record = NULL;
+      uschar linebuffer[32767];
+      int trigger = 0;
+      int result;
+      int ovector[30];
+      
+      /* find scanner command line */
+      if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
+                                          cmdline_scanner_buffer,
+                                          sizeof(cmdline_scanner_buffer))) == NULL) {
+        /* no command line supplied */
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: missing commandline specification for cmdline scanner type.");
+        return DEFER; 
+      };
+    
+      /* find scanner output trigger */
+      if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
+                                          cmdline_trigger_buffer,
+                                          sizeof(cmdline_trigger_buffer))) == NULL) {
+        /* no trigger regex supplied */
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: missing trigger specification for cmdline scanner type.");
+        return DEFER; 
+      };
+    
+      /* precompile trigger regex */
+      cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+      if (cmdline_trigger_re == NULL) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
+        return DEFER;
+      };
+    
+      /* find scanner name regex */
+      if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
+                                             cmdline_regex_buffer,
+                                             sizeof(cmdline_regex_buffer))) == NULL) {
+        /* no name regex supplied */
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: missing virus name regex specification for cmdline scanner type.");
+        return DEFER; 
+      };
+    
+      /* precompile name regex */
+      cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
+      if (cmdline_regex_re == NULL) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
+        return DEFER;
+      };
+    
+      /* prepare scanner call */
+      snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
+      snprintf(CS commandline,1024, CS cmdline_scanner,file_name);
+      /* redirect STDERR too */
+      Ustrcat(commandline," 2>&1");
+      
+      /* store exims signal handlers */
+      eximsigchld = signal(SIGCHLD,SIG_DFL);
+      eximsigpipe = signal(SIGPIPE,SIG_DFL);
+      
+      scanner_out = popen(CS commandline,"r");
+      if (scanner_out == NULL) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
+        signal(SIGCHLD,eximsigchld);
+        signal(SIGPIPE,eximsigpipe);
+        return DEFER;
+      };
+      
+      snprintf(CS file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
+      scanner_record = fopen(CS file_name,"w");
+      
+      if (scanner_record == NULL) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
+        pclose(scanner_out);
+        signal(SIGCHLD,eximsigchld);
+        signal(SIGPIPE,eximsigpipe);
+        return DEFER;
+      };
+      
+      /* look for trigger while recording output */
+      while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
+        if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
+          /* short write */
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                 "malware acl condition: short write on scanner output file (%s).", file_name);
+          pclose(scanner_out);
+          signal(SIGCHLD,eximsigchld);
+          signal(SIGPIPE,eximsigpipe);
+          return DEFER;
+        };
+        /* try trigger match */
+        if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
+          trigger = 1;
+      };
+      
+      fclose(scanner_record);
+      pclose(scanner_out);
+      signal(SIGCHLD,eximsigchld);
+      signal(SIGPIPE,eximsigpipe);
+      
+      if (trigger) {
+        /* setup default virus name */
+        Ustrcpy(malware_name_buffer,"unknown");
+        malware_name = malware_name_buffer;
+        
+        /* re-open the scanner output file, look for name match */
+        scanner_record = fopen(CS file_name,"r");
+        while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
+          /* try match */
+          result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
+          if (result >= 2) {
+            pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
+          };
+        };
+        fclose(scanner_record);
+      }
+      else {
+        /* no virus found */
+        malware_name = NULL;
+      };
+    }
+    /* ----------------------------------------------------------------------- */
+    
+    
+    /* "sophie" scanner type ------------------------------------------------- */
+    else if (strcmpic(scanner_name,US"sophie") == 0) {
+      uschar *sophie_options;
+      uschar sophie_options_buffer[1024];
+      uschar sophie_options_default[] = "/var/run/sophie";
+      int bread = 0;
+      struct sockaddr_un server;
+      int sock;
+      uschar file_name[1024];
+      uschar av_buffer[1024];
+      
+      if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
+                                          sophie_options_buffer,
+                                          sizeof(sophie_options_buffer))) == NULL) {
+        /* no options supplied, use default options */
+        sophie_options = sophie_options_default;
+      };
+    
+      /* open the sophie socket */
+      sock = socket(AF_UNIX, SOCK_STREAM, 0);
+      if (sock < 0) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: can't open UNIX socket.");
+        return DEFER; 
+      }
+      server.sun_family = AF_UNIX;
+      Ustrcpy(server.sun_path, sophie_options);
+      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
+        return DEFER;
+      }
+      
+      /* pass the scan directory to sophie */
+      snprintf(CS file_name,1024,"%s/scan/%s", spool_directory, message_id);
+      if (write(sock, file_name, Ustrlen(file_name)) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
+        return DEFER; 
+      };
+      
+      write(sock, "\n", 1);
+      
+      /* wait for result */
+      memset(av_buffer, 0, sizeof(av_buffer));
+      if ((!(bread = read(sock, av_buffer, sizeof(av_buffer))) > 0)) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
+        return DEFER;
+      };
+    
+      close(sock);
+    
+      /* infected ? */
+      if (av_buffer[0] == '1') {
+        if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
+        Ustrcpy(malware_name_buffer,&av_buffer[2]);
+        malware_name = malware_name_buffer;
+      }
+      else if (!strncmp(CS av_buffer, "-1", 2)) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: malware acl condition: sophie reported error");
+        return DEFER;
+      }
+      else {
+        /* all ok, no virus */
+        malware_name = NULL;
+      };
+    }
+    /* ----------------------------------------------------------------------- */
+    
+
+    /* "clamd" scanner type ------------------------------------------------- */
+    /* This code was contributed by David Saez <david@ols.es> */
+    else if (strcmpic(scanner_name,US"clamd") == 0) {
+      uschar *clamd_options;
+      uschar clamd_options_buffer[1024];
+      uschar clamd_options_default[] = "/tmp/clamd";
+      uschar *p,*vname;
+      struct sockaddr_un server;
+      int sock,port,bread=0;
+      uschar file_name[1024];
+      uschar av_buffer[1024];
+      uschar hostname[256];
+      struct hostent *he;
+      struct in_addr in;
+
+      if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
+                                             clamd_options_buffer,
+                                             sizeof(clamd_options_buffer))) == NULL) {
+        /* no options supplied, use default options */
+        clamd_options = clamd_options_default;
+      }
+    
+      /* socket does not start with '/' -> network socket */
+      if (*clamd_options != '/') {
+    
+        /* extract host and port part */
+        if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: clamd: invalid socket '%s'", clamd_options);
+          return DEFER;
+        };
+    
+        /* Lookup the host */
+        if((he = gethostbyname(CS hostname)) == 0) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: clamd: failed to lookup host '%s'", hostname);
+          return DEFER;
+        }
+    
+        in = *(struct in_addr *) he->h_addr_list[0];
+    
+        /* Open the ClamAV Socket */
+        if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: clamd: unable to acquire socket (%s)",
+                    strerror(errno));
+          return DEFER;
+        }
+    
+        if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
+          close(sock); 
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: clamd: connection to %s, port %u failed (%s)",
+                    inet_ntoa(in), port, strerror(errno));
+          return DEFER;
+        }
+      }
+      else {
+        /* open the local socket */
+        if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: clamd: unable to acquire socket (%s)",
+                    strerror(errno));
+          return DEFER;
+        }
+    
+        server.sun_family = AF_UNIX;
+        Ustrcpy(server.sun_path, clamd_options);
+    
+        if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+          close(sock);
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
+                    clamd_options, strerror(errno) );
+          return DEFER;
+        }
+      }
+    
+      /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
+    
+      snprintf(CS file_name,1024,"SCAN %s/scan/%s\n", spool_directory, message_id);
+    
+      if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
+                  strerror(errno));
+        return DEFER;
+      }
+    
+      /* 
+        We're done sending, close socket for writing.
+        
+        One user reported that clamd 0.70 does not like this any more ...
+        
+      */
+      
+      /* shutdown(sock, SHUT_WR); */
+    
+      /* Read the result */
+      memset(av_buffer, 0, sizeof(av_buffer));
+      bread = read(sock, av_buffer, sizeof(av_buffer));
+      close(sock);
+
+      if (!(bread  > 0)) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: clamd: unable to read from socket (%s)",
+                  strerror(errno));
+        return DEFER;
+      }
+    
+      if (bread == sizeof(av_buffer)) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: clamd: buffer too small");
+        return DEFER;
+      }
+    
+      /* Check the result. ClamAV Returns
+         infected: -> "<filename>: <virusname> FOUND"
+         not-infected: -> "<filename>: OK"
+	        error: -> "<filename>: <errcode> ERROR */
+         
+      if (!(*av_buffer)) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: clamd: ClamAV returned null");
+        return DEFER;
+      }
+    
+      /* colon in returned output? */
+      if((p = Ustrrchr(av_buffer,':')) == NULL) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+                  "malware acl condition: clamd: ClamAV returned malformed result: %s",
+                  av_buffer);  
+        return DEFER;
+      }
+    
+      /* strip filename strip CR at the end */
+      ++p;
+      while (*p == ' ') ++p;
+      vname = p;
+      p = vname + Ustrlen(vname) - 1;
+      if( *p == '\n' ) *p = '\0';
+
+      if ((p = Ustrstr(vname, "FOUND"))!=NULL) {
+           *p=0;
+           for (--p;p>vname && *p<=32;p--) *p=0;
+           for (;*vname==32;vname++);
+           Ustrcpy(malware_name_buffer,vname);
+           malware_name = malware_name_buffer;
+      }
+      else {
+           if (Ustrstr(vname, "ERROR")!=NULL) {
+              /* ClamAV reports ERROR
+              Find line start */
+              for (;*vname!='\n' && vname>av_buffer; vname--);
+              if (*vname=='\n') vname++;
+
+              log_write(0, LOG_MAIN|LOG_PANIC,
+                     "malware acl condition: clamd: ClamAV returned %s",vname);
+              return DEFER;
+           }
+           else {
+              /* Everything should be OK */
+              malware_name = NULL;
+           }
+      }
+    }
+    /* ----------------------------------------------------------------------- */
+    
+    
+    /* "mksd" scanner type --------------------------------------------------- */
+    else if (strcmpic(scanner_name,US"mksd") == 0) {
+      uschar *mksd_options;
+      char *mksd_options_end;
+      uschar mksd_options_buffer[32];
+      int mksd_maxproc = 1;  /* default, if no option supplied */
+      struct sockaddr_un server;
+      int sock;
+      int retval;
+      
+      if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
+                                            mksd_options_buffer,
+                                            sizeof(mksd_options_buffer))) != NULL) {
+        mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
+	      if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
+	          (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: mksd: invalid option '%s'", mksd_options);
+          return DEFER;
+        }
+      }
+      
+      /* open the mksd socket */
+      sock = socket(AF_UNIX, SOCK_STREAM, 0);
+      if (sock < 0) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: can't open UNIX socket.");
+        return DEFER; 
+      }
+      server.sun_family = AF_UNIX;
+      Ustrcpy(server.sun_path, "/var/run/mksd/socket");
+      if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+        close(sock);
+        log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
+        return DEFER;
+      }
+      
+      malware_name = NULL;
+      
+      /* choose the appropriate scan routine */
+      retval = demime_ok ?
+               mksd_scan_unpacked(sock, mksd_maxproc) :
+               mksd_scan_packed(sock);
+      
+      if (retval != OK)
+        return retval;
+    }
+    /* ----------------------------------------------------------------------- */
+
+    /* "unknown" scanner type ------------------------------------------------- */
+    else {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+             "malware condition: unknown scanner type '%s'", scanner_name);
+      return DEFER;
+    };
+    /* ----------------------------------------------------------------------- */
+  
+    /* set "been here, done that" marker */
+    malware_ok = 1;
+  };
+
+  /* match virus name against pattern (caseless ------->----------v) */
+  if ( (malware_name != NULL) &&
+       (regex_match_and_setup(re, malware_name, 0, -1)) ) {
+    return OK;
+  }
+  else {
+    return FAIL;
+  };
+}
+
+
+/* simple wrapper for reading lines from sockets */
+int recv_line(int sock, uschar *buffer, int size) {
+  uschar *p = buffer;
+
+  memset(buffer,0,size);
+  /* read until \n */
+  while(recv(sock,p,1,0) > -1) {
+    if ((p-buffer) > (size-2)) break;
+    if (*p == '\n') break;
+    if (*p != '\r') p++;
+  };
+  *p = '\0';
+
+  return (p-buffer);
+}
+
+
+/* ============= private routines for the "mksd" scanner type ============== */
+
+#include <sys/uio.h>
+
+int mksd_writev (int sock, struct iovec *iov, int iovcnt)
+{
+  int i;
+  
+  for (;;) {
+    do
+      i = writev (sock, iov, iovcnt);
+    while ((i < 0) && (errno == EINTR));
+    if (i <= 0) {
+      close (sock);
+      log_write(0, LOG_MAIN|LOG_PANIC,
+                "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
+      return -1;
+    }
+    
+    for (;;)
+      if (i >= iov->iov_len) {
+        if (--iovcnt == 0)
+          return 0;
+        i -= iov->iov_len;
+        iov++;
+      } else {
+        iov->iov_len -= i;
+        iov->iov_base = CS iov->iov_base + i;
+        break;
+      }
+  }
+}
+
+int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
+{
+  int offset = 0;
+  int i;
+  
+  do {
+    if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
+      close (sock);
+      log_write(0, LOG_MAIN|LOG_PANIC,
+                "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
+      return -1;
+    }
+    
+    offset += i;
+    /* offset == av_buffer_size -> buffer full */
+    if (offset == av_buffer_size) {
+      close (sock);
+      log_write(0, LOG_MAIN|LOG_PANIC,
+                "malware acl condition: malformed reply received from mksd");
+      return -1;
+    }
+  } while (av_buffer[offset-1] != '\n');
+  
+  av_buffer[offset] = '\0';
+  return offset;
+}
+
+int mksd_parse_line (char *line)
+{
+  char *p;
+  
+  switch (*line) {
+    case 'O':
+      /* OK */
+      return OK;
+    case 'E':
+    case 'A':
+      /* ERR */
+      if ((p = strchr (line, '\n')) != NULL)
+        (*p) = '\0';
+      log_write(0, LOG_MAIN|LOG_PANIC,
+                "malware acl condition: mksd scanner failed: %s", line);
+      return DEFER;
+    default:
+      /* VIR */
+      if ((p = strchr (line, '\n')) != NULL) {
+        (*p) = '\0';
+        if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
+          if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
+            (*p) = '\0';
+            Ustrcpy (malware_name_buffer, line+4);
+	    malware_name = malware_name_buffer;
+            return OK;
+          }
+      }
+      log_write(0, LOG_MAIN|LOG_PANIC,
+                "malware acl condition: malformed reply received from mksd: %s", line);
+      return DEFER;
+  }
+}
+
+int mksd_scan_packed (int sock)
+{
+  struct iovec iov[7];
+  char *cmd = "MSQ/scan/.eml\n";
+  uschar av_buffer[1024];
+  
+  iov[0].iov_base = cmd;
+  iov[0].iov_len = 3;
+  iov[1].iov_base = CS spool_directory;
+  iov[1].iov_len = Ustrlen (spool_directory);
+  iov[2].iov_base = cmd + 3;
+  iov[2].iov_len = 6;
+  iov[3].iov_base = iov[5].iov_base = CS message_id;
+  iov[3].iov_len = iov[5].iov_len = Ustrlen (message_id);
+  iov[4].iov_base = cmd + 3;
+  iov[4].iov_len = 1;
+  iov[6].iov_base = cmd + 9;
+  iov[6].iov_len = 5;
+  
+  if (mksd_writev (sock, iov, 7) < 0)
+    return DEFER;
+  
+  if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
+    return DEFER;
+  
+  close (sock);
+  
+  return mksd_parse_line (CS av_buffer);
+}
+
+int mksd_scan_unpacked (int sock, int maxproc)
+{
+  struct iovec iov[5];
+  char *cmd = "\nSQ/";
+  DIR *unpdir;
+  struct dirent *entry;
+  int pending = 0;
+  uschar *line;
+  int i, offset;
+  uschar mbox_name[1024];
+  uschar unpackdir[1024];
+  uschar av_buffer[16384];
+  
+  snprintf (CS mbox_name, sizeof (mbox_name), "%s.eml", CS message_id);
+  snprintf (CS unpackdir, sizeof (unpackdir), "%s/scan/%s", CS spool_directory, CS message_id);
+  
+  if ((unpdir = opendir (CS unpackdir)) == NULL) {
+    close (sock);
+    log_write(0, LOG_MAIN|LOG_PANIC,
+              "malware acl condition: unable to scan spool directory");
+    return DEFER;
+  }
+  
+  iov[0].iov_base = cmd;
+  iov[0].iov_len = 3;
+  iov[1].iov_base = CS unpackdir;
+  iov[1].iov_len = Ustrlen (unpackdir);
+  iov[2].iov_base = cmd + 3;
+  iov[2].iov_len = 1;
+  iov[4].iov_base = cmd;
+  iov[4].iov_len = 1;
+  
+  /* main loop */
+  while ((unpdir != NULL) || (pending > 0)) {
+  
+    /* write loop */
+    while ((pending < maxproc) && (unpdir != NULL)) {
+      if ((entry = readdir (unpdir)) != NULL) {
+        if ((Ustrcmp (entry->d_name, ".") != 0) &&
+            (Ustrcmp (entry->d_name, "..") != 0) &&
+            (Ustrcmp (entry->d_name, mbox_name) != 0)) {
+          iov[3].iov_base = entry->d_name;
+          iov[3].iov_len = strlen (entry->d_name);
+          if (mksd_writev (sock, iov, 5) < 0) {
+            closedir (unpdir);
+            return DEFER;
+          }
+          iov[0].iov_base = cmd + 1;
+          iov[0].iov_len = 2;
+          pending++;
+        }
+      } else {
+        closedir (unpdir);
+        unpdir = NULL;
+      }
+    }
+    
+    /* read and parse */
+    if (pending > 0) {
+      if ((offset = mksd_read_lines (sock, av_buffer, sizeof (av_buffer))) < 0) {
+        if (unpdir != NULL)
+          closedir (unpdir);
+        return DEFER;
+      }
+      line = av_buffer;
+      do {
+        if (((i = mksd_parse_line (CS line)) != OK) || (malware_name != NULL)) {
+          close (sock);
+          if (unpdir != NULL)
+            closedir (unpdir);
+          return i;
+        }
+        pending--;
+        if ((line = Ustrchr (line, '\n')) == NULL) {
+          close (sock);
+          if (unpdir != NULL)
+            closedir (unpdir);
+          log_write(0, LOG_MAIN|LOG_PANIC,
+                    "malware acl condition: unterminated line received from mksd");
+          return DEFER;
+        }
+      } while (++line != (av_buffer + offset));
+      offset = 0;
+    }
+  }
+  
+  close (sock);
+  return OK;
+}
+
diff -urN exim-4.44-orig/src/mime.c exim-4.44/src/mime.c
--- exim-4.44-orig/src/mime.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/mime.c	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,712 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 */
+/* License: GPL */
+
+#include "exim.h"
+#include "mime.h"
+#include <sys/stat.h>
+
+FILE *mime_stream = NULL;
+uschar *mime_current_boundary = NULL;
+
+
+/*************************************************
+* decode quoted-printable chars                  *
+*************************************************/
+
+/* gets called when we hit a =
+   returns: new pointer position
+   result code in c:
+          -2 - decode error
+          -1 - soft line break, no char
+           0-255 - char to write
+*/
+
+unsigned int mime_qp_hstr_i(uschar *cptr) {
+  unsigned int i, j = 0;
+  while (cptr && *cptr && isxdigit(*cptr)) {
+    i = *cptr++ - '0';
+    if (9 < i) i -= 7;
+    j <<= 4;
+    j |= (i & 0x0f);
+  }
+  return(j);
+}
+
+uschar *mime_decode_qp_char(uschar *qp_p,int *c) {
+  uschar hex[] = {0,0,0};
+  int nan = 0;
+  uschar *initial_pos = qp_p;
+  
+  /* advance one char */
+  qp_p++;
+  
+  REPEAT_FIRST:
+  if ( (*qp_p == '\t') || (*qp_p == ' ') || (*qp_p == '\r') )  {
+    /* tab or whitespace may follow
+       just ignore it, but remember
+       that this is not a valid hex
+       encoding any more */
+    nan = 1;
+    qp_p++;
+    goto REPEAT_FIRST;
+  }
+  else if ( (('0' <= *qp_p) && (*qp_p <= '9')) || (('A' <= *qp_p) && (*qp_p <= 'F'))  || (('a' <= *qp_p) && (*qp_p <= 'f')) ) {
+    /* this is a valid hex char, if nan is unset */
+    if (nan) {
+      /* this is illegal */
+      *c = -2;
+      return initial_pos;
+    }
+    else {
+      hex[0] = *qp_p;
+      qp_p++;
+    };
+  }
+  else if (*qp_p == '\n') {    
+    /* hit soft line break already, continue */
+    *c = -1;
+    return qp_p;
+  }
+  else {
+    /* illegal char here */
+    *c = -2;
+    return initial_pos;
+  };
+  
+  if ( (('0' <= *qp_p) && (*qp_p <= '9')) || (('A' <= *qp_p) && (*qp_p <= 'F')) || (('a' <= *qp_p) && (*qp_p <= 'f')) ) {
+    if (hex[0] > 0) {
+      hex[1] = *qp_p;
+      /* do hex conversion */
+      *c = mime_qp_hstr_i(hex);
+      qp_p++;
+      return qp_p;
+    }
+    else {
+      /* huh ? */
+      *c = -2;
+      return initial_pos;  
+    };
+  }
+  else {
+    /* illegal char */
+    *c = -2;
+    return initial_pos;  
+  };
+}
+
+
+uschar *mime_parse_line(uschar *buffer, uschar *encoding, int *num_decoded) {
+  uschar *data = NULL;
+
+  data = (uschar *)malloc(Ustrlen(buffer)+2);
+
+  if (encoding == NULL) {
+    /* no encoding type at all */
+    NO_DECODING:
+    memcpy(data, buffer, Ustrlen(buffer));
+    data[(Ustrlen(buffer))] = 0;
+    *num_decoded = Ustrlen(data);
+    return data;
+  }
+  else if (Ustrcmp(encoding,"base64") == 0) {
+    uschar *p = buffer;
+    int offset = 0;
+    
+    /* ----- BASE64 ---------------------------------------------------- */
+    /* NULL out '\r' and '\n' chars */
+    while (Ustrrchr(p,'\r') != NULL) {
+      *(Ustrrchr(p,'\r')) = '\0';
+    };
+    while (Ustrrchr(p,'\n') != NULL) {
+      *(Ustrrchr(p,'\n')) = '\0';
+    };
+
+    while (*(p+offset) != '\0') {
+      /* hit illegal char ? */
+      if (mime_b64[*(p+offset)] == 128) {
+        offset++;
+      }
+      else {
+        *p = mime_b64[*(p+offset)];
+        p++;
+      };
+    };
+    *p = 255;
+   
+    /* line is translated, start bit shifting */
+    p = buffer;
+    *num_decoded = 0;  
+    while(*p != 255) {
+      uschar tmp_c;
+      
+      /* byte 0 ---------------------- */
+      if (*(p+1) == 255) {
+        break;
+      }
+      data[(*num_decoded)] = *p;
+      data[(*num_decoded)] <<= 2;
+      tmp_c = *(p+1);
+      tmp_c >>= 4;
+      data[(*num_decoded)] |= tmp_c;
+      (*num_decoded)++;
+      p++;
+      /* byte 1 ---------------------- */
+      if (*(p+1) == 255) {
+        break;
+      }
+      data[(*num_decoded)] = *p;
+      data[(*num_decoded)] <<= 4;
+      tmp_c = *(p+1);
+      tmp_c >>= 2;
+      data[(*num_decoded)] |= tmp_c;
+      (*num_decoded)++;
+      p++;
+      /* byte 2 ---------------------- */
+      if (*(p+1) == 255) {
+        break;
+      }
+      data[(*num_decoded)] = *p;
+      data[(*num_decoded)] <<= 6;
+      data[(*num_decoded)] |= *(p+1); 
+      (*num_decoded)++;
+      p+=2;
+      
+    };
+    return data;
+    /* ----------------------------------------------------------------- */
+  }
+  else if (Ustrcmp(encoding,"quoted-printable") == 0) {
+    uschar *p = buffer;
+
+    /* ----- QP -------------------------------------------------------- */
+    *num_decoded = 0;
+    while (*p != 0) {
+      if (*p == '=') {
+        int decode_qp_result;
+        
+        p = mime_decode_qp_char(p,&decode_qp_result);
+              
+        if (decode_qp_result == -2) {
+          /* Error from decoder. p is unchanged. */
+          data[(*num_decoded)] = '=';
+          (*num_decoded)++;
+          p++;
+        }
+        else if (decode_qp_result == -1) {
+          break;
+        }
+        else if (decode_qp_result >= 0) {
+          data[(*num_decoded)] = decode_qp_result;
+          (*num_decoded)++;
+        };
+      }
+      else {
+        data[(*num_decoded)] = *p;
+        (*num_decoded)++;
+        p++;
+      };
+    };
+    return data;
+    /* ----------------------------------------------------------------- */
+  }
+  /* unknown encoding type, just dump as-is */
+  else goto NO_DECODING;
+}
+
+
+FILE *mime_get_decode_file(uschar *pname, uschar *fname) {
+  FILE *f;
+  uschar *filename;
+  
+  filename = (uschar *)malloc(2048);
+  
+  if ((pname != NULL) && (fname != NULL)) {
+    snprintf(CS filename, 2048, "%s/%s", pname, fname);
+    f = fopen(CS filename,"w+");
+  }
+  else if (pname == NULL) {
+    f = fopen(CS fname,"w+");
+  }
+  else if (fname == NULL) {
+    int file_nr = 0;
+    int result = 0;
+
+    /* must find first free sequential filename */
+    do {
+      struct stat mystat;
+      snprintf(CS filename,2048,"%s/%s-%05u", pname, message_id, file_nr);
+      file_nr++;
+      /* security break */
+      if (file_nr >= 1024)
+        break;
+      result = stat(CS filename,&mystat);
+    }
+    while(result != -1);
+    f = fopen(CS filename,"w+");
+  };
+  
+  /* set expansion variable */
+  mime_decoded_filename = filename;
+  
+  return f;
+}
+
+
+int mime_decode(uschar **listptr) {
+  int sep = 0;
+  uschar *list = *listptr;
+  uschar *option;
+  uschar option_buffer[1024];
+  uschar decode_path[1024];
+  FILE *decode_file = NULL;
+  uschar *buffer = NULL;
+  long f_pos = 0;
+  unsigned int size_counter = 0;
+
+  if (mime_stream == NULL)
+    return FAIL;
+  
+  f_pos = ftell(mime_stream);
+  
+  /* build default decode path (will exist since MBOX must be spooled up) */
+  snprintf(CS decode_path,1024,"%s/scan/%s",spool_directory,message_id);
+  
+  /* reserve a line buffer to work in */
+  buffer = (uschar *)malloc(MIME_MAX_LINE_LENGTH+1);
+  if (buffer == NULL) {
+    log_write(0, LOG_PANIC,
+                 "decode ACL condition: can't allocate %d bytes of memory.", MIME_MAX_LINE_LENGTH+1);
+    return DEFER;
+  };
+  
+  /* try to find 1st option */
+  if ((option = string_nextinlist(&list, &sep,
+                                  option_buffer,
+                                  sizeof(option_buffer))) != NULL) {
+    
+    /* parse 1st option */
+    if ( (Ustrcmp(option,"false") == 0) || (Ustrcmp(option,"0") == 0) ) {
+      /* explicitly no decoding */
+      return FAIL;
+    };
+    
+    if (Ustrcmp(option,"default") == 0) {
+      /* explicit default path + file names */
+      goto DEFAULT_PATH;
+    };
+    
+    if (option[0] == '/') {
+      struct stat statbuf;
+
+      memset(&statbuf,0,sizeof(statbuf));
+      
+      /* assume either path or path+file name */
+      if ( (stat(CS option, &statbuf) == 0) && S_ISDIR(statbuf.st_mode) )
+        /* is directory, use it as decode_path */
+        decode_file = mime_get_decode_file(option, NULL);
+      else
+        /* does not exist or is a file, use as full file name */
+        decode_file = mime_get_decode_file(NULL, option);
+    }
+    else
+      /* assume file name only, use default path */
+      decode_file = mime_get_decode_file(decode_path, option);
+  }
+  else
+    /* no option? patch default path */
+    DEFAULT_PATH: decode_file = mime_get_decode_file(decode_path, NULL);
+  
+  if (decode_file == NULL)
+    return DEFER;
+  
+  /* read data linewise and dump it to the file,
+     while looking for the current boundary */
+  while(fgets(CS buffer, MIME_MAX_LINE_LENGTH, mime_stream) != NULL) {
+    uschar *decoded_line = NULL;
+    int decoded_line_length = 0;
+    
+    if (mime_current_boundary != NULL) {
+      /* boundary line must start with 2 dashes */
+      if (Ustrncmp(buffer,"--",2) == 0) {
+        if (Ustrncmp((buffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
+          break;
+      };
+    };
+  
+    decoded_line = mime_parse_line(buffer, mime_content_transfer_encoding, &decoded_line_length);
+    /* write line to decode file */
+    if (fwrite(decoded_line, 1, decoded_line_length, decode_file) < decoded_line_length) {
+      /* error/short write */
+      clearerr(mime_stream);
+      fseek(mime_stream,f_pos,SEEK_SET);
+      return DEFER;
+    };
+    size_counter += decoded_line_length;
+    
+    if (size_counter > 1023) { 
+      if ((mime_content_size + (size_counter / 1024)) < 65535)
+        mime_content_size += (size_counter / 1024);
+      else 
+        mime_content_size = 65535;
+      size_counter = (size_counter % 1024);
+    };
+    
+    free(decoded_line);
+  }
+  
+  fclose(decode_file);
+  
+  clearerr(mime_stream);
+  fseek(mime_stream,f_pos,SEEK_SET);
+  
+  /* round up remaining size bytes to one k */
+  if (size_counter) {
+    mime_content_size++;
+  };
+  
+  return OK;
+}
+
+int mime_get_header(FILE *f, uschar *header) {
+  int c = EOF;
+  int done = 0;
+  int header_value_mode = 0;
+  int header_open_brackets = 0;
+  int num_copied = 0;
+  
+  while(!done) {
+    
+    c = fgetc(f);
+    if (c == EOF) break;
+   
+    /* always skip CRs */
+    if (c == '\r') continue;
+    
+    if (c == '\n') {
+      if (num_copied > 0) {
+        /* look if next char is '\t' or ' ' */
+        c = fgetc(f);
+        if (c == EOF) break;
+        if ( (c == '\t') || (c == ' ') ) continue;
+        ungetc(c,f);
+      };
+      /* end of the header, terminate with ';' */
+      c = ';';
+      done = 1;
+    };
+  
+    /* skip control characters */
+    if (c < 32) continue;
+
+    if (header_value_mode) {
+      /* --------- value mode ----------- */
+      /* skip leading whitespace */
+      if ( ((c == '\t') || (c == ' ')) && (header_value_mode == 1) )
+        continue;
+      
+      /* we have hit a non-whitespace char, start copying value data */
+      header_value_mode = 2;
+      
+      /* skip quotes */
+      if (c == '"') continue;
+      
+      /* leave value mode on ';' */
+      if (c == ';') {
+        header_value_mode = 0;
+      };
+      /* -------------------------------- */
+    }
+    else {
+      /* -------- non-value mode -------- */
+      /* skip whitespace + tabs */
+      if ( (c == ' ') || (c == '\t') )
+        continue;
+      if (c == '\\') {
+        /* quote next char. can be used
+        to escape brackets. */
+        c = fgetc(f);
+        if (c == EOF) break;
+      }
+      else if (c == '(') {
+        header_open_brackets++;
+        continue;
+      }
+      else if ((c == ')') && header_open_brackets) {
+        header_open_brackets--;
+        continue;
+      }
+      else if ( (c == '=') && !header_open_brackets ) {
+        /* enter value mode */
+        header_value_mode = 1;
+      };
+      
+      /* skip chars while we are in a comment */
+      if (header_open_brackets > 0)
+        continue;
+      /* -------------------------------- */
+    };
+    
+    /* copy the char to the buffer */
+    header[num_copied] = (uschar)c;
+    /* raise counter */
+    num_copied++;
+    
+    /* break if header buffer is full */
+    if (num_copied > MIME_MAX_HEADER_SIZE-1) {
+      done = 1;
+    };
+  };
+
+  if (header[num_copied-1] != ';') {
+    header[num_copied-1] = ';';
+  };
+
+  /* 0-terminate */
+  header[num_copied] = '\0';
+  
+  /* return 0 for EOF or empty line */
+  if ((c == EOF) || (num_copied == 1))
+    return 0;
+  else
+    return 1;
+}
+
+
+int mime_acl_check(FILE *f, struct mime_boundary_context *context, uschar 
+                   **user_msgptr, uschar **log_msgptr) {
+  int rc = OK;
+  uschar *header = NULL;
+  struct mime_boundary_context nested_context;
+
+  /* reserve a line buffer to work in */
+  header = (uschar *)malloc(MIME_MAX_HEADER_SIZE+1);
+  if (header == NULL) {
+    log_write(0, LOG_PANIC,
+                 "acl_smtp_mime: can't allocate %d bytes of memory.", MIME_MAX_HEADER_SIZE+1);
+    return DEFER;
+  };
+
+  /* Not actually used at the moment, but will be vital to fixing
+   * some RFC 2046 nonconformance later... */
+  nested_context.parent = context;
+
+  /* loop through parts */
+  while(1) {
+  
+    /* reset all per-part mime variables */
+    mime_anomaly_level     = NULL;
+    mime_anomaly_text      = NULL;
+    mime_boundary          = NULL;
+    mime_charset           = NULL;
+    mime_decoded_filename  = NULL;
+    mime_filename          = NULL;
+    mime_content_description = NULL;
+    mime_content_disposition = NULL;
+    mime_content_id        = NULL;
+    mime_content_transfer_encoding = NULL;
+    mime_content_type      = NULL;
+    mime_is_multipart      = 0;
+    mime_content_size      = 0;
+  
+    /*
+    If boundary is null, we assume that *f is positioned on the start of headers (for example,
+    at the very beginning of a message.
+    If a boundary is given, we must first advance to it to reach the start of the next header
+    block.
+    */
+    
+    /* NOTE -- there's an error here -- RFC2046 specifically says to
+     * check for outer boundaries.  This code doesn't do that, and
+     * I haven't fixed this.
+     *
+     * (I have moved partway towards adding support, however, by adding 
+     * a "parent" field to my new boundary-context structure.)
+     */
+    if (context != NULL) {
+      while(fgets(CS header, MIME_MAX_HEADER_SIZE, f) != NULL) {
+        /* boundary line must start with 2 dashes */
+        if (Ustrncmp(header,"--",2) == 0) {
+          if (Ustrncmp((header+2),context->boundary,Ustrlen(context->boundary)) == 0) {
+            /* found boundary */
+            if (Ustrncmp((header+2+Ustrlen(context->boundary)),"--",2) == 0) {
+              /* END boundary found */
+              debug_printf("End boundary found %s\n", context->boundary);
+              return rc;
+            }
+            else {
+              debug_printf("Next part with boundary %s\n", context->boundary);
+            };
+            /* can't use break here */
+            goto DECODE_HEADERS;
+          }
+        };
+      }
+      /* Hit EOF or read error. Ugh. */
+      debug_printf("Hit EOF ...\n");
+      return rc;
+    };
+  
+    DECODE_HEADERS:
+    /* parse headers, set up expansion variables */
+    while(mime_get_header(f,header)) {
+      int i;
+      /* loop through header list */
+      for (i = 0; i < mime_header_list_size; i++) {
+        uschar *header_value = NULL;
+        int header_value_len = 0;
+        
+        /* found an interesting header? */
+        if (strncmpic(mime_header_list[i].name,header,mime_header_list[i].namelen) == 0) {
+          uschar *p = header + mime_header_list[i].namelen;
+          /* yes, grab the value (normalize to lower case)
+             and copy to its corresponding expansion variable */
+          while(*p != ';') {
+            *p = tolower(*p);
+            p++;
+          };
+          header_value_len = (p - (header + mime_header_list[i].namelen));
+          header_value = (uschar *)malloc(header_value_len+1);
+          memset(header_value,0,header_value_len+1);
+          p = header + mime_header_list[i].namelen;
+          Ustrncpy(header_value, p, header_value_len);
+          debug_printf("Found %s MIME header, value is '%s'\n", mime_header_list[i].name, header_value);
+          *((uschar **)(mime_header_list[i].value)) = header_value;
+          
+          /* make p point to the next character after the closing ';' */
+          p += (header_value_len+1);
+          
+          /* grab all param=value tags on the remaining line, check if they are interesting */
+          NEXT_PARAM_SEARCH: while (*p != 0) {
+            int j;
+            for (j = 0; j < mime_parameter_list_size; j++) {
+              uschar *param_value = NULL;
+              int param_value_len = 0;
+              
+              /* found an interesting parameter? */
+              if (strncmpic(mime_parameter_list[j].name,p,mime_parameter_list[j].namelen) == 0) {
+                uschar *q = p + mime_parameter_list[j].namelen;
+                /* yes, grab the value and copy to its corresponding expansion variable */
+                while(*q != ';') q++;
+                param_value_len = (q - (p + mime_parameter_list[j].namelen));
+                param_value = (uschar *)malloc(param_value_len+1);
+                memset(param_value,0,param_value_len+1);
+                q = p + mime_parameter_list[j].namelen;
+                Ustrncpy(param_value, q, param_value_len);
+                param_value = rfc2047_decode(param_value, TRUE, NULL, 32, &param_value_len, &q);
+                debug_printf("Found %s MIME parameter in %s header, value is '%s'\n", mime_parameter_list[j].name, mime_header_list[i].name, param_value);
+                *((uschar **)(mime_parameter_list[j].value)) = param_value;
+                p += (mime_parameter_list[j].namelen + param_value_len + 1);
+                goto NEXT_PARAM_SEARCH;
+              };
+            }
+            /* There is something, but not one of our interesting parameters.
+               Advance to the next semicolon */
+            while(*p != ';') p++;
+            p++;
+          };
+        };
+      };
+    };
+    
+    /* set additional flag variables (easier access) */
+    if ( (mime_content_type != NULL) &&
+         (Ustrncmp(mime_content_type,"multipart",9) == 0) )
+      mime_is_multipart = 1;
+    
+    /* Make a copy of the boundary pointer.
+       Required since mime_boundary is global
+       and can be overwritten further down in recursion */
+    nested_context.boundary = mime_boundary;
+    
+    /* raise global counter */
+    mime_part_count++;
+    
+    /* copy current file handle to global variable */
+    mime_stream = f;
+    mime_current_boundary = context ? context->boundary : 0;
+
+    /* Note the context */
+    mime_is_coverletter = !(context && context->context == MBC_ATTACHMENT);
+    
+    /* call ACL handling function */
+    rc = acl_check(ACL_WHERE_MIME, NULL, acl_smtp_mime, user_msgptr, log_msgptr);
+    
+    mime_stream = NULL;
+    mime_current_boundary = NULL;
+    
+    if (rc != OK) break;
+    
+    /* If we have a multipart entity and a boundary, go recursive */
+    if ( (mime_content_type != NULL) &&
+         (nested_context.boundary != NULL) &&
+         (Ustrncmp(mime_content_type,"multipart",9) == 0) ) {
+      debug_printf("Entering multipart recursion, boundary '%s'\n", nested_context.boundary);
+
+      if (context && context->context == MBC_ATTACHMENT)
+        nested_context.context = MBC_ATTACHMENT;
+      else if (!Ustrcmp(mime_content_type,"multipart/alternative")
+            || !Ustrcmp(mime_content_type,"multipart/related"))
+        nested_context.context = MBC_COVERLETTER_ALL;
+      else
+        nested_context.context = MBC_COVERLETTER_ONESHOT;
+
+      rc = mime_acl_check(f, &nested_context, user_msgptr, log_msgptr);
+      if (rc != OK) break;
+    }
+    else if ( (mime_content_type != NULL) &&
+            (Ustrncmp(mime_content_type,"message/rfc822",14) == 0) ) {
+      uschar *rfc822name = NULL;
+      uschar filename[2048];
+      int file_nr = 0;
+      int result = 0;
+      
+      /* must find first free sequential filename */
+      do {
+        struct stat mystat;
+        snprintf(CS filename,2048,"%s/scan/%s/__rfc822_%05u", spool_directory, message_id, file_nr);
+        file_nr++;
+        /* security break */
+        if (file_nr >= 128)
+          goto NO_RFC822;
+        result = stat(CS filename,&mystat);
+      }
+      while(result != -1);
+      
+      rfc822name = filename;
+      
+      /* decode RFC822 attachment */
+      mime_decoded_filename = NULL;
+      mime_stream = f;
+      mime_current_boundary = context ? context->boundary : NULL;
+      mime_decode(&rfc822name);
+      mime_stream = NULL;
+      mime_current_boundary = NULL;
+      if (mime_decoded_filename == NULL) {
+        /* decoding failed */
+        log_write(0, LOG_MAIN,
+             "mime_regex acl condition warning - could not decode RFC822 MIME part to file.");
+        return DEFER;
+      };
+      mime_decoded_filename = NULL;
+    };
+    
+    NO_RFC822:
+    /* If the boundary of this instance is NULL, we are finished here */
+    if (context == NULL) break;
+
+    if (context->context == MBC_COVERLETTER_ONESHOT)
+      context->context = MBC_ATTACHMENT;
+  
+  };
+
+  return rc;
+}
+
+
diff -urN exim-4.44-orig/src/mime.h exim-4.44/src/mime.h
--- exim-4.44-orig/src/mime.h	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/mime.h	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,77 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 */
+/* License: GPL */
+
+
+#define MIME_MAX_HEADER_SIZE 8192
+#define MIME_MAX_LINE_LENGTH 32768
+
+#define MBC_ATTACHMENT            0
+#define MBC_COVERLETTER_ONESHOT   1
+#define MBC_COVERLETTER_ALL       2
+
+struct mime_boundary_context
+{
+  struct mime_boundary_context *parent;
+  unsigned char *boundary;
+  int context;
+};
+
+typedef struct mime_header {
+  uschar *name;
+  int    namelen;
+  void   *value;
+} mime_header;
+
+static mime_header mime_header_list[] = {
+  { US"content-type:", 13, &mime_content_type },
+  { US"content-disposition:", 20, &mime_content_disposition },
+  { US"content-transfer-encoding:", 26, &mime_content_transfer_encoding },
+  { US"content-id:", 11, &mime_content_id },
+  { US"content-description:", 20 , &mime_content_description }
+};
+
+static int mime_header_list_size = sizeof(mime_header_list)/sizeof(mime_header);
+
+
+
+typedef struct mime_parameter {
+  uschar *name;
+  int    namelen;
+  void   *value;
+} mime_parameter;
+
+static mime_parameter mime_parameter_list[] = {
+  { US"name=", 5, &mime_filename },
+  { US"filename=", 9, &mime_filename },
+  { US"charset=", 8, &mime_charset },
+  { US"boundary=", 9, &mime_boundary }
+};
+
+static int mime_parameter_list_size = sizeof(mime_parameter_list)/sizeof(mime_parameter);
+
+/* BASE64 decoder matrix */
+static unsigned char mime_b64[256]={
+/*   0 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/*  16 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128, 
+/*  32 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,   62,  128,  128,  128,   63,
+/*  48 */   52,   53,   54,   55,   56,   57,   58,   59,   60,   61,  128,  128,  128,  255,  128,  128,
+/*  64 */  128,    0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13,   14,
+/*  80 */   15,   16,   17,   18,   19,   20,   21,   22,   23,   24,   25,  128,  128,  128,  128,  128,
+/*  96 */  128,   26,   27,   28,   29,   30,   31,   32,   33,   34,   35,   36,   37,   38,   39,   40,
+/* 112 */   41,   42,   43,   44,   45,   46,   47,   48,   49,   50,   51,  128,  128,  128,  128,  128,
+/* 128 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/* 144 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/* 160 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/* 176 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/* 192 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/* 208 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/* 224 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,
+/* 240 */  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128,  128 
+};
diff -urN exim-4.44-orig/src/readconf.c exim-4.44/src/readconf.c
--- exim-4.44-orig/src/readconf.c	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/readconf.c	Fri Jan 14 20:35:27 2005
@@ -141,6 +141,7 @@
   { "acl_smtp_helo",            opt_stringptr,   &acl_smtp_helo },
   { "acl_smtp_mail",            opt_stringptr,   &acl_smtp_mail },
   { "acl_smtp_mailauth",        opt_stringptr,   &acl_smtp_mailauth },
+  { "acl_smtp_mime",            opt_stringptr,   &acl_smtp_mime },
   { "acl_smtp_predata",         opt_stringptr,   &acl_smtp_predata },
   { "acl_smtp_quit",            opt_stringptr,   &acl_smtp_quit },
   { "acl_smtp_rcpt",            opt_stringptr,   &acl_smtp_rcpt },
@@ -154,7 +155,11 @@
   { "allow_utf8_domains",       opt_bool,        &allow_utf8_domains },
   { "auth_advertise_hosts",     opt_stringptr,   &auth_advertise_hosts },
   { "auto_thaw",                opt_time,        &auto_thaw },
+  { "av_scanner",               opt_stringptr,   &av_scanner },
   { "bi_command",               opt_stringptr,   &bi_command },
+#ifdef BRIGHTMAIL
+  { "bmi_config_file",          opt_stringptr,   &bmi_config_file },
+#endif
   { "bounce_message_file",      opt_stringptr,   &bounce_message_file },
   { "bounce_message_text",      opt_stringptr,   &bounce_message_text },
   { "bounce_return_body",       opt_bool,        &bounce_return_body },
@@ -316,8 +321,12 @@
   { "smtp_receive_timeout",     opt_time,        &smtp_receive_timeout },
   { "smtp_reserve_hosts",       opt_stringptr,   &smtp_reserve_hosts },
   { "smtp_return_error_details",opt_bool,        &smtp_return_error_details },
+  { "spamd_address",            opt_stringptr,   &spamd_address },
   { "split_spool_directory",    opt_bool,        &split_spool_directory },
   { "spool_directory",          opt_stringptr,   &spool_directory },
+#ifdef SRS
+  { "srs_config",               opt_stringptr,   &srs_config },
+#endif
   { "strip_excess_angle_brackets", opt_bool,     &strip_excess_angle_brackets },
   { "strip_trailing_dot",       opt_bool,        &strip_trailing_dot },
   { "syslog_duplication",       opt_bool,        &syslog_duplication },
diff -urN exim-4.44-orig/src/receive.c exim-4.44/src/receive.c
--- exim-4.44-orig/src/receive.c	Tue Jan 11 14:23:52 2005
+++ exim-4.44/src/receive.c	Fri Jan 14 20:35:27 2005
@@ -10,7 +10,9 @@
 
 #include "exim.h"
 
-
+#ifdef BRIGHTMAIL
+#include "bmi_spam.h"
+#endif
 
 /*************************************************
 *                Local static variables          *
@@ -436,6 +438,13 @@
 
 recipients_list[recipients_count].address = recipient;
 recipients_list[recipients_count].pno = pno;
+
+#ifdef BRIGHTMAIL
+recipients_list[recipients_count].bmi_optin = bmi_current_optin;
+/* reset optin string pointer for next recipient */
+bmi_current_optin = NULL;
+#endif
+
 recipients_list[recipients_count++].errors_to = NULL;
 }
 
@@ -884,6 +893,21 @@
     DEBUG(D_receive|D_acl) debug_printf("  (after Received:)");
     break;
 
+    case htype_add_spf:
+    /* add header before any header which is NOT Received: or Resent- */
+    last_received = header_list;
+    while ( (last_received->next != NULL) &&
+            ( (header_testname(last_received->next, US"Received", 8, FALSE)) ||
+              (header_testname_incomplete(last_received->next, US"Resent-", 7, FALSE)) ) )
+              last_received = last_received->next;
+    /* last_received now points to the last Received: or Resent-* header
+       in an uninterrupted chain of those header types (seen from the beginning
+       of all headers. Our current header must follow it. */
+    h->next = last_received->next;
+    last_received->next = h;
+    DEBUG(D_receive|D_acl) debug_printf("  (before any non-Received: or Resent-*: header)");        
+    break;
+
     default:
     h->next = NULL;
     header_last->next = h;
@@ -1076,7 +1100,7 @@
 
 /* Working header pointers */
 
-header_line *h, *next;
+header_line *h, *next, *header_list_p;
 
 /* Flags for noting the existence of certain headers */
 
@@ -2682,6 +2706,124 @@
 
   if (smtp_input && !smtp_batched_input)
     {
+    if (acl_smtp_mime != NULL && recipients_count > 0)
+      {
+      FILE *mbox_file;
+      uschar rfc822_file_path[2048];
+      unsigned long long mbox_size;
+      header_line *my_headerlist;
+      uschar *user_msg, *log_msg;
+      int mime_part_count_buffer = -1;
+      
+      memset(CS rfc822_file_path,0,2048);
+      
+      /* check if it is a MIME message */
+      my_headerlist = header_list;
+      while (my_headerlist != NULL) {
+        /* skip deleted headers */
+        if (my_headerlist->type == '*') {
+          my_headerlist = my_headerlist->next;
+          continue;
+        };
+        if (strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0) {
+          DEBUG(D_receive) debug_printf("Found Content-Type: header - executing acl_smtp_mime.\n");
+          goto DO_MIME_ACL;
+        };
+        my_headerlist = my_headerlist->next;
+      };
+      
+      DEBUG(D_receive) debug_printf("No Content-Type: header - presumably not a MIME message.\n");
+      goto NO_MIME_ACL;
+      
+      DO_MIME_ACL:
+      /* make sure the eml mbox file is spooled up */
+      mbox_file = spool_mbox(&mbox_size);
+      if (mbox_file == NULL) {
+        /* error while spooling */
+        log_write(0, LOG_MAIN|LOG_PANIC,
+               "acl_smtp_mime: error while creating mbox spool file, message temporarily rejected.");
+        Uunlink(spool_name);
+        unspool_mbox(); 
+        smtp_respond(451, TRUE, US"temporary local problem");
+        message_id[0] = 0;            /* Indicate no message accepted */
+        smtp_reply = US"";            /* Indicate reply already sent */
+        goto TIDYUP;                  /* Skip to end of function */
+      };
+      
+      mime_is_rfc822 = 0;
+
+      MIME_ACL_CHECK:
+      mime_part_count = -1;
+      rc = mime_acl_check(mbox_file, NULL, &user_msg, &log_msg);
+      fclose(mbox_file);
+      
+      if (Ustrlen(rfc822_file_path) > 0) {
+        mime_part_count = mime_part_count_buffer;
+        
+        if (unlink(CS rfc822_file_path) == -1) {
+          log_write(0, LOG_PANIC,
+               "acl_smtp_mime: can't unlink RFC822 spool file, skipping.");
+            goto END_MIME_ACL;
+        };
+      };
+      
+      /* check if we must check any message/rfc822 attachments */
+      if (rc == OK) {
+        uschar temp_path[1024];
+        int n;
+        struct dirent *entry;
+        DIR *tempdir;
+ 
+        snprintf(CS temp_path, 1024, "%s/scan/%s", spool_directory, message_id);
+
+      	tempdir = opendir(CS temp_path);
+      	n = 0;
+      	do {
+      	  entry = readdir(tempdir);
+      	  if (entry == NULL) break;
+          if (strncmpic(US entry->d_name,US"__rfc822_",9) == 0) {
+            snprintf(CS rfc822_file_path, 2048,"%s/scan/%s/%s", spool_directory, message_id, entry->d_name);
+      	    debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path);
+      	    break;
+          }; 
+      	} while (1);
+      	closedir(tempdir);
+        
+        if (entry != NULL) {
+          mbox_file = Ufopen(rfc822_file_path,"r");
+          if (mbox_file == NULL) {
+            log_write(0, LOG_PANIC,
+               "acl_smtp_mime: can't open RFC822 spool file, skipping.");
+            unlink(CS rfc822_file_path);
+            goto END_MIME_ACL;
+          };
+          /* set RFC822 expansion variable */
+          mime_is_rfc822 = 1;
+          mime_part_count_buffer = mime_part_count;
+          goto MIME_ACL_CHECK;
+        };
+      };
+      
+      END_MIME_ACL:
+      add_acl_headers(US"MIME");
+      if (rc == DISCARD)
+        {
+        recipients_count = 0;
+        blackholed_by = US"MIME ACL";
+        }
+      else if (rc != OK)
+        {
+        Uunlink(spool_name);
+        unspool_mbox();
+        if (smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0)
+          smtp_yield = FALSE;    /* No more messsages after dropped connection */
+        smtp_reply = US"";       /* Indicate reply already sent */
+        message_id[0] = 0;       /* Indicate no message accepted */
+        goto TIDYUP;             /* Skip to end of function */
+        }; 
+      }
+ 
+    NO_MIME_ACL:
     if (acl_smtp_data != NULL && recipients_count > 0)
       {
       uschar *user_msg, *log_msg;
@@ -2695,6 +2837,7 @@
       else if (rc != OK)
         {
         Uunlink(spool_name);
+        unspool_mbox();
         if (smtp_handle_acl_fail(ACL_WHERE_DATA, rc, user_msg, log_msg) != 0)
           smtp_yield = FALSE;    /* No more messsages after dropped connection */
         smtp_reply = US"";       /* Indicate reply already sent */
@@ -2704,6 +2847,7 @@
       }
     }
 
+
   /* Handle non-SMTP and batch SMTP (i.e. non-interactive) messages. Note that
   we cannot take different actions for permanent and temporary rejections. */
 
@@ -2745,6 +2889,8 @@
   enable_dollar_recipients = FALSE;
   }
 
+unspool_mbox();
+
 /* The final check on the message is to run the scan_local() function. The
 version supplied with Exim always accepts, but this is a hook for sysadmins to
 supply their own checking code. The local_scan() function is run even when all
@@ -2915,6 +3061,14 @@
 
 deliver_firsttime = TRUE;
 
+#ifdef BRIGHTMAIL
+if (bmi_run == 1) {
+  /* rewind data file */
+  lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET);
+  bmi_verdicts = bmi_process_message(header_list, data_fd);
+};
+#endif
+
 /* Update the timstamp in our Received: header to account for any time taken by
 an ACL or by local_scan(). The new time is the time that all reception
 processing is complete. */
@@ -3175,6 +3329,7 @@
 if this happens? */
 
 TIDYUP:
+
 process_info[process_info_len] = 0;          /* Remove message id */
 if (data_file != NULL) fclose(data_file);    /* Frees the lock */
 
@@ -3201,12 +3356,31 @@
     {
     if (smtp_reply == NULL)
       {
-      smtp_printf("250 OK id=%s\r\n", message_id);
+        if (fake_reject)
+        {
+          smtp_printf("550-FAKE_REJECT id=%s\r\n", message_id);
+          smtp_printf("550-Your message has been rejected but is being kept for evaluation.\r\n");
+          smtp_printf("550 If it was a legit message, it may still be delivered to the target recipient(s).\r\n");
+        }
+        else  
+          smtp_printf("250 OK id=%s\r\n", message_id);
+      
       if (host_checking)
         fprintf(stdout,
           "\n**** SMTP testing: that is not a real message id!\n\n");
+      
       }
-    else if (smtp_reply[0] != 0) smtp_printf("%.1024s\r\n", smtp_reply);
+    else if (smtp_reply[0] != 0)
+      {
+        if (fake_reject && (smtp_reply[0] == '2'))
+        {
+          smtp_printf("550-FAKE_REJECT id=%s\r\n", message_id);
+          smtp_printf("550-Your message has been rejected but is being kept for evaluation.\r\n");
+          smtp_printf("550 If it was a legit message, it may still be delivered to the target recipient(s).\r\n");
+        }
+        else 
+          smtp_printf("%.1024s\r\n", smtp_reply);
+      };
     }
 
   /* For batched SMTP, generate an error message on failure, and do
diff -urN exim-4.44-orig/src/regex.c exim-4.44/src/regex.c
--- exim-4.44-orig/src/regex.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/regex.c	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,246 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* Code for matching regular expressions against headers and body.
+ Called from acl.c. */
+
+#include "exim.h"
+#include <unistd.h>
+#include <sys/mman.h>
+
+/* Structure to hold a list of Regular expressions */
+typedef struct pcre_list {
+  pcre *re;
+  uschar *pcre_text;
+  struct pcre_list *next;
+} pcre_list;
+
+uschar regex_match_string_buffer[1024];
+
+extern FILE *mime_stream;
+extern uschar *mime_current_boundary;
+
+int regex(uschar **listptr) {
+  int sep = 0;
+  uschar *list = *listptr;
+  uschar *regex_string;
+  uschar regex_string_buffer[1024];
+  unsigned long long mbox_size;
+  FILE *mbox_file;
+  pcre *re;
+  pcre_list *re_list_head = NULL;
+  pcre_list *re_list_item;
+  const char *pcre_error;
+  int pcre_erroffset;
+  uschar *linebuffer;
+  long f_pos = 0;
+  
+  /* reset expansion variable */
+  regex_match_string = NULL;
+  
+  if (mime_stream == NULL) {
+    /* We are in the DATA ACL */
+    mbox_file = spool_mbox(&mbox_size);
+    if (mbox_file == NULL) {
+      /* error while spooling */
+      log_write(0, LOG_MAIN|LOG_PANIC,
+             "regex acl condition: error while creating mbox spool file");
+      return DEFER;
+    };
+  }
+  else {
+    f_pos = ftell(mime_stream);
+    mbox_file = mime_stream;
+  };
+  
+  /* precompile our regexes */
+  while ((regex_string = string_nextinlist(&list, &sep,
+                                           regex_string_buffer,
+                                           sizeof(regex_string_buffer))) != NULL) {
+    
+    /* parse option */
+    if ( (strcmpic(regex_string,US"false") == 0) || 
+         (Ustrcmp(regex_string,"0") == 0) ) {
+      /* explicitly no matching */
+      continue;
+    };
+    
+    /* compile our regular expression */
+    re = pcre_compile( CS regex_string,
+                       0,
+                       &pcre_error,
+                       &pcre_erroffset,
+                       NULL );
+
+    if (re == NULL) {
+      log_write(0, LOG_MAIN,
+           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
+      continue;
+    }
+    else {
+      re_list_item = store_get(sizeof(pcre_list));
+      re_list_item->re = re;
+      re_list_item->pcre_text = string_copy(regex_string);
+      re_list_item->next = re_list_head;
+      re_list_head = re_list_item;
+    };
+  };
+  
+  /* no regexes -> nothing to do */
+  if (re_list_head == NULL) {
+    return FAIL;
+  };
+  
+  /* match each line against all regexes */
+  linebuffer = store_get(32767);
+  while (fgets(CS linebuffer, 32767, mbox_file) != NULL) {  
+    if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) {
+      /* check boundary */
+      if (Ustrncmp(linebuffer,"--",2) == 0) {
+        if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
+          /* found boundary */
+          break;
+      };
+    };
+    re_list_item = re_list_head;
+    do {
+      /* try matcher on the line */
+      if (pcre_exec(re_list_item->re, NULL, CS linebuffer,
+  	                (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) {
+        Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
+        regex_match_string = regex_match_string_buffer;
+        if (mime_stream == NULL)
+          fclose(mbox_file);
+        else {
+          clearerr(mime_stream);
+          fseek(mime_stream,f_pos,SEEK_SET);
+        };
+        return OK;
+      };
+      re_list_item = re_list_item->next;
+    } while (re_list_item != NULL);
+  };
+  
+  if (mime_stream == NULL)
+    fclose(mbox_file);
+  else {
+    clearerr(mime_stream);
+    fseek(mime_stream,f_pos,SEEK_SET);
+  };
+    
+  /* no matches ... */
+  return FAIL;
+}
+
+
+int mime_regex(uschar **listptr) {
+  int sep = 0;
+  uschar *list = *listptr;
+  uschar *regex_string;
+  uschar regex_string_buffer[1024];
+  pcre *re;
+  pcre_list *re_list_head = NULL;
+  pcre_list *re_list_item;
+  const char *pcre_error;
+  int pcre_erroffset;
+  FILE *f;
+  uschar *mime_subject = NULL;
+  int mime_subject_len = 0;
+
+  /* reset expansion variable */
+  regex_match_string = NULL;
+
+  /* precompile our regexes */
+  while ((regex_string = string_nextinlist(&list, &sep,
+                                           regex_string_buffer,
+                                           sizeof(regex_string_buffer))) != NULL) {
+    
+    /* parse option */
+    if ( (strcmpic(regex_string,US"false") == 0) || 
+         (Ustrcmp(regex_string,"0") == 0) ) {
+      /* explicitly no matching */
+      continue;
+    };
+    
+    /* compile our regular expression */
+    re = pcre_compile( CS regex_string,
+                       0,
+                       &pcre_error,
+                       &pcre_erroffset,
+                       NULL );
+
+    if (re == NULL) {
+      log_write(0, LOG_MAIN,
+           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
+      continue;
+    }
+    else {
+      re_list_item = store_get(sizeof(pcre_list));
+      re_list_item->re = re;
+      re_list_item->pcre_text = string_copy(regex_string);
+      re_list_item->next = re_list_head;
+      re_list_head = re_list_item;
+    };
+  };
+  
+  /* no regexes -> nothing to do */
+  if (re_list_head == NULL) {
+    return FAIL;
+  };
+  
+  /* check if the file is already decoded */
+  if (mime_decoded_filename == NULL) {
+    uschar *empty = US"";
+    /* no, decode it first */
+    mime_decode(&empty);
+    if (mime_decoded_filename == NULL) {
+      /* decoding failed */
+      log_write(0, LOG_MAIN,
+           "mime_regex acl condition warning - could not decode MIME part to file.");
+      return DEFER;
+    };
+  };
+
+
+  /* open file */
+  f = fopen(CS mime_decoded_filename, "r");
+  if (f == NULL) {
+    /* open failed */
+    log_write(0, LOG_MAIN,
+         "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename);
+    return DEFER;
+  };
+  
+  /* get 32k memory */
+  mime_subject = (uschar *)store_get(32767);
+  
+  /* read max 32k chars from file */
+  mime_subject_len = fread(mime_subject, 1, 32766, f);
+  
+  re_list_item = re_list_head;
+  do {
+    /* try matcher on the mmapped file */
+    debug_printf("Matching '%s'\n", re_list_item->pcre_text);
+    if (pcre_exec(re_list_item->re, NULL, CS mime_subject,
+                  mime_subject_len, 0, 0, NULL, 0) >= 0) {
+      Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
+      regex_match_string = regex_match_string_buffer;
+      fclose(f);
+      return OK;
+    };
+    re_list_item = re_list_item->next;
+  } while (re_list_item != NULL);
+
+  fclose(f);
+  
+  /* no matches ... */
+  return FAIL;
+}
+
diff -urN exim-4.44-orig/src/route.c exim-4.44/src/route.c
--- exim-4.44-orig/src/route.c	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/route.c	Fri Jan 14 20:35:27 2005
@@ -10,6 +10,9 @@
 
 #include "exim.h"
 
+#ifdef BRIGHTMAIL
+#include "bmi_spam.h"
+#endif
 
 
 /* Generic options for routers, all of which live inside router_instance
@@ -32,6 +35,16 @@
                  (void *)(offsetof(router_instance, address_data)) },
   { "address_test",       opt_bool|opt_public,
                  (void *)(offsetof(router_instance, address_test)) },
+#ifdef BRIGHTMAIL
+  { "bmi_deliver_alternate",   opt_bool | opt_public,
+                 (void *)(offsetof(router_instance, bmi_deliver_alternate)) },
+  { "bmi_deliver_default",   opt_bool | opt_public,
+                 (void *)(offsetof(router_instance, bmi_deliver_default)) },
+  { "bmi_dont_deliver",   opt_bool | opt_public,
+                 (void *)(offsetof(router_instance, bmi_dont_deliver)) },
+  { "bmi_rule",           opt_stringptr|opt_public,
+                 (void *)(offsetof(router_instance, bmi_rule)) },
+#endif
   { "cannot_route_message", opt_stringptr | opt_public,
                  (void *)(offsetof(router_instance, cannot_route_message)) },
   { "caseful_local_part", opt_bool | opt_public,
@@ -980,6 +993,49 @@
     }
   }
 
+#ifdef BRIGHTMAIL
+
+/* check if a specific Brightmail AntiSpam rule fired on the message */
+if (r->bmi_rule != NULL) {
+  DEBUG(D_route) debug_printf("checking bmi_rule\n");
+  if (bmi_check_rule(bmi_base64_verdict, r->bmi_rule) == 0) {
+    /* none of the rules fired */
+    DEBUG(D_route)
+      debug_printf("%s router skipped: none of bmi_rule rules fired\n", r->name);
+    return SKIP;
+  };
+};
+
+/* check if message should not be delivered */
+if (r->bmi_dont_deliver) {
+  if (bmi_deliver == 1) {
+    DEBUG(D_route)
+      debug_printf("%s router skipped: bmi_dont_deliver is FALSE\n", r->name);
+    return SKIP;
+  };
+};
+
+/* check if message should go to an alternate location */
+if (r->bmi_deliver_alternate) {
+  if ((bmi_deliver == 0) || (bmi_alt_location == NULL)) {
+    DEBUG(D_route)
+      debug_printf("%s router skipped: bmi_deliver_alternate is FALSE\n", r->name);
+    return SKIP;
+  };
+};
+
+/* check if message should go to default location */
+if (r->bmi_deliver_default) {
+  if ((bmi_deliver == 0) || (bmi_alt_location != NULL)) {
+    DEBUG(D_route)
+      debug_printf("%s router skipped: bmi_deliver_default is FALSE\n", r->name);
+    return SKIP;
+  };
+};
+
+#endif
+
+
 /* All the checks passed. */
 
 return OK;
diff -urN exim-4.44-orig/src/routers/redirect.c exim-4.44/src/routers/redirect.c
--- exim-4.44-orig/src/routers/redirect.c	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/routers/redirect.c	Fri Jan 14 20:35:27 2005
@@ -9,6 +9,7 @@
 #include "../exim.h"
 #include "rf_functions.h"
 #include "redirect.h"
+#include "../srs.h"
 
 
 
@@ -95,6 +96,16 @@
       (void *)offsetof(redirect_router_options_block, sieve_vacation_directory) },
   { "skip_syntax_errors", opt_bool,
       (void *)offsetof(redirect_router_options_block, skip_syntax_errors) },
+#ifdef SRS
+  { "srs",                opt_stringptr,
+  (void *)offsetof(redirect_router_options_block, srs) },
+  { "srs_alias",          opt_stringptr,
+  (void *)offsetof(redirect_router_options_block, srs_alias) },
+  { "srs_condition",      opt_stringptr,
+  (void *)offsetof(redirect_router_options_block, srs_condition) },
+  { "srs_db",             opt_stringptr,
+  (void *)offsetof(redirect_router_options_block, srs_db) },
+#endif
   { "syntax_errors_text", opt_stringptr,
       (void *)offsetof(redirect_router_options_block, syntax_errors_text) },
   { "syntax_errors_to",   opt_stringptr,
@@ -128,6 +139,12 @@
   NULL,        /* qualify_domain */
   NULL,        /* owners */
   NULL,        /* owngroups */
+#ifdef SRS
+  NULL,        /* srs */
+  NULL,        /* srs_condition */
+  NULL,        /* srs_db */
+  NULL,        /* srs_alias */
+#endif
   022,         /* modemask */
   RDO_REWRITE, /* bit_options */
   FALSE,       /* check_ancestor */
@@ -328,6 +345,7 @@
       }
     }
 
+
   /* A user filter may, under some circumstances, set up an errors address.
   If so, we must take care to re-instate it when we copy in the propagated
   data so that it overrides any errors_to setting on the router. */
@@ -520,6 +538,39 @@
   ugid.gid_set = TRUE;
   }
 
+
+#ifdef SRS
+/* For reverse SRS, fill the srs_recipient expandsion variable,
+on failure, return decline/fail as relevant */
+
+  if(ob->srs != NULL)
+  {
+    BOOL usesrs = TRUE;
+    
+    if(ob->srs_condition != NULL)
+      usesrs = expand_check_condition(ob->srs_condition, "srs_condition expansion failed", NULL);
+    
+    if(usesrs)
+      if(Ustrcmp(ob->srs, "reverse") == 0 || Ustrcmp(ob->srs, "reverseandforward") == 0)
+      {
+        uschar *res;
+        int n_srs;
+    
+        srs_orig_recipient = addr->address;
+        eximsrs_init();
+        if(ob->srs_db)
+          eximsrs_db_set(TRUE, ob->srs_db);
+        if((n_srs = eximsrs_reverse(&res, addr->address)) != OK)
+          return n_srs;
+        srs_recipient = res;
+        eximsrs_done();
+        DEBUG(D_any)
+          debug_printf("SRS: Recipient '%s' rewriteen to '%s'\n", srs_orig_recipient, srs_recipient);
+      }
+  }
+#endif
+
+
 /* Call the function that interprets redirection data, either inline or from a
 file. This is a separate function so that the system filter can use it. It will
 run the function in a subprocess if necessary. If qualify_preserve_domain is
@@ -743,6 +794,42 @@
     (addr_prop.errors_address != NULL)? "\n" : "");
   }
 
+
+#ifdef SRS
+/* On successful redirection, check for SRS forwarding and adjust sender */
+
+  if(ob->srs != NULL)
+  {
+    BOOL usesrs = TRUE;
+    
+    if(ob->srs_condition != NULL)
+      usesrs = expand_check_condition(ob->srs_condition, "srs_condition expansion failed", NULL);
+    
+    if(usesrs)
+      if((Ustrcmp(ob->srs, "forward") == 0 || Ustrcmp(ob->srs, "reverseandforward") == 0) && !verify)
+      {
+        uschar *res;
+        uschar *usedomain;
+        int n_srs;
+      
+        srs_orig_sender = sender_address;
+        eximsrs_init();
+        if(ob->srs_db)
+          eximsrs_db_set(FALSE, ob->srs_db);
+
+        if(ob->srs_alias != NULL ? (usedomain = expand_string(ob->srs_alias)) == NULL : 1)
+          usedomain = deliver_domain;
+                
+        if((n_srs = eximsrs_forward(&res, sender_address, usedomain)) != OK)
+          return n_srs;
+        sender_address = res;
+        DEBUG(D_any)
+          debug_printf("SRS: Sender '%s' rewritten to '%s'\n", srs_orig_sender, sender_address);
+    }
+  }
+#endif
+
+
 /* Control gets here only when the address has been completely handled. Put the
 original address onto the succeed queue so that any retry items that get
 attached to it get processed. */
diff -urN exim-4.44-orig/src/routers/redirect.h exim-4.44/src/routers/redirect.h
--- exim-4.44-orig/src/routers/redirect.h	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/routers/redirect.h	Fri Jan 14 20:35:27 2005
@@ -31,6 +31,13 @@
   uid_t  *owners;
   gid_t  *owngroups;
 
+#ifdef SRS
+  uschar *srs;
+  uschar *srs_condition;
+  uschar *srs_db;
+  uschar *srs_alias;
+#endif
+
   int   modemask;
   int   bit_options;
   BOOL  check_ancestor;
@@ -42,7 +49,7 @@
   BOOL  hide_child_in_errmsg;
   BOOL  one_time;
   BOOL  qualify_preserve_domain;
-  BOOL  skip_syntax_errors;
+  BOOL  skip_syntax_errors;  
 } redirect_router_options_block;
 
 /* Data for reading the private options. */
diff -urN exim-4.44-orig/src/smtp_in.c exim-4.44/src/smtp_in.c
--- exim-4.44-orig/src/smtp_in.c	Tue Jan 11 14:30:55 2005
+++ exim-4.44/src/smtp_in.c	Fri Jan 14 20:35:27 2005
@@ -10,6 +10,9 @@
 
 #include "exim.h"
 
+#ifdef SPF
+#include "spf.h"
+#endif
 
 /* Initialize for TCP wrappers if so configured. It appears that the macro
 HAVE_IPV6 is used in some versions of the tcpd.h header, so we unset it before
@@ -802,6 +805,8 @@
 acl_warn_headers = NULL;
 queue_only_policy = FALSE;
 deliver_freeze = FALSE;             /* Can be set by ACL */
+fake_reject = FALSE;                /* Can be set by ACL */
+no_mbox_unspool = FALSE;            /* Can be set by ACL */
 submission_mode = FALSE;            /* Can be set by ACL */
 sender_address = NULL;
 raw_sender = NULL;                  /* After SMTP rewrite, before qualifying */
@@ -810,6 +815,18 @@
 memset(sender_address_cache, 0, sizeof(sender_address_cache));
 memset(sender_domain_cache, 0, sizeof(sender_domain_cache));
 authenticated_sender = NULL;
+#ifdef BRIGHTMAIL
+bmi_run = 0;
+bmi_verdicts = NULL;
+#endif
+#ifdef SPF
+spf_header_comment = NULL;
+spf_received = NULL;
+spf_result = NULL;  
+spf_smtp_comment = NULL;
+#endif
+
+
 body_linecount = body_zerocount = 0;
 
 for (i = 0; i < ACL_M_MAX; i++) acl_var[ACL_C_MAX + i] = NULL;
@@ -1769,6 +1786,7 @@
 uschar *lognl;
 uschar *sender_info = US"";
 uschar *what = (where == ACL_WHERE_PREDATA)? US"DATA" :
+               (where == ACL_WHERE_MIME)? US"during MIME ACL checks" :
                (where == ACL_WHERE_DATA)? US"after DATA" :
   string_sprintf("%s %s", acl_wherenames[where], smtp_data);
 
@@ -1780,7 +1798,7 @@
 this is what should be logged, so I've changed to logging the unrewritten
 address to retain backward compatibility. */
 
-if (where == ACL_WHERE_RCPT || where == ACL_WHERE_DATA)
+if (where == ACL_WHERE_RCPT || where == ACL_WHERE_DATA || where == ACL_WHERE_MIME)
   {
   sender_info = string_sprintf("F=<%s> ", (sender_address_unrewritten != NULL)?
     sender_address_unrewritten : sender_address);
@@ -2335,6 +2353,11 @@
         }
       }
 
+#ifdef SPF
+    /* set up SPF context */
+    spf_init(sender_helo_name, sender_host_address);
+#endif
+
     /* Apply an ACL check if one is defined */
 
     if (acl_smtp_helo != NULL)
diff -urN exim-4.44-orig/src/spam.c exim-4.44/src/spam.c
--- exim-4.44-orig/src/spam.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/spam.c	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,338 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* Code for calling spamassassin's spamd. Called from acl.c. */
+
+#include "exim.h"
+#include "spam.h"
+
+uschar spam_score_buffer[16];
+uschar spam_score_int_buffer[16];
+uschar spam_bar_buffer[128];
+uschar spam_report_buffer[32600];
+uschar prev_user_name[128] = "";
+int spam_ok = 0;
+int spam_rc = 0;
+
+int spam(uschar **listptr) {
+  int sep = 0;
+  uschar *list = *listptr;
+  uschar *user_name;
+  uschar user_name_buffer[128];
+  unsigned long long mbox_size;
+  FILE *mbox_file;
+  int spamd_sock;
+  uschar spamd_buffer[32600];
+  int i, j, offset;
+  uschar spamd_version[8];
+  uschar spamd_score_char;
+  double spamd_threshold, spamd_score;
+  int spamd_report_offset;
+  uschar *p,*q;
+  int override = 0;
+  struct sockaddr_un server;
+
+  /* find the username from the option list */
+  if ((user_name = string_nextinlist(&list, &sep,
+                                     user_name_buffer,
+                                     sizeof(user_name_buffer))) == NULL) {
+    /* no username given, this means no scanning should be done */
+    return FAIL;
+  };
+
+  /* if username is "0" or "false", do not scan */
+  if ( (Ustrcmp(user_name,"0") == 0) ||
+       (strcmpic(user_name,US"false") == 0) ) {
+    return FAIL;
+  };
+
+  /* if there is an additional option, check if it is "true" */
+  if (strcmpic(list,US"true") == 0) {
+    /* in that case, always return true later */
+    override = 1;
+  };
+
+  /* if we scanned for this username last time, just return */ 
+  if ( spam_ok && ( Ustrcmp(prev_user_name, user_name) == 0 ) ) {
+    if (override)
+      return OK;
+    else
+      return spam_rc;
+  };
+  
+  /* make sure the eml mbox file is spooled up */
+  mbox_file = spool_mbox(&mbox_size);
+  
+  if (mbox_file == NULL) {
+    /* error while spooling */
+    log_write(0, LOG_MAIN|LOG_PANIC,
+           "spam acl condition: error while creating mbox spool file");
+    return DEFER;
+  };
+
+  /* socket does not start with '/' -> network socket */
+  if (*spamd_address != '/') {
+    time_t now = time(NULL);
+    int num_servers = 0;
+    int current_server = 0;
+    int start_server = 0;
+    uschar *address = NULL;
+    uschar *spamd_address_list_ptr = spamd_address;
+    uschar address_buffer[256];
+    spamd_address_container * spamd_address_vector[32];
+
+    /* Check how many spamd servers we have
+       and register their addresses */
+    while ((address = string_nextinlist(&spamd_address_list_ptr, &sep,
+                                        address_buffer,
+                                        sizeof(address_buffer))) != NULL) {
+      
+      spamd_address_container *this_spamd =
+        (spamd_address_container *)store_get(sizeof(spamd_address_container));
+      
+      /* grok spamd address and port */
+      if( sscanf(CS address, "%s %u", this_spamd->tcp_addr, &(this_spamd->tcp_port)) != 2 ) {
+        log_write(0, LOG_MAIN,
+          "spam acl condition: warning - invalid spamd address: '%s'", address);
+        continue;
+      };
+      
+      spamd_address_vector[num_servers] = this_spamd;
+      num_servers++;
+      if (num_servers > 31)
+        break;
+    };
+    
+    /* check if we have at least one server */
+    if (!num_servers) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+         "spam acl condition: no useable spamd server addresses in spamd_address configuration option.");
+      fclose(mbox_file);
+      return DEFER;
+    };
+
+    current_server = start_server = (int)now % num_servers;
+
+    while (1) {
+      
+      debug_printf("trying server %s, port %u\n",
+                   spamd_address_vector[current_server]->tcp_addr,
+                   spamd_address_vector[current_server]->tcp_port);
+      
+      /* contact a spamd */
+      if ( (spamd_sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+           "spam acl condition: error creating IP socket for spamd");
+        fclose(mbox_file);
+        return DEFER; 
+      };
+      
+      if (ip_connect( spamd_sock,
+                      AF_INET,
+                      spamd_address_vector[current_server]->tcp_addr,
+                      spamd_address_vector[current_server]->tcp_port,
+                      5 ) > -1) {
+        /* connection OK */
+        break;
+      };
+      
+      log_write(0, LOG_MAIN|LOG_PANIC,
+         "spam acl condition: warning - spamd connection to %s, port %u failed: %s",
+         spamd_address_vector[current_server]->tcp_addr,
+         spamd_address_vector[current_server]->tcp_port,
+         strerror(errno));
+      current_server++;
+      if (current_server >= num_servers)
+        current_server = 0;
+      if (current_server == start_server) {
+        log_write(0, LOG_MAIN|LOG_PANIC, "spam acl condition: all spamd servers failed");
+        fclose(mbox_file);
+        close(spamd_sock);
+        return DEFER;
+      };
+    };
+
+  }
+  else {
+    /* open the local socket */
+
+    if ((spamd_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+                "malware acl condition: spamd: unable to acquire socket (%s)",
+                strerror(errno));
+      fclose(mbox_file);
+      return DEFER;
+    }
+
+    server.sun_family = AF_UNIX;
+    Ustrcpy(server.sun_path, spamd_address);
+
+    if (connect(spamd_sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+                "malware acl condition: spamd: unable to connect to UNIX socket %s (%s)",
+                spamd_address, strerror(errno) );
+      fclose(mbox_file);
+      close(spamd_sock);
+      return DEFER;
+    }
+
+  }
+
+  /* now we are connected to spamd on spamd_sock */
+  snprintf(CS spamd_buffer,
+           sizeof(spamd_buffer),
+           "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %lld\r\n\r\n",
+           user_name,
+           mbox_size);
+
+  /* send our request */
+  if (send(spamd_sock, spamd_buffer, Ustrlen(spamd_buffer), 0) < 0) {
+    close(spamd_sock);
+    log_write(0, LOG_MAIN|LOG_PANIC,
+         "spam acl condition: spamd send failed: %s", strerror(errno));
+    fclose(mbox_file);
+    close(spamd_sock);
+    return DEFER;
+  };
+
+  /* now send the file */
+  do {
+    j = fread(spamd_buffer,1,sizeof(spamd_buffer),mbox_file);
+    if (j > 0) {
+      i = send(spamd_sock,spamd_buffer,j,0);
+      if (i != j) {
+        log_write(0, LOG_MAIN|LOG_PANIC,
+          "spam acl condition: error/short send to spamd");
+        close(spamd_sock);
+        fclose(mbox_file);
+        return DEFER;
+      };
+    };
+  }
+  while (j > 0);
+
+  fclose(mbox_file);
+
+  /* we're done sending, close socket for writing */
+  shutdown(spamd_sock,SHUT_WR);
+  
+  /* read spamd response */
+  memset(spamd_buffer, 0, sizeof(spamd_buffer));
+  offset = 0;
+  while((i = ip_recv(spamd_sock,
+                     spamd_buffer + offset,
+                     sizeof(spamd_buffer) - offset - 1,
+                     SPAMD_READ_TIMEOUT)) > 0 ) {
+    offset += i;
+  }
+
+  /* error handling */
+  if((i <= 0) && (errno != 0)) {
+    log_write(0, LOG_MAIN|LOG_PANIC,
+         "spam acl condition: error reading from spamd socket: %s", strerror(errno));
+    close(spamd_sock);
+    return DEFER;
+  }
+
+  /* reading done */
+  close(spamd_sock);
+
+  /* dig in the spamd output and put the report in a multiline header, if requested */
+  if( sscanf(CS spamd_buffer,"SPAMD/%s 0 EX_OK\r\nContent-length: %*u\r\n\r\n%lf/%lf\r\n%n",
+             spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3 ) {
+              
+    /* try to fall back to pre-2.50 spamd output */
+    if( sscanf(CS spamd_buffer,"SPAMD/%s 0 EX_OK\r\nSpam: %*s ; %lf / %lf\r\n\r\n%n",
+               spamd_version,&spamd_score,&spamd_threshold,&spamd_report_offset) != 3 ) {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+         "spam acl condition: cannot parse spamd output");
+      return DEFER;
+    };
+  };
+
+  /* Create report. Since this is a multiline string,
+  we must hack it into shape first */
+  p = &spamd_buffer[spamd_report_offset];
+  q = spam_report_buffer;
+  while (*p != '\0') {
+    /* skip \r */
+    if (*p == '\r') {
+      p++;
+      continue;
+    };
+    *q = *p;
+    q++;
+    if (*p == '\n') {
+      *q = '\t';
+      q++;
+      /* eat whitespace */
+      while( (*p <= ' ') && (*p != '\0') ) {
+        p++;
+      };
+      p--;
+    };
+    p++;
+  };
+  /* NULL-terminate */
+  *q = '\0';
+  q--;
+  /* cut off trailing leftovers */
+  while (*q <= ' ') {
+    *q = '\0';
+    q--;
+  };
+  spam_report = spam_report_buffer;
+
+  /* create spam bar */
+  spamd_score_char = spamd_score > 0 ? '+' : '-';
+  j = abs((int)(spamd_score));
+  i = 0;
+  if( j != 0 ) {
+    while((i < j) && (i <= MAX_SPAM_BAR_CHARS))
+       spam_bar_buffer[i++] = spamd_score_char;
+  }
+  else{
+    spam_bar_buffer[0] = '/';
+    i = 1;
+  }
+  spam_bar_buffer[i] = '\0';
+  spam_bar = spam_bar_buffer;
+
+  /* create "float" spam score */
+  snprintf(CS spam_score_buffer, sizeof(spam_score_buffer),"%.1f", spamd_score);
+  spam_score = spam_score_buffer;
+
+  /* create "int" spam score */
+  j = (int)((spamd_score + 0.001)*10);
+  snprintf(CS spam_score_int_buffer, sizeof(spam_score_int_buffer), "%d", j);
+  spam_score_int = spam_score_int_buffer;
+
+  /* compare threshold against score */
+  if (spamd_score >= spamd_threshold) {
+    /* spam as determined by user's threshold */
+    spam_rc = OK;
+  }
+  else {
+    /* not spam */
+    spam_rc = FAIL;
+  };
+  
+  /* remember user name and "been here" for it */
+  Ustrcpy(prev_user_name, user_name);
+  spam_ok = 1;
+  
+  if (override) {
+    /* always return OK, no matter what the score */
+    return OK;
+  }
+  else {
+    return spam_rc;
+  };
+}
diff -urN exim-4.44-orig/src/spam.h exim-4.44/src/spam.h
--- exim-4.44-orig/src/spam.h	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/spam.h	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,30 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* spam defines */
+
+/* timeout for reading from spamd */
+#define SPAMD_READ_TIMEOUT 3600
+
+/* maximum length of the spam bar */
+#define MAX_SPAM_BAR_CHARS 50
+
+/* SHUT_WR seems to be undefined on Unixware ? */
+#ifndef SHUT_WR
+#define SHUT_WR 1
+#endif
+
+typedef struct spamd_address_container {
+  uschar tcp_addr[24];
+  unsigned int tcp_port;
+} spamd_address_container;
+
+
+
diff -urN exim-4.44-orig/src/spf.c exim-4.44/src/spf.c
--- exim-4.44-orig/src/spf.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/spf.c	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,131 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* Code for calling spf checks via libspf-alt. Called from acl.c. */
+
+#ifdef SPF
+
+#include "exim.h"
+#include "spf.h"
+
+SPF_config_t        spfcid = NULL;
+SPF_dns_config_t    spfdcid_resolv = NULL;
+SPF_dns_config_t    spfdcid = NULL;
+
+
+/* spf_init sets up a context that can be re-used for several
+   messages on the same SMTP connection (that come from the
+   same host with the same HELO string) */
+   
+int spf_init(uschar *spf_helo_domain, uschar *spf_remote_addr) {
+  uschar *p;
+  
+  /* paranoia */
+  spfcid = NULL;
+  spfdcid_resolv = NULL;
+  spfdcid = NULL;
+  
+  spfcid = SPF_create_config();
+  if ( spfcid == NULL ) {
+    debug_printf("spf: SPF_create_config() failed.\n");
+	  return 0;
+  }
+
+  /* set up resolver */
+  spfdcid_resolv = SPF_dns_create_config_resolv(NULL, 0);
+  spfdcid = SPF_dns_create_config_cache(spfdcid_resolv, 8, 0);
+
+  if (spfdcid == NULL) {
+    debug_printf("spf: SPF_dns_create_config_cache() failed.\n");
+    spfcid = NULL;
+    spfdcid_resolv = NULL;
+	  return 0;
+  }
+
+  if (SPF_set_ip_str(spfcid, spf_remote_addr)) {
+    debug_printf("spf: SPF_set_ip_str() failed.\n");
+    spfcid = NULL;
+    spfdcid_resolv = NULL;
+	  return 0;
+  }
+
+  if (SPF_set_helo_dom(spfcid, spf_helo_domain)) {
+    debug_printf("spf: SPF_set_helo_dom() failed.\n");
+    spfcid = NULL;
+    spfdcid_resolv = NULL;
+	  return 0;
+  }
+  
+  return 1;
+}
+
+
+/* spf_process adds the envelope sender address to the existing
+   context (if any), retrieves the result, sets up expansion
+   strings and evaluates the condition outcome. */
+
+int spf_process(uschar **listptr, uschar *spf_envelope_sender) {
+  int sep = 0;
+  uschar *list = *listptr;
+  uschar *spf_result_id;
+  uschar spf_result_id_buffer[128];
+  SPF_output_t spf_output;
+  int rc = SPF_RESULT_ERROR;
+ 
+  if (!(spfcid && spfdcid)) {
+    /* no global context, assume temp error and skip to evaluation */
+    rc = SPF_RESULT_ERROR;
+    goto SPF_EVALUATE;
+  };
+
+  if (SPF_set_env_from(spfcid, spf_envelope_sender)) {
+    /* Invalid sender address. This should be a real rare occurence */
+    rc = SPF_RESULT_ERROR;
+    goto SPF_EVALUATE;
+  } 
+
+  /* get SPF result */
+  spf_output = SPF_result(spfcid, spfdcid);
+
+  /* set up expansion items */
+  spf_header_comment     = spf_output.header_comment ? (uschar *)spf_output.header_comment : NULL;
+  spf_received           = spf_output.received_spf ? (uschar *)spf_output.received_spf : NULL;
+  spf_result             = (uschar *)SPF_strresult(spf_output.result);
+  spf_smtp_comment       = spf_output.smtp_comment ? (uschar *)spf_output.smtp_comment : NULL;
+
+  rc = spf_output.result;
+
+  /* We got a result. Now see if we should return OK or FAIL for it */
+  SPF_EVALUATE:
+  debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
+  while ((spf_result_id = string_nextinlist(&list, &sep,
+                                     spf_result_id_buffer,
+                                     sizeof(spf_result_id_buffer))) != NULL) {
+    int negate = 0;
+    int result = 0;
+
+    /* Check for negation */
+    if (spf_result_id[0] == '!') {
+      negate = 1;
+      spf_result_id++;
+    };
+
+    /* Check the result identifier */
+    result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name);
+    if (!negate && result==0) return OK;
+    if (negate && result!=0) return OK;
+  };
+
+  /* no match */
+  return FAIL;
+}
+
+#endif
+
diff -urN exim-4.44-orig/src/spf.h exim-4.44/src/spf.h
--- exim-4.44-orig/src/spf.h	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/spf.h	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,39 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+ 
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+#ifdef SPF
+
+#include <spf2/spf.h>
+#include <spf2/spf_dns_resolv.h>
+#include <spf2/spf_dns_cache.h>
+
+typedef struct spf_result_id {
+  uschar *name;
+  int    value;
+} spf_result_id;
+
+/* must be kept in numeric order */
+static spf_result_id spf_result_id_list[] = {
+  { US"pass", 0 },
+  { US"fail", 1 },
+  { US"softfail", 2 },
+  { US"neutral", 3 },
+  { US"err_perm", 4 },
+  { US"err_temp", 5 },
+  { US"none", 6 }
+};
+
+static int spf_result_id_list_size = sizeof(spf_result_id_list)/sizeof(spf_result_id);
+
+/* prototypes */
+int spf_init(uschar *,uschar *);
+int spf_process(uschar **, uschar *);
+
+#endif
diff -urN exim-4.44-orig/src/spool_in.c exim-4.44/src/spool_in.c
--- exim-4.44-orig/src/spool_in.c	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/spool_in.c	Fri Jan 14 20:35:27 2005
@@ -244,6 +244,7 @@
 body_zerocount = 0;
 deliver_firsttime = FALSE;
 deliver_freeze = FALSE;
+fake_reject = FALSE;
 deliver_frozen_at = 0;
 deliver_manual_thaw = FALSE;
 /* dont_deliver must NOT be reset */
@@ -253,6 +254,7 @@
 interface_port = 0;
 local_error_message = FALSE;
 local_scan_data = NULL;
+spam_score_int = NULL;
 message_linecount = 0;
 received_protocol = NULL;
 received_count = 0;
@@ -269,6 +271,11 @@
 sender_set_untrusted = FALSE;
 tree_nonrecipients = NULL;
 
+#ifdef BRIGHTMAIL
+bmi_run = 0;
+bmi_verdicts = NULL;
+#endif
+
 #ifdef SUPPORT_TLS
 tls_certificate_verified = FALSE;
 tls_cipher = NULL;
@@ -367,6 +374,12 @@
     local_error_message = TRUE;
   else if (Ustrncmp(big_buffer, "-local_scan ", 12) == 0)
     local_scan_data = string_copy(big_buffer + 12);
+  else if (Ustrncmp(big_buffer, "-spam_score_int ", 16) == 0)
+    spam_score_int = string_copy(big_buffer + 16);
+#ifdef BRIGHTMAIL
+  else if (Ustrncmp(big_buffer, "-bmi_verdicts ", 14) == 0)
+    bmi_verdicts = string_copy(big_buffer + 14);
+#endif
   else if (Ustrcmp(big_buffer, "-host_lookup_failed") == 0)
     host_lookup_failed = TRUE;
   else if (Ustrncmp(big_buffer, "-body_linecount", 15) == 0)
diff -urN exim-4.44-orig/src/spool_mbox.c exim-4.44/src/spool_mbox.c
--- exim-4.44-orig/src/spool_mbox.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/spool_mbox.c	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,196 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-???? */
+/* License: GPL */
+
+/* Code for setting up a MBOX style spool file inside a /scan/<msgid>
+sub directory of exim's spool directory. */
+
+#include "exim.h"
+
+/* externals, we must reset them on unspooling */
+extern int demime_ok;
+extern int malware_ok;
+extern int spam_ok;
+extern struct file_extension *file_extensions;
+
+int spool_mbox_ok = 0;
+uschar spooled_message_id[17];
+
+/* returns a pointer to the FILE, and puts the size in bytes into mbox_file_size */
+
+FILE *spool_mbox(unsigned long long *mbox_file_size) {
+  uschar mbox_path[1024];
+  uschar message_subdir[2];
+  uschar data_buffer[65535];
+  FILE *mbox_file;
+  FILE *data_file = NULL;
+  header_line *my_headerlist;
+  struct stat statbuf;
+  int i,j;
+  
+  /*
+  uschar *received;
+  uschar *timestamp;
+  */
+  
+  if (!spool_mbox_ok) {
+    /* create scan directory, if not present */
+    if (!directory_make(spool_directory, US "scan", 0750, FALSE)) {
+      debug_printf("unable to create directory: %s/scan\n", spool_directory);
+      return NULL;
+    };
+    
+    /* create temp directory inside scan dir */
+    snprintf(CS mbox_path, 1024, "%s/scan/%s", spool_directory, message_id);
+    if (!directory_make(NULL, mbox_path, 0750, FALSE)) {
+      debug_printf("unable to create directory: %s/scan/%s\n", spool_directory, message_id);
+      return NULL;
+    };
+    
+    /* open [message_id].eml file for writing */
+    snprintf(CS mbox_path, 1024, "%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
+    mbox_file = Ufopen(mbox_path,"w");
+    
+    if (mbox_file == NULL) {
+      debug_printf("unable to open file for writing: %s\n", mbox_path);
+      return NULL;
+    };
+    
+    /* Generate a preliminary Received: header and put it in the file.
+       We need to do this so SA can do DNS list checks */
+       
+    /* removed for 4.34
+    
+    timestamp = expand_string(US"${tod_full}");
+    received = expand_string(received_header_text);
+    if (received != NULL) {
+      uschar *my_received;
+      if (received[0] == 0) {
+        my_received = string_sprintf("Received: ; %s\n", timestamp);
+      }
+      else {
+        my_received = string_sprintf("%s; %s\n", received, timestamp);
+      }
+      i = fwrite(my_received, 1, Ustrlen(my_received), mbox_file);
+      if (i != Ustrlen(my_received)) {
+        debug_printf("error/short write on writing in: %s", mbox_path);
+        fclose(mbox_file);
+        return NULL;
+      };
+    };
+    
+    */
+    
+    /* write all header lines to mbox file */
+    my_headerlist = header_list;
+    while (my_headerlist != NULL) {
+      
+      /* skip deleted headers */
+      if (my_headerlist->type == '*') {
+        my_headerlist = my_headerlist->next;
+        continue;
+      };
+  
+      i = fwrite(my_headerlist->text, 1, my_headerlist->slen, mbox_file);
+      if (i != my_headerlist->slen) {
+        debug_printf("error/short write on writing in: %s", mbox_path);
+        fclose(mbox_file);
+        return NULL;
+      };
+      
+      my_headerlist = my_headerlist->next;
+    };
+  
+    /* copy body file */
+    message_subdir[1] = '\0';
+    for (i = 0; i < 2; i++) {
+      message_subdir[0] = (split_spool_directory == (i == 0))? message_id[5] : 0;
+      sprintf(CS mbox_path, "%s/input/%s/%s-D", spool_directory, message_subdir, message_id);
+      data_file = Ufopen(mbox_path,"r");
+      if (data_file != NULL)
+        break;
+    };
+
+    fread(data_buffer, 1, 18, data_file);
+    
+    do {
+      j = fread(data_buffer, 1, sizeof(data_buffer), data_file);
+      if (j > 0) {
+        i = fwrite(data_buffer, 1, j, mbox_file);
+        if (i != j) {
+          debug_printf("error/short write on writing in: %s", mbox_path);
+          fclose(mbox_file);
+          fclose(data_file);
+          return NULL;
+        };
+      };
+    } while (j > 0);
+    
+    fclose(data_file);
+    fclose(mbox_file);
+    Ustrcpy(spooled_message_id, message_id);
+    spool_mbox_ok = 1;
+  };
+
+  snprintf(CS mbox_path, 1024, "%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
+
+  /* get the size of the mbox message */
+  stat(CS mbox_path, &statbuf);
+  *mbox_file_size = statbuf.st_size;
+
+  /* open [message_id].eml file for reading */
+  mbox_file = Ufopen(mbox_path,"r");
+  
+  return mbox_file;
+}
+
+/* remove mbox spool file, demimed files and temp directory */
+void unspool_mbox(void) {
+
+  /* reset all exiscan state variables */
+  demime_ok = 0;
+  demime_errorlevel = 0;
+  demime_reason = NULL;
+  file_extensions = NULL;
+  spam_ok = 0;
+  malware_ok = 0;
+  
+  if (spool_mbox_ok) {
+
+    spool_mbox_ok = 0;
+    
+    if (!no_mbox_unspool) {
+      uschar mbox_path[1024];
+      uschar file_path[1024];
+      int n;
+      struct dirent *entry;
+      DIR *tempdir;
+      
+      snprintf(CS mbox_path, 1024, "%s/scan/%s", spool_directory, spooled_message_id);
+    	
+    	tempdir = opendir(CS mbox_path);
+    	/* loop thru dir & delete entries */
+    	n = 0;
+    	do {
+    	  entry = readdir(tempdir);
+    	  if (entry == NULL) break;
+    	  snprintf(CS file_path, 1024,"%s/scan/%s/%s", spool_directory, spooled_message_id, entry->d_name);
+    	  if ( (Ustrcmp(entry->d_name,"..") != 0) && (Ustrcmp(entry->d_name,".") != 0) ) {
+    	    debug_printf("unspool_mbox(): unlinking '%s'\n", file_path);
+              n = unlink(CS file_path);
+            }; 
+    	} while (n > -1);
+    	
+    	closedir(tempdir);
+    	
+    	/* remove directory */
+    	n = rmdir(CS mbox_path);
+    };
+  };
+}
diff -urN exim-4.44-orig/src/spool_out.c exim-4.44/src/spool_out.c
--- exim-4.44-orig/src/spool_out.c	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/spool_out.c	Fri Jan 14 20:35:27 2005
@@ -214,9 +214,14 @@
 if (sender_local) fprintf(f, "-local\n");
 if (local_error_message) fprintf(f, "-localerror\n");
 if (local_scan_data != NULL) fprintf(f, "-local_scan %s\n", local_scan_data);
+if (spam_score_int != NULL) fprintf(f,"-spam_score_int %s\n", spam_score_int);
 if (deliver_manual_thaw) fprintf(f, "-manual_thaw\n");
 if (sender_set_untrusted) fprintf(f, "-sender_set_untrusted\n");
 
+#ifdef BRIGHTMAIL
+if (bmi_verdicts != NULL) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts);
+#endif
+
 #ifdef SUPPORT_TLS
 if (tls_certificate_verified) fprintf(f, "-tls_certificate_verified\n");
 if (tls_cipher != NULL) fprintf(f, "-tls_cipher %s\n", tls_cipher);
diff -urN exim-4.44-orig/src/srs.c exim-4.44/src/srs.c
--- exim-4.44-orig/src/srs.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/srs.c	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,209 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is an extension to Exim and is not part of the standard
+   Exim distribution */
+   
+/* (c)2004 Miles Wilton <miles@mirtol.com> */
+
+/* License: GPL */
+
+
+#ifdef SRS
+
+#include <srs_alt.h>
+
+#include "exim.h"
+#include "srs.h"
+
+srs_t    *srs                   = NULL;
+uschar   *srs_db_forward        = NULL;
+uschar   *srs_db_reverse        = NULL;
+
+
+/* srs_init just initialises libsrs and creates (if necessary)
+   an srs object to use for all srs calls in this instance */
+   
+int eximsrs_init()
+{
+  int co;
+  uschar *list = srs_config;
+  char secret_buf[SRS_MAX_SECRET_LENGTH];
+  char *secret;
+  char sbuf[4];
+  char *sbufp;
+  int hashlen, maxage;
+
+  
+  if(!srs)
+  {
+    // Check config
+    if(!srs_config)
+    {
+      log_write(0, LOG_MAIN | LOG_PANIC,
+          "SRS Configuration Error");
+      return DEFER;
+    }
+    
+    // Get config
+    co = 0;
+    if((secret = string_nextinlist(&list, &co, secret_buf,
+                                   SRS_MAX_SECRET_LENGTH)) == NULL)
+    {
+      log_write(0, LOG_MAIN | LOG_PANIC,
+          "SRS Configuration Error: No secret specified");
+      return DEFER;
+    }
+    
+    if((sbufp = string_nextinlist(&list, &co, sbuf, sizeof(sbuf))) == NULL)
+      maxage = 31;
+    else
+      maxage = atoi(sbuf);
+    if(maxage < 0 || maxage > 365)
+    {
+      log_write(0, LOG_MAIN | LOG_PANIC,
+          "SRS Configuration Error: Invalid maximum timestamp age");
+      return DEFER;
+    }
+
+    if((sbufp = string_nextinlist(&list, &co, sbuf, sizeof(sbuf))) == NULL)
+      hashlen = 6;
+    else
+      hashlen = atoi(sbuf);
+    if(hashlen < 1 || hashlen > 20)
+    {
+      log_write(0, LOG_MAIN | LOG_PANIC,
+          "SRS Configuration Error: Invalid hash length");
+      return DEFER;
+    }
+    
+    
+    if((srs = srs_open(secret, strnlen(secret, SRS_MAX_SECRET_LENGTH),
+                      maxage, hashlen, hashlen)) == NULL)
+    {
+      log_write(0, LOG_MAIN | LOG_PANIC,
+          "Failed to allocate SRS memory");
+      return DEFER;
+    }
+
+
+    if((sbufp = string_nextinlist(&list, &co, sbuf, sizeof(sbuf))) != NULL)
+      srs_set_option(srs, SRS_OPTION_USETIMESTAMP, atoi(sbuf));
+    
+    if((sbufp = string_nextinlist(&list, &co, sbuf, sizeof(sbuf))) != NULL)
+      srs_set_option(srs, SRS_OPTION_USEHASH, atoi(sbuf));
+
+    DEBUG(D_any)
+      debug_printf("SRS initialized\n");
+  }
+
+  return OK;
+}
+
+
+int eximsrs_done()
+{
+  if(srs)
+    srs_close(srs);
+  
+  srs = NULL;
+
+  return OK;
+}
+
+
+int eximsrs_forward(uschar **result, uschar *orig_sender, uschar *domain)
+{
+  char res[512];
+  int n;
+
+  if((n = srs_forward(srs, orig_sender, domain, res, sizeof(res))) & SRS_RESULT_FAIL)
+  {
+    DEBUG(D_any)
+      debug_printf("srs_forward failed (%s, %s): %s\n", orig_sender, domain, srs_geterrormsg(n));
+    return DEFER;
+  }
+
+  *result = string_copy(res);
+  return OK;
+}
+
+
+int eximsrs_reverse(uschar **result, uschar *address)
+{
+  char res[512];
+  int n;
+
+  if((n = srs_reverse(srs, address, res, sizeof(res))) & SRS_RESULT_FAIL)
+  {
+    DEBUG(D_any)
+      debug_printf("srs_reverse failed (%s): %s\n", address, srs_geterrormsg(n));
+    if(n == SRS_RESULT_NOTSRS || n == SRS_RESULT_BADSRS)
+      return DECLINE;
+    if(n == SRS_RESULT_BADHASH || n == SRS_RESULT_BADTIMESTAMP || n == SRS_RESULT_TIMESTAMPEXPIRED)
+      return FAIL;
+    return DEFER;
+  }
+
+  *result = string_copy(res);
+  return OK;
+}
+
+
+int eximsrs_db_set(BOOL reverse, uschar *srs_db)
+{
+  if(reverse)
+    srs_db_reverse = string_copy(srs_db);
+  else
+    srs_db_forward = string_copy(srs_db);
+    
+  if(srs_set_db_functions(srs, eximsrs_db_insert, eximsrs_db_lookup) * SRS_RESULT_FAIL)
+    return DEFER;
+  
+  return OK;
+}
+
+
+srs_result eximsrs_db_insert(srs_t *srs, char *data, uint data_len, char *result, uint result_len)
+{
+  uschar *res;
+  char buf[64];
+
+  srs_db_address = string_copyn(data, data_len);
+  if(srs_generate_unique_id(srs, srs_db_address, buf, 64) & SRS_RESULT_FAIL)
+    return DEFER;
+  
+  srs_db_key = string_copyn(buf, 16);
+  
+  if((res = expand_string(srs_db_forward)) == NULL)
+    return SRS_RESULT_DBERROR;
+  
+  if(result_len < 17)
+    return SRS_RESULT_DBERROR;
+    
+  strncpy(result, srs_db_key, result_len);
+
+  return SRS_RESULT_OK;
+}
+
+
+srs_result eximsrs_db_lookup(srs_t *srs, char *data, uint data_len, char *result, uint result_len)
+{
+  uschar *res;
+
+  srs_db_key = string_copyn(data, data_len);
+  if((res = expand_string(srs_db_reverse)) == NULL)
+    return SRS_RESULT_DBERROR;
+  
+  if(Ustrlen(res) >= result_len)
+    return SRS_RESULT_ADDRESSTOOLONG;
+    
+  strncpy(result, res, result_len);
+
+  return SRS_RESULT_OK;
+}
+
+
+#endif
+
diff -urN exim-4.44-orig/src/srs.h exim-4.44/src/srs.h
--- exim-4.44-orig/src/srs.h	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/srs.h	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,33 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is an extension to Exim and is not part of the standard
+   Exim distribution */
+   
+/* ©2004 Miles Wilton <miles@mirtol.com> */
+
+/* License: GPL */
+
+#ifndef __SRS_H__
+
+#define __SRS_H__ 1
+
+
+#ifdef SRS
+
+#include "mytypes.h"
+#include <srs_alt.h>
+
+int eximsrs_init();
+int eximsrs_done();
+int eximsrs_forward(uschar **result, uschar *orig_sender, uschar *domain);
+int eximsrs_reverse(uschar **result, uschar *address);
+int eximsrs_db(BOOL reverse, uschar *srs_db);
+
+srs_result eximsrs_db_insert(srs_t *srs, char *data, uint data_len, char *result, uint result_len);
+srs_result eximsrs_db_lookup(srs_t *srs, char *data, uint data_len, char *result, uint result_len);
+
+#endif
+
+#endif
diff -urN exim-4.44-orig/src/structs.h exim-4.44/src/structs.h
--- exim-4.44-orig/src/structs.h	Tue Jan 11 13:54:54 2005
+++ exim-4.44/src/structs.h	Fri Jan 14 20:35:27 2005
@@ -219,6 +219,9 @@
   uschar *driver_name;            /* Must be first */
 
   uschar *address_data;           /* Arbitrary data */
+#ifdef BRIGHTMAIL
+  uschar *bmi_rule;               /* Brightmail AntiSpam rule checking */
+#endif
   uschar *cannot_route_message;   /* Used when routing fails */
   uschar *condition;              /* General condition */
   uschar *current_directory;      /* For use during delivery */
@@ -247,6 +250,11 @@
   uschar *transport_name;         /* Transport name */
 
   BOOL    address_test;           /* Use this router when testing addresses */
+#ifdef BRIGHTMAIL
+  BOOL    bmi_deliver_alternate;  /* TRUE => BMI said that message should be delivered to alternate location */
+  BOOL    bmi_deliver_default;    /* TRUE => BMI said that message should be delivered to default location */
+  BOOL    bmi_dont_deliver;       /* TRUE => BMI said that message should not be delivered at all */
+#endif
   BOOL    expn;                   /* Use this router when processing EXPN */
   BOOL    caseful_local_part;     /* TRUE => don't lowercase */
   BOOL    check_local_user;       /* TRUE => check local user */
diff -urN exim-4.44-orig/src/tnef.c exim-4.44/src/tnef.c
--- exim-4.44-orig/src/tnef.c	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/tnef.c	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,757 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/* Code for unpacking TNEF containers. Called from demime.c. */
+
+/***************************************************************************
+ * tnef2txt
+*   A program to decode application/ms-tnef MIME attachments into text
+*   for those fortunate enough not to be running either a Microsoft
+*   operating system or mailer.
+*
+ * 18/10/2001
+* Brutally cropped by Paul L Daniels (pldaniels@pldaniels.com) in order
+* to accommodate the needs of ripMIME/Xamime/Inflex without carrying too
+* much excess baggage.
+*
+ * Brandon Long (blong@uiuc.edu), April 1997
+* 1.0 Version
+*   Supports most types, but doesn't decode properties.  Maybe some other
+*   time.
+*
+ * 1.1 Version (7/1/97)
+*   Supports saving of attAttachData to a file given by attAttachTitle
+*   start of property decoding support
+*
+ * 1.2 Version (7/19/97)
+*   Some architectures don't like reading 16/32 bit data on unaligned
+*   boundaries.  Fixed, losing efficiency, but this doesn't really
+*   need efficiency anyways.  (Still...)
+*   Also, the #pragma pack from the MSVC include file wasn't liked
+*   by most Unix compilers, replaced with a GCCism.  This should work
+*   with GCC, but other compilers I don't know.
+*
+ * 1.3 Version (7/22/97)
+*   Ok, take out the DTR over the stream, now uses read_16.
+*
+ * NOTE: THIS SOFTWARE IS FOR YOUR PERSONAL GRATIFICATION ONLY.  I DON'T
+* IMPLY IN ANY LEGAL SENSE THAT THIS SOFTWARE DOES ANYTHING OR THAT IT WILL
+* BE USEFULL IN ANY WAY.  But, you can send me fixes to it, I don't mind.
+***************************************************************************/
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include "tnef.h"
+
+
+#define VERSION "pldtnef/0.0.1"
+
+int _TNEF_syslogging = 0;
+int _TNEF_stderrlogging = 0;
+int _TNEF_verbose = 0;
+int _TNEF_debug = 0;
+
+int Verbose = FALSE;
+int SaveData = FALSE;
+
+char _TNEF_path[1024]="";
+
+uint8 *tnef_home;
+uint8 *tnef_limit;
+
+/*------------------------------------------------------------------------
+Procedure:     TNEF_set_path ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int TNEF_set_path( char *path )
+{
+	snprintf(_TNEF_path,1023,"%s",path);
+
+	return 0;
+}
+
+
+/*------------------------------------------------------------------------
+Procedure:     TNEF_set_verbosity ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int TNEF_set_verbosity( int level )
+{
+	_TNEF_verbose = level;
+	return _TNEF_verbose;
+}
+
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     TNEF_set_debug ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int TNEF_set_debug( int level )
+{
+	_TNEF_debug = level;
+	TNEF_set_verbosity( level );
+	return _TNEF_debug;
+}
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     TNEF_set_syslogging ID:1
+Purpose:       Turns on/off the syslog feature for TNEF error messages
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int TNEF_set_syslogging( int level )
+{
+	_TNEF_syslogging = level;
+	return _TNEF_syslogging;
+}
+
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     TNEF_set_stderrlogging ID:1
+Purpose:       Turns on/off the stderr feature for TNEF error messages
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int TNEF_set_stderrlogging( int level )
+{
+	_TNEF_stderrlogging = level;
+	return _TNEF_stderrlogging;
+}
+
+
+/* Some systems don't like to read unaligned data */
+/*------------------------------------------------------------------------
+Procedure:     read_32 ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+uint32 read_32(uint8 *tsp)
+{
+	uint8 a,b,c,d;
+	uint32 ret;
+
+	if (tsp+4 > tnef_limit)
+	{
+		if ((_TNEF_verbose)||(_TNEF_stderrlogging)||(_TNEF_debug)) fprintf(stderr,"TNEF read_32() Attempting to read past end\n");
+		return -1;
+	}
+
+	a = *tsp;
+	b = *(tsp+1);
+	c = *(tsp+2);
+	d = *(tsp+3);
+
+	ret =  long_little_endian(a<<24 | b<<16 | c<<8 | d);
+
+	return ret;
+}
+
+/*------------------------------------------------------------------------
+Procedure:     read_16 ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+uint16 read_16(uint8 *tsp)
+{
+	uint8 a,b;
+	uint16 ret;
+
+	if (tsp+2 > tnef_limit)
+	{
+		if ((_TNEF_verbose)||(_TNEF_stderrlogging)||(_TNEF_debug)) fprintf(stderr,"TNEF read_16() Attempting to read past end\n");
+		return -1;
+	}
+
+
+	a = *tsp;
+	b = *(tsp + 1);
+
+	ret = little_endian(a<<8 | b);
+
+	return ret;
+}
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     make_string ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+char *make_string(uint8 *tsp, int size)
+{
+	static char s[256] = "";
+	int len = (size>sizeof(s)-1) ? sizeof(s)-1 : size;
+
+	strncpy(s,(char *)tsp, len);
+	s[len] = '\0';
+	return s;
+}
+
+
+/*------------------------------------------------------------------------
+Procedure:     handle_props ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+
+int save_attach_data(char *, uint8 *, uint32);
+
+int handle_props(uint8 *tsp)
+{
+	int bytes = 0;
+	uint32 num_props = 0;
+	uint32 x = 0;
+
+
+	num_props = read_32(tsp);
+	bytes += sizeof(num_props);
+
+	while (x < num_props)
+	{
+		uint32 prop_tag;
+		uint32 num;
+		char filename[256];
+		static int file_num = 0;
+
+		prop_tag = read_32(tsp+bytes);
+		bytes += sizeof(prop_tag);
+
+		switch (prop_tag & PROP_TYPE_MASK)
+		{
+		case PT_BINARY:
+			num = read_32(tsp+bytes);
+			bytes += sizeof(num);
+			num = read_32(tsp+bytes);
+			bytes += sizeof(num);
+			if (prop_tag == PR_RTF_COMPRESSED)
+			{
+				sprintf (filename, "XAM_%d.rtf", file_num);
+				file_num++;
+				save_attach_data(filename, tsp+bytes, num);
+			}
+			/* num + PAD */
+			bytes += num + ((num % 4) ? (4 - num%4) : 0);
+			break;
+		case PT_STRING8:
+			num = read_32(tsp+bytes);
+			bytes += sizeof(num);
+			num = read_32(tsp+bytes);
+			bytes += sizeof(num);
+			make_string(tsp+bytes,num);
+			bytes += num + ((num % 4) ? (4 - num%4) : 0);
+			break;
+		case PT_UNICODE:
+		case PT_OBJECT:
+			break;
+		case PT_I2:
+			bytes += 2;
+			break;
+		case PT_LONG:
+			bytes += 4;
+			break;
+		case PT_R4:
+			bytes += 4;
+			break;
+		case PT_DOUBLE:
+			bytes += 8;
+			break;
+		case PT_CURRENCY:
+		case PT_APPTIME:
+		case PT_ERROR:
+			bytes += 4;
+			break;
+		case PT_BOOLEAN:
+			bytes += 4;
+			break;
+		case PT_I8:
+			bytes += 8;
+		case PT_SYSTIME:
+			bytes += 8;
+			break;
+		}
+		x++;
+	}
+
+	return 0;
+}
+
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     save_attach_data ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int save_attach_data(char *title, uint8 *tsp, uint32 size)
+{
+	FILE *out;
+	char filename[1024];
+
+	/*
+	if ((*tsp +size) > _TNEF_size)
+	{
+	return -1;
+	}
+	*/
+	snprintf(filename,1023,"%s/%s",_TNEF_path,title);
+
+	out = fopen(filename, "w");
+	if (!out)
+	{
+		if (_TNEF_stderrlogging > 0) fprintf(stderr, "Error openning file %s for writing\n", filename);
+		return -1;
+	}
+
+	fwrite(tsp, sizeof(uint8), size, out);
+	fclose(out);
+	return 0;
+}
+
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     default_handler ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int default_handler(uint32 attribute, uint8 *tsp, uint32 size)
+{
+	uint16 type = ATT_TYPE(attribute);
+
+	switch (type) {
+	case atpTriples:
+		break;
+	case atpString:
+	case atpText:
+		break;
+	case atpDate:
+		break;
+	case atpShort:
+		break;
+	case atpLong:
+		break;
+	case atpByte:
+		break;
+	case atpWord:
+		break;
+	case atpDword:
+		break;
+	default:
+		break;
+	}
+	return 0;
+
+}
+
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     read_attribute ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int read_attribute(uint8 *tsp)
+{
+
+	int bytes = 0, header = 0;
+	uint32 attribute;
+	uint8 component = 0;
+	uint32 size = 0;
+	uint16 checksum = 0;
+	static char attach_title[256] = {
+		0				};
+	static uint32 attach_size = 0;
+	static uint32 attach_loc  = 0;
+
+	/* What component are we look at? */
+ component = *tsp;
+
+	bytes += sizeof(uint8);
+
+	/* Read the attributes of this component */
+
+	if (_TNEF_debug) fprintf(stderr,"read_attribute: Reading Attribute...\n");
+	attribute = read_32(tsp+bytes);
+	if (attribute == -1) return -1;
+	bytes += sizeof(attribute);
+
+	/* Read the size of the information we have to read */
+
+	if (_TNEF_debug) fprintf(stderr,"read_attribute: Reading Size...\n");
+	size = read_32(tsp+bytes);
+	if (size == -1) return -1;
+	bytes += sizeof(size);
+
+	/* The header size equals the sum of all the things we've read
+ 	  so far. */
+
+	header = bytes;
+
+	/* The is a bit of a tricky one [if you're being slow
+	   it moves the number of bytes ahead by the amount of data of
+ 	   the attribute we're about to read, so that for next
+	   "read_attribute()"
+	   call starts in the right place.
+	*/
+
+	bytes += size;
+
+	/* Read in the checksum for this component
+	
+	 AMMENDMENT - 19/07/02 - 17H01
+	 Small code change to deal with strange sitations that occur with non
+			english characters. - Submitted by wtcheuk@netvigator.com @ 19/07/02
+  */
+
+	if ( bytes < 0 ) return -1;
+
+	/* --END of ammendment. */
+
+	if (_TNEF_debug) fprintf(stderr,"read_attribute: Reading Checksum...(offset %d, bytes=%d)\n", tsp -tnef_home, bytes);
+	checksum = read_16(tsp+bytes);
+	bytes += sizeof(checksum);
+
+	if (_TNEF_debug) fprintf(stderr,"Decoding attribute %d\n",attribute);
+
+	switch (attribute) {
+	case attNull:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attFrom:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attSubject:
+		break;
+	case attDateSent:
+		break;
+	case attDateRecd:
+		break;
+	case attMessageStatus:
+		break;
+	case attMessageClass:
+		break;
+	case attMessageID:
+		break;
+	case attParentID:
+		break;
+	case attConversationID:
+		break;
+	case attBody:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attPriority:
+		break;
+	case attAttachData:
+		attach_size=size;
+		attach_loc =(int)tsp+header;
+		if (SaveData && strlen(attach_title)>0 && attach_size > 0) {
+			if (!save_attach_data(attach_title, (uint8 *)attach_loc,attach_size))
+			{
+				if (_TNEF_verbose) fprintf(stdout,"Decoding %s\n", attach_title);
+			}
+			else
+			    {
+				if (_TNEF_syslogging > 0) syslog(1,"TNEF: Error saving attachment %s\n",attach_title);
+			}
+		}
+		break;
+	case attAttachTitle:
+		strncpy(attach_title, make_string(tsp+header,size),255);
+		if (SaveData && strlen(attach_title)>0 && attach_size > 0) {
+			if (!save_attach_data(attach_title, (uint8 *)attach_loc,attach_size))
+			{
+				if (_TNEF_verbose) fprintf(stdout,"Decoding %s\n", attach_title);
+			}
+			else
+			    {
+				if (_TNEF_syslogging > 0) syslog(1,"TNEF: Error saving attachment %s\n",attach_title);
+			}
+		}
+		break;
+	case attAttachMetaFile:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attAttachCreateDate:
+		break;
+	case attAttachModifyDate:
+		break;
+	case attDateModified:
+		break;
+	case attAttachTransportFilename:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attAttachRenddata:
+		attach_title[0]=0;
+		attach_size=0;
+		attach_loc=0;
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attMAPIProps:
+		handle_props(tsp+header);
+		break;
+	case attRecipTable:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attAttachment:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attTnefVersion:
+		{
+			uint32 version;
+			version = read_32(tsp+header);
+			if (version == -1) return -1;
+		}
+		break;
+	case attOemCodepage:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attOriginalMessageClass:
+		break;
+	case attOwner:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attSentFor:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attDelegate:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attDateStart:
+		break;
+	case attDateEnd:
+		break;
+	case attAidOwner:
+		default_handler(attribute, tsp+header, size);
+		break;
+	case attRequestRes:
+		default_handler(attribute, tsp+header, size);
+		break;
+	default:
+		default_handler(attribute, tsp+header, size);
+		break;
+	}
+	return bytes;
+
+}
+
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     decode_tnef ID:1
+Purpose:
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int TNEF_decode_tnef(uint8 *tnef_stream, int size)
+{
+
+	int ra_response;
+	uint8 *tsp;
+
+	if (_TNEF_debug) fprintf(stderr,"TNEF_decode_tnef: Start. Size = %d\n",size);
+
+	if (size < 4)
+	{
+		if (_TNEF_debug) fprintf(stderr,"TNEF_decode_tnef: Skipping short file\n");
+		return 0;
+	}
+
+	/* TSP == TNEF Stream Pointer (well memory block actually!)
+	*/
+	tsp = tnef_stream;
+
+	/* Read in the signature of this TNEF
+	*/
+	if (TNEF_SIGNATURE == read_32(tsp))
+	{
+		if (_TNEF_debug) fprintf(stderr,"TNEF signature is good\n");
+	}
+	else
+	    {
+		if (_TNEF_stderrlogging > 0) fprintf(stderr,"TNEF_decode_tnef: Bad TNEF signature, expecting %x got %lx\n",TNEF_SIGNATURE,read_32(tsp));
+	}
+
+	/* Move tsp pointer along
+	*/
+	tsp += sizeof(TNEF_SIGNATURE);
+
+	/* This extra check is here just in case we're running with
+	 * _TNEF_debug set and try to calculate TNEF Attach Key
+	 * when we shouldn't.
+	*/
+	if (tsp + sizeof(uint16) - tnef_stream > size)
+	{
+		if (_TNEF_debug) fprintf(stderr,"TNEF_decode_tnef: Skipping short file\n");
+		return 0;
+	}
+
+	if (_TNEF_debug)  fprintf(stderr,"TNEF Attach Key: %x\n",read_16(tsp));
+	/* Move tsp pointer along
+	*/
+  	tsp += sizeof(uint16);
+
+	/* While we still have more bytes to process,
+			go through entire memory block and extract
+			all the required attributes and files
+	*/
+	if (_TNEF_debug) fprintf(stderr,"TNEF - Commence reading attributes\n");
+	while ((tsp - tnef_stream) < size)
+	{
+		if (_TNEF_debug) fprintf(stderr,"Offset = %d\n",tsp -tnef_home);
+		ra_response = read_attribute(tsp);
+		if ( ra_response > 0 )
+		{
+			tsp += ra_response;
+		} else {
+
+			/* Must find out /WHY/ this happens, and, how to rectify the issue. */
+
+			tsp++;
+			if (_TNEF_debug) fprintf(stderr,"TNEF - Attempting to read attribute resulted in a sub-zero response, ending decoding to be safe\n");
+			break;
+		}
+	}
+
+	if (_TNEF_debug) fprintf(stderr,"TNEF - DONE.\n");
+
+	return 0;
+}
+
+
+
+
+
+
+/*------------------------------------------------------------------------
+Procedure:     TNEF_main ID:1
+Purpose:       Decodes a given TNEF encoded file
+Input:
+Output:
+Errors:
+------------------------------------------------------------------------*/
+int TNEF_main( char *filename )
+{
+	FILE *fp;
+	struct stat sb;
+	uint8 *tnef_stream;
+	int size, nread;
+
+	if (_TNEF_debug) fprintf(stderr,"TNEF_main: Start, decoding %s\n",filename);
+
+	SaveData = TRUE;
+
+	/* Test to see if the file actually exists
+	*/
+	if (stat(filename,&sb) == -1)
+	{
+		if (_TNEF_stderrlogging > 0) fprintf(stderr,"Error stating file %s (%s)\n", filename,strerror(errno));
+		return -1;
+	}
+
+	/* Get the filesize */
+	
+	size = sb.st_size;
+
+	/* Allocate enough memory to read in the ENTIRE file
+	 FIXME - This could be a real consumer if multiple
+	 instances of TNEF decoding is going on
+	*/
+	
+	tnef_home = tnef_stream = (uint8 *)malloc(size);
+	tnef_limit = tnef_home +size;
+
+	/* If we were unable to allocate enough memory, then we
+	   should report this */
+	   
+	if (tnef_stream == NULL)
+	{
+		if (_TNEF_stderrlogging > 0)  fprintf(stderr,"Error allocating %d bytes for loading file (%s)\n", size,strerror(errno));
+		return -1;
+	}
+
+	/* Attempt to open up the TNEF encoded file... if it fails
+	 	then report the failed condition to syslog */
+	
+	if ((fp = fopen(filename,"r")) == NULL)
+	{
+		if (_TNEF_stderrlogging > 0)  fprintf(stderr,"Error opening file %s for reading (%s)\n", filename,strerror(errno));
+		return -1;
+	}
+
+	/* Attempt to read in the entire file */
+	
+	nread = fread(tnef_stream, sizeof(uint8), size, fp);
+
+	if (_TNEF_debug) fprintf(stderr,"TNEF: Read %d bytes\n",nread);
+
+	/* If we did not read in all the bytes, then let syslogs know! */
+	
+	if (nread < size)
+	{
+		return -1;
+	}
+
+	/* Close the file */
+	
+	fclose(fp);
+
+	/* Proceed to decode the file */
+	
+	TNEF_decode_tnef(tnef_stream,size);
+
+
+	if (_TNEF_debug) fprintf(stderr,"TNEF - finished decoding.\n");
+
+	return 0;
+}
+
+
+/* --------------------------END. */
+
+
+
diff -urN exim-4.44-orig/src/tnef.h exim-4.44/src/tnef.h
--- exim-4.44-orig/src/tnef.h	Thu Jan  1 01:00:00 1970
+++ exim-4.44/src/tnef.h	Fri Jan 14 20:35:27 2005
@@ -0,0 +1,1841 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* This file is part of the exiscan-acl content scanner
+patch. It is NOT part of the standard exim distribution. */
+
+/***************************************************************************
+ *
+ * config.h for tnef decoder by Brandon Long
+ * Based on config.h from S3MOD by Dan Marks and David Jeske
+ *
+ * (C) 1994,1995 By Daniel Marks and David Jeske
+ *
+ * While we retain the copyright to this code, this source code is FREE.
+ * You may use it in any way you wish, in any product you wish. You may 
+ * NOT steal the copyright for this code from us.
+ *
+ * We respectfully ask that you email one of us, if possible, if you
+ * produce something significant with this code, or if you have any bug 
+ * fixes to contribute.  We also request that you give credit where
+ * credit is due if you include part of this code in a program of your own.
+ *                                                 
+ ***************************************************************************
+ *
+ * config.h - compile time configuration options and system specific defines
+ *
+ */
+
+/* 2003-02-03 Merged all TNEF and MAPI related headers in this file to reduce
+   clutter
+   - Tom Kistner
+*/
+
+#include <exim.h>
+
+#ifndef _CONFIG_H
+#define _CONFIG_H 1
+
+/***************************************************************************/
+/* The following are system specific settings */
+/***************************************************************************/
+
+#if defined(SUN)
+#define BIT_32
+#define ___TNEF_BYTE_ORDER 4321
+#undef NEAR_FAR_PTR
+
+#elif defined (HPUX)
+#define BIT_32
+#define ___TNEF_BYTE_ORDER 4321
+#undef NEAR_FAR_PTR
+
+#elif defined(DEC)
+#undef NEAR_FAR_PTR
+
+#elif defined(__sgi)
+#define BIT_32
+#define ___TNEF_BYTE_ORDER 4321
+#undef NEAR_FAR_PTR
+
+#elif defined(AIX)
+#undef NEAR_FAR_PTR
+#define ___TNEF_BYTE_ORDER 4321
+#define BIT_32
+
+#elif defined(LINUX)
+#define BIT_32
+#undef NEAR_FAR_PTR
+
+#elif defined(MSDOS)
+#define NEAR_FAR_PTR
+#undef BIT_32
+
+#else
+#undef NEAR_FAR_PTR
+#define BIT_32
+
+
+#endif /* OS/MACH TYPE */
+
+/***************************************************************************/
+/* 16/32 Bit and Byte Order hacks */
+/***************************************************************************/
+
+#ifdef BIT_32
+typedef short int int16;
+typedef unsigned short int uint16;
+typedef int int32;
+typedef unsigned int uint32;
+/* typedef char int8; */
+typedef unsigned char uint8;
+#else
+typedef int int16;
+typedef unsigned int uint16;
+typedef long int int32;
+typedef unsigned long int uint32;
+typedef char int8;
+typedef unsigned char uint8;
+#endif /* BIT_32 */
+
+#ifndef WIN32_TYPES
+#define ULONG uint32
+#define SCODE uint32
+#define FAR
+#define LPVOID void *
+#define WORD uint16
+#define DWORD uint32
+#define LONG int32
+#define BYTE uint8
+#endif /* !WIN32_TYPES */
+
+#define endian_switch(x) (((((uint16)(x)) & 0xFF00) >> 8) | \
+			  ((((uint16)(x)) & 0xFF) << 8))
+
+#define long_endian_switch(x) ( ((((uint32)(x)) & 0xFF00UL) << 8) | \
+			        ((((uint32)(x)) & 0xFFUL) << 24) | \
+			        ((((uint32)(x)) & 0xFF0000UL) >> 8) | \
+			        ((((uint32)(x)) & 0xFF000000UL) >> 24))
+
+#if ___TNEF_BYTE_ORDER == 4321
+#define big_endian(x) (x)
+#define long_big_endian(x) (x)
+#define little_endian(x) (endian_switch(x))
+#define long_little_endian(x) (long_endian_switch(x))
+#else
+#define big_endian(x) (endian_switch(x))
+#define long_big_endian(x) (long_endian_switch(x))
+#define little_endian(x) (x)
+#define long_little_endian(x) (x)
+#endif /* ___TNEF_BYTE_ORDER */
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+#endif /* _CONFIG_H */
+/*
+ *  Taken from the Win32 SDK or the MSVC4 include files, I'm not sure which.
+ *  The document describing the TNEF format alludes to this document for more
+ *  information.  This file was stripped a bit to allow it to compile with
+ *  GCC and without random other Windows header files so it could be used
+ *  to decode TNEF bitstreams with tnef2txt.
+ *
+ *  T N E F . H
+ *
+ *
+ *  This file contains structure and function definitions for the
+ *  MAPI implementation of the Transport Neutral Encapsilation Format
+ *  used by MAPI providers for the neutral serialization of a MAPI
+ *  message.  This implementation sits on top of the IStream object as
+ *  documented in the OLE 2 Specs.
+ *
+ *  Copyright 1986-1996 Microsoft Corporation. All Rights Reserved.
+ */
+
+#ifndef TNEF_H
+#define TNEF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef BEGIN_INTERFACE
+#define BEGIN_INTERFACE
+#endif
+
+#ifndef MAPI_DIM
+#define MAPI_DIM 1
+#endif
+
+#define TNTNoffsetof(s,m) (unsigned long)&(((s *)0)->m)
+
+/* ------------------------------------ */
+/* TNEF Problem and TNEF Problem Arrays */
+/* ------------------------------------ */
+
+typedef struct _STnefProblem
+{
+    ULONG   ulComponent;
+    ULONG   ulAttribute;
+    ULONG   ulPropTag;
+    SCODE   scode;
+} STnefProblem;
+
+typedef struct _STnefProblemArray
+{
+    ULONG           cProblem;
+    STnefProblem    aProblem[MAPI_DIM];
+} STnefProblemArray, FAR * LPSTnefProblemArray;
+
+#if 0
+#define CbNewSTnefProblemArray(_cprob) \
+    (TNoffsetof(STnefProblemArray,aProblem) + (_cprob)*sizeof(STnefProblem))
+#define CbSTnefProblemArray(_lparray) \
+    (TNoffsetof(STnefProblemArray,aProblem) + \
+    (UINT) ((_lparray)->cProblem*sizeof(STnefProblem)))
+#endif
+
+/* Pointers to TNEF Interface ---------------------------------------- */
+
+#if 0
+DECLARE_MAPI_INTERFACE_PTR(ITnef, LPITNEF);
+#endif
+
+/*  OpenTNEFStream */
+
+#define TNEF_DECODE                 ((ULONG) 0)
+#define TNEF_ENCODE                 ((ULONG) 2)
+
+#define TNEF_PURE                   ((ULONG) 0x00010000)
+#define TNEF_COMPATIBILITY          ((ULONG) 0x00020000)
+#define TNEF_BEST_DATA              ((ULONG) 0x00040000)
+#define TNEF_COMPONENT_ENCODING     ((ULONG) 0x80000000)
+
+/*  AddProps, ExtractProps */
+
+#define TNEF_PROP_INCLUDE           ((ULONG) 0x00000001)
+#define TNEF_PROP_EXCLUDE           ((ULONG) 0x00000002)
+#define TNEF_PROP_CONTAINED         ((ULONG) 0x00000004)
+#define TNEF_PROP_MESSAGE_ONLY      ((ULONG) 0x00000008)
+#define TNEF_PROP_ATTACHMENTS_ONLY  ((ULONG) 0x00000010)
+#define TNEF_PROP_CONTAINED_TNEF    ((ULONG) 0x00000040)
+
+/*  FinishComponent */
+
+#define TNEF_COMPONENT_MESSAGE      ((ULONG) 0x00001000)
+#define TNEF_COMPONENT_ATTACHMENT   ((ULONG) 0x00002000)
+
+#if 0
+#define MAPI_ITNEF_METHODS(IPURE)                                       \
+    MAPIMETHOD(AddProps)                                                \
+        (THIS_  ULONG                       ulFlags,                    \
+                ULONG                       ulElemID,                   \
+                LPVOID                      lpvData,                    \
+                LPSPropTagArray             lpPropList) IPURE;          \
+    MAPIMETHOD(ExtractProps)                                            \
+        (THIS_  ULONG                       ulFlags,                    \
+                LPSPropTagArray             lpPropList,                 \
+                LPSTnefProblemArray FAR *   lpProblems) IPURE;          \
+    MAPIMETHOD(Finish)                                                  \
+        (THIS_  ULONG                       ulFlags,                    \
+                WORD FAR *                  lpKey,                      \
+                LPSTnefProblemArray FAR *   lpProblems) IPURE;          \
+    MAPIMETHOD(OpenTaggedBody)                                          \
+        (THIS_  LPMESSAGE                   lpMessage,                  \
+                ULONG                       ulFlags,                    \
+                LPSTREAM FAR *              lppStream) IPURE;           \
+    MAPIMETHOD(SetProps)                                                \
+        (THIS_  ULONG                       ulFlags,                    \
+                ULONG                       ulElemID,                   \
+                ULONG                       cValues,                    \
+                LPSPropValue                lpProps) IPURE;             \
+    MAPIMETHOD(EncodeRecips)                                            \
+        (THIS_  ULONG                       ulFlags,                    \
+                LPMAPITABLE                 lpRecipientTable) IPURE;    \
+    MAPIMETHOD(FinishComponent)                                         \
+        (THIS_  ULONG                       ulFlags,                    \
+                ULONG                       ulComponentID,              \
+                LPSPropTagArray             lpCustomPropList,           \
+                LPSPropValue                lpCustomProps,              \
+                LPSPropTagArray             lpPropList,                 \
+                LPSTnefProblemArray FAR *   lpProblems) IPURE;          \
+
+#undef       INTERFACE
+#define      INTERFACE  ITnef
+DECLARE_MAPI_INTERFACE_(ITnef, IUnknown)
+{
+    BEGIN_INTERFACE
+    MAPI_IUNKNOWN_METHODS(PURE)
+    MAPI_ITNEF_METHODS(PURE)
+};
+
+STDMETHODIMP OpenTnefStream(
+    LPVOID              lpvSupport,
+    LPSTREAM            lpStream,
+    LPTSTR              lpszStreamName,
+    ULONG               ulFlags,
+    LPMESSAGE           lpMessage,
+    WORD                wKeyVal,
+    LPITNEF FAR *       lppTNEF);
+
+typedef HRESULT (STDMETHODCALLTYPE FAR * LPOPENTNEFSTREAM) (
+    LPVOID              lpvSupport,
+    LPSTREAM            lpStream,
+    LPTSTR              lpszStreamName,
+    ULONG               ulFlags,
+    LPMESSAGE           lpMessage,
+    WORD                wKeyVal,
+    LPITNEF FAR *       lppTNEF);
+
+STDMETHODIMP OpenTnefStreamEx(
+    LPVOID              lpvSupport,
+    LPSTREAM            lpStream,
+    LPTSTR              lpszStreamName,
+    ULONG               ulFlags,
+    LPMESSAGE           lpMessage,
+    WORD                wKeyVal,
+    LPADRBOOK           lpAdressBook,
+    LPITNEF FAR *       lppTNEF);
+
+typedef HRESULT (STDMETHODCALLTYPE FAR * LPOPENTNEFSTREAMEX) (
+    LPVOID              lpvSupport,
+    LPSTREAM            lpStream,
+    LPTSTR              lpszStreamName,
+    ULONG               ulFlags,
+    LPMESSAGE           lpMessage,
+    WORD                wKeyVal,
+    LPADRBOOK           lpAdressBook,
+    LPITNEF FAR *       lppTNEF);
+
+STDMETHODIMP GetTnefStreamCodepage (
+    LPSTREAM            lpStream,
+    ULONG FAR *         lpulCodepage,
+    ULONG FAR *         lpulSubCodepage);
+
+typedef HRESULT (STDMETHODCALLTYPE FAR * LPGETTNEFSTREAMCODEPAGE) (
+    LPSTREAM            lpStream,
+    ULONG FAR *         lpulCodepage,
+    ULONG FAR *         lpulSubCodepage);
+
+#define OPENTNEFSTREAM "OpenTnefStream"
+#define OPENTNEFSTREAMEX "OpenTnefStreamEx"
+#define GETTNEFSTREAMCODEPAGE "GetTnefStreamCodePage"
+#endif
+
+/* -------------------------- */
+/* TNEF Signature and Version */
+/* -------------------------- */
+
+#define MAKE_TNEF_VERSION(_mj,_mn)  (((ULONG)(0x0000FFFF & _mj) << 16) | (ULONG)(0x0000FFFF & _mn))
+#define TNEF_SIGNATURE  ((ULONG) 0x223E9F78)
+#define TNEF_VERSION    ((ULONG) MAKE_TNEF_VERSION(1,0))
+
+
+/* ------------------------------------------- */
+/* TNEF Down-level Attachment Types/Structures */
+/* ------------------------------------------- */
+
+typedef WORD ATYP;
+enum { atypNull, atypFile, atypOle, atypPicture, atypMax };
+
+#define MAC_BINARY  ((DWORD) 0x00000001)
+
+typedef struct _renddata
+{
+    ATYP    atyp;
+    ULONG   ulPosition;
+    WORD    dxWidth;
+    WORD    dyHeight;
+    DWORD   dwFlags;
+
+} RENDDATA, *PRENDDATA;
+
+/* ----------------------------------- */
+/* TNEF Down-level Date/Time Structure */
+/* ----------------------------------- */
+
+typedef struct _dtr
+{
+    WORD    wYear;
+    WORD    wMonth;
+    WORD    wDay;
+    WORD    wHour;
+    WORD    wMinute;
+    WORD    wSecond;
+    WORD    wDayOfWeek;
+
+} DTR;
+
+
+/* ----------------------------- */
+/* TNEF Down-level Message Flags */
+/* ----------------------------- */
+
+#define fmsNull         ((BYTE) 0x00)
+#define fmsModified     ((BYTE) 0x01)
+#define fmsLocal        ((BYTE) 0x02)
+#define fmsSubmitted    ((BYTE) 0x04)
+#define fmsRead         ((BYTE) 0x20)
+#define fmsHasAttach    ((BYTE) 0x80)
+
+
+/* ----------------------------------------- */
+/* TNEF Down-level Triple Address Structures */
+/* ----------------------------------------- */
+
+#define trpidNull                   ((WORD) 0x0000)
+#define trpidUnresolved             ((WORD) 0x0001)
+#define trpidResolvedNSID           ((WORD) 0x0002)
+#define trpidResolvedAddress        ((WORD) 0x0003)
+#define trpidOneOff                 ((WORD) 0x0004)
+#define trpidGroupNSID              ((WORD) 0x0005)
+#define trpidOffline                ((WORD) 0x0006)
+#define trpidIgnore                 ((WORD) 0x0007)
+#define trpidClassEntry             ((WORD) 0x0008)
+#define trpidResolvedGroupAddress   ((WORD) 0x0009)
+typedef struct _trp
+{
+    WORD    trpid;
+    WORD    cbgrtrp;
+    WORD    cch;
+    WORD    cbRgb;
+
+} TRP, *PTRP, *PGRTRP, FAR * LPTRP;
+#define CbOfTrp(_p)     (sizeof(TRP) + (_p)->cch + (_p)->cbRgb)
+#define LpszOfTrp(_p)   ((LPSTR)(((LPTRP) (_p)) + 1))
+#define LpbOfTrp(_p)    (((LPBYTE)(((LPTRP)(_p)) + 1)) + (_p)->cch)
+#define LptrpNext(_p)   ((LPTRP)((LPBYTE)(_p) + CbOfTrp(_p)))
+
+typedef DWORD XTYPE;
+#define xtypeUnknown    ((XTYPE) 0)
+#define xtypeInternet   ((XTYPE) 6)
+
+#define cbDisplayName   41
+#define cbEmailName     11
+#define cbSeverName     12
+typedef struct _ADDR_ALIAS
+{
+    char    rgchName[cbDisplayName];
+    char    rgchEName[cbEmailName];
+    char    rgchSrvr[cbSeverName];
+    ULONG   dibDetail;
+    WORD    type;
+
+} ADDRALIAS, FAR * LPADDRALIAS;
+#define cbALIAS sizeof(ALIAS)
+
+#define cbTYPE              16
+#define cbMaxIdData         200
+typedef struct _NSID
+{
+    DWORD   dwSize;
+    unsigned char   uchType[cbTYPE];
+    XTYPE   xtype;
+    LONG    lTime;
+
+    union
+    {
+        ADDRALIAS   alias;
+        char        rgchInterNet[1];
+
+    } address;
+
+} NSID, * LPNSID;
+#define cbNSID sizeof(NSID)
+
+
+/* -------------------------- */
+/* TNEF Down-level Priorities */
+/* -------------------------- */
+
+#define prioLow     3
+#define prioNorm    2
+#define prioHigh    1
+
+
+/* ------------------------------------- */
+/* TNEF Down-level Attributes/Properties */
+/* ------------------------------------- */
+
+#define atpTriples      ((WORD) 0x0000)
+#define atpString       ((WORD) 0x0001)
+#define atpText         ((WORD) 0x0002)
+#define atpDate         ((WORD) 0x0003)
+#define atpShort        ((WORD) 0x0004)
+#define atpLong         ((WORD) 0x0005)
+#define atpByte         ((WORD) 0x0006)
+#define atpWord         ((WORD) 0x0007)
+#define atpDword        ((WORD) 0x0008)
+#define atpMax          ((WORD) 0x0009)
+
+#define LVL_MESSAGE     ((BYTE) 0x01)
+#define LVL_ATTACHMENT  ((BYTE) 0x02)
+
+#define ATT_ID(_att)                ((WORD) ((_att) & 0x0000FFFF))
+#define ATT_TYPE(_att)              ((WORD) (((_att) >> 16) & 0x0000FFFF))
+#define ATT(_atp, _id)              ((((DWORD) (_atp)) << 16) | ((WORD) (_id)))
+
+#define attNull                     ATT( 0,             0x0000)
+#define attFrom                     ATT( atpTriples,    0x8000) /* PR_ORIGINATOR_RETURN_ADDRESS */
+#define attSubject                  ATT( atpString,     0x8004) /* PR_SUBJECT */
+#define attDateSent                 ATT( atpDate,       0x8005) /* PR_CLIENT_SUBMIT_TIME */
+#define attDateRecd                 ATT( atpDate,       0x8006) /* PR_MESSAGE_DELIVERY_TIME */
+#define attMessageStatus            ATT( atpByte,       0x8007) /* PR_MESSAGE_FLAGS */
+#define attMessageClass             ATT( atpWord,       0x8008) /* PR_MESSAGE_CLASS */
+#define attMessageID                ATT( atpString,     0x8009) /* PR_MESSAGE_ID */
+#define attParentID                 ATT( atpString,     0x800A) /* PR_PARENT_ID */
+#define attConversationID           ATT( atpString,     0x800B) /* PR_CONVERSATION_ID */
+#define attBody                     ATT( atpText,       0x800C) /* PR_BODY */
+#define attPriority                 ATT( atpShort,      0x800D) /* PR_IMPORTANCE */
+#define attAttachData               ATT( atpByte,       0x800F) /* PR_ATTACH_DATA_xxx */
+#define attAttachTitle              ATT( atpString,     0x8010) /* PR_ATTACH_FILENAME */
+#define attAttachMetaFile           ATT( atpByte,       0x8011) /* PR_ATTACH_RENDERING */
+#define attAttachCreateDate         ATT( atpDate,       0x8012) /* PR_CREATION_TIME */
+#define attAttachModifyDate         ATT( atpDate,       0x8013) /* PR_LAST_MODIFICATION_TIME */
+#define attDateModified             ATT( atpDate,       0x8020) /* PR_LAST_MODIFICATION_TIME */
+#define attAttachTransportFilename  ATT( atpByte,       0x9001) /* PR_ATTACH_TRANSPORT_NAME */
+#define attAttachRenddata           ATT( atpByte,       0x9002)
+#define attMAPIProps                ATT( atpByte,       0x9003)
+#define attRecipTable               ATT( atpByte,       0x9004) /* PR_MESSAGE_RECIPIENTS */
+#define attAttachment               ATT( atpByte,       0x9005)
+#define attTnefVersion              ATT( atpDword,      0x9006)
+#define attOemCodepage              ATT( atpByte,       0x9007)
+#define attOriginalMessageClass     ATT( atpWord,       0x0006) /* PR_ORIG_MESSAGE_CLASS */
+
+#define attOwner                    ATT( atpByte,       0x0000) /* PR_RCVD_REPRESENTING_xxx  or
+                                                                   PR_SENT_REPRESENTING_xxx */
+#define attSentFor                  ATT( atpByte,       0x0001) /* PR_SENT_REPRESENTING_xxx */
+#define attDelegate                 ATT( atpByte,       0x0002) /* PR_RCVD_REPRESENTING_xxx */
+#define attDateStart                ATT( atpDate,       0x0006) /* PR_DATE_START */
+#define attDateEnd                  ATT( atpDate,       0x0007) /* PR_DATE_END */
+#define attAidOwner                 ATT( atpLong,       0x0008) /* PR_OWNER_APPT_ID */
+#define attRequestRes               ATT( atpShort,      0x0009) /* PR_RESPONSE_REQUESTED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /*  defined TNEF_H */
+/*
+ *  M A P I D E F S . H
+ *
+ *  Definitions used by MAPI clients and service providers.
+ *
+ *  Copyright 1986-1996 Microsoft Corporation. All Rights Reserved.
+ */
+
+#ifndef MAPIDEFS_H
+#define MAPIDEFS_H
+
+
+/* Array dimension for structures with variable-sized arrays at the end. */
+
+/* Simple data types */
+
+
+typedef WORD                WCHAR;
+
+#ifdef UNICODE
+typedef WCHAR               TCHAR;
+#else
+typedef char                TCHAR;
+#endif
+
+typedef WCHAR *         LPWSTR;
+typedef const WCHAR *   LPCWSTR;
+typedef TCHAR *         LPTSTR;
+typedef const TCHAR *   LPCTSTR;
+typedef BYTE *          LPBYTE;
+
+typedef ULONG *         LPULONG;
+
+#ifndef __LHANDLE
+#define __LHANDLE
+typedef unsigned long   LHANDLE, * LPLHANDLE;
+#endif
+
+#if !defined(_WINBASE_) && !defined(_FILETIME_)
+#define _FILETIME_
+typedef struct _FILETIME
+{
+    DWORD dwLowDateTime;
+    DWORD dwHighDateTime;
+} FILETIME, * LPFILETIME;
+#endif
+
+/*
+ *  This flag is used in many different MAPI calls to signify that
+ *  the object opened by the call should be modifiable (MAPI_MODIFY).
+ *  If the flag MAPI_MAX_ACCESS is set, the object returned should be
+ *  returned at the maximum access level allowed.  An additional
+ *  property available on the object (PR_ACCESS_LEVEL) uses the same
+ *  MAPI_MODIFY flag to say just what this new access level is.
+ */
+
+#define MAPI_MODIFY             ((ULONG) 0x00000001)
+
+/*
+ *  The following flags are used to indicate to the client what access
+ *  level is permissible in the object. They appear in PR_ACCESS in
+ *  message and folder objects as well as in contents and associated
+ *  contents tables
+ */
+
+#define MAPI_ACCESS_MODIFY                  ((ULONG) 0x00000001)
+#define MAPI_ACCESS_READ                    ((ULONG) 0x00000002)
+#define MAPI_ACCESS_DELETE                  ((ULONG) 0x00000004)
+#define MAPI_ACCESS_CREATE_HIERARCHY        ((ULONG) 0x00000008)
+#define MAPI_ACCESS_CREATE_CONTENTS         ((ULONG) 0x00000010)
+#define MAPI_ACCESS_CREATE_ASSOCIATED       ((ULONG) 0x00000020)
+
+/*
+ *  The MAPI_UNICODE flag is used in many different MAPI calls to signify
+ *  that strings passed through the interface are in Unicode (a 16-bit
+ *  character set). The default is an 8-bit character set.
+ *
+ *  The value fMapiUnicode can be used as the 'normal' value for
+ *  that bit, given the application's default character set.
+ */
+
+#define MAPI_UNICODE            ((ULONG) 0x80000000)
+
+#ifdef UNICODE
+#define fMapiUnicode            MAPI_UNICODE
+#else
+#define fMapiUnicode            0
+#endif
+
+/* successful HRESULT */
+#define hrSuccess               0
+
+
+
+/* Recipient types */
+#ifndef MAPI_ORIG               /* also defined in mapi.h */
+#define MAPI_ORIG   0           /* Recipient is message originator          */
+#define MAPI_TO     1           /* Recipient is a primary recipient         */
+#define MAPI_CC     2           /* Recipient is a copy recipient            */
+#define MAPI_BCC    3           /* Recipient is blind copy recipient        */
+#define MAPI_P1     0x10000000  /* Recipient is a P1 resend recipient       */
+#define MAPI_SUBMITTED 0x80000000 /* Recipient is already processed         */
+/* #define MAPI_AUTHORIZE 4        recipient is a CMC authorizing user      */
+/*#define MAPI_DISCRETE 0x10000000 Recipient is a P1 resend recipient       */
+#endif
+
+/* Bit definitions for abFlags[0] of ENTRYID */
+#define MAPI_SHORTTERM          0x80
+#define MAPI_NOTRECIP           0x40
+#define MAPI_THISSESSION        0x20
+#define MAPI_NOW                0x10
+#define MAPI_NOTRESERVED        0x08
+
+/* Bit definitions for abFlags[1] of ENTRYID */
+#define MAPI_COMPOUND           0x80
+
+/* ENTRYID */
+typedef struct
+{
+    BYTE    abFlags[4];
+    BYTE    ab[MAPI_DIM];
+} ENTRYID, *LPENTRYID;
+
+#define CbNewENTRYID(_cb)       (offsetof(ENTRYID,ab) + (_cb))
+#define CbENTRYID(_cb)          (offsetof(ENTRYID,ab) + (_cb))
+
+/* Byte-order-independent version of GUID (world-unique identifier) */
+typedef struct _MAPIUID
+{
+    BYTE ab[16];
+} MAPIUID, * LPMAPIUID;
+
+/* Note:  need to include C run-times (memory.h) to use this macro */
+
+#define IsEqualMAPIUID(lpuid1, lpuid2)  (!memcmp(lpuid1, lpuid2, sizeof(MAPIUID)))
+
+/*
+ * Constants for one-off entry ID:
+ * The MAPIUID that identifies the one-off provider;
+ * the flag that defines whether the embedded strings are Unicode;
+ * the flag that specifies whether the recipient gets TNEF or not.
+ */
+
+#define MAPI_ONE_OFF_UID { 0x81, 0x2b, 0x1f, 0xa4, 0xbe, 0xa3, 0x10, 0x19, 0x9d, 0x6e, 0x00, 0xdd, 0x01, 0x0f, 0x54, 0x02 }
+#define MAPI_ONE_OFF_UNICODE        0x8000
+#define MAPI_ONE_OFF_NO_RICH_INFO   0x0001
+
+/* Object type */
+
+#define MAPI_STORE      ((ULONG) 0x00000001)    /* Message Store */
+#define MAPI_ADDRBOOK   ((ULONG) 0x00000002)    /* Address Book */
+#define MAPI_FOLDER     ((ULONG) 0x00000003)    /* Folder */
+#define MAPI_ABCONT     ((ULONG) 0x00000004)    /* Address Book Container */
+#define MAPI_MESSAGE    ((ULONG) 0x00000005)    /* Message */
+#define MAPI_MAILUSER   ((ULONG) 0x00000006)    /* Individual Recipient */
+#define MAPI_ATTACH     ((ULONG) 0x00000007)    /* Attachment */
+#define MAPI_DISTLIST   ((ULONG) 0x00000008)    /* Distribution List Recipient */
+#define MAPI_PROFSECT   ((ULONG) 0x00000009)    /* Profile Section */
+#define MAPI_STATUS     ((ULONG) 0x0000000A)    /* Status Object */
+#define MAPI_SESSION    ((ULONG) 0x0000000B)    /* Session */
+#define MAPI_FORMINFO   ((ULONG) 0x0000000C)    /* Form Information */
+
+
+/*
+ *  Maximum length of profile names and passwords, not including
+ *  the null termination character.
+ */
+#ifndef cchProfileNameMax
+#define cchProfileNameMax   64
+#define cchProfilePassMax   64
+#endif
+
+
+/* Property Types */
+
+#define MV_FLAG         0x1000          /* Multi-value flag */
+
+#define PT_UNSPECIFIED  ((ULONG)  0)    /* (Reserved for interface use) type doesn't matter to caller */
+#define PT_NULL         ((ULONG)  1)    /* NULL property value */
+#define PT_I2           ((ULONG)  2)    /* Signed 16-bit value */
+#define PT_LONG         ((ULONG)  3)    /* Signed 32-bit value */
+#define PT_R4           ((ULONG)  4)    /* 4-byte floating point */
+#define PT_DOUBLE       ((ULONG)  5)    /* Floating point double */
+#define PT_CURRENCY     ((ULONG)  6)    /* Signed 64-bit int (decimal w/    4 digits right of decimal pt) */
+#define PT_APPTIME      ((ULONG)  7)    /* Application time */
+#define PT_ERROR        ((ULONG) 10)    /* 32-bit error value */
+#define PT_BOOLEAN      ((ULONG) 11)    /* 16-bit boolean (non-zero true) */
+#define PT_OBJECT       ((ULONG) 13)    /* Embedded object in a property */
+#define PT_I8           ((ULONG) 20)    /* 8-byte signed integer */
+#define PT_STRING8      ((ULONG) 30)    /* Null terminated 8-bit character string */
+#define PT_UNICODE      ((ULONG) 31)    /* Null terminated Unicode string */
+#define PT_SYSTIME      ((ULONG) 64)    /* FILETIME 64-bit int w/ number of 100ns periods since Jan 1,1601 */
+#define PT_CLSID        ((ULONG) 72)    /* OLE GUID */
+#define PT_BINARY       ((ULONG) 258)   /* Uninterpreted (counted byte array) */
+/* Changes are likely to these numbers, and to their structures. */
+
+/* Alternate property type names for ease of use */
+#define PT_SHORT    PT_I2
+#define PT_I4       PT_LONG
+#define PT_FLOAT    PT_R4
+#define PT_R8       PT_DOUBLE
+#define PT_LONGLONG PT_I8
+
+/*
+ *  The type of a MAPI-defined string property is indirected, so
+ *  that it defaults to Unicode string on a Unicode platform and to
+ *  String8 on an ANSI or DBCS platform.
+ *
+ *  Macros are defined here both for the property type, and for the
+ *  field of the property value structure which should be
+ *  dereferenced to obtain the string pointer.
+ */
+
+#ifdef  UNICODE
+#define PT_TSTRING          PT_UNICODE
+#define PT_MV_TSTRING       (MV_FLAG|PT_UNICODE)
+#define LPSZ                lpszW
+#define LPPSZ               lppszW
+#define MVSZ                MVszW
+#else
+#define PT_TSTRING          PT_STRING8
+#define PT_MV_TSTRING       (MV_FLAG|PT_STRING8)
+#define LPSZ                lpszA
+#define LPPSZ               lppszA
+#define MVSZ                MVszA
+#endif
+
+
+/* Property Tags
+ *
+ * By convention, MAPI never uses 0 or FFFF as a property ID.
+ * Use as null values, initializers, sentinels, or what have you.
+ */
+
+#define PROP_TYPE_MASK          ((ULONG)0x0000FFFF) /* Mask for Property type */
+#define PROP_TYPE(ulPropTag)    (((ULONG)(ulPropTag))&PROP_TYPE_MASK)
+#define PROP_ID(ulPropTag)      (((ULONG)(ulPropTag))>>16)
+#define PROP_TAG(ulPropType,ulPropID)   ((((ULONG)(ulPropID))<<16)|((ULONG)(ulPropType)))
+#define PROP_ID_NULL            0
+#define PROP_ID_INVALID         0xFFFF
+#define PR_NULL                 PROP_TAG( PT_NULL, PROP_ID_NULL)
+#if 0
+#define CHANGE_PROP_TYPE(ulPropTag, ulPropType) \
+                        (((ULONG)0xFFFF0000 & ulPropTag) | ulPropType)
+#endif
+
+
+/* Multi-valued Property Types */
+
+#define PT_MV_I2        (MV_FLAG|PT_I2)
+#define PT_MV_LONG      (MV_FLAG|PT_LONG)
+#define PT_MV_R4        (MV_FLAG|PT_R4)
+#define PT_MV_DOUBLE    (MV_FLAG|PT_DOUBLE)
+#define PT_MV_CURRENCY  (MV_FLAG|PT_CURRENCY)
+#define PT_MV_APPTIME   (MV_FLAG|PT_APPTIME)
+#define PT_MV_SYSTIME   (MV_FLAG|PT_SYSTIME)
+#define PT_MV_STRING8   (MV_FLAG|PT_STRING8)
+#define PT_MV_BINARY    (MV_FLAG|PT_BINARY)
+#define PT_MV_UNICODE   (MV_FLAG|PT_UNICODE)
+#define PT_MV_CLSID     (MV_FLAG|PT_CLSID)
+#define PT_MV_I8        (MV_FLAG|PT_I8)
+
+/* Alternate property type names for ease of use */
+#define PT_MV_SHORT     PT_MV_I2
+#define PT_MV_I4        PT_MV_LONG
+#define PT_MV_FLOAT     PT_MV_R4
+#define PT_MV_R8        PT_MV_DOUBLE
+#define PT_MV_LONGLONG  PT_MV_I8
+
+/*
+ *  Property type reserved bits
+ *
+ *  MV_INSTANCE is used as a flag in table operations to request
+ *  that a multi-valued property be presented as a single-valued
+ *  property appearing in multiple rows.
+ */
+
+#define MV_INSTANCE     0x2000
+#define MVI_FLAG        (MV_FLAG | MV_INSTANCE)
+#define MVI_PROP(tag)   ((tag) | MVI_FLAG)
+
+
+
+#endif /* MAPIDEFS_H */
+/*
+ *  M A P I T A G S . H
+ *
+ *  Property tag definitions for standard properties of MAPI
+ *  objects.
+ *
+ *  The following ranges should be used for all property IDs. Note that
+ *  property IDs for objects other than messages and recipients should
+ *  all fall in the range 0x3000 to 0x3FFF:
+ *
+ *  From    To      Kind of property
+ *  --------------------------------
+ *  0001    0BFF    MAPI_defined envelope property
+ *  0C00    0DFF    MAPI_defined per-recipient property
+ *  0E00    0FFF    MAPI_defined non-transmittable property
+ *  1000    2FFF    MAPI_defined message content property
+ *
+ *  3000    3FFF    MAPI_defined property (usually not message or recipient)
+ *
+ *  4000    57FF    Transport-defined envelope property
+ *  5800    5FFF    Transport-defined per-recipient property
+ *  6000    65FF    User-defined non-transmittable property
+ *  6600    67FF    Provider-defined internal non-transmittable property
+ *  6800    7BFF    Message class-defined content property
+ *  7C00    7FFF    Message class-defined non-transmittable
+ *                  property
+ *
+ *  8000    FFFE    User-defined Name-to-id mapped property
+ *
+ *  The 3000-3FFF range is further subdivided as follows:
+ *
+ *  From    To      Kind of property
+ *  --------------------------------
+ *  3000    33FF    Common property such as display name, entry ID
+ *  3400    35FF    Message store object
+ *  3600    36FF    Folder or AB container
+ *  3700    38FF    Attachment
+ *  3900    39FF    Address book object
+ *  3A00    3BFF    Mail user
+ *  3C00    3CFF    Distribution list
+ *  3D00    3DFF    Profile section
+ *  3E00    3FFF    Status object
+ *
+ *  Copyright 1986-1996 Microsoft Corporation. All Rights Reserved.
+ */
+
+#ifndef MAPITAGS_H
+#define MAPITAGS_H
+
+/* Determine if a property is transmittable. */
+
+#define FIsTransmittable(ulPropTag) \
+    ((PROP_ID (ulPropTag) <  (ULONG)0x0E00) || \
+    (PROP_ID (ulPropTag)  >= (ULONG)0x8000) || \
+    ((PROP_ID (ulPropTag) >= (ULONG)0x1000) && (PROP_ID (ulPropTag) < (ULONG)0x6000)) || \
+    ((PROP_ID (ulPropTag) >= (ULONG)0x6800) && (PROP_ID (ulPropTag) < (ULONG)0x7C00)))
+
+/*
+ *  Message envelope properties
+ */
+
+#define PR_ACKNOWLEDGEMENT_MODE                     PROP_TAG( PT_LONG,      0x0001)
+#define PR_ALTERNATE_RECIPIENT_ALLOWED              PROP_TAG( PT_BOOLEAN,   0x0002)
+#define PR_AUTHORIZING_USERS                        PROP_TAG( PT_BINARY,    0x0003)
+#define PR_AUTO_FORWARD_COMMENT                     PROP_TAG( PT_TSTRING,   0x0004)
+#define PR_AUTO_FORWARD_COMMENT_W                   PROP_TAG( PT_UNICODE,   0x0004)
+#define PR_AUTO_FORWARD_COMMENT_A                   PROP_TAG( PT_STRING8,   0x0004)
+#define PR_AUTO_FORWARDED                           PROP_TAG( PT_BOOLEAN,   0x0005)
+#define PR_CONTENT_CONFIDENTIALITY_ALGORITHM_ID     PROP_TAG( PT_BINARY,    0x0006)
+#define PR_CONTENT_CORRELATOR                       PROP_TAG( PT_BINARY,    0x0007)
+#define PR_CONTENT_IDENTIFIER                       PROP_TAG( PT_TSTRING,   0x0008)
+#define PR_CONTENT_IDENTIFIER_W                     PROP_TAG( PT_UNICODE,   0x0008)
+#define PR_CONTENT_IDENTIFIER_A                     PROP_TAG( PT_STRING8,   0x0008)
+#define PR_CONTENT_LENGTH                           PROP_TAG( PT_LONG,      0x0009)
+#define PR_CONTENT_RETURN_REQUESTED                 PROP_TAG( PT_BOOLEAN,   0x000A)
+
+
+
+#define PR_CONVERSATION_KEY                         PROP_TAG( PT_BINARY,    0x000B)
+
+#define PR_CONVERSION_EITS                          PROP_TAG( PT_BINARY,    0x000C)
+#define PR_CONVERSION_WITH_LOSS_PROHIBITED          PROP_TAG( PT_BOOLEAN,   0x000D)
+#define PR_CONVERTED_EITS                           PROP_TAG( PT_BINARY,    0x000E)
+#define PR_DEFERRED_DELIVERY_TIME                   PROP_TAG( PT_SYSTIME,   0x000F)
+#define PR_DELIVER_TIME                             PROP_TAG( PT_SYSTIME,   0x0010)
+#define PR_DISCARD_REASON                           PROP_TAG( PT_LONG,      0x0011)
+#define PR_DISCLOSURE_OF_RECIPIENTS                 PROP_TAG( PT_BOOLEAN,   0x0012)
+#define PR_DL_EXPANSION_HISTORY                     PROP_TAG( PT_BINARY,    0x0013)
+#define PR_DL_EXPANSION_PROHIBITED                  PROP_TAG( PT_BOOLEAN,   0x0014)
+#define PR_EXPIRY_TIME                              PROP_TAG( PT_SYSTIME,   0x0015)
+#define PR_IMPLICIT_CONVERSION_PROHIBITED           PROP_TAG( PT_BOOLEAN,   0x0016)
+#define PR_IMPORTANCE                               PROP_TAG( PT_LONG,      0x0017)
+#define PR_IPM_ID                                   PROP_TAG( PT_BINARY,    0x0018)
+#define PR_LATEST_DELIVERY_TIME                     PROP_TAG( PT_SYSTIME,   0x0019)
+#define PR_MESSAGE_CLASS                            PROP_TAG( PT_TSTRING,   0x001A)
+#define PR_MESSAGE_CLASS_W                          PROP_TAG( PT_UNICODE,   0x001A)
+#define PR_MESSAGE_CLASS_A                          PROP_TAG( PT_STRING8,   0x001A)
+#define PR_MESSAGE_DELIVERY_ID                      PROP_TAG( PT_BINARY,    0x001B)
+
+
+
+
+
+#define PR_MESSAGE_SECURITY_LABEL                   PROP_TAG( PT_BINARY,    0x001E)
+#define PR_OBSOLETED_IPMS                           PROP_TAG( PT_BINARY,    0x001F)
+#define PR_ORIGINALLY_INTENDED_RECIPIENT_NAME       PROP_TAG( PT_BINARY,    0x0020)
+#define PR_ORIGINAL_EITS                            PROP_TAG( PT_BINARY,    0x0021)
+#define PR_ORIGINATOR_CERTIFICATE                   PROP_TAG( PT_BINARY,    0x0022)
+#define PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED     PROP_TAG( PT_BOOLEAN,   0x0023)
+#define PR_ORIGINATOR_RETURN_ADDRESS                PROP_TAG( PT_BINARY,    0x0024)
+
+
+
+#define PR_PARENT_KEY                               PROP_TAG( PT_BINARY,    0x0025)
+#define PR_PRIORITY                                 PROP_TAG( PT_LONG,      0x0026)
+
+
+
+#define PR_ORIGIN_CHECK                             PROP_TAG( PT_BINARY,    0x0027)
+#define PR_PROOF_OF_SUBMISSION_REQUESTED            PROP_TAG( PT_BOOLEAN,   0x0028)
+#define PR_READ_RECEIPT_REQUESTED                   PROP_TAG( PT_BOOLEAN,   0x0029)
+#define PR_RECEIPT_TIME                             PROP_TAG( PT_SYSTIME,   0x002A)
+#define PR_RECIPIENT_REASSIGNMENT_PROHIBITED        PROP_TAG( PT_BOOLEAN,   0x002B)
+#define PR_REDIRECTION_HISTORY                      PROP_TAG( PT_BINARY,    0x002C)
+#define PR_RELATED_IPMS                             PROP_TAG( PT_BINARY,    0x002D)
+#define PR_ORIGINAL_SENSITIVITY                     PROP_TAG( PT_LONG,      0x002E)
+#define PR_LANGUAGES                                PROP_TAG( PT_TSTRING,   0x002F)
+#define PR_LANGUAGES_W                              PROP_TAG( PT_UNICODE,   0x002F)
+#define PR_LANGUAGES_A                              PROP_TAG( PT_STRING8,   0x002F)
+#define PR_REPLY_TIME                               PROP_TAG( PT_SYSTIME,   0x0030)
+#define PR_REPORT_TAG                               PROP_TAG( PT_BINARY,    0x0031)
+#define PR_REPORT_TIME                              PROP_TAG( PT_SYSTIME,   0x0032)
+#define PR_RETURNED_IPM                             PROP_TAG( PT_BOOLEAN,   0x0033)
+#define PR_SECURITY                                 PROP_TAG( PT_LONG,      0x0034)
+#define PR_INCOMPLETE_COPY                          PROP_TAG( PT_BOOLEAN,   0x0035)
+#define PR_SENSITIVITY                              PROP_TAG( PT_LONG,      0x0036)
+#define PR_SUBJECT                                  PROP_TAG( PT_TSTRING,   0x0037)
+#define PR_SUBJECT_W                                PROP_TAG( PT_UNICODE,   0x0037)
+#define PR_SUBJECT_A                                PROP_TAG( PT_STRING8,   0x0037)
+#define PR_SUBJECT_IPM                              PROP_TAG( PT_BINARY,    0x0038)
+#define PR_CLIENT_SUBMIT_TIME                       PROP_TAG( PT_SYSTIME,   0x0039)
+#define PR_REPORT_NAME                              PROP_TAG( PT_TSTRING,   0x003A)
+#define PR_REPORT_NAME_W                            PROP_TAG( PT_UNICODE,   0x003A)
+#define PR_REPORT_NAME_A                            PROP_TAG( PT_STRING8,   0x003A)
+#define PR_SENT_REPRESENTING_SEARCH_KEY             PROP_TAG( PT_BINARY,    0x003B)
+#define PR_X400_CONTENT_TYPE                        PROP_TAG( PT_BINARY,    0x003C)
+#define PR_SUBJECT_PREFIX                           PROP_TAG( PT_TSTRING,   0x003D)
+#define PR_SUBJECT_PREFIX_W                         PROP_TAG( PT_UNICODE,   0x003D)
+#define PR_SUBJECT_PREFIX_A                         PROP_TAG( PT_STRING8,   0x003D)
+#define PR_NON_RECEIPT_REASON                       PROP_TAG( PT_LONG,      0x003E)
+#define PR_RECEIVED_BY_ENTRYID                      PROP_TAG( PT_BINARY,    0x003F)
+#define PR_RECEIVED_BY_NAME                         PROP_TAG( PT_TSTRING,   0x0040)
+#define PR_RECEIVED_BY_NAME_W                       PROP_TAG( PT_UNICODE,   0x0040)
+#define PR_RECEIVED_BY_NAME_A                       PROP_TAG( PT_STRING8,   0x0040)
+#define PR_SENT_REPRESENTING_ENTRYID                PROP_TAG( PT_BINARY,    0x0041)
+#define PR_SENT_REPRESENTING_NAME                   PROP_TAG( PT_TSTRING,   0x0042)
+#define PR_SENT_REPRESENTING_NAME_W                 PROP_TAG( PT_UNICODE,   0x0042)
+#define PR_SENT_REPRESENTING_NAME_A                 PROP_TAG( PT_STRING8,   0x0042)
+#define PR_RCVD_REPRESENTING_ENTRYID                PROP_TAG( PT_BINARY,    0x0043)
+#define PR_RCVD_REPRESENTING_NAME                   PROP_TAG( PT_TSTRING,   0x0044)
+#define PR_RCVD_REPRESENTING_NAME_W                 PROP_TAG( PT_UNICODE,   0x0044)
+#define PR_RCVD_REPRESENTING_NAME_A                 PROP_TAG( PT_STRING8,   0x0044)
+#define PR_REPORT_ENTRYID                           PROP_TAG( PT_BINARY,    0x0045)
+#define PR_READ_RECEIPT_ENTRYID                     PROP_TAG( PT_BINARY,    0x0046)
+#define PR_MESSAGE_SUBMISSION_ID                    PROP_TAG( PT_BINARY,    0x0047)
+#define PR_PROVIDER_SUBMIT_TIME                     PROP_TAG( PT_SYSTIME,   0x0048)
+#define PR_ORIGINAL_SUBJECT                         PROP_TAG( PT_TSTRING,   0x0049)
+#define PR_ORIGINAL_SUBJECT_W                       PROP_TAG( PT_UNICODE,   0x0049)
+#define PR_ORIGINAL_SUBJECT_A                       PROP_TAG( PT_STRING8,   0x0049)
+#define PR_DISC_VAL                                 PROP_TAG( PT_BOOLEAN,   0x004A)
+#define PR_ORIG_MESSAGE_CLASS                       PROP_TAG( PT_TSTRING,   0x004B)
+#define PR_ORIG_MESSAGE_CLASS_W                     PROP_TAG( PT_UNICODE,   0x004B)
+#define PR_ORIG_MESSAGE_CLASS_A                     PROP_TAG( PT_STRING8,   0x004B)
+#define PR_ORIGINAL_AUTHOR_ENTRYID                  PROP_TAG( PT_BINARY,    0x004C)
+#define PR_ORIGINAL_AUTHOR_NAME                     PROP_TAG( PT_TSTRING,   0x004D)
+#define PR_ORIGINAL_AUTHOR_NAME_W                   PROP_TAG( PT_UNICODE,   0x004D)
+#define PR_ORIGINAL_AUTHOR_NAME_A                   PROP_TAG( PT_STRING8,   0x004D)
+#define PR_ORIGINAL_SUBMIT_TIME                     PROP_TAG( PT_SYSTIME,   0x004E)
+#define PR_REPLY_RECIPIENT_ENTRIES                  PROP_TAG( PT_BINARY,    0x004F)
+#define PR_REPLY_RECIPIENT_NAMES                    PROP_TAG( PT_TSTRING,   0x0050)
+#define PR_REPLY_RECIPIENT_NAMES_W                  PROP_TAG( PT_UNICODE,   0x0050)
+#define PR_REPLY_RECIPIENT_NAMES_A                  PROP_TAG( PT_STRING8,   0x0050)
+
+#define PR_RECEIVED_BY_SEARCH_KEY                   PROP_TAG( PT_BINARY,    0x0051)
+#define PR_RCVD_REPRESENTING_SEARCH_KEY             PROP_TAG( PT_BINARY,    0x0052)
+#define PR_READ_RECEIPT_SEARCH_KEY                  PROP_TAG( PT_BINARY,    0x0053)
+#define PR_REPORT_SEARCH_KEY                        PROP_TAG( PT_BINARY,    0x0054)
+#define PR_ORIGINAL_DELIVERY_TIME                   PROP_TAG( PT_SYSTIME,   0x0055)
+#define PR_ORIGINAL_AUTHOR_SEARCH_KEY               PROP_TAG( PT_BINARY,    0x0056)
+
+#define PR_MESSAGE_TO_ME                            PROP_TAG( PT_BOOLEAN,   0x0057)
+#define PR_MESSAGE_CC_ME                            PROP_TAG( PT_BOOLEAN,   0x0058)
+#define PR_MESSAGE_RECIP_ME                         PROP_TAG( PT_BOOLEAN,   0x0059)
+
+#define PR_ORIGINAL_SENDER_NAME                     PROP_TAG( PT_TSTRING,   0x005A)
+#define PR_ORIGINAL_SENDER_NAME_W                   PROP_TAG( PT_UNICODE,   0x005A)
+#define PR_ORIGINAL_SENDER_NAME_A                   PROP_TAG( PT_STRING8,   0x005A)
+#define PR_ORIGINAL_SENDER_ENTRYID                  PROP_TAG( PT_BINARY,    0x005B)
+#define PR_ORIGINAL_SENDER_SEARCH_KEY               PROP_TAG( PT_BINARY,    0x005C)
+#define PR_ORIGINAL_SENT_REPRESENTING_NAME          PROP_TAG( PT_TSTRING,   0x005D)
+#define PR_ORIGINAL_SENT_REPRESENTING_NAME_W        PROP_TAG( PT_UNICODE,   0x005D)
+#define PR_ORIGINAL_SENT_REPRESENTING_NAME_A        PROP_TAG( PT_STRING8,   0x005D)
+#define PR_ORIGINAL_SENT_REPRESENTING_ENTRYID       PROP_TAG( PT_BINARY,    0x005E)
+#define PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY    PROP_TAG( PT_BINARY,    0x005F)
+
+#define PR_START_DATE                               PROP_TAG( PT_SYSTIME,   0x0060)
+#define PR_END_DATE                                 PROP_TAG( PT_SYSTIME,   0x0061)
+#define PR_OWNER_APPT_ID                            PROP_TAG( PT_LONG,      0x0062)
+#define PR_RESPONSE_REQUESTED                       PROP_TAG( PT_BOOLEAN,   0x0063)
+
+#define PR_SENT_REPRESENTING_ADDRTYPE               PROP_TAG( PT_TSTRING,   0x0064)
+#define PR_SENT_REPRESENTING_ADDRTYPE_W             PROP_TAG( PT_UNICODE,   0x0064)
+#define PR_SENT_REPRESENTING_ADDRTYPE_A             PROP_TAG( PT_STRING8,   0x0064)
+#define PR_SENT_REPRESENTING_EMAIL_ADDRESS          PROP_TAG( PT_TSTRING,   0x0065)
+#define PR_SENT_REPRESENTING_EMAIL_ADDRESS_W        PROP_TAG( PT_UNICODE,   0x0065)
+#define PR_SENT_REPRESENTING_EMAIL_ADDRESS_A        PROP_TAG( PT_STRING8,   0x0065)
+
+#define PR_ORIGINAL_SENDER_ADDRTYPE                 PROP_TAG( PT_TSTRING,   0x0066)
+#define PR_ORIGINAL_SENDER_ADDRTYPE_W               PROP_TAG( PT_UNICODE,   0x0066)
+#define PR_ORIGINAL_SENDER_ADDRTYPE_A               PROP_TAG( PT_STRING8,   0x0066)
+#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS            PROP_TAG( PT_TSTRING,   0x0067)
+#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS_W          PROP_TAG( PT_UNICODE,   0x0067)
+#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS_A          PROP_TAG( PT_STRING8,   0x0067)
+
+#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE      PROP_TAG( PT_TSTRING,   0x0068)
+#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_W    PROP_TAG( PT_UNICODE,   0x0068)
+#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_A    PROP_TAG( PT_STRING8,   0x0068)
+#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING,   0x0069)
+#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_W   PROP_TAG( PT_UNICODE,   0x0069)
+#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_A   PROP_TAG( PT_STRING8,   0x0069)
+
+#define PR_CONVERSATION_TOPIC                       PROP_TAG( PT_TSTRING,   0x0070)
+#define PR_CONVERSATION_TOPIC_W                     PROP_TAG( PT_UNICODE,   0x0070)
+#define PR_CONVERSATION_TOPIC_A                     PROP_TAG( PT_STRING8,   0x0070)
+#define PR_CONVERSATION_INDEX                       PROP_TAG( PT_BINARY,    0x0071)
+
+#define PR_ORIGINAL_DISPLAY_BCC                     PROP_TAG( PT_TSTRING,   0x0072)
+#define PR_ORIGINAL_DISPLAY_BCC_W                   PROP_TAG( PT_UNICODE,   0x0072)
+#define PR_ORIGINAL_DISPLAY_BCC_A                   PROP_TAG( PT_STRING8,   0x0072)
+#define PR_ORIGINAL_DISPLAY_CC                      PROP_TAG( PT_TSTRING,   0x0073)
+#define PR_ORIGINAL_DISPLAY_CC_W                    PROP_TAG( PT_UNICODE,   0x0073)
+#define PR_ORIGINAL_DISPLAY_CC_A                    PROP_TAG( PT_STRING8,   0x0073)
+#define PR_ORIGINAL_DISPLAY_TO                      PROP_TAG( PT_TSTRING,   0x0074)
+#define PR_ORIGINAL_DISPLAY_TO_W                    PROP_TAG( PT_UNICODE,   0x0074)
+#define PR_ORIGINAL_DISPLAY_TO_A                    PROP_TAG( PT_STRING8,   0x0074)
+
+#define PR_RECEIVED_BY_ADDRTYPE                     PROP_TAG( PT_TSTRING,   0x0075)
+#define PR_RECEIVED_BY_ADDRTYPE_W                   PROP_TAG( PT_UNICODE,   0x0075)
+#define PR_RECEIVED_BY_ADDRTYPE_A                   PROP_TAG( PT_STRING8,   0x0075)
+#define PR_RECEIVED_BY_EMAIL_ADDRESS                PROP_TAG( PT_TSTRING,   0x0076)
+#define PR_RECEIVED_BY_EMAIL_ADDRESS_W              PROP_TAG( PT_UNICODE,   0x0076)
+#define PR_RECEIVED_BY_EMAIL_ADDRESS_A              PROP_TAG( PT_STRING8,   0x0076)
+
+#define PR_RCVD_REPRESENTING_ADDRTYPE               PROP_TAG( PT_TSTRING,   0x0077)
+#define PR_RCVD_REPRESENTING_ADDRTYPE_W             PROP_TAG( PT_UNICODE,   0x0077)
+#define PR_RCVD_REPRESENTING_ADDRTYPE_A             PROP_TAG( PT_STRING8,   0x0077)
+#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS          PROP_TAG( PT_TSTRING,   0x0078)
+#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS_W        PROP_TAG( PT_UNICODE,   0x0078)
+#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS_A        PROP_TAG( PT_STRING8,   0x0078)
+
+#define PR_ORIGINAL_AUTHOR_ADDRTYPE                 PROP_TAG( PT_TSTRING,   0x0079)
+#define PR_ORIGINAL_AUTHOR_ADDRTYPE_W               PROP_TAG( PT_UNICODE,   0x0079)
+#define PR_ORIGINAL_AUTHOR_ADDRTYPE_A               PROP_TAG( PT_STRING8,   0x0079)
+#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS            PROP_TAG( PT_TSTRING,   0x007A)
+#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_W          PROP_TAG( PT_UNICODE,   0x007A)
+#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_A          PROP_TAG( PT_STRING8,   0x007A)
+
+#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE       PROP_TAG( PT_TSTRING,   0x007B)
+#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_W     PROP_TAG( PT_UNICODE,   0x007B)
+#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_A     PROP_TAG( PT_STRING8,   0x007B)
+#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS  PROP_TAG( PT_TSTRING,   0x007C)
+#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_W    PROP_TAG( PT_UNICODE,   0x007C)
+#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_A    PROP_TAG( PT_STRING8,   0x007C)
+
+#define PR_TRANSPORT_MESSAGE_HEADERS                PROP_TAG(PT_TSTRING,    0x007D)
+#define PR_TRANSPORT_MESSAGE_HEADERS_W              PROP_TAG(PT_UNICODE,    0x007D)
+#define PR_TRANSPORT_MESSAGE_HEADERS_A              PROP_TAG(PT_STRING8,    0x007D)
+
+#define PR_DELEGATION                               PROP_TAG(PT_BINARY,     0x007E)
+
+#define PR_TNEF_CORRELATION_KEY                     PROP_TAG(PT_BINARY,     0x007F)
+
+
+
+/*
+ *  Message content properties
+ */
+
+#define PR_BODY                                     PROP_TAG( PT_TSTRING,   0x1000)
+#define PR_BODY_W                                   PROP_TAG( PT_UNICODE,   0x1000)
+#define PR_BODY_A                                   PROP_TAG( PT_STRING8,   0x1000)
+#define PR_REPORT_TEXT                              PROP_TAG( PT_TSTRING,   0x1001)
+#define PR_REPORT_TEXT_W                            PROP_TAG( PT_UNICODE,   0x1001)
+#define PR_REPORT_TEXT_A                            PROP_TAG( PT_STRING8,   0x1001)
+#define PR_ORIGINATOR_AND_DL_EXPANSION_HISTORY      PROP_TAG( PT_BINARY,    0x1002)
+#define PR_REPORTING_DL_NAME                        PROP_TAG( PT_BINARY,    0x1003)
+#define PR_REPORTING_MTA_CERTIFICATE                PROP_TAG( PT_BINARY,    0x1004)
+
+/*  Removed PR_REPORT_ORIGIN_AUTHENTICATION_CHECK with DCR 3865, use PR_ORIGIN_CHECK */
+
+#define PR_RTF_SYNC_BODY_CRC                        PROP_TAG( PT_LONG,      0x1006)
+#define PR_RTF_SYNC_BODY_COUNT                      PROP_TAG( PT_LONG,      0x1007)
+#define PR_RTF_SYNC_BODY_TAG                        PROP_TAG( PT_TSTRING,   0x1008)
+#define PR_RTF_SYNC_BODY_TAG_W                      PROP_TAG( PT_UNICODE,   0x1008)
+#define PR_RTF_SYNC_BODY_TAG_A                      PROP_TAG( PT_STRING8,   0x1008)
+#define PR_RTF_COMPRESSED                           PROP_TAG( PT_BINARY,    0x1009)
+#define PR_RTF_SYNC_PREFIX_COUNT                    PROP_TAG( PT_LONG,      0x1010)
+#define PR_RTF_SYNC_TRAILING_COUNT                  PROP_TAG( PT_LONG,      0x1011)
+#define PR_ORIGINALLY_INTENDED_RECIP_ENTRYID        PROP_TAG( PT_BINARY,    0x1012)
+
+/*
+ *  Reserved 0x1100-0x1200
+ */
+
+
+/*
+ *  Message recipient properties
+ */
+
+#define PR_CONTENT_INTEGRITY_CHECK                  PROP_TAG( PT_BINARY,    0x0C00)
+#define PR_EXPLICIT_CONVERSION                      PROP_TAG( PT_LONG,      0x0C01)
+#define PR_IPM_RETURN_REQUESTED                     PROP_TAG( PT_BOOLEAN,   0x0C02)
+#define PR_MESSAGE_TOKEN                            PROP_TAG( PT_BINARY,    0x0C03)
+#define PR_NDR_REASON_CODE                          PROP_TAG( PT_LONG,      0x0C04)
+#define PR_NDR_DIAG_CODE                            PROP_TAG( PT_LONG,      0x0C05)
+#define PR_NON_RECEIPT_NOTIFICATION_REQUESTED       PROP_TAG( PT_BOOLEAN,   0x0C06)
+#define PR_DELIVERY_POINT                           PROP_TAG( PT_LONG,      0x0C07)
+
+#define PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED PROP_TAG( PT_BOOLEAN,   0x0C08)
+#define PR_ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT PROP_TAG( PT_BINARY,    0x0C09)
+#define PR_PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY    PROP_TAG( PT_BOOLEAN,   0x0C0A)
+#define PR_PHYSICAL_DELIVERY_MODE                   PROP_TAG( PT_LONG,      0x0C0B)
+#define PR_PHYSICAL_DELIVERY_REPORT_REQUEST         PROP_TAG( PT_LONG,      0x0C0C)
+#define PR_PHYSICAL_FORWARDING_ADDRESS              PROP_TAG( PT_BINARY,    0x0C0D)
+#define PR_PHYSICAL_FORWARDING_ADDRESS_REQUESTED    PROP_TAG( PT_BOOLEAN,   0x0C0E)
+#define PR_PHYSICAL_FORWARDING_PROHIBITED           PROP_TAG( PT_BOOLEAN,   0x0C0F)
+#define PR_PHYSICAL_RENDITION_ATTRIBUTES            PROP_TAG( PT_BINARY,    0x0C10)
+#define PR_PROOF_OF_DELIVERY                        PROP_TAG( PT_BINARY,    0x0C11)
+#define PR_PROOF_OF_DELIVERY_REQUESTED              PROP_TAG( PT_BOOLEAN,   0x0C12)
+#define PR_RECIPIENT_CERTIFICATE                    PROP_TAG( PT_BINARY,    0x0C13)
+#define PR_RECIPIENT_NUMBER_FOR_ADVICE              PROP_TAG( PT_TSTRING,   0x0C14)
+#define PR_RECIPIENT_NUMBER_FOR_ADVICE_W            PROP_TAG( PT_UNICODE,   0x0C14)
+#define PR_RECIPIENT_NUMBER_FOR_ADVICE_A            PROP_TAG( PT_STRING8,   0x0C14)
+#define PR_RECIPIENT_TYPE                           PROP_TAG( PT_LONG,      0x0C15)
+#define PR_REGISTERED_MAIL_TYPE                     PROP_TAG( PT_LONG,      0x0C16)
+#define PR_REPLY_REQUESTED                          PROP_TAG( PT_BOOLEAN,   0x0C17)
+#define PR_REQUESTED_DELIVERY_METHOD                PROP_TAG( PT_LONG,      0x0C18)
+#define PR_SENDER_ENTRYID                           PROP_TAG( PT_BINARY,    0x0C19)
+#define PR_SENDER_NAME                              PROP_TAG( PT_TSTRING,   0x0C1A)
+#define PR_SENDER_NAME_W                            PROP_TAG( PT_UNICODE,   0x0C1A)
+#define PR_SENDER_NAME_A                            PROP_TAG( PT_STRING8,   0x0C1A)
+#define PR_SUPPLEMENTARY_INFO                       PROP_TAG( PT_TSTRING,   0x0C1B)
+#define PR_SUPPLEMENTARY_INFO_W                     PROP_TAG( PT_UNICODE,   0x0C1B)
+#define PR_SUPPLEMENTARY_INFO_A                     PROP_TAG( PT_STRING8,   0x0C1B)
+#define PR_TYPE_OF_MTS_USER                         PROP_TAG( PT_LONG,      0x0C1C)
+#define PR_SENDER_SEARCH_KEY                        PROP_TAG( PT_BINARY,    0x0C1D)
+#define PR_SENDER_ADDRTYPE                          PROP_TAG( PT_TSTRING,   0x0C1E)
+#define PR_SENDER_ADDRTYPE_W                        PROP_TAG( PT_UNICODE,   0x0C1E)
+#define PR_SENDER_ADDRTYPE_A                        PROP_TAG( PT_STRING8,   0x0C1E)
+#define PR_SENDER_EMAIL_ADDRESS                     PROP_TAG( PT_TSTRING,   0x0C1F)
+#define PR_SENDER_EMAIL_ADDRESS_W                   PROP_TAG( PT_UNICODE,   0x0C1F)
+#define PR_SENDER_EMAIL_ADDRESS_A                   PROP_TAG( PT_STRING8,   0x0C1F)
+
+/*
+ *  Message non-transmittable properties
+ */
+
+/*
+ * The two tags, PR_MESSAGE_RECIPIENTS and PR_MESSAGE_ATTACHMENTS,
+ * are to be used in the exclude list passed to
+ * IMessage::CopyTo when the caller wants either the recipients or attachments
+ * of the message to not get copied.  It is also used in the ProblemArray
+ * return from IMessage::CopyTo when an error is encountered copying them
+ */
+
+#define PR_CURRENT_VERSION                          PROP_TAG( PT_I8,        0x0E00)
+#define PR_DELETE_AFTER_SUBMIT                      PROP_TAG( PT_BOOLEAN,   0x0E01)
+#define PR_DISPLAY_BCC                              PROP_TAG( PT_TSTRING,   0x0E02)
+#define PR_DISPLAY_BCC_W                            PROP_TAG( PT_UNICODE,   0x0E02)
+#define PR_DISPLAY_BCC_A                            PROP_TAG( PT_STRING8,   0x0E02)
+#define PR_DISPLAY_CC                               PROP_TAG( PT_TSTRING,   0x0E03)
+#define PR_DISPLAY_CC_W                             PROP_TAG( PT_UNICODE,   0x0E03)
+#define PR_DISPLAY_CC_A                             PROP_TAG( PT_STRING8,   0x0E03)
+#define PR_DISPLAY_TO                               PROP_TAG( PT_TSTRING,   0x0E04)
+#define PR_DISPLAY_TO_W                             PROP_TAG( PT_UNICODE,   0x0E04)
+#define PR_DISPLAY_TO_A                             PROP_TAG( PT_STRING8,   0x0E04)
+#define PR_PARENT_DISPLAY                           PROP_TAG( PT_TSTRING,   0x0E05)
+#define PR_PARENT_DISPLAY_W                         PROP_TAG( PT_UNICODE,   0x0E05)
+#define PR_PARENT_DISPLAY_A                         PROP_TAG( PT_STRING8,   0x0E05)
+#define PR_MESSAGE_DELIVERY_TIME                    PROP_TAG( PT_SYSTIME,   0x0E06)
+#define PR_MESSAGE_FLAGS                            PROP_TAG( PT_LONG,      0x0E07)
+#define PR_MESSAGE_SIZE                             PROP_TAG( PT_LONG,      0x0E08)
+#define PR_PARENT_ENTRYID                           PROP_TAG( PT_BINARY,    0x0E09)
+#define PR_SENTMAIL_ENTRYID                         PROP_TAG( PT_BINARY,    0x0E0A)
+#define PR_CORRELATE                                PROP_TAG( PT_BOOLEAN,   0x0E0C)
+#define PR_CORRELATE_MTSID                          PROP_TAG( PT_BINARY,    0x0E0D)
+#define PR_DISCRETE_VALUES                          PROP_TAG( PT_BOOLEAN,   0x0E0E)
+#define PR_RESPONSIBILITY                           PROP_TAG( PT_BOOLEAN,   0x0E0F)
+#define PR_SPOOLER_STATUS                           PROP_TAG( PT_LONG,      0x0E10)
+#define PR_TRANSPORT_STATUS                         PROP_TAG( PT_LONG,      0x0E11)
+#define PR_MESSAGE_RECIPIENTS                       PROP_TAG( PT_OBJECT,    0x0E12)
+#define PR_MESSAGE_ATTACHMENTS                      PROP_TAG( PT_OBJECT,    0x0E13)
+#define PR_SUBMIT_FLAGS                             PROP_TAG( PT_LONG,      0x0E14)
+#define PR_RECIPIENT_STATUS                         PROP_TAG( PT_LONG,      0x0E15)
+#define PR_TRANSPORT_KEY                            PROP_TAG( PT_LONG,      0x0E16)
+#define PR_MSG_STATUS                               PROP_TAG( PT_LONG,      0x0E17)
+#define PR_MESSAGE_DOWNLOAD_TIME                    PROP_TAG( PT_LONG,      0x0E18)
+#define PR_CREATION_VERSION                         PROP_TAG( PT_I8,        0x0E19)
+#define PR_MODIFY_VERSION                           PROP_TAG( PT_I8,        0x0E1A)
+#define PR_HASATTACH                                PROP_TAG( PT_BOOLEAN,   0x0E1B)
+#define PR_BODY_CRC                                 PROP_TAG( PT_LONG,      0x0E1C)
+#define PR_NORMALIZED_SUBJECT                       PROP_TAG( PT_TSTRING,   0x0E1D)
+#define PR_NORMALIZED_SUBJECT_W                     PROP_TAG( PT_UNICODE,   0x0E1D)
+#define PR_NORMALIZED_SUBJECT_A                     PROP_TAG( PT_STRING8,   0x0E1D)
+#define PR_RTF_IN_SYNC                              PROP_TAG( PT_BOOLEAN,   0x0E1F)
+#define PR_ATTACH_SIZE                              PROP_TAG( PT_LONG,      0x0E20)
+#define PR_ATTACH_NUM                               PROP_TAG( PT_LONG,      0x0E21)
+#define PR_PREPROCESS                               PROP_TAG( PT_BOOLEAN,   0x0E22)
+
+/* PR_ORIGINAL_DISPLAY_TO, _CC, and _BCC moved to transmittible range 03/09/95 */
+
+#define PR_ORIGINATING_MTA_CERTIFICATE              PROP_TAG( PT_BINARY,    0x0E25)
+#define PR_PROOF_OF_SUBMISSION                      PROP_TAG( PT_BINARY,    0x0E26)
+
+
+/*
+ * The range of non-message and non-recipient property IDs (0x3000 - 0x3FFF) is
+ * further broken down into ranges to make assigning new property IDs easier.
+ *
+ *  From    To      Kind of property
+ *  --------------------------------
+ *  3000    32FF    MAPI_defined common property
+ *  3200    33FF    MAPI_defined form property
+ *  3400    35FF    MAPI_defined message store property
+ *  3600    36FF    MAPI_defined Folder or AB Container property
+ *  3700    38FF    MAPI_defined attachment property
+ *  3900    39FF    MAPI_defined address book property
+ *  3A00    3BFF    MAPI_defined mailuser property
+ *  3C00    3CFF    MAPI_defined DistList property
+ *  3D00    3DFF    MAPI_defined Profile Section property
+ *  3E00    3EFF    MAPI_defined Status property
+ *  3F00    3FFF    MAPI_defined display table property
+ */
+
+/*
+ *  Properties common to numerous MAPI objects.
+ *
+ *  Those properties that can appear on messages are in the
+ *  non-transmittable range for messages. They start at the high
+ *  end of that range and work down.
+ *
+ *  Properties that never appear on messages are defined in the common
+ *  property range (see above).
+ */
+
+/*
+ * properties that are common to multiple objects (including message objects)
+ * -- these ids are in the non-transmittable range
+ */
+
+#define PR_ENTRYID                                  PROP_TAG( PT_BINARY,    0x0FFF)
+#define PR_OBJECT_TYPE                              PROP_TAG( PT_LONG,      0x0FFE)
+#define PR_ICON                                     PROP_TAG( PT_BINARY,    0x0FFD)
+#define PR_MINI_ICON                                PROP_TAG( PT_BINARY,    0x0FFC)
+#define PR_STORE_ENTRYID                            PROP_TAG( PT_BINARY,    0x0FFB)
+#define PR_STORE_RECORD_KEY                         PROP_TAG( PT_BINARY,    0x0FFA)
+#define PR_RECORD_KEY                               PROP_TAG( PT_BINARY,    0x0FF9)
+#define PR_MAPPING_SIGNATURE                        PROP_TAG( PT_BINARY,    0x0FF8)
+#define PR_ACCESS_LEVEL                             PROP_TAG( PT_LONG,      0x0FF7)
+#define PR_INSTANCE_KEY                             PROP_TAG( PT_BINARY,    0x0FF6)
+#define PR_ROW_TYPE                                 PROP_TAG( PT_LONG,      0x0FF5)
+#define PR_ACCESS                                   PROP_TAG( PT_LONG,      0x0FF4)
+
+/*
+ * properties that are common to multiple objects (usually not including message objects)
+ * -- these ids are in the transmittable range
+ */
+
+#define PR_ROWID                                    PROP_TAG( PT_LONG,      0x3000)
+#define PR_DISPLAY_NAME                             PROP_TAG( PT_TSTRING,   0x3001)
+#define PR_DISPLAY_NAME_W                           PROP_TAG( PT_UNICODE,   0x3001)
+#define PR_DISPLAY_NAME_A                           PROP_TAG( PT_STRING8,   0x3001)
+#define PR_ADDRTYPE                                 PROP_TAG( PT_TSTRING,   0x3002)
+#define PR_ADDRTYPE_W                               PROP_TAG( PT_UNICODE,   0x3002)
+#define PR_ADDRTYPE_A                               PROP_TAG( PT_STRING8,   0x3002)
+#define PR_EMAIL_ADDRESS                            PROP_TAG( PT_TSTRING,   0x3003)
+#define PR_EMAIL_ADDRESS_W                          PROP_TAG( PT_UNICODE,   0x3003)
+#define PR_EMAIL_ADDRESS_A                          PROP_TAG( PT_STRING8,   0x3003)
+#define PR_COMMENT                                  PROP_TAG( PT_TSTRING,   0x3004)
+#define PR_COMMENT_W                                PROP_TAG( PT_UNICODE,   0x3004)
+#define PR_COMMENT_A                                PROP_TAG( PT_STRING8,   0x3004)
+#define PR_DEPTH                                    PROP_TAG( PT_LONG,      0x3005)
+#define PR_PROVIDER_DISPLAY                         PROP_TAG( PT_TSTRING,   0x3006)
+#define PR_PROVIDER_DISPLAY_W                       PROP_TAG( PT_UNICODE,   0x3006)
+#define PR_PROVIDER_DISPLAY_A                       PROP_TAG( PT_STRING8,   0x3006)
+#define PR_CREATION_TIME                            PROP_TAG( PT_SYSTIME,   0x3007)
+#define PR_LAST_MODIFICATION_TIME                   PROP_TAG( PT_SYSTIME,   0x3008)
+#define PR_RESOURCE_FLAGS                           PROP_TAG( PT_LONG,      0x3009)
+#define PR_PROVIDER_DLL_NAME                        PROP_TAG( PT_TSTRING,   0x300A)
+#define PR_PROVIDER_DLL_NAME_W                      PROP_TAG( PT_UNICODE,   0x300A)
+#define PR_PROVIDER_DLL_NAME_A                      PROP_TAG( PT_STRING8,   0x300A)
+#define PR_SEARCH_KEY                               PROP_TAG( PT_BINARY,    0x300B)
+#define PR_PROVIDER_UID                             PROP_TAG( PT_BINARY,    0x300C)
+#define PR_PROVIDER_ORDINAL                         PROP_TAG( PT_LONG,      0x300D)
+
+/*
+ *  MAPI Form properties
+ */
+#define PR_FORM_VERSION                             PROP_TAG(PT_TSTRING,    0x3301)
+#define PR_FORM_VERSION_W                           PROP_TAG(PT_UNICODE,    0x3301)
+#define PR_FORM_VERSION_A                           PROP_TAG(PT_STRING8,    0x3301)
+#define PR_FORM_CLSID                               PROP_TAG(PT_CLSID,      0x3302)
+#define PR_FORM_CONTACT_NAME                        PROP_TAG(PT_TSTRING,    0x3303)
+#define PR_FORM_CONTACT_NAME_W                      PROP_TAG(PT_UNICODE,    0x3303)
+#define PR_FORM_CONTACT_NAME_A                      PROP_TAG(PT_STRING8,    0x3303)
+#define PR_FORM_CATEGORY                            PROP_TAG(PT_TSTRING,    0x3304)
+#define PR_FORM_CATEGORY_W                          PROP_TAG(PT_UNICODE,    0x3304)
+#define PR_FORM_CATEGORY_A                          PROP_TAG(PT_STRING8,    0x3304)
+#define PR_FORM_CATEGORY_SUB                        PROP_TAG(PT_TSTRING,    0x3305)
+#define PR_FORM_CATEGORY_SUB_W                      PROP_TAG(PT_UNICODE,    0x3305)
+#define PR_FORM_CATEGORY_SUB_A                      PROP_TAG(PT_STRING8,    0x3305)
+#define PR_FORM_HOST_MAP                            PROP_TAG(PT_MV_LONG,    0x3306)
+#define PR_FORM_HIDDEN                              PROP_TAG(PT_BOOLEAN,    0x3307)
+#define PR_FORM_DESIGNER_NAME                       PROP_TAG(PT_TSTRING,    0x3308)
+#define PR_FORM_DESIGNER_NAME_W                     PROP_TAG(PT_UNICODE,    0x3308)
+#define PR_FORM_DESIGNER_NAME_A                     PROP_TAG(PT_STRING8,    0x3308)
+#define PR_FORM_DESIGNER_GUID                       PROP_TAG(PT_CLSID,      0x3309)
+#define PR_FORM_MESSAGE_BEHAVIOR                    PROP_TAG(PT_LONG,       0x330A)
+
+/*
+ *  Message store properties
+ */
+
+#define PR_DEFAULT_STORE                            PROP_TAG( PT_BOOLEAN,   0x3400)
+#define PR_STORE_SUPPORT_MASK                       PROP_TAG( PT_LONG,      0x340D)
+#define PR_STORE_STATE                              PROP_TAG( PT_LONG,      0x340E)
+
+#define PR_IPM_SUBTREE_SEARCH_KEY                   PROP_TAG( PT_BINARY,    0x3410)
+#define PR_IPM_OUTBOX_SEARCH_KEY                    PROP_TAG( PT_BINARY,    0x3411)
+#define PR_IPM_WASTEBASKET_SEARCH_KEY               PROP_TAG( PT_BINARY,    0x3412)
+#define PR_IPM_SENTMAIL_SEARCH_KEY                  PROP_TAG( PT_BINARY,    0x3413)
+#define PR_MDB_PROVIDER                             PROP_TAG( PT_BINARY,    0x3414)
+#define PR_RECEIVE_FOLDER_SETTINGS                  PROP_TAG( PT_OBJECT,    0x3415)
+
+#define PR_VALID_FOLDER_MASK                        PROP_TAG( PT_LONG,      0x35DF)
+#define PR_IPM_SUBTREE_ENTRYID                      PROP_TAG( PT_BINARY,    0x35E0)
+
+#define PR_IPM_OUTBOX_ENTRYID                       PROP_TAG( PT_BINARY,    0x35E2)
+#define PR_IPM_WASTEBASKET_ENTRYID                  PROP_TAG( PT_BINARY,    0x35E3)
+#define PR_IPM_SENTMAIL_ENTRYID                     PROP_TAG( PT_BINARY,    0x35E4)
+#define PR_VIEWS_ENTRYID                            PROP_TAG( PT_BINARY,    0x35E5)
+#define PR_COMMON_VIEWS_ENTRYID                     PROP_TAG( PT_BINARY,    0x35E6)
+#define PR_FINDER_ENTRYID                           PROP_TAG( PT_BINARY,    0x35E7)
+
+/* Proptags 0x35E8-0x35FF reserved for folders "guaranteed" by PR_VALID_FOLDER_MASK */
+
+
+/*
+ *  Folder and AB Container properties
+ */
+
+#define PR_CONTAINER_FLAGS                          PROP_TAG( PT_LONG,      0x3600)
+#define PR_FOLDER_TYPE                              PROP_TAG( PT_LONG,      0x3601)
+#define PR_CONTENT_COUNT                            PROP_TAG( PT_LONG,      0x3602)
+#define PR_CONTENT_UNREAD                           PROP_TAG( PT_LONG,      0x3603)
+#define PR_CREATE_TEMPLATES                         PROP_TAG( PT_OBJECT,    0x3604)
+#define PR_DETAILS_TABLE                            PROP_TAG( PT_OBJECT,    0x3605)
+#define PR_SEARCH                                   PROP_TAG( PT_OBJECT,    0x3607)
+#define PR_SELECTABLE                               PROP_TAG( PT_BOOLEAN,   0x3609)
+#define PR_SUBFOLDERS                               PROP_TAG( PT_BOOLEAN,   0x360A)
+#define PR_STATUS                                   PROP_TAG( PT_LONG,      0x360B)
+#define PR_ANR                                      PROP_TAG( PT_TSTRING,   0x360C)
+#define PR_ANR_W                                    PROP_TAG( PT_UNICODE,   0x360C)
+#define PR_ANR_A                                    PROP_TAG( PT_STRING8,   0x360C)
+#define PR_CONTENTS_SORT_ORDER                      PROP_TAG( PT_MV_LONG,   0x360D)
+#define PR_CONTAINER_HIERARCHY                      PROP_TAG( PT_OBJECT,    0x360E)
+#define PR_CONTAINER_CONTENTS                       PROP_TAG( PT_OBJECT,    0x360F)
+#define PR_FOLDER_ASSOCIATED_CONTENTS               PROP_TAG( PT_OBJECT,    0x3610)
+#define PR_DEF_CREATE_DL                            PROP_TAG( PT_BINARY,    0x3611)
+#define PR_DEF_CREATE_MAILUSER                      PROP_TAG( PT_BINARY,    0x3612)
+#define PR_CONTAINER_CLASS                          PROP_TAG( PT_TSTRING,   0x3613)
+#define PR_CONTAINER_CLASS_W                        PROP_TAG( PT_UNICODE,   0x3613)
+#define PR_CONTAINER_CLASS_A                        PROP_TAG( PT_STRING8,   0x3613)
+#define PR_CONTAINER_MODIFY_VERSION                 PROP_TAG( PT_I8,        0x3614)
+#define PR_AB_PROVIDER_ID                           PROP_TAG( PT_BINARY,    0x3615)
+#define PR_DEFAULT_VIEW_ENTRYID                     PROP_TAG( PT_BINARY,    0x3616)
+#define PR_ASSOC_CONTENT_COUNT                      PROP_TAG( PT_LONG,      0x3617)
+
+/* Reserved 0x36C0-0x36FF */
+
+/*
+ *  Attachment properties
+ */
+
+#define PR_ATTACHMENT_X400_PARAMETERS               PROP_TAG( PT_BINARY,    0x3700)
+#define PR_ATTACH_DATA_OBJ                          PROP_TAG( PT_OBJECT,    0x3701)
+#define PR_ATTACH_DATA_BIN                          PROP_TAG( PT_BINARY,    0x3701)
+#define PR_ATTACH_ENCODING                          PROP_TAG( PT_BINARY,    0x3702)
+#define PR_ATTACH_EXTENSION                         PROP_TAG( PT_TSTRING,   0x3703)
+#define PR_ATTACH_EXTENSION_W                       PROP_TAG( PT_UNICODE,   0x3703)
+#define PR_ATTACH_EXTENSION_A                       PROP_TAG( PT_STRING8,   0x3703)
+#define PR_ATTACH_FILENAME                          PROP_TAG( PT_TSTRING,   0x3704)
+#define PR_ATTACH_FILENAME_W                        PROP_TAG( PT_UNICODE,   0x3704)
+#define PR_ATTACH_FILENAME_A                        PROP_TAG( PT_STRING8,   0x3704)
+#define PR_ATTACH_METHOD                            PROP_TAG( PT_LONG,      0x3705)
+#define PR_ATTACH_LONG_FILENAME                     PROP_TAG( PT_TSTRING,   0x3707)
+#define PR_ATTACH_LONG_FILENAME_W                   PROP_TAG( PT_UNICODE,   0x3707)
+#define PR_ATTACH_LONG_FILENAME_A                   PROP_TAG( PT_STRING8,   0x3707)
+#define PR_ATTACH_PATHNAME                          PROP_TAG( PT_TSTRING,   0x3708)
+#define PR_ATTACH_PATHNAME_W                        PROP_TAG( PT_UNICODE,   0x3708)
+#define PR_ATTACH_PATHNAME_A                        PROP_TAG( PT_STRING8,   0x3708)
+#define PR_ATTACH_RENDERING                         PROP_TAG( PT_BINARY,    0x3709)
+#define PR_ATTACH_TAG                               PROP_TAG( PT_BINARY,    0x370A)
+#define PR_RENDERING_POSITION                       PROP_TAG( PT_LONG,      0x370B)
+#define PR_ATTACH_TRANSPORT_NAME                    PROP_TAG( PT_TSTRING,   0x370C)
+#define PR_ATTACH_TRANSPORT_NAME_W                  PROP_TAG( PT_UNICODE,   0x370C)
+#define PR_ATTACH_TRANSPORT_NAME_A                  PROP_TAG( PT_STRING8,   0x370C)
+#define PR_ATTACH_LONG_PATHNAME                     PROP_TAG( PT_TSTRING,   0x370D)
+#define PR_ATTACH_LONG_PATHNAME_W                   PROP_TAG( PT_UNICODE,   0x370D)
+#define PR_ATTACH_LONG_PATHNAME_A                   PROP_TAG( PT_STRING8,   0x370D)
+#define PR_ATTACH_MIME_TAG                          PROP_TAG( PT_TSTRING,   0x370E)
+#define PR_ATTACH_MIME_TAG_W                        PROP_TAG( PT_UNICODE,   0x370E)
+#define PR_ATTACH_MIME_TAG_A                        PROP_TAG( PT_STRING8,   0x370E)
+#define PR_ATTACH_ADDITIONAL_INFO                   PROP_TAG( PT_BINARY,    0x370F)
+
+/*
+ *  AB Object properties
+ */
+
+#define PR_DISPLAY_TYPE                             PROP_TAG( PT_LONG,      0x3900)
+#define PR_TEMPLATEID                               PROP_TAG( PT_BINARY,    0x3902)
+#define PR_PRIMARY_CAPABILITY                       PROP_TAG( PT_BINARY,    0x3904)
+
+
+/*
+ *  Mail user properties
+ */
+#define PR_7BIT_DISPLAY_NAME                        PROP_TAG( PT_STRING8,   0x39FF)
+#define PR_ACCOUNT                                  PROP_TAG( PT_TSTRING,   0x3A00)
+#define PR_ACCOUNT_W                                PROP_TAG( PT_UNICODE,   0x3A00)
+#define PR_ACCOUNT_A                                PROP_TAG( PT_STRING8,   0x3A00)
+#define PR_ALTERNATE_RECIPIENT                      PROP_TAG( PT_BINARY,    0x3A01)
+#define PR_CALLBACK_TELEPHONE_NUMBER                PROP_TAG( PT_TSTRING,   0x3A02)
+#define PR_CALLBACK_TELEPHONE_NUMBER_W              PROP_TAG( PT_UNICODE,   0x3A02)
+#define PR_CALLBACK_TELEPHONE_NUMBER_A              PROP_TAG( PT_STRING8,   0x3A02)
+#define PR_CONVERSION_PROHIBITED                    PROP_TAG( PT_BOOLEAN,   0x3A03)
+#define PR_DISCLOSE_RECIPIENTS                      PROP_TAG( PT_BOOLEAN,   0x3A04)
+#define PR_GENERATION                               PROP_TAG( PT_TSTRING,   0x3A05)
+#define PR_GENERATION_W                             PROP_TAG( PT_UNICODE,   0x3A05)
+#define PR_GENERATION_A                             PROP_TAG( PT_STRING8,   0x3A05)
+#define PR_GIVEN_NAME                               PROP_TAG( PT_TSTRING,   0x3A06)
+#define PR_GIVEN_NAME_W                             PROP_TAG( PT_UNICODE,   0x3A06)
+#define PR_GIVEN_NAME_A                             PROP_TAG( PT_STRING8,   0x3A06)
+#define PR_GOVERNMENT_ID_NUMBER                     PROP_TAG( PT_TSTRING,   0x3A07)
+#define PR_GOVERNMENT_ID_NUMBER_W                   PROP_TAG( PT_UNICODE,   0x3A07)
+#define PR_GOVERNMENT_ID_NUMBER_A                   PROP_TAG( PT_STRING8,   0x3A07)
+#define PR_BUSINESS_TELEPHONE_NUMBER                PROP_TAG( PT_TSTRING,   0x3A08)
+#define PR_BUSINESS_TELEPHONE_NUMBER_W              PROP_TAG( PT_UNICODE,   0x3A08)
+#define PR_BUSINESS_TELEPHONE_NUMBER_A              PROP_TAG( PT_STRING8,   0x3A08)
+#define PR_OFFICE_TELEPHONE_NUMBER                  PR_BUSINESS_TELEPHONE_NUMBER
+#define PR_OFFICE_TELEPHONE_NUMBER_W                PR_BUSINESS_TELEPHONE_NUMBER_W
+#define PR_OFFICE_TELEPHONE_NUMBER_A                PR_BUSINESS_TELEPHONE_NUMBER_A
+#define PR_HOME_TELEPHONE_NUMBER                    PROP_TAG( PT_TSTRING,   0x3A09)
+#define PR_HOME_TELEPHONE_NUMBER_W                  PROP_TAG( PT_UNICODE,   0x3A09)
+#define PR_HOME_TELEPHONE_NUMBER_A                  PROP_TAG( PT_STRING8,   0x3A09)
+#define PR_INITIALS                                 PROP_TAG( PT_TSTRING,   0x3A0A)
+#define PR_INITIALS_W                               PROP_TAG( PT_UNICODE,   0x3A0A)
+#define PR_INITIALS_A                               PROP_TAG( PT_STRING8,   0x3A0A)
+#define PR_KEYWORD                                  PROP_TAG( PT_TSTRING,   0x3A0B)
+#define PR_KEYWORD_W                                PROP_TAG( PT_UNICODE,   0x3A0B)
+#define PR_KEYWORD_A                                PROP_TAG( PT_STRING8,   0x3A0B)
+#define PR_LANGUAGE                                 PROP_TAG( PT_TSTRING,   0x3A0C)
+#define PR_LANGUAGE_W                               PROP_TAG( PT_UNICODE,   0x3A0C)
+#define PR_LANGUAGE_A                               PROP_TAG( PT_STRING8,   0x3A0C)
+#define PR_LOCATION                                 PROP_TAG( PT_TSTRING,   0x3A0D)
+#define PR_LOCATION_W                               PROP_TAG( PT_UNICODE,   0x3A0D)
+#define PR_LOCATION_A                               PROP_TAG( PT_STRING8,   0x3A0D)
+#define PR_MAIL_PERMISSION                          PROP_TAG( PT_BOOLEAN,   0x3A0E)
+#define PR_MHS_COMMON_NAME                          PROP_TAG( PT_TSTRING,   0x3A0F)
+#define PR_MHS_COMMON_NAME_W                        PROP_TAG( PT_UNICODE,   0x3A0F)
+#define PR_MHS_COMMON_NAME_A                        PROP_TAG( PT_STRING8,   0x3A0F)
+#define PR_ORGANIZATIONAL_ID_NUMBER                 PROP_TAG( PT_TSTRING,   0x3A10)
+#define PR_ORGANIZATIONAL_ID_NUMBER_W               PROP_TAG( PT_UNICODE,   0x3A10)
+#define PR_ORGANIZATIONAL_ID_NUMBER_A               PROP_TAG( PT_STRING8,   0x3A10)
+#define PR_SURNAME                                  PROP_TAG( PT_TSTRING,   0x3A11)
+#define PR_SURNAME_W                                PROP_TAG( PT_UNICODE,   0x3A11)
+#define PR_SURNAME_A                                PROP_TAG( PT_STRING8,   0x3A11)
+#define PR_ORIGINAL_ENTRYID                         PROP_TAG( PT_BINARY,    0x3A12)
+#define PR_ORIGINAL_DISPLAY_NAME                    PROP_TAG( PT_TSTRING,   0x3A13)
+#define PR_ORIGINAL_DISPLAY_NAME_W                  PROP_TAG( PT_UNICODE,   0x3A13)
+#define PR_ORIGINAL_DISPLAY_NAME_A                  PROP_TAG( PT_STRING8,   0x3A13)
+#define PR_ORIGINAL_SEARCH_KEY                      PROP_TAG( PT_BINARY,    0x3A14)
+#define PR_POSTAL_ADDRESS                           PROP_TAG( PT_TSTRING,   0x3A15)
+#define PR_POSTAL_ADDRESS_W                         PROP_TAG( PT_UNICODE,   0x3A15)
+#define PR_POSTAL_ADDRESS_A                         PROP_TAG( PT_STRING8,   0x3A15)
+#define PR_COMPANY_NAME                             PROP_TAG( PT_TSTRING,   0x3A16)
+#define PR_COMPANY_NAME_W                           PROP_TAG( PT_UNICODE,   0x3A16)
+#define PR_COMPANY_NAME_A                           PROP_TAG( PT_STRING8,   0x3A16)
+#define PR_TITLE                                    PROP_TAG( PT_TSTRING,   0x3A17)
+#define PR_TITLE_W                                  PROP_TAG( PT_UNICODE,   0x3A17)
+#define PR_TITLE_A                                  PROP_TAG( PT_STRING8,   0x3A17)
+#define PR_DEPARTMENT_NAME                          PROP_TAG( PT_TSTRING,   0x3A18)
+#define PR_DEPARTMENT_NAME_W                        PROP_TAG( PT_UNICODE,   0x3A18)
+#define PR_DEPARTMENT_NAME_A                        PROP_TAG( PT_STRING8,   0x3A18)
+#define PR_OFFICE_LOCATION                          PROP_TAG( PT_TSTRING,   0x3A19)
+#define PR_OFFICE_LOCATION_W                        PROP_TAG( PT_UNICODE,   0x3A19)
+#define PR_OFFICE_LOCATION_A                        PROP_TAG( PT_STRING8,   0x3A19)
+#define PR_PRIMARY_TELEPHONE_NUMBER                 PROP_TAG( PT_TSTRING,   0x3A1A)
+#define PR_PRIMARY_TELEPHONE_NUMBER_W               PROP_TAG( PT_UNICODE,   0x3A1A)
+#define PR_PRIMARY_TELEPHONE_NUMBER_A               PROP_TAG( PT_STRING8,   0x3A1A)
+#define PR_BUSINESS2_TELEPHONE_NUMBER               PROP_TAG( PT_TSTRING,   0x3A1B)
+#define PR_BUSINESS2_TELEPHONE_NUMBER_W             PROP_TAG( PT_UNICODE,   0x3A1B)
+#define PR_BUSINESS2_TELEPHONE_NUMBER_A             PROP_TAG( PT_STRING8,   0x3A1B)
+#define PR_OFFICE2_TELEPHONE_NUMBER                 PR_BUSINESS2_TELEPHONE_NUMBER
+#define PR_OFFICE2_TELEPHONE_NUMBER_W               PR_BUSINESS2_TELEPHONE_NUMBER_W
+#define PR_OFFICE2_TELEPHONE_NUMBER_A               PR_BUSINESS2_TELEPHONE_NUMBER_A
+#define PR_MOBILE_TELEPHONE_NUMBER                  PROP_TAG( PT_TSTRING,   0x3A1C)
+#define PR_MOBILE_TELEPHONE_NUMBER_W                PROP_TAG( PT_UNICODE,   0x3A1C)
+#define PR_MOBILE_TELEPHONE_NUMBER_A                PROP_TAG( PT_STRING8,   0x3A1C)
+#define PR_CELLULAR_TELEPHONE_NUMBER                PR_MOBILE_TELEPHONE_NUMBER
+#define PR_CELLULAR_TELEPHONE_NUMBER_W              PR_MOBILE_TELEPHONE_NUMBER_W
+#define PR_CELLULAR_TELEPHONE_NUMBER_A              PR_MOBILE_TELEPHONE_NUMBER_A
+#define PR_RADIO_TELEPHONE_NUMBER                   PROP_TAG( PT_TSTRING,   0x3A1D)
+#define PR_RADIO_TELEPHONE_NUMBER_W                 PROP_TAG( PT_UNICODE,   0x3A1D)
+#define PR_RADIO_TELEPHONE_NUMBER_A                 PROP_TAG( PT_STRING8,   0x3A1D)
+#define PR_CAR_TELEPHONE_NUMBER                     PROP_TAG( PT_TSTRING,   0x3A1E)
+#define PR_CAR_TELEPHONE_NUMBER_W                   PROP_TAG( PT_UNICODE,   0x3A1E)
+#define PR_CAR_TELEPHONE_NUMBER_A                   PROP_TAG( PT_STRING8,   0x3A1E)
+#define PR_OTHER_TELEPHONE_NUMBER                   PROP_TAG( PT_TSTRING,   0x3A1F)
+#define PR_OTHER_TELEPHONE_NUMBER_W                 PROP_TAG( PT_UNICODE,   0x3A1F)
+#define PR_OTHER_TELEPHONE_NUMBER_A                 PROP_TAG( PT_STRING8,   0x3A1F)
+#define PR_TRANSMITABLE_DISPLAY_NAME                PROP_TAG( PT_TSTRING,   0x3A20)
+#define PR_TRANSMITABLE_DISPLAY_NAME_W              PROP_TAG( PT_UNICODE,   0x3A20)
+#define PR_TRANSMITABLE_DISPLAY_NAME_A              PROP_TAG( PT_STRING8,   0x3A20)
+#define PR_PAGER_TELEPHONE_NUMBER                   PROP_TAG( PT_TSTRING,   0x3A21)
+#define PR_PAGER_TELEPHONE_NUMBER_W                 PROP_TAG( PT_UNICODE,   0x3A21)
+#define PR_PAGER_TELEPHONE_NUMBER_A                 PROP_TAG( PT_STRING8,   0x3A21)
+#define PR_BEEPER_TELEPHONE_NUMBER                  PR_PAGER_TELEPHONE_NUMBER
+#define PR_BEEPER_TELEPHONE_NUMBER_W                PR_PAGER_TELEPHONE_NUMBER_W
+#define PR_BEEPER_TELEPHONE_NUMBER_A                PR_PAGER_TELEPHONE_NUMBER_A
+#define PR_USER_CERTIFICATE                         PROP_TAG( PT_BINARY,    0x3A22)
+#define PR_PRIMARY_FAX_NUMBER                       PROP_TAG( PT_TSTRING,   0x3A23)
+#define PR_PRIMARY_FAX_NUMBER_W                     PROP_TAG( PT_UNICODE,   0x3A23)
+#define PR_PRIMARY_FAX_NUMBER_A                     PROP_TAG( PT_STRING8,   0x3A23)
+#define PR_BUSINESS_FAX_NUMBER                      PROP_TAG( PT_TSTRING,   0x3A24)
+#define PR_BUSINESS_FAX_NUMBER_W                    PROP_TAG( PT_UNICODE,   0x3A24)
+#define PR_BUSINESS_FAX_NUMBER_A                    PROP_TAG( PT_STRING8,   0x3A24)
+#define PR_HOME_FAX_NUMBER                          PROP_TAG( PT_TSTRING,   0x3A25)
+#define PR_HOME_FAX_NUMBER_W                        PROP_TAG( PT_UNICODE,   0x3A25)
+#define PR_HOME_FAX_NUMBER_A                        PROP_TAG( PT_STRING8,   0x3A25)
+#define PR_COUNTRY                                  PROP_TAG( PT_TSTRING,   0x3A26)
+#define PR_COUNTRY_W                                PROP_TAG( PT_UNICODE,   0x3A26)
+#define PR_COUNTRY_A                                PROP_TAG( PT_STRING8,   0x3A26)
+#define PR_BUSINESS_ADDRESS_COUNTRY                 PR_COUNTRY
+#define PR_BUSINESS_ADDRESS_COUNTRY_W               PR_COUNTRY_W
+#define PR_BUSINESS_ADDRESS_COUNTRY_A               PR_COUNTRY_A
+
+#define PR_LOCALITY                                 PROP_TAG( PT_TSTRING,   0x3A27)
+#define PR_LOCALITY_W                               PROP_TAG( PT_UNICODE,   0x3A27)
+#define PR_LOCALITY_A                               PROP_TAG( PT_STRING8,   0x3A27)
+#define PR_BUSINESS_ADDRESS_CITY                    PR_LOCALITY
+#define PR_BUSINESS_ADDRESS_CITY_W                  PR_LOCALITY_W
+#define PR_BUSINESS_ADDRESS_CITY_A                  PR_LOCALITY_A
+
+#define PR_STATE_OR_PROVINCE                        PROP_TAG( PT_TSTRING,   0x3A28)
+#define PR_STATE_OR_PROVINCE_W                      PROP_TAG( PT_UNICODE,   0x3A28)
+#define PR_STATE_OR_PROVINCE_A                      PROP_TAG( PT_STRING8,   0x3A28)
+#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE       PR_STATE_OR_PROVINCE
+#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_W     PR_STATE_OR_PROVINCE_W
+#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A     PR_STATE_OR_PROVINCE_A
+
+#define PR_STREET_ADDRESS                           PROP_TAG( PT_TSTRING,   0x3A29)
+#define PR_STREET_ADDRESS_W                         PROP_TAG( PT_UNICODE,   0x3A29)
+#define PR_STREET_ADDRESS_A                         PROP_TAG( PT_STRING8,   0x3A29)
+#define PR_BUSINESS_ADDRESS_STREET                  PR_STREET_ADDRESS
+#define PR_BUSINESS_ADDRESS_STREET_W                PR_STREET_ADDRESS_W
+#define PR_BUSINESS_ADDRESS_STREET_A                PR_STREET_ADDRESS_A
+
+#define PR_POSTAL_CODE                              PROP_TAG( PT_TSTRING,   0x3A2A)
+#define PR_POSTAL_CODE_W                            PROP_TAG( PT_UNICODE,   0x3A2A)
+#define PR_POSTAL_CODE_A                            PROP_TAG( PT_STRING8,   0x3A2A)
+#define PR_BUSINESS_ADDRESS_POSTAL_CODE             PR_POSTAL_CODE
+#define PR_BUSINESS_ADDRESS_POSTAL_CODE_W           PR_POSTAL_CODE_W
+#define PR_BUSINESS_ADDRESS_POSTAL_CODE_A           PR_POSTAL_CODE_A
+
+
+#define PR_POST_OFFICE_BOX                          PROP_TAG( PT_TSTRING,   0x3A2B)
+#define PR_POST_OFFICE_BOX_W                        PROP_TAG( PT_UNICODE,   0x3A2B)
+#define PR_POST_OFFICE_BOX_A                        PROP_TAG( PT_STRING8,   0x3A2B)
+#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX         PR_POST_OFFICE_BOX
+#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_W       PR_POST_OFFICE_BOX_W
+#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_A       PR_POST_OFFICE_BOX_A
+
+
+#define PR_TELEX_NUMBER                             PROP_TAG( PT_TSTRING,   0x3A2C)
+#define PR_TELEX_NUMBER_W                           PROP_TAG( PT_UNICODE,   0x3A2C)
+#define PR_TELEX_NUMBER_A                           PROP_TAG( PT_STRING8,   0x3A2C)
+#define PR_ISDN_NUMBER                              PROP_TAG( PT_TSTRING,   0x3A2D)
+#define PR_ISDN_NUMBER_W                            PROP_TAG( PT_UNICODE,   0x3A2D)
+#define PR_ISDN_NUMBER_A                            PROP_TAG( PT_STRING8,   0x3A2D)
+#define PR_ASSISTANT_TELEPHONE_NUMBER               PROP_TAG( PT_TSTRING,   0x3A2E)
+#define PR_ASSISTANT_TELEPHONE_NUMBER_W             PROP_TAG( PT_UNICODE,   0x3A2E)
+#define PR_ASSISTANT_TELEPHONE_NUMBER_A             PROP_TAG( PT_STRING8,   0x3A2E)
+#define PR_HOME2_TELEPHONE_NUMBER                   PROP_TAG( PT_TSTRING,   0x3A2F)
+#define PR_HOME2_TELEPHONE_NUMBER_W                 PROP_TAG( PT_UNICODE,   0x3A2F)
+#define PR_HOME2_TELEPHONE_NUMBER_A                 PROP_TAG( PT_STRING8,   0x3A2F)
+#define PR_ASSISTANT                                PROP_TAG( PT_TSTRING,   0x3A30)
+#define PR_ASSISTANT_W                              PROP_TAG( PT_UNICODE,   0x3A30)
+#define PR_ASSISTANT_A                              PROP_TAG( PT_STRING8,   0x3A30)
+#define PR_SEND_RICH_INFO                           PROP_TAG( PT_BOOLEAN,   0x3A40)
+
+#define PR_WEDDING_ANNIVERSARY                      PROP_TAG( PT_SYSTIME, 0x3A41)
+#define PR_BIRTHDAY                                 PROP_TAG( PT_SYSTIME, 0x3A42)
+
+
+#define PR_HOBBIES                                  PROP_TAG( PT_TSTRING, 0x3A43)
+#define PR_HOBBIES_W                                PROP_TAG( PT_UNICODE, 0x3A43)
+#define PR_HOBBIES_A                                PROP_TAG( PT_STRING8, 0x3A43)
+
+#define PR_MIDDLE_NAME                              PROP_TAG( PT_TSTRING, 0x3A44)
+#define PR_MIDDLE_NAME_W                            PROP_TAG( PT_UNICODE, 0x3A44)
+#define PR_MIDDLE_NAME_A                            PROP_TAG( PT_STRING8, 0x3A44)
+
+#define PR_DISPLAY_NAME_PREFIX                      PROP_TAG( PT_TSTRING, 0x3A45)
+#define PR_DISPLAY_NAME_PREFIX_W                    PROP_TAG( PT_UNICODE, 0x3A45)
+#define PR_DISPLAY_NAME_PREFIX_A                    PROP_TAG( PT_STRING8, 0x3A45)
+
+#define PR_PROFESSION                               PROP_TAG( PT_TSTRING, 0x3A46)
+#define PR_PROFESSION_W                             PROP_TAG( PT_UNICODE, 0x3A46)
+#define PR_PROFESSION_A                             PROP_TAG( PT_STRING8, 0x3A46)
+
+#define PR_PREFERRED_BY_NAME                        PROP_TAG( PT_TSTRING, 0x3A47)
+#define PR_PREFERRED_BY_NAME_W                      PROP_TAG( PT_UNICODE, 0x3A47)
+#define PR_PREFERRED_BY_NAME_A                      PROP_TAG( PT_STRING8, 0x3A47)
+
+#define PR_SPOUSE_NAME                              PROP_TAG( PT_TSTRING, 0x3A48)
+#define PR_SPOUSE_NAME_W                            PROP_TAG( PT_UNICODE, 0x3A48)
+#define PR_SPOUSE_NAME_A                            PROP_TAG( PT_STRING8, 0x3A48)
+
+#define PR_COMPUTER_NETWORK_NAME                    PROP_TAG( PT_TSTRING, 0x3A49)
+#define PR_COMPUTER_NETWORK_NAME_W                  PROP_TAG( PT_UNICODE, 0x3A49)
+#define PR_COMPUTER_NETWORK_NAME_A                  PROP_TAG( PT_STRING8, 0x3A49)
+
+#define PR_CUSTOMER_ID                              PROP_TAG( PT_TSTRING, 0x3A4A)
+#define PR_CUSTOMER_ID_W                            PROP_TAG( PT_UNICODE, 0x3A4A)
+#define PR_CUSTOMER_ID_A                            PROP_TAG( PT_STRING8, 0x3A4A)
+
+#define PR_TTYTDD_PHONE_NUMBER                      PROP_TAG( PT_TSTRING, 0x3A4B)
+#define PR_TTYTDD_PHONE_NUMBER_W                    PROP_TAG( PT_UNICODE, 0x3A4B)
+#define PR_TTYTDD_PHONE_NUMBER_A                    PROP_TAG( PT_STRING8, 0x3A4B)
+
+#define PR_FTP_SITE                                 PROP_TAG( PT_TSTRING, 0x3A4C)
+#define PR_FTP_SITE_W                               PROP_TAG( PT_UNICODE, 0x3A4C)
+#define PR_FTP_SITE_A                               PROP_TAG( PT_STRING8, 0x3A4C)
+
+#define PR_GENDER                                   PROP_TAG( PT_SHORT, 0x3A4D)
+
+#define PR_MANAGER_NAME                             PROP_TAG( PT_TSTRING, 0x3A4E)
+#define PR_MANAGER_NAME_W                           PROP_TAG( PT_UNICODE, 0x3A4E)
+#define PR_MANAGER_NAME_A                           PROP_TAG( PT_STRING8, 0x3A4E)
+
+#define PR_NICKNAME                                 PROP_TAG( PT_TSTRING, 0x3A4F)
+#define PR_NICKNAME_W                               PROP_TAG( PT_UNICODE, 0x3A4F)
+#define PR_NICKNAME_A                               PROP_TAG( PT_STRING8, 0x3A4F)
+
+#define PR_PERSONAL_HOME_PAGE                       PROP_TAG( PT_TSTRING, 0x3A50)
+#define PR_PERSONAL_HOME_PAGE_W                     PROP_TAG( PT_UNICODE, 0x3A50)
+#define PR_PERSONAL_HOME_PAGE_A                     PROP_TAG( PT_STRING8, 0x3A50)
+
+
+#define PR_BUSINESS_HOME_PAGE                       PROP_TAG( PT_TSTRING, 0x3A51)
+#define PR_BUSINESS_HOME_PAGE_W                     PROP_TAG( PT_UNICODE, 0x3A51)
+#define PR_BUSINESS_HOME_PAGE_A                     PROP_TAG( PT_STRING8, 0x3A51)
+
+#define PR_CONTACT_VERSION                          PROP_TAG( PT_CLSID, 0x3A52)
+#define PR_CONTACT_ENTRYIDS                         PROP_TAG( PT_MV_BINARY, 0x3A53)
+
+#define PR_CONTACT_ADDRTYPES                        PROP_TAG( PT_MV_TSTRING, 0x3A54)
+#define PR_CONTACT_ADDRTYPES_W                      PROP_TAG( PT_MV_UNICODE, 0x3A54)
+#define PR_CONTACT_ADDRTYPES_A                      PROP_TAG( PT_MV_STRING8, 0x3A54)
+
+#define PR_CONTACT_DEFAULT_ADDRESS_INDEX            PROP_TAG( PT_LONG, 0x3A55)
+
+#define PR_CONTACT_EMAIL_ADDRESSES                  PROP_TAG( PT_MV_TSTRING, 0x3A56)
+#define PR_CONTACT_EMAIL_ADDRESSES_W                PROP_TAG( PT_MV_UNICODE, 0x3A56)
+#define PR_CONTACT_EMAIL_ADDRESSES_A                PROP_TAG( PT_MV_STRING8, 0x3A56)
+
+
+#define PR_COMPANY_MAIN_PHONE_NUMBER                PROP_TAG( PT_TSTRING, 0x3A57)
+#define PR_COMPANY_MAIN_PHONE_NUMBER_W              PROP_TAG( PT_UNICODE, 0x3A57)
+#define PR_COMPANY_MAIN_PHONE_NUMBER_A              PROP_TAG( PT_STRING8, 0x3A57)
+
+#define PR_CHILDRENS_NAMES                          PROP_TAG( PT_MV_TSTRING, 0x3A58)
+#define PR_CHILDRENS_NAMES_W                        PROP_TAG( PT_MV_UNICODE, 0x3A58)
+#define PR_CHILDRENS_NAMES_A                        PROP_TAG( PT_MV_STRING8, 0x3A58)
+
+
+
+#define PR_HOME_ADDRESS_CITY                        PROP_TAG( PT_TSTRING, 0x3A59)
+#define PR_HOME_ADDRESS_CITY_W                      PROP_TAG( PT_UNICODE, 0x3A59)
+#define PR_HOME_ADDRESS_CITY_A                      PROP_TAG( PT_STRING8, 0x3A59)
+
+#define PR_HOME_ADDRESS_COUNTRY                     PROP_TAG( PT_TSTRING, 0x3A5A)
+#define PR_HOME_ADDRESS_COUNTRY_W                   PROP_TAG( PT_UNICODE, 0x3A5A)
+#define PR_HOME_ADDRESS_COUNTRY_A                   PROP_TAG( PT_STRING8, 0x3A5A)
+
+#define PR_HOME_ADDRESS_POSTAL_CODE                 PROP_TAG( PT_TSTRING, 0x3A5B)
+#define PR_HOME_ADDRESS_POSTAL_CODE_W               PROP_TAG( PT_UNICODE, 0x3A5B)
+#define PR_HOME_ADDRESS_POSTAL_CODE_A               PROP_TAG( PT_STRING8, 0x3A5B)
+
+#define PR_HOME_ADDRESS_STATE_OR_PROVINCE           PROP_TAG( PT_TSTRING, 0x3A5C)
+#define PR_HOME_ADDRESS_STATE_OR_PROVINCE_W         PROP_TAG( PT_UNICODE, 0x3A5C)
+#define PR_HOME_ADDRESS_STATE_OR_PROVINCE_A         PROP_TAG( PT_STRING8, 0x3A5C)
+
+#define PR_HOME_ADDRESS_STREET                      PROP_TAG( PT_TSTRING, 0x3A5D)
+#define PR_HOME_ADDRESS_STREET_W                    PROP_TAG( PT_UNICODE, 0x3A5D)
+#define PR_HOME_ADDRESS_STREET_A                    PROP_TAG( PT_STRING8, 0x3A5D)
+
+#define PR_HOME_ADDRESS_POST_OFFICE_BOX             PROP_TAG( PT_TSTRING, 0x3A5E)
+#define PR_HOME_ADDRESS_POST_OFFICE_BOX_W           PROP_TAG( PT_UNICODE, 0x3A5E)
+#define PR_HOME_ADDRESS_POST_OFFICE_BOX_A           PROP_TAG( PT_STRING8, 0x3A5E)
+
+#define PR_OTHER_ADDRESS_CITY                       PROP_TAG( PT_TSTRING, 0x3A5F)
+#define PR_OTHER_ADDRESS_CITY_W                     PROP_TAG( PT_UNICODE, 0x3A5F)
+#define PR_OTHER_ADDRESS_CITY_A                     PROP_TAG( PT_STRING8, 0x3A5F)
+
+#define PR_OTHER_ADDRESS_COUNTRY                    PROP_TAG( PT_TSTRING, 0x3A60)
+#define PR_OTHER_ADDRESS_COUNTRY_W                  PROP_TAG( PT_UNICODE, 0x3A60)
+#define PR_OTHER_ADDRESS_COUNTRY_A                  PROP_TAG( PT_STRING8, 0x3A60)
+
+#define PR_OTHER_ADDRESS_POSTAL_CODE                PROP_TAG( PT_TSTRING, 0x3A61)
+#define PR_OTHER_ADDRESS_POSTAL_CODE_W              PROP_TAG( PT_UNICODE, 0x3A61)
+#define PR_OTHER_ADDRESS_POSTAL_CODE_A              PROP_TAG( PT_STRING8, 0x3A61)
+
+#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE          PROP_TAG( PT_TSTRING, 0x3A62)
+#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE_W        PROP_TAG( PT_UNICODE, 0x3A62)
+#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE_A        PROP_TAG( PT_STRING8, 0x3A62)
+
+#define PR_OTHER_ADDRESS_STREET                     PROP_TAG( PT_TSTRING, 0x3A63)
+#define PR_OTHER_ADDRESS_STREET_W                   PROP_TAG( PT_UNICODE, 0x3A63)
+#define PR_OTHER_ADDRESS_STREET_A                   PROP_TAG( PT_STRING8, 0x3A63)
+
+#define PR_OTHER_ADDRESS_POST_OFFICE_BOX            PROP_TAG( PT_TSTRING, 0x3A64)
+#define PR_OTHER_ADDRESS_POST_OFFICE_BOX_W          PROP_TAG( PT_UNICODE, 0x3A64)
+#define PR_OTHER_ADDRESS_POST_OFFICE_BOX_A          PROP_TAG( PT_STRING8, 0x3A64)
+
+
+/*
+ *  Profile section properties
+ */
+
+#define PR_STORE_PROVIDERS                          PROP_TAG( PT_BINARY,    0x3D00)
+#define PR_AB_PROVIDERS                             PROP_TAG( PT_BINARY,    0x3D01)
+#define PR_TRANSPORT_PROVIDERS                      PROP_TAG( PT_BINARY,    0x3D02)
+
+#define PR_DEFAULT_PROFILE                          PROP_TAG( PT_BOOLEAN,   0x3D04)
+#define PR_AB_SEARCH_PATH                           PROP_TAG( PT_MV_BINARY, 0x3D05)
+#define PR_AB_DEFAULT_DIR                           PROP_TAG( PT_BINARY,    0x3D06)
+#define PR_AB_DEFAULT_PAB                           PROP_TAG( PT_BINARY,    0x3D07)
+
+#define PR_FILTERING_HOOKS                          PROP_TAG( PT_BINARY,    0x3D08)
+#define PR_SERVICE_NAME                             PROP_TAG( PT_TSTRING,   0x3D09)
+#define PR_SERVICE_NAME_W                           PROP_TAG( PT_UNICODE,   0x3D09)
+#define PR_SERVICE_NAME_A                           PROP_TAG( PT_STRING8,   0x3D09)
+#define PR_SERVICE_DLL_NAME                         PROP_TAG( PT_TSTRING,   0x3D0A)
+#define PR_SERVICE_DLL_NAME_W                       PROP_TAG( PT_UNICODE,   0x3D0A)
+#define PR_SERVICE_DLL_NAME_A                       PROP_TAG( PT_STRING8,   0x3D0A)
+#define PR_SERVICE_ENTRY_NAME                       PROP_TAG( PT_STRING8,   0x3D0B)
+#define PR_SERVICE_UID                              PROP_TAG( PT_BINARY,    0x3D0C)
+#define PR_SERVICE_EXTRA_UIDS                       PROP_TAG( PT_BINARY,    0x3D0D)
+#define PR_SERVICES                                 PROP_TAG( PT_BINARY,    0x3D0E)
+#define PR_SERVICE_SUPPORT_FILES                    PROP_TAG( PT_MV_TSTRING, 0x3D0F)
+#define PR_SERVICE_SUPPORT_FILES_W                  PROP_TAG( PT_MV_UNICODE, 0x3D0F)
+#define PR_SERVICE_SUPPORT_FILES_A                  PROP_TAG( PT_MV_STRING8, 0x3D0F)
+#define PR_SERVICE_DELETE_FILES                     PROP_TAG( PT_MV_TSTRING, 0x3D10)
+#define PR_SERVICE_DELETE_FILES_W                   PROP_TAG( PT_MV_UNICODE, 0x3D10)
+#define PR_SERVICE_DELETE_FILES_A                   PROP_TAG( PT_MV_STRING8, 0x3D10)
+#define PR_AB_SEARCH_PATH_UPDATE                    PROP_TAG( PT_BINARY,     0x3D11)
+#define PR_PROFILE_NAME                             PROP_TAG( PT_TSTRING,   0x3D12)
+#define PR_PROFILE_NAME_A                           PROP_TAG( PT_STRING8,   0x3D12)
+#define PR_PROFILE_NAME_W                           PROP_TAG( PT_UNICODE,   0x3D12)
+
+/*
+ *  Status object properties
+ */
+
+#define PR_IDENTITY_DISPLAY                         PROP_TAG( PT_TSTRING,   0x3E00)
+#define PR_IDENTITY_DISPLAY_W                       PROP_TAG( PT_UNICODE,   0x3E00)
+#define PR_IDENTITY_DISPLAY_A                       PROP_TAG( PT_STRING8,   0x3E00)
+#define PR_IDENTITY_ENTRYID                         PROP_TAG( PT_BINARY,    0x3E01)
+#define PR_RESOURCE_METHODS                         PROP_TAG( PT_LONG,      0x3E02)
+#define PR_RESOURCE_TYPE                            PROP_TAG( PT_LONG,      0x3E03)
+#define PR_STATUS_CODE                              PROP_TAG( PT_LONG,      0x3E04)
+#define PR_IDENTITY_SEARCH_KEY                      PROP_TAG( PT_BINARY,    0x3E05)
+#define PR_OWN_STORE_ENTRYID                        PROP_TAG( PT_BINARY,    0x3E06)
+#define PR_RESOURCE_PATH                            PROP_TAG( PT_TSTRING,   0x3E07)
+#define PR_RESOURCE_PATH_W                          PROP_TAG( PT_UNICODE,   0x3E07)
+#define PR_RESOURCE_PATH_A                          PROP_TAG( PT_STRING8,   0x3E07)
+#define PR_STATUS_STRING                            PROP_TAG( PT_TSTRING,   0x3E08)
+#define PR_STATUS_STRING_W                          PROP_TAG( PT_UNICODE,   0x3E08)
+#define PR_STATUS_STRING_A                          PROP_TAG( PT_STRING8,   0x3E08)
+#define PR_X400_DEFERRED_DELIVERY_CANCEL            PROP_TAG( PT_BOOLEAN,   0x3E09)
+#define PR_HEADER_FOLDER_ENTRYID                    PROP_TAG( PT_BINARY,    0x3E0A)
+#define PR_REMOTE_PROGRESS                          PROP_TAG( PT_LONG,      0x3E0B)
+#define PR_REMOTE_PROGRESS_TEXT                     PROP_TAG( PT_TSTRING,   0x3E0C)
+#define PR_REMOTE_PROGRESS_TEXT_W                   PROP_TAG( PT_UNICODE,   0x3E0C)
+#define PR_REMOTE_PROGRESS_TEXT_A                   PROP_TAG( PT_STRING8,   0x3E0C)
+#define PR_REMOTE_VALIDATE_OK                       PROP_TAG( PT_BOOLEAN,   0x3E0D)
+
+/*
+ * Display table properties
+ */
+
+#define PR_CONTROL_FLAGS                            PROP_TAG( PT_LONG,      0x3F00)
+#define PR_CONTROL_STRUCTURE                        PROP_TAG( PT_BINARY,    0x3F01)
+#define PR_CONTROL_TYPE                             PROP_TAG( PT_LONG,      0x3F02)
+#define PR_DELTAX                                   PROP_TAG( PT_LONG,      0x3F03)
+#define PR_DELTAY                                   PROP_TAG( PT_LONG,      0x3F04)
+#define PR_XPOS                                     PROP_TAG( PT_LONG,      0x3F05)
+#define PR_YPOS                                     PROP_TAG( PT_LONG,      0x3F06)
+#define PR_CONTROL_ID                               PROP_TAG( PT_BINARY,    0x3F07)
+#define PR_INITIAL_DETAILS_PANE                     PROP_TAG( PT_LONG,      0x3F08)
+
+/*
+ * Secure property id range
+ */
+
+#define PROP_ID_SECURE_MIN                          0x67F0
+#define PROP_ID_SECURE_MAX                          0x67FF
+
+
+#endif  /* MAPITAGS_H */
diff -urN exim-4.44-orig/src/version.c exim-4.44/src/version.c
--- exim-4.44-orig/src/version.c	Tue Jan 11 14:12:44 2005
+++ exim-4.44/src/version.c	Fri Jan 14 20:46:24 2005
@@ -11,6 +11,7 @@
 
 
 #define THIS_VERSION  "4.44"
+#define EXISCAN_VERSION "28"
 
 
 /* The header file cnumber.h contains a single line containing the
@@ -40,6 +41,7 @@
 version_cnumber_format = US"%d\0<<eximcnumber>>";
 sprintf(CS version_cnumber, CS version_cnumber_format, cnumber);
 version_string = US THIS_VERSION "\0<<eximversion>>";
+exiscan_version_string = US EXISCAN_VERSION;
 
 Ustrcpy(today, __DATE__);
 if (today[4] == ' ') today[4] = '0';
