diff -urN exim-4.34-orig/OS/Makefile-Base exim-4.34/OS/Makefile-Base
--- exim-4.34-orig/OS/Makefile-Base	Mon May 10 14:31:20 2004
+++ exim-4.34/OS/Makefile-Base	Mon May 10 16:14:47 2004
@@ -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 spool_in.o spool_mbox.o spool_out.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 \
@@ -498,12 +498,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
@@ -517,7 +519,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
@@ -525,6 +529,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
@@ -533,11 +538,14 @@
 sieve.o:         $(HDRS) sieve.c
 smtp_in.o:       $(HDRS) smtp_in.c
 smtp_out.o:      $(HDRS) smtp_out.c
+spam.o:          $(HDRS) spam.c
 spool_in.o:      $(HDRS) spool_in.c
+spool_mbox.o:    $(HDRS) spool_mbox.c
 spool_out.o:     $(HDRS) spool_out.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.34-orig/README.EXISCAN exim-4.34/README.EXISCAN
--- exim-4.34-orig/README.EXISCAN	Thu Jan  1 01:00:00 1970
+++ exim-4.34/README.EXISCAN	Mon May 10 16:14:47 2004
@@ -0,0 +1 @@
+Please refer to doc/exiscan-acl-spec.txt
diff -urN exim-4.34-orig/doc/exiscan-acl-examples.txt exim-4.34/doc/exiscan-acl-examples.txt
--- exim-4.34-orig/doc/exiscan-acl-examples.txt	Thu Jan  1 01:00:00 1970
+++ exim-4.34/doc/exiscan-acl-examples.txt	Mon May 10 16:14:47 2004
@@ -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.34-orig/doc/exiscan-acl-spec.txt exim-4.34/doc/exiscan-acl-spec.txt
--- exim-4.34-orig/doc/exiscan-acl-spec.txt	Thu Jan  1 01:00:00 1970
+++ exim-4.34/doc/exiscan-acl-spec.txt	Mon May 10 16:14:47 2004
@@ -0,0 +1,1072 @@
+--------------------------------------------------------------
+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.
+ - 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)
+- 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
+
+
+  kavdaemon   Kapersky's kavdaemon  is a  daemon-type scanner.
+              You    can    get    a    trial    version    at
+              http://www.kapersky.com. 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
+              
+              
+  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
+---------------------- */
+
+
+
+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.
+  
+  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.
+
+
+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.
+  
+--------------------------------------------------------------
+End of file
+--------------------------------------------------------------
diff -urN exim-4.34-orig/exim_monitor/em_globals.c exim-4.34/exim_monitor/em_globals.c
--- exim-4.34-orig/exim_monitor/em_globals.c	Mon May 10 14:31:20 2004
+++ exim-4.34/exim_monitor/em_globals.c	Mon May 10 16:14:47 2004
@@ -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"
@@ -127,6 +131,8 @@
 BOOL    deliver_manual_thaw    = FALSE;
 BOOL    dont_deliver           = FALSE;
 
+BOOL	fake_reject            = FALSE;
+
 header_line *header_last       = NULL;
 header_line *header_list       = NULL;
 
@@ -136,6 +142,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.34-orig/scripts/MakeLinks exim-4.34/scripts/MakeLinks
--- exim-4.34-orig/scripts/MakeLinks	Mon May 10 14:31:20 2004
+++ exim-4.34/scripts/MakeLinks	Mon May 10 16:14:47 2004
@@ -170,19 +170,25 @@
 # 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/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
@@ -190,6 +196,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
@@ -208,7 +215,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
@@ -216,6 +225,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
@@ -224,13 +234,16 @@
 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/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/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.34-orig/src/acl.c exim-4.34/src/acl.c
--- exim-4.34-orig/src/acl.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/acl.c	Mon May 10 16:14:47 2004
@@ -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,26 @@
 /* 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, 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", 
   US"verify" };
 
 /* Flags to indicate for which conditions /modifiers a string expansion is done
@@ -54,9 +66,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 */
@@ -65,11 +82,15 @@
   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 */
   TRUE     /* verify */
 };
 
@@ -78,9 +99,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 */
@@ -89,11 +115,15 @@
   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 */
   FALSE    /* verify */
 };
 
@@ -102,8 +132,20 @@
 
 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),                                                 
+#endif
+    
   0,                                               /* condition */
 
   (1<<ACL_WHERE_AUTH)|(1<<ACL_WHERE_CONNECT)|      /* control */
