summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Morgan <jmorgan@gentoo.org>2003-06-11 07:25:08 +0000
committerJack Morgan <jmorgan@gentoo.org>2003-06-11 07:25:08 +0000
commit5a61f999f62b254221ea9a903af8d6827465e15c (patch)
tree703a36daf2c046a13afdc0b4ab6b438a6070ad3e /net-mail/exim
parentupdated use.local.desc for eximscan (diff)
downloadgentoo-2-5a61f999f62b254221ea9a903af8d6827465e15c.tar.gz
gentoo-2-5a61f999f62b254221ea9a903af8d6827465e15c.tar.bz2
gentoo-2-5a61f999f62b254221ea9a903af8d6827465e15c.zip
added new exim-4.20.ebuild
Diffstat (limited to 'net-mail/exim')
-rw-r--r--net-mail/exim/ChangeLog10
-rw-r--r--net-mail/exim/Manifest5
-rw-r--r--net-mail/exim/exim-4.20.ebuild194
-rw-r--r--net-mail/exim/files/digest-exim-4.201
-rw-r--r--net-mail/exim/files/exiscan-acl-4.20-09.patch6707
5 files changed, 6914 insertions, 3 deletions
diff --git a/net-mail/exim/ChangeLog b/net-mail/exim/ChangeLog
index c17066ac06e5..294d1d6c560e 100644
--- a/net-mail/exim/ChangeLog
+++ b/net-mail/exim/ChangeLog
@@ -1,8 +1,14 @@
# ChangeLog for net-mail/exim
# Copyright 2002-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo-x86/net-mail/exim/ChangeLog,v 1.28 2003/05/31 05:03:01 jmorgan Exp $
+# $Header: /var/cvsroot/gentoo-x86/net-mail/exim/ChangeLog,v 1.29 2003/06/11 07:24:47 jmorgan Exp $
-*exim-4.14 (30 My 2003)
+*exim-4.20 (10 Jun 2003)
+
+ 10 Jun 2003; Jack Morgan <jmorgan@gentoo.org> exim-4.20.ebuild:
+ Added new edbuild. Includes exiscan-acl support. Thanks to Tim Schafer <tschafer@hotmail.com>
+ Closes bug #21302
+
+*exim-4.14 (30 May 2003)
30 May 2003; Jack Morgan <jmorgan@gentoo.org> exim-4.14.ebuild:
Updates exiscan, closes bug #20613
diff --git a/net-mail/exim/Manifest b/net-mail/exim/Manifest
index 66245e6b47f2..1891ac3ccbc2 100644
--- a/net-mail/exim/Manifest
+++ b/net-mail/exim/Manifest
@@ -1,7 +1,8 @@
-MD5 8de0065908b6dc195e23c9efb5904960 ChangeLog 4917
+MD5 ecb811a7e554a0f64d06e91dab5a6a44 ChangeLog 5127
MD5 ea8d4d3bdf95464d1afcb9c503696b21 exim-4.10.ebuild 6211
MD5 d1323582ec8ef8dfdbc230560e99d857 exim-4.12.ebuild 5433
MD5 346dc545948dbc5c2b71b03f19b0dd7c exim-4.14.ebuild 5328
+MD5 8b67772f90434334b40d7a113bc3c92a exim-4.20.ebuild 5461
MD5 68fc403ba2c98ccba281939085cb9052 files/auth_conf.sub 775
MD5 0cb2ffe88c81fd7ac8429b5a19d58b35 files/configure 16346
MD5 34def7b529693beda5b84c422d25a878 files/digest-exim-4.10 128
@@ -14,3 +15,5 @@ MD5 eb249c90af3ab11e5a4d307e184a75ac files/exiscan.conf 22113
MD5 d230e0fa45f2b65d5bc50c0879c40148 files/pam.d-exim 101
MD5 0883fe67a34142a57e6f39ed9419cbee files/system_filter.exim 8118
MD5 091d3610d9a2a0b879f543cc070ef933 files/digest-exim-4.14 128
+MD5 0cc15aecabaeace6ff4cdd1a462b9eb0 files/digest-exim-4.20 62
+MD5 5898fa2e00e85c771cffe741f3198c07 files/exiscan-acl-4.20-09.patch 263216
diff --git a/net-mail/exim/exim-4.20.ebuild b/net-mail/exim/exim-4.20.ebuild
new file mode 100644
index 000000000000..0955ddaf6107
--- /dev/null
+++ b/net-mail/exim/exim-4.20.ebuild
@@ -0,0 +1,194 @@
+# Copyright 1999-2003 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+
+
+IUSE="tcpd ssl postgres mysql ldap pam exiscan exiscan-acl"
+
+EXISCAN_VER=${PV}-26
+EXISCANACL_VER=${PV}-09
+DESCRIPTION="A highly configurable, drop-in replacement for sendmail"
+SRC_URI="ftp://ftp.exim.org/pub/exim/exim4/${P}.tar.gz
+ exiscan? ( http://duncanthrax.net/exiscan/exiscan-${EXISCAN_VER}.tar.gz )
+ exiscan-acl? ( http://duncanthrax.net/exiscan-acl/exiscan-acl-${EXISCANACL_VER}.patch )"
+
+
+HOMEPAGE="http://www.exim.org/"
+
+SLOT="0"
+LICENSE="GPL-2"
+KEYWORDS="~x86 ~sparc"
+
+PROVIDE="virtual/mta"
+
+DEPEND=">=sys-apps/sed-4.0.5
+ dev-lang/perl
+ >=sys-libs/db-3.2
+ >=dev-libs/libpcre-3.4
+ pam? ( >=sys-libs/pam-0.75 )
+ tcpd? ( sys-apps/tcp-wrappers )
+ ssl? ( >=dev-libs/openssl-0.9.6 )
+ ldap? ( >=net-nds/openldap-2.0.7 )
+ mysql? ( >=dev-db/mysql-3.23.28 )
+ postgres? ( >=dev-db/postgresql-7 )"
+
+RDEPEND="${DEPEND}
+ !virtual/mta
+ >=net-mail/mailbase-0.00"
+
+src_unpack() {
+ unpack ${A}
+
+ local myconf
+
+ cd ${S}
+ if use exiscan; then
+ einfo "Patching exiscan support into exim ${PV}.."
+ epatch ${WORKDIR}/exiscan-${EXISCAN_VER}/exiscan-${EXISCAN_VER}.patch
+ fi
+ if use exiscan-acl; then
+ einfo "Patching exican-acl support into exim ${PV}.."
+ epatch ${DISTDIR}/exiscan-acl-${EXISCANACL_VER}.patch
+ fi
+
+ sed -e "48i\CFLAGS=${CFLAGS}" \
+ -e "s:# AUTH_CRAM_MD5=yes:AUTH_CRAM_MD5=yes:" \
+ -e "s:# AUTH_PLAINTEXT=yes:AUTH_PLAINTEXT=yes:" \
+ -e "s:BIN_DIRECTORY=/usr/exim/bin:BIN_DIRECTORY=/usr/sbin:" \
+ -e "s:COMPRESS_COMMAND=/opt/gnu/bin/gzip:COMPRESS_COMMAND=/usr/bin/gzip:" \
+ -e "s:ZCAT_COMMAND=/opt/gnu/bin/zcat:ZCAT_COMMAND=/usr/bin/zcat:" \
+ -e "s:CONFIGURE_FILE=/usr/exim/configure:CONFIGURE_FILE=/etc/exim/exim.conf:" \
+ -e "s:EXIM_MONITOR=eximon.bin:# EXIM_MONITOR=eximon.bin:" \
+ -e "s:# EXIM_PERL=perl.o:EXIM_PERL=perl.o:" \
+ -e "s:# INFO_DIRECTORY=/usr/local/info:INFO_DIRECTORY=/usr/share/info:" \
+ -e "s:# LOG_FILE_PATH=syslog:LOG_FILE_PATH=syslog:" \
+ -e "s:LOG_FILE_PATH=syslog\:/var/log/exim_%slog::" \
+ -e "s:# PID_FILE_PATH=/var/lock/exim%s.pid:PID_FILE_PATH=/var/run/exim%s.pid:" \
+ -e "s:# SPOOL_DIRECTORY=/var/spool/exim:SPOOL_DIRECTORY=/var/spool/exim:" \
+ -e "s:# SUPPORT_MAILDIR=yes:SUPPORT_MAILDIR=yes:" \
+ -e "s:# SUPPORT_MAILSTOR=yes:SUPPORT_MAILSTORE=yes:" \
+ -e "s:# SUPPORT_MBX=yes:SUPPORT_MBX=yes:" \
+ -e "s:EXIM_USER=:EXIM_USER=mail:" \
+ -e "s:# AUTH_SPA=yes:AUTH_SPA=yes:" \
+ src/EDITME > Local/Makefile
+
+ cd Local
+ if use pam; then
+ sed -i "s:# \(SUPPORT_PAM=yes\):\1:" Makefile
+ myconf="${myconf} -lpam"
+ fi
+ if use tcpd; then
+ sed -i "s:# \(USE_TCP_WRAPPERS=yes\):\1:" Makefile
+ myconf="${myconf} -lwrap"
+ fi
+ if [ -n "$myconf" ] ; then
+ echo "EXTRALIBS=${myconf}" >> Makefile
+ fi
+
+ cd ${S}
+ if use ssl; then
+ sed -i \
+ -e "s:# \(SUPPORT_TLS=yes\):\1:" \
+ -e "s:# \(TLS_LIBS=-lssl -lcrypto\):\1:" Local/Makefile
+ fi
+
+ LOOKUP_INCLUDE=
+ LOOKUP_LIBS=
+
+ if use ldap; then
+ sed -i \
+ -e "s:# \(LOOKUP_LDAP=yes\):\1:" \
+ -e "s:# \(LDAP_LIB_TYPE=OPENLDAP2\):\1:" Local/Makefile
+ LOOKUP_INCLUDE="-I/usr/include/ldap"
+ LOOKUP_LIBS="-L/usr/lib -lldap -llber"
+ fi
+
+ if use mysql; then
+ sed -i "s:# LOOKUP_MYSQL=yes:LOOKUP_MYSQL=yes:" Local/Makefile
+ LOOKUP_INCLUDE="$LOOKUP_INCLUDE -I/usr/include/mysql"
+ LOOKUP_LIBS="$LOOKUP_LIBS -L/usr/lib -lmysqlclient"
+ fi
+
+ if use postgres; then
+ sed -i "s:# LOOKUP_PGSQL=yes:LOOKUP_PGSQL=yes:" Local/Makefile
+ LOOKUP_INCLUDE="$LOOKUP_INCLUDE -I/usr/include/postgresql"
+ LOOKUP_LIBS="$LOOKUP_LIBS -lpq"
+ fi
+
+ if [ -n "$LOOKUP_INCLUDE" ]; then
+ sed -i "s:# LOOKUP_INCLUDE=-I /usr/local/ldap/include -I /usr/local/mysql/include -I /usr/local/pgsql/include:LOOKUP_INCLUDE=$LOOKUP_INCLUDE:" \
+ Local/Makefile
+ fi
+
+ if [ -n "$LOOKUP_LIBS" ]; then
+ sed -i "s:# LOOKUP_LIBS=-L/usr/local/lib -lldap -llber -lmysqlclient -lpq:LOOKUP_LIBS=$LOOKUP_LIBS:" \
+ Local/Makefile
+ fi
+
+
+ cat Makefile | sed -e 's/^buildname=.*/buildname=exim-gentoo/g' > Makefile.gentoo && mv -f Makefile.gentoo Makefile
+
+ sed -i "s:# LOOKUP_DSEARCH=yes:LOOKUP_DSEARCH=yes:" Local/Makefile
+
+ sed -i "s:# LOOKUP_CDB=yes:LOOKUP_CDB=yes:" Local/Makefile
+}
+
+src_compile() {
+ make || die
+}
+
+
+src_install () {
+
+ cd ${S}/build-exim-gentoo
+ exeinto /usr/sbin
+ doexe exim
+ fperms 4755 /usr/sbin/exim
+
+ dodir /usr/bin /usr/sbin /usr/lib
+ dosym exim /usr/bin/mailq
+ dosym exim /usr/bin/newaliases
+ dosym exim /usr/bin/mail
+ dosym exim /usr/sbin/rsmtp
+ dosym exim /usr/sbin/rmail
+ dosym exim /usr/sbin/sendmail
+ dosym exim /usr/lib/sendmail
+
+ exeinto /usr/sbin
+ for i in exicyclog exim_dbmbuild exim_dumpdb exim_fixdb exim_lock \
+ exim_tidydb exinext exiwhat exigrep eximstats exiqsumm \
+ convert4r3 convert4r4
+ do
+ doexe $i
+ done
+
+ dodoc ${S}/doc/*
+ doman ${S}/doc/exim.8
+
+ # conf files
+ insinto /etc/exim
+ newins ${S}/src/configure.default exim.conf.dist
+ doins ${FILESDIR}/system_filter.exim
+ doins ${FILESDIR}/auth_conf.sub
+ if use exiscan; then
+ doins ${FILESDIR}/exiscan.conf
+ fi
+
+ # INSTALL a pam.d file for SMTP AUTH that works with gentoo's pam
+ insinto /etc/pam.d
+ newins ${FILESDIR}/pam.d-exim exim
+
+ exeinto /etc/init.d
+ newexe ${FILESDIR}/exim.rc6 exim
+
+ insinto /etc/conf.d
+ newins ${FILESDIR}/exim.confd exim
+}
+
+
+pkg_postinst() {
+
+ einfo "/etc/exim/system_filter.exim is a sample system_filter."
+ einfo "/etc/exim/auth_conf.sub contains the configuration sub for using smtp auth."
+ einfo "Please create /etc/exim/exim.conf from /etc/exim/exim.conf.dist."
+
+}
diff --git a/net-mail/exim/files/digest-exim-4.20 b/net-mail/exim/files/digest-exim-4.20
new file mode 100644
index 000000000000..368f6db1d642
--- /dev/null
+++ b/net-mail/exim/files/digest-exim-4.20
@@ -0,0 +1 @@
+MD5 a1f06f1de1ab602a25d78ad2a20819f2 exim-4.20.tar.gz 1549612
diff --git a/net-mail/exim/files/exiscan-acl-4.20-09.patch b/net-mail/exim/files/exiscan-acl-4.20-09.patch
new file mode 100644
index 000000000000..42d62d26be3f
--- /dev/null
+++ b/net-mail/exim/files/exiscan-acl-4.20-09.patch
@@ -0,0 +1,6707 @@
+diff -urN exim-4.20-orig/OS/Makefile-Base exim-4.20/OS/Makefile-Base
+--- exim-4.20-orig/OS/Makefile-Base Mon May 12 15:39:15 2003
++++ exim-4.20/OS/Makefile-Base Wed May 14 12:07:55 2003
+@@ -246,14 +246,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 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 moan.o \
+ os.o parse.o queue.o \
+- rda.o readconf.o receive.o retry.o rewrite.o \
+- route.o search.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 \
++ route.o search.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 \
+@@ -465,6 +465,7 @@
+ 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
+@@ -478,6 +479,7 @@
+ 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
+ moan.o: $(HDRS) moan.c
+ os.o: $(HDRS) os.c
+@@ -486,17 +488,21 @@
+ 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
+ route.o: $(HDRS) route.c
+ search.o: $(HDRS) search.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.20-orig/README.EXISCAN exim-4.20/README.EXISCAN
+--- exim-4.20-orig/README.EXISCAN Thu Jan 1 01:00:00 1970
++++ exim-4.20/README.EXISCAN Wed May 14 12:04:24 2003
+@@ -0,0 +1 @@
++Please refer to doc/exiscan-acl-spec.txt
+diff -urN exim-4.20-orig/doc/exiscan-acl-examples.txt exim-4.20/doc/exiscan-acl-examples.txt
+--- exim-4.20-orig/doc/exiscan-acl-examples.txt Thu Jan 1 01:00:00 1970
++++ exim-4.20/doc/exiscan-acl-examples.txt Fri Jun 6 13:18:19 2003
+@@ -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.20-orig/doc/exiscan-acl-spec.txt exim-4.20/doc/exiscan-acl-spec.txt
+--- exim-4.20-orig/doc/exiscan-acl-spec.txt Thu Jan 1 01:00:00 1970
++++ exim-4.20/doc/exiscan-acl-spec.txt Mon May 26 11:24:25 2003
+@@ -0,0 +1,426 @@
++--------------------------------------------------------------
++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 facilities:
++
++ - MIME unpacking, sanity checking, file extension blocking
++ - Antivirus using 3rd party scanners
++ - Antispam using SpamAssassin
++ - Regular expression match against headers and body
++
++These facilities are hooked into exim by adding new conditions
++to exim's ACL system. 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.
++
++The exiscan-acl patch also defines several expansion variables
++that can be used to customise the error responses sent to the
++remote server.
++
++The default exim configure file contains commented
++configuration examples for all facilites.
++
++
++0. Overall concept
++--------------------------------------------------------------
++
++The exiscan-acl patch adds the following conditions
++(facilities), which can ONLY be used in the ACL after DATA
++(acl_smtp_data):
++
++- demime (MIME unpacking and file extension checks)
++- regex (match regular expressions against message headers
++ and body)
++- malware (attach 3rd party virus/malware scanner)
++- spam (attach SpamAssassin)
++
++Each of these facilities has its own chapter further below in
++this document. There is also a commented sample configuration
++in the "configure" file of the exim distribution.
++
++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
++condition 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
++"demime" condition has been used, this directory will also
++contain files produced by the MIME decoder.
++
++
++1. The "demime" facility
++ MIME unpacking, sanity checking and file extension blocking
++--------------------------------------------------------------
++
++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
++--------------------- */
++
++
++
++2. The "spam" facility
++ Antispam measures with SpamAssassin
++--------------------------------------------------------------
++
++The "spam" facility 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 it's 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
++
++If you use the above mentioned default, you do NOT need to set
++this option.
++
++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 it's
++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.
++
++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}}
++----------------- */
++
++
++
++3. The "regex" facility
++ Match headers and body lines of the message against regular
++ expressions
++--------------------------------------------------------------
++
++The "regex" condition takes one or more regular expressions as
++arguments and matches them against the full message, that is
++all headers and the complete body. It is particularly useful
++to filter trash that cannot be recognized by the spam or
++malware conditions. With large messages, this condition can be
++fairly CPU-intensive.
++
++The regular expressions are matched linewise, with a maximum
++line length of 32k characters.
++
++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 condition returns true if one of the regular expressions
++has matched a line of the message. The $regex_match_string
++variable is then set up and contains the matching regular
++expression.
++
++
++
++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
++
++
++ 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 = *
++---------------------- */
++
++
++--------------------------------------------------------------
++End of file
++--------------------------------------------------------------
+diff -urN exim-4.20-orig/exim_monitor/em_globals.c exim-4.20/exim_monitor/em_globals.c
+--- exim-4.20-orig/exim_monitor/em_globals.c Mon May 12 15:39:23 2003
++++ exim-4.20/exim_monitor/em_globals.c Wed May 14 12:04:24 2003
+@@ -133,6 +133,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.20-orig/scripts/MakeLinks exim-4.20/scripts/MakeLinks
+--- exim-4.20-orig/scripts/MakeLinks Mon May 12 15:39:17 2003
++++ exim-4.20/scripts/MakeLinks Wed May 14 12:04:24 2003
+@@ -166,6 +166,7 @@
+
+ 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
+@@ -173,8 +174,10 @@
+ ln -s ../src/macros.h macros.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/buildconfig.c buildconfig.c
+@@ -184,6 +187,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
+@@ -202,6 +206,7 @@
+ 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/moan.c moan.c
+ ln -s ../src/parse.c parse.c
+@@ -210,19 +215,23 @@
+ 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/route.c route.c
+ ln -s ../src/search.c search.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.20-orig/src/acl.c exim-4.20/src/acl.c
+--- exim-4.20-orig/src/acl.c Mon May 12 15:39:17 2003
++++ exim-4.20/src/acl.c Fri May 16 09:11:35 2003
+@@ -7,6 +7,8 @@
+
+ /* Code for handling Access Control Lists (ACLs) */
+
++/* This file has been modified by the exiscan-acl patch. */
++
+ #include "exim.h"
+
+
+@@ -32,19 +34,19 @@
+ /* 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, ACLC_CONDITION, ACLC_CONTROL, ACLC_DELAY, ACLC_DEMIME,
+ ACLC_DNSLISTS, ACLC_DOMAINS, ACLC_ENCRYPTED, ACLC_ENDPASS, ACLC_HOSTS,
+- ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_MESSAGE, ACLC_RECIPIENTS,
+- ACLC_SENDER_DOMAINS, ACLC_SENDERS, ACLC_SET, ACLC_VERIFY };
++ ACLC_LOCAL_PARTS, ACLC_LOG_MESSAGE, ACLC_MALWARE, ACLC_MESSAGE, ACLC_RECIPIENTS,
++ ACLC_REGEX, ACLC_SENDER_DOMAINS, ACLC_SENDERS, ACLC_SET, ACLC_SPAM, ACLC_VERIFY };
+
+ /* ACL conditions/modifiers: "delay", "control", "endpass", "message",
+-"log_message", and "set" are modifiers that look like conditions but always
++"log_message", "set" and "spam" 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"message",
+- US"recipients", US"sender_domains", US"senders", US"set", US"verify" };
++ US"control", US"delay", US"demime", US"dnslists", US"domains", US"encrypted",
++ US"endpass", US"hosts", US"local_parts", US"log_message", US"malware", US"message",
++ 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
+ at the outer level. In the other cases, expansion already occurs in the
+@@ -56,6 +58,7 @@
+ TRUE, /* condition */
+ TRUE, /* control */
+ TRUE, /* delay */
++ FALSE, /* demime */
+ TRUE, /* dnslists */
+ FALSE, /* domains */
+ FALSE, /* encrypted */
+@@ -63,11 +66,14 @@
+ FALSE, /* hosts */
+ FALSE, /* local_parts */
+ TRUE, /* log_message */
++ TRUE, /* malware */
+ TRUE, /* message */
+ FALSE, /* recipients */
++ TRUE, /* regex */
+ FALSE, /* sender_domains */
+ FALSE, /* senders */
+ TRUE, /* set */
++ TRUE, /* spam */
+ TRUE /* verify */
+ };
+
+@@ -79,6 +85,7 @@
+ FALSE, /* condition */
+ TRUE, /* control */
+ TRUE, /* delay */
++ FALSE, /* demime */
+ FALSE, /* dnslists */
+ FALSE, /* domains */
+ FALSE, /* encrypted */
+@@ -86,11 +93,14 @@
+ FALSE, /* hosts */
+ FALSE, /* local_parts */
+ TRUE, /* log_message */
++ FALSE, /* malware */
+ TRUE, /* message */
+ FALSE, /* recipients */
++ FALSE, /* regex */
+ FALSE, /* sender_domains */
+ FALSE, /* senders */
+ TRUE, /* set */
++ FALSE, /* spam */
+ FALSE /* verify */
+ };
+
+@@ -109,6 +119,13 @@
+ (1<<ACL_WHERE_STARTTLS)|(ACL_WHERE_VRFY),
+
+ 0, /* delay */
++
++ (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)| /* demime */
++ (1<<ACL_WHERE_CONNECT)|
++ (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
++ (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 */
+@@ -131,6 +148,13 @@
+ (1<<ACL_WHERE_VRFY),
+
+ 0, /* log_message */
++
++ (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)| /* malware */
++ (1<<ACL_WHERE_CONNECT)|
++ (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
++ (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
++ (1<<ACL_WHERE_VRFY),
++
+ 0, /* message */
+
+ (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)| /* recipients */
+@@ -140,6 +164,12 @@
+ (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_ETRN)|(1<<ACL_WHERE_EXPN)|
++ (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_ETRN)|(1<<ACL_WHERE_EXPN)|
+@@ -152,6 +182,12 @@
+
+ 0, /* set */
+
++ (1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_AUTH)| /* spam */
++ (1<<ACL_WHERE_CONNECT)|
++ (1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
++ (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 */
+
+@@ -1096,6 +1132,23 @@
+ rc = verify_check_dnsbl(&arg);
+ break;
+
++
++ case ACLC_DEMIME:
++ rc = demime(&arg);
++ break;
++
++ case ACLC_MALWARE:
++ rc = malware(&arg);
++ break;
++
++ case ACLC_SPAM:
++ rc = spam(&arg);
++ break;
++
++ case ACLC_REGEX:
++ rc = regex(&arg);
++ break;
++
+ case ACLC_DOMAINS:
+ rc = match_isinlist(addr->domain, &arg, 0, &domainlist_anchor,
+ addr->domain_cache, MCL_DOMAIN, TRUE, &deliver_domain_data);
+@@ -1618,6 +1671,10 @@
+ return DISCARD;
+ }
+
++/* Remove spooled mbox and demimed files.
++Will immediately return if no files had been created */
++unspool_mbox();
++
+ /* Before giving an error response, take a look at the length of any user
+ message, and split it up into multiple lines if possible. */
+
+diff -urN exim-4.20-orig/src/configure.default exim-4.20/src/configure.default
+--- exim-4.20-orig/src/configure.default Mon May 12 15:39:18 2003
++++ exim-4.20/src/configure.default Tue May 27 13:32:26 2003
+@@ -108,6 +108,25 @@
+
+ # You should not change that setting until you understand how ACLs work.
+
++# The following ACL entry is used if you want to do content scanning with the
++# exiscan-acl patch. When you uncomment this line, you must also review the
++# acl_check_content entry in the ACL section further below.
++
++# 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
+@@ -308,6 +327,52 @@
+ deny message = relay not permitted
+
+
++# This access control list is used for content scanning with the exiscan-acl
++# patch. You must also uncomment the entry for acl_smtp_data (scroll up),
++# otherwise the ACL 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_content:
++
++ # First unpack MIME containers and reject serious errors.
++ deny message = This message contains a MIME error ($demime_reason)
++ demime = *
++ condition = ${if >{$demime_errorlevel}{2}{1}{0}}
++
++ # Reject typically wormish file extensions. There is almost no
++ # sense in sending such files by email.
++ deny message = This message contains an unwanted file extension ($found_extension)
++ demime = scr:vbs:bat:lnk:pif
++
++ # Reject virus infested messages.
++ deny message = This message contains malware ($malware_name)
++ malware = *
++
++ # Reject messages containing "viagra" in all kinds of whitespace/case combinations
++ # WARNING: this is an example !
++ deny message = This message matches a blacklisted regular expression ($regex_match_string)
++ regex = [Vv] *[Ii] *[Aa] *[Gg] *[Rr] *[Aa]
++
++ # 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.20-orig/src/demime.c exim-4.20/src/demime.c
+--- exim-4.20-orig/src/demime.c Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/demime.c Fri May 16 17:08:52 2003
+@@ -0,0 +1,1243 @@
++/*************************************************
++* 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];
++int demime_ok = 0;
++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;
++
++ /* 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|LOG_PANIC,
++ "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) ) {
++ 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;
++ 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') {
++ /* look if next char is '\t' or ' ' */
++ c = fgetc(f);
++ if (c == EOF) break;
++ if ( (c == '\t') || (c == ' ') ) continue;
++ /* end of the header, terminate with ';' */
++ ungetc(c,f);
++ 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;
++
++ /* check for '--' first */
++ if (Ustrncmp(line,"--",2) == 0) {
++ while(thisboundary != NULL) {
++ if (Ustrncmp(&line[2],thisboundary->boundary_string,Ustrlen(thisboundary->boundary_string)) == 0) {
++ if (Ustrncmp(&line[(2+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 %u bytes",line_len);
++ return -2;
++ };
++
++ work = (uschar *)malloc(line_len);
++ if (work == NULL) {
++ snprintf(CS info, 1024,"unable to allocate %u 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 %u 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|LOG_PANIC, 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;
++
++ /* 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;
++
++ /* 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) {
++ 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) {
++ fclose(mime_dump_file); mime_dump_file = NULL;
++ };
++ }
++ else {
++ uschar *data;
++ long data_len;
++
++ /* 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.20-orig/src/demime.h exim-4.20/src/demime.h
+--- exim-4.20-orig/src/demime.h Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/demime.h Wed May 14 12:04:24 2003
+@@ -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.20-orig/src/expand.c exim-4.20/src/expand.c
+--- exim-4.20-orig/src/expand.c Mon May 12 15:39:19 2003
++++ exim-4.20/src/expand.c Wed May 14 12:04:24 2003
+@@ -218,11 +218,14 @@
+ { "caller_uid", vtype_int, &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 },
+ { "domain", vtype_stringptr, &deliver_domain },
+ { "domain_data", vtype_stringptr, &deliver_domain_data },
++ { "found_extension", vtype_stringptr, &found_extension },
+ { "home", vtype_stringptr, &deliver_home },
+ { "host", vtype_stringptr, &deliver_host },
+ { "host_address", vtype_stringptr, &deliver_host_address },
+@@ -241,6 +244,7 @@
+ { "local_part_suffix", vtype_stringptr, &deliver_localpart_suffix },
+ { "local_scan_data", vtype_stringptr, &local_scan_data },
+ { "localhost_number", vtype_int, &host_number },
++ { "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 },
+@@ -275,6 +279,7 @@
+ { "received_protocol", vtype_stringptr, &received_protocol },
+ { "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, &return_size_limit },
+@@ -302,6 +307,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.20-orig/src/functions.h exim-4.20/src/functions.h
+--- exim-4.20-orig/src/functions.h Mon May 12 15:39:19 2003
++++ exim-4.20/src/functions.h Wed May 14 12:04:24 2003
+@@ -64,6 +64,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_init(BOOL, BOOL);
+@@ -114,6 +115,7 @@
+
+ extern void log_close_all(void);
+
++extern int malware(uschar **);
+ extern int match_address_list(uschar *, BOOL, uschar **, unsigned int *,
+ int, int);
+ extern int match_check_list(uschar **, int, tree_node **, unsigned int **,
+@@ -227,6 +229,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 *);
+@@ -277,6 +281,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.20-orig/src/globals.c exim-4.20/src/globals.c
+--- exim-4.20-orig/src/globals.c Mon May 12 15:39:19 2003
++++ exim-4.20/src/globals.c Fri May 23 11:06:00 2003
+@@ -286,6 +286,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=
+@@ -400,6 +401,8 @@
+ BOOL deliver_selectstring_regex = FALSE;
+ uschar *deliver_selectstring_sender = NULL;
+ BOOL deliver_selectstring_sender_regex = FALSE;
++int demime_errorlevel = 0;
++uschar *demime_reason = NULL;
+ BOOL disable_logging = FALSE;
+
+ uschar *dns_again_means_nonexist = NULL;
+@@ -443,6 +446,7 @@
+ uschar *filter_test = NULL;
+ uschar *filter_thisaddress = NULL;
+ int finduser_retries = 0;
++uschar *found_extension = NULL;
+ uschar *freeze_tell = NULL;
+
+ uschar *gecos_name = NULL;
+@@ -565,6 +569,7 @@
+ uschar *lookup_value = NULL;
+
+ macro_item *macros = NULL;
++uschar *malware_name = NULL;
+ int max_username_length = 0;
+ int message_age = 0;
+ uschar *message_body = NULL;
+@@ -680,6 +685,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;
+@@ -846,6 +852,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.20-orig/src/globals.h exim-4.20/src/globals.h
+--- exim-4.20-orig/src/globals.h Mon May 12 15:39:19 2003
++++ exim-4.20/src/globals.h Wed May 14 12:04:24 2003
+@@ -129,6 +129,7 @@
+ 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 */
+@@ -210,6 +211,8 @@
+ 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 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 */
+@@ -250,6 +253,7 @@
+ extern uschar *filter_test; /* Run as a filter tester on this file */
+ extern uschar *filter_thisaddress; /* For address looping */
+ extern int finduser_retries; /* Retry count for getpwnam() */
++extern uschar *found_extension; /* demime acl condition: file extension found */
+ extern uschar *freeze_tell; /* Message on (some) freezings */
+
+ extern uschar *gecos_name; /* To be expanded when pattern matches */
+@@ -322,6 +326,7 @@
+ extern uschar *lookup_value; /* Value looked up from file */
+
+ extern macro_item *macros; /* Configuration macros */
++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 */
+@@ -421,6 +426,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 */
+@@ -511,6 +517,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.20-orig/src/malware.c exim-4.20/src/malware.c
+--- exim-4.20-orig/src/malware.c Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/malware.c Thu Jun 5 09:33:29 2003
+@@ -0,0 +1,635 @@
++/*************************************************
++* 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"
++
++/* SHUT_WR seems to be undefined on Unixware ? */
++#ifndef SHUT_WR
++#define SHUT_WR 1
++#endif
++
++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;
++ };
++
++
++
++ /* "kavdaemon" scanner type ------------------------------------------------ */
++ 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 lower nibble */
++ kav_rc = tmpbuf[0] & 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)) {
++
++ /* setup default virus name */
++ Ustrcpy(malware_name_buffer,"unknown");
++ malware_name = malware_name_buffer;
++
++ /* read the report, if available */
++ if( tmpbuf[1] == 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];
++ 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");
++
++ 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));
++ 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);
++ 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);
++ 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);
++
++ 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,i,offset=0,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 */
++ 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 */
++ 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;
++ 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;
++ }
++ }
++ }
++ /* ----------------------------------------------------------------------- */
++
++
++
++ /* "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;
++ };
++}
+diff -urN exim-4.20-orig/src/readconf.c exim-4.20/src/readconf.c
+--- exim-4.20-orig/src/readconf.c Mon May 12 15:39:21 2003
++++ exim-4.20/src/readconf.c Wed May 14 12:04:24 2003
+@@ -152,6 +152,7 @@
+ { "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 },
+ { "bounce_message_file", opt_stringptr, &bounce_message_file },
+ { "bounce_message_text", opt_stringptr, &bounce_message_text },
+@@ -297,6 +298,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.20-orig/src/regex.c exim-4.20/src/regex.c
+--- exim-4.20-orig/src/regex.c Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/regex.c Wed May 14 12:04:24 2003
+@@ -0,0 +1,110 @@
++/*************************************************
++* 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"
++
++/* 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];
++
++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;
++
++ /* reset expansion variable */
++ regex_match_string = NULL;
++
++ /* 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,
++ "regex acl condition: error while creating mbox spool file");
++ return DEFER;
++ };
++
++ /* 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) {
++ 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;
++ fclose(mbox_file);
++ return OK;
++ };
++ re_list_item = re_list_item->next;
++ } while (re_list_item != NULL);
++ };
++
++ fclose(mbox_file);
++
++ /* no matches ... */
++ return FAIL;
++}
+diff -urN exim-4.20-orig/src/spam.c exim-4.20/src/spam.c
+--- exim-4.20-orig/src/spam.c Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/spam.c Wed May 21 09:35:15 2003
+@@ -0,0 +1,264 @@
++/*************************************************
++* 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 tcp_addr[24];
++ unsigned int tcp_port;
++ 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;
++
++ /* 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;
++ };
++
++ /* contact spamd */
++ spamd_sock = ip_socket(SOCK_STREAM, AF_INET);
++ if (spamd_sock < 0) {
++ log_write(0, LOG_MAIN|LOG_PANIC,
++ "spam acl condition: error creating IP socket for spamd");
++ fclose(mbox_file);
++ return DEFER;
++ };
++
++ if (ip_bind(spamd_sock, AF_INET, US"0.0.0.0", 0) < 0) {
++ log_write(0, LOG_MAIN|LOG_PANIC,
++ "spam acl condition: bind socket for spamd failed: %s",strerror(errno));
++ fclose(mbox_file);
++ close(spamd_sock);
++ return DEFER;
++ };
++
++ /* grok spamd address and port */
++ if( sscanf(CS spamd_address, "%s %u", tcp_addr, &tcp_port) != 2 ) {
++ log_write(0, LOG_MAIN|LOG_PANIC,
++ "spam acl condition: invalid spamd address: '%s'", spamd_address);
++ fclose(mbox_file);
++ close(spamd_sock);
++ return DEFER;
++ };
++
++ if (ip_connect(spamd_sock, AF_INET, tcp_addr, tcp_port, 5) < 0) {
++ log_write(0, LOG_MAIN|LOG_PANIC,
++ "spam acl condition: spamd connection to %s, port %u failed: %s", tcp_addr, tcp_port, 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*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.20-orig/src/spam.h exim-4.20/src/spam.h
+--- exim-4.20-orig/src/spam.h Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/spam.h Wed May 14 12:04:24 2003
+@@ -0,0 +1,23 @@
++/*************************************************
++* 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
++
+diff -urN exim-4.20-orig/src/spool_in.c exim-4.20/src/spool_in.c
+--- exim-4.20-orig/src/spool_in.c Mon May 12 15:39:22 2003
++++ exim-4.20/src/spool_in.c Wed May 14 12:04:24 2003
+@@ -248,6 +248,7 @@
+ interface_port = 0;
+ local_error_message = FALSE;
+ local_scan_data = NULL;
++spam_score_int = NULL;
+ message_linecount = 0;
+ received_protocol = NULL;
+ recipients_list = NULL;
+@@ -347,6 +348,8 @@
+ 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);
+ 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.20-orig/src/spool_mbox.c exim-4.20/src/spool_mbox.c
+--- exim-4.20-orig/src/spool_mbox.c Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/spool_mbox.c Tue Jun 10 15:43:43 2003
+@@ -0,0 +1,157 @@
++/*************************************************
++* 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;
++
++/* 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;
++ header_line *my_headerlist;
++ struct stat statbuf;
++ int i,j;
++
++ 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;
++ };
++
++ /* 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);
++ 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;
++ file_extensions = NULL;
++ spam_ok = 0;
++ malware_ok = 0;
++
++ if (spool_mbox_ok) {
++ uschar mbox_path[1024];
++ uschar file_path[1024];
++ int n;
++ struct dirent *entry;
++ DIR *tempdir;
++
++ spool_mbox_ok = 0;
++
++ snprintf(CS mbox_path, 1024, "%s/scan/%s", spool_directory, 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, message_id, entry->d_name);
++ if ( (Ustrcmp(entry->d_name,"..") != 0) && (Ustrcmp(entry->d_name,".") != 0) )
++ n = unlink(CS file_path);
++ } while (n > -1);
++
++ closedir(tempdir);
++
++ /* remove directory */
++ n = rmdir(CS mbox_path);
++ };
++}
+diff -urN exim-4.20-orig/src/spool_out.c exim-4.20/src/spool_out.c
+--- exim-4.20-orig/src/spool_out.c Mon May 12 15:39:22 2003
++++ exim-4.20/src/spool_out.c Wed May 14 12:04:24 2003
+@@ -200,6 +200,7 @@
+ 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");
+
+diff -urN exim-4.20-orig/src/tnef.c exim-4.20/src/tnef.c
+--- exim-4.20-orig/src/tnef.c Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/tnef.c Wed May 14 12:04:24 2003
+@@ -0,0 +1,741 @@
++/*************************************************
++* 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 > 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 > 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 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;
++ uint8 *ptr;
++
++ /* 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);
++
++ /* 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 %lx got %lx\n",TNEF_SIGNATURE,read_32(tsp));
++ }
++
++ /* Move tsp pointer along
++ */
++ tsp += sizeof(TNEF_SIGNATURE);
++
++ 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 at %d 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;
++ int dump = 0;
++ int x;
++
++ 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.20-orig/src/tnef.h exim-4.20/src/tnef.h
+--- exim-4.20-orig/src/tnef.h Thu Jan 1 01:00:00 1970
++++ exim-4.20/src/tnef.h Wed May 14 12:04:24 2003
+@@ -0,0 +1,1839 @@
++/*************************************************
++* 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
++*/
++
++#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 */