diff options
author | Jack Morgan <jmorgan@gentoo.org> | 2003-06-11 07:25:08 +0000 |
---|---|---|
committer | Jack Morgan <jmorgan@gentoo.org> | 2003-06-11 07:25:08 +0000 |
commit | 5a61f999f62b254221ea9a903af8d6827465e15c (patch) | |
tree | 703a36daf2c046a13afdc0b4ab6b438a6070ad3e /net-mail/exim | |
parent | updated use.local.desc for eximscan (diff) | |
download | gentoo-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/ChangeLog | 10 | ||||
-rw-r--r-- | net-mail/exim/Manifest | 5 | ||||
-rw-r--r-- | net-mail/exim/exim-4.20.ebuild | 194 | ||||
-rw-r--r-- | net-mail/exim/files/digest-exim-4.20 | 1 | ||||
-rw-r--r-- | net-mail/exim/files/exiscan-acl-4.20-09.patch | 6707 |
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, ®ex_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 */ |