@@ -112,12 +154,29 @@
     (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
     (1<<ACL_WHERE_STARTTLS)|(ACL_WHERE_VRFY),
 
+  (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),
+
   0,                                               /* delay */
+
+  (1<<ACL_WHERE_NOTSMTP)|(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_NOTSMTP),                          /* dnslists */
 
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* domains */
     (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_DATA)|
+    (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)|
@@ -125,29 +184,56 @@
 
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_CONNECT)|   /* encrypted */
     (1<<ACL_WHERE_HELO),
+    
   0,                                               /* endpass */
+  
   (1<<ACL_WHERE_NOTSMTP),                          /* hosts */
 
   (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* local_parts */
     (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_DATA)|
+    (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),
 
   0,                                               /* log_message */
+  
   0,                                               /* logwrite */
+
+  (1<<ACL_WHERE_NOTSMTP)|(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),
+  
   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_NOTSMTP)|(1<<ACL_WHERE_AUTH)|      /* recipients */
     (1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
-    (1<<ACL_WHERE_DATA)|
+    (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_NOTSMTP)|(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_AUTH)|(1<<ACL_WHERE_CONNECT)|      /* sender_domains */
     (1<<ACL_WHERE_HELO)|
     (1<<ACL_WHERE_MAILAUTH)|
@@ -162,6 +248,14 @@
 
   0,                                               /* set */
 
+  (1<<ACL_WHERE_NOTSMTP)|(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),
+
   /* Certain types of verify are always allowed, so we let it through
   always and check in the verify function itself */
 
@@ -447,7 +541,7 @@
 /* If this isn't a message ACL, we can't do anything with a user message.
 Log an error. */
 
-if (where != ACL_WHERE_MAIL && where != ACL_WHERE_RCPT &&
+if (where != ACL_WHERE_MAIL && where != ACL_WHERE_MIME && where != ACL_WHERE_RCPT &&
     where != ACL_WHERE_DATA && where != ACL_WHERE_NOTSMTP)
   {
   log_write(0, LOG_MAIN|LOG_PANIC, "ACL \"warn\" with \"message\" setting "
@@ -1005,6 +1099,7 @@
 uschar *user_message = NULL;
 uschar *log_message = NULL;
 int rc = OK;
+int sep = '/';
 
 for (; cb != NULL; cb = cb->next)
   {
@@ -1136,6 +1231,20 @@
       deliver_freeze = TRUE;
       deliver_frozen_at = time(NULL);
       }
+    else if (Ustrcmp(arg, "fakereject") == 0)
+      {
+      fake_reject = TRUE;
+      }
+#ifdef BRIGHTMAIL
+    else if (Ustrcmp(arg, "bmi_run") == 0)
+      {
+      bmi_run = 1;
+      }
+#endif
+    else if (Ustrcmp(arg, "no_mbox_unspool") == 0)
+      {
+      no_mbox_unspool = TRUE;
+      }
     else if (Ustrcmp(arg, "queue_only") == 0)
       {
       queue_only_policy = TRUE;
@@ -1167,7 +1276,70 @@
     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;
+
+    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:
@@ -1836,6 +2008,7 @@
   if (where != ACL_WHERE_MAIL &&
       where != ACL_WHERE_RCPT &&
       where != ACL_WHERE_DATA &&
+      where != ACL_WHERE_MIME &&
       where != ACL_WHERE_NOTSMTP)
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "\"discard\" verb not allowed in %s "
diff -urN exim-4.34-orig/src/bmi_spam.c exim-4.34/src/bmi_spam.c
--- exim-4.34-orig/src/bmi_spam.c	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/bmi_spam.c	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/bmi_spam.h exim-4.34/src/bmi_spam.h
--- exim-4.34-orig/src/bmi_spam.h	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/bmi_spam.h	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/config.h.defaults exim-4.34/src/config.h.defaults
--- exim-4.34-orig/src/config.h.defaults	Mon May 10 14:31:20 2004
+++ exim-4.34/src/config.h.defaults	Mon May 10 16:14:47 2004
@@ -101,7 +101,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.34-orig/src/configure.default exim-4.34/src/configure.default
--- exim-4.34-orig/src/configure.default	Mon May 10 14:31:20 2004
+++ exim-4.34/src/configure.default	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/deliver.c exim-4.34/src/deliver.c
--- exim-4.34-orig/src/deliver.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/deliver.c	Mon May 10 16:14:47 2004
@@ -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. */
@@ -152,6 +155,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)
@@ -201,6 +211,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.34-orig/src/demime.c exim-4.34/src/demime.c
--- exim-4.34-orig/src/demime.c	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/demime.c	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/demime.h exim-4.34/src/demime.h
--- exim-4.34-orig/src/demime.h	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/demime.h	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/exim.c exim-4.34/src/exim.c
--- exim-4.34-orig/src/exim.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/exim.c	Mon May 10 16:14:47 2004
@@ -1530,6 +1530,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.34-orig/src/expand.c exim-4.34/src/expand.c
--- exim-4.34-orig/src/expand.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/expand.c	Mon May 10 16:14:47 2004
@@ -294,6 +294,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 },
   { "bounce_recipient",    vtype_stringptr,   &bounce_recipient },
   { "bounce_return_size_limit", vtype_int,    &bounce_return_size_limit },
@@ -301,6 +307,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 },
@@ -309,6 +317,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 },
@@ -330,6 +339,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 },
@@ -337,6 +347,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] },
@@ -366,6 +392,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 },
@@ -394,6 +421,10 @@
   { "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 },
   { "spool_directory",     vtype_stringptr,   &spool_directory },
   { "thisaddress",         vtype_stringptr,   &filter_thisaddress },
   { "tls_certificate_verified", vtype_int,    &tls_certificate_verified },
diff -urN exim-4.34-orig/src/functions.h exim-4.34/src/functions.h
--- exim-4.34-orig/src/functions.h	Mon May 10 14:31:20 2004
+++ exim-4.34/src/functions.h	Mon May 10 16:14:47 2004
@@ -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 *);
@@ -119,6 +120,7 @@
 
 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 **,
@@ -132,6 +134,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 *);
@@ -175,6 +182,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);
@@ -234,6 +242,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 *);
@@ -285,6 +295,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, BOOL *);
 extern int     verify_check_dnsbl(uschar **);
 extern int     verify_check_header_address(uschar **, uschar **, int);
diff -urN exim-4.34-orig/src/globals.c exim-4.34/src/globals.c
--- exim-4.34-orig/src/globals.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/globals.c	Mon May 10 16:14:47 2004
@@ -161,6 +161,7 @@
 uschar *acl_smtp_helo          = NULL;
 uschar *acl_smtp_mail          = NULL;
 uschar *acl_smtp_mailauth      = NULL;
+uschar *acl_smtp_mime          = NULL;
 uschar *acl_smtp_rcpt          = NULL;
 uschar *acl_smtp_starttls      = NULL;
 uschar *acl_smtp_vrfy          = NULL;
@@ -181,6 +182,7 @@
                                    US"EHLO or HELO",
                                    US"MAIL",
                                    US"MAILAUTH",
+                                   US"MIME",
                                    US"RCPT",
                                    US"STARTTLS",
                                    US"VRFY",
@@ -194,6 +196,7 @@
                                    550,     /* HELO/EHLO */
                                    550,     /* MAIL */
                                    0,       /* MAILAUTH; not relevant */
+                                   550,     /* MIME */
                                    550,     /* RCPT */
                                    550,     /* STARTTLS */
                                    252,     /* VRFY */
@@ -296,6 +299,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=
@@ -303,6 +307,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;
 uschar *bounce_message_file    = NULL;
 uschar *bounce_message_text    = NULL;
@@ -412,6 +425,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;
@@ -441,6 +457,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;
@@ -450,12 +467,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"";
@@ -609,6 +628,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;
@@ -629,8 +649,25 @@
 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;
 
 uid_t  *never_users            = NULL;
+BOOL    no_mbox_unspool        = FALSE;
 
 uid_t   original_euid;
 gid_t   originator_gid;
@@ -729,6 +766,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;
@@ -753,6 +791,9 @@
     NULL,                      /* driver name */
 
     NULL,                      /* address_data */
+#ifdef BRIGHTMAIL
+    NULL,                      /* bmi_rule */
+#endif
     NULL,                      /* cannot_route_message */
     NULL,                      /* condition */
     NULL,                      /* current_directory */
@@ -781,6 +822,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 */
@@ -907,6 +953,11 @@
 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;
 BOOL    split_spool_directory  = FALSE;
 uschar *spool_directory        = US SPOOL_DIRECTORY
                            "\0<--------------Space to patch spool_directory->";
diff -urN exim-4.34-orig/src/globals.h exim-4.34/src/globals.h
--- exim-4.34-orig/src/globals.h	Mon May 10 14:31:20 2004
+++ exim-4.34/src/globals.h	Mon May 10 16:14:47 2004
@@ -103,6 +103,7 @@
 extern uschar *acl_smtp_helo;          /* ACL run after HELO/EHLO */
 extern uschar *acl_smtp_mail;          /* ACL run after MAIL */
 extern uschar *acl_smtp_mailauth;      /* ACL run after MAIL AUTH */
+extern uschar *acl_smtp_mime;          /* ACL run after DATA, before acl_smtp_data, for each MIME part */
 extern uschar *acl_smtp_rcpt;          /* ACL run after RCPT */
 extern uschar *acl_smtp_starttls;      /* ACL run after STARTTLS */
 extern uschar *acl_smtp_vrfy;          /* ACL run after VRFY */
@@ -137,12 +138,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 uschar *bounce_message_file;    /* Template file */
 extern uschar *bounce_message_text;    /* One-liner */
@@ -220,6 +231,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 */
@@ -249,6 +263,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 */
@@ -257,6 +272,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 */
@@ -264,6 +280,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 */
 
@@ -342,6 +359,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 */
@@ -361,8 +379,25 @@
 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 uid_t  *never_users;            /* List of uids never to be used */
+extern BOOL    no_mbox_unspool;        /* don't unspool exiscan files */
 
 extern optionlist optionlist_auths[];      /* These option lists are made */
 extern int     optionlist_auths_size;      /* global so that readconf can */
@@ -446,6 +481,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 */
@@ -536,6 +572,11 @@
 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) */
 extern uschar *spool_directory;        /* Name of spool directory */
 extern int     string_datestamp_offset;/* After insertion by string_format */
 extern BOOL    strip_excess_angle_brackets; /* Surrounding route-addrs */
diff -urN exim-4.34-orig/src/local_scan.h exim-4.34/src/local_scan.h
--- exim-4.34-orig/src/local_scan.h	Mon May 10 14:31:20 2004
+++ exim-4.34/src/local_scan.h	Mon May 10 16:14:47 2004
@@ -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;
 
 
diff -urN exim-4.34-orig/src/macros.h exim-4.34/src/macros.h
--- exim-4.34-orig/src/macros.h	Mon May 10 14:31:20 2004
+++ exim-4.34/src/macros.h	Mon May 10 16:14:47 2004
@@ -710,7 +710,7 @@
 
 enum { ACL_WHERE_AUTH,     ACL_WHERE_CONNECT, ACL_WHERE_DATA, ACL_WHERE_ETRN,
        ACL_WHERE_EXPN,     ACL_WHERE_HELO,    ACL_WHERE_MAIL,
-       ACL_WHERE_MAILAUTH, ACL_WHERE_RCPT,
+       ACL_WHERE_MAILAUTH, ACL_WHERE_MIME,    ACL_WHERE_RCPT,
        ACL_WHERE_STARTTLS, ACL_WHERE_VRFY,    ACL_WHERE_NOTSMTP };
 
 /* Situations for spool_write_header() */
diff -urN exim-4.34-orig/src/malware.c exim-4.34/src/malware.c
--- exim-4.34-orig/src/malware.c	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/malware.c	Mon May 10 16:14:47 2004
@@ -0,0 +1,1200 @@
+/*************************************************
+*     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 	0x0001
+#define DRWEBD_RETURN_VIRUSES	0x0001
+
+/* 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;
+  };
+
+  /* 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);
+			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);
+			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);
+	}
+	/* ----------------------------------------------------------------------- */
+    
+    /* "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;
+  };
+}
+
+/* ============= 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.34-orig/src/mime.c exim-4.34/src/mime.c
--- exim-4.34-orig/src/mime.c	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/mime.c	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/mime.h exim-4.34/src/mime.h
--- exim-4.34-orig/src/mime.h	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/mime.h	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/readconf.c exim-4.34/src/readconf.c
--- exim-4.34-orig/src/readconf.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/readconf.c	Mon May 10 16:14:47 2004
@@ -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_rcpt",            opt_stringptr,   &acl_smtp_rcpt },
 #ifdef SUPPORT_TLS
   { "acl_smtp_starttls",        opt_stringptr,   &acl_smtp_starttls },
@@ -152,7 +153,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 },
@@ -312,6 +317,7 @@
   { "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 },
   { "strip_excess_angle_brackets", opt_bool,     &strip_excess_angle_brackets },
diff -urN exim-4.34-orig/src/receive.c exim-4.34/src/receive.c
--- exim-4.34-orig/src/receive.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/receive.c	Mon May 10 16:14:47 2004
@@ -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;
 }
 
@@ -2530,6 +2539,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;
@@ -2543,6 +2670,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 */
@@ -2552,6 +2680,7 @@
       }
     }
 
+
   /* Handle non-SMTP and batch SMTP (i.e. non-interactive) messages. Note that
   we cannot take different actions for permanent and temporary rejections. */
 
@@ -2592,6 +2721,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
@@ -2769,6 +2900,16 @@
 timestamp = expand_string(US"${tod_full}");
 memcpy(header_list->text + received_length + 2, timestamp, Ustrlen(timestamp));
 
+
+#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
+
+
 /* Keep the data file open until we have written the header file, in order to
 hold onto the lock. In a -bh run, or if the message is to be blackholed, we
 don't write the header file, and we unlink the data file. If writing the header
@@ -3011,6 +3152,7 @@
 if this happens? */
 
 TIDYUP:
+
 process_info[process_info_len] = 0;          /* Remove message id */
 if (data_file != NULL) fclose(data_file);    /* Frees the lock */
 
@@ -3037,12 +3179,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.34-orig/src/regex.c exim-4.34/src/regex.c
--- exim-4.34-orig/src/regex.c	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/regex.c	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/route.c exim-4.34/src/route.c
--- exim-4.34-orig/src/route.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/route.c	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/smtp_in.c exim-4.34/src/smtp_in.c
--- exim-4.34-orig/src/smtp_in.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/smtp_in.c	Mon May 10 16:14:47 2004
@@ -790,6 +790,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 */
 sender_address = NULL;
 raw_sender = NULL;                  /* After SMTP rewrite, before qualifying */
 sender_address_unrewritten = NULL;  /* Set only after verify rewrite */
@@ -797,6 +799,10 @@
 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
 
 for (i = 0; i < ACL_M_MAX; i++) acl_var[ACL_C_MAX + i] = NULL;
 
@@ -1747,8 +1753,10 @@
 BOOL drop = rc == FAIL_DROP;
 uschar *lognl;
 uschar *sender_info = US"";
-uschar *what = (where == ACL_WHERE_DATA)? US"after DATA" :
-  string_sprintf("%s %s", acl_wherenames[where], smtp_data);
+uschar *what = string_sprintf("%s %s", acl_wherenames[where], smtp_data);
+
+if (where == ACL_WHERE_DATA) what = US"after DATA";
+if (where == ACL_WHERE_MIME) what = US"during MIME ACL checks";
 
 if (drop) rc = FAIL;
 
@@ -1758,7 +1766,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);
diff -urN exim-4.34-orig/src/spam.c exim-4.34/src/spam.c
--- exim-4.34-orig/src/spam.c	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/spam.c	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/spam.h exim-4.34/src/spam.h
--- exim-4.34-orig/src/spam.h	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/spam.h	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/spool_in.c exim-4.34/src/spool_in.c
--- exim-4.34-orig/src/spool_in.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/spool_in.c	Mon May 10 16:14:47 2004
@@ -241,6 +241,7 @@
 body_linecount = 0;
 deliver_firsttime = FALSE;
 deliver_freeze = FALSE;
+fake_reject = FALSE;
 deliver_frozen_at = 0;
 deliver_manual_thaw = FALSE;
 /* dont_deliver must NOT be reset */
@@ -250,6 +251,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;
@@ -266,6 +268,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;
@@ -364,6 +371,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.34-orig/src/spool_mbox.c exim-4.34/src/spool_mbox.c
--- exim-4.34-orig/src/spool_mbox.c	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/spool_mbox.c	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/spool_out.c exim-4.34/src/spool_out.c
--- exim-4.34-orig/src/spool_out.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/spool_out.c	Mon May 10 16:14:47 2004
@@ -209,9 +209,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.34-orig/src/structs.h exim-4.34/src/structs.h
--- exim-4.34-orig/src/structs.h	Mon May 10 14:31:20 2004
+++ exim-4.34/src/structs.h	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/tnef.c exim-4.34/src/tnef.c
--- exim-4.34-orig/src/tnef.c	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/tnef.c	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/tnef.h exim-4.34/src/tnef.h
--- exim-4.34-orig/src/tnef.h	Thu Jan  1 01:00:00 1970
+++ exim-4.34/src/tnef.h	Mon May 10 16:14:47 2004
@@ -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.34-orig/src/version.c exim-4.34/src/version.c
--- exim-4.34-orig/src/version.c	Mon May 10 14:31:20 2004
+++ exim-4.34/src/version.c	Mon May 10 16:15:55 2004
@@ -9,9 +9,8 @@
 
 #include "exim.h"
 
-
 #define THIS_VERSION  "4.34"
-
+#define EXISCAN_VERSION "21"
 
 /* The header file cnumber.h contains a single line containing the
 compilation number, making it easy to have it updated automatically.
@@ -40,6 +39,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';
