Page MenuHome GnuPG

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/ChangeLog b/ChangeLog
index 66cdcaf..946bfab 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,249 +1,253 @@
+2009-08-28 Werner Koch <wk@g10code.com>
+
+ * configure.ac [GCC]: Test for -Wno-pointer-sign.
+
2009-06-18 Werner Koch <wk@g10code.com>
Release 1.0.0.
2009-02-27 Werner Koch <wk@g10code.com>
Release 0.10.19.
2009-01-28 Werner Koch <wk@g10code.com>
Release 0.10.18.
* po/de.po: s/Unterschrift/Signatur/.
2008-11-14 Werner Koch <wk@g10code.com>
Release 0.10.17.
2008-11-11 Werner Koch <wk@g10code.com>
Release 0.10.16.
2008-10-27 Werner Koch <wk@g10code.com>
- * forms/gpgol-cs_de.cfg: New.
+ * forms/gpgol-cs_de.cfg: New.
2008-08-06 Werner Koch <wk@g10code.com>
Release 0.10.15.
* forms/sign-l.ico, forms/sign-s.ico: New.
* forms/encr-l.ico, forms/encr-s.ico: New
2008-08-04 Werner Koch <wk@g10code.com>
* Makefile.am (SUBDIRS): Add forms.
* forms/Makefile.am: New.
* forms/gpgol_de.cfg, forms/gpgol-ms_de.cfg: New.
2008-06-04 Werner Koch <wk@g10code.com>
* doc/gpgol.texi (Assuan Protocol): Remove protocol specs. They
are now part of the GPGME manual.
2008-05-28 Werner Koch <wk@g10code.com>
* Release 0.10.14.
2008-05-06 Werner Koch <wk@g10code.com>
* Release 0.10.13.
2008-04-16 Werner Koch <wk@g10code.com>
* Release 0.10.12.
2008-04-04 Werner Koch <wk@g10code.com>
* Release 0.10.11.
2008-04-02 Werner Koch <wk@g10code.com>
* Release 0.10.10.
2008-04-01 Werner Koch <wk@g10code.com>
* configure.ac (AC_INIT): Fix quoting.
2008-03-19 Werner Koch <wk@g10code.com>
* Release 0.10.9.
2008-03-18 Werner Koch <wk@g10code.com>
* Release 0.10.8.
2008-03-11 Werner Koch <wk@g10code.com>
* Release 0.10.7.
2008-03-10 Werner Koch <wk@g10code.com>
* Release 0.10.6.
2008-03-07 Werner Koch <wk@g10code.com>
* configure.ac (SVN_REVISION): New AC_DEFINE.
2008-03-06 Werner Koch <wk@g10code.com>
* doc/gpgol.texi (SIGN): Extend the SIGNER command to allow
suggestion of a protocol.
2008-02-18 Werner Koch <wk@g10code.com>
Release 0.10.5 development version.
2008-02-15 Werner Koch <wk@g10code.com>
* po/de.po: Describe more explicit on how to start the UI-server.
2008-02-06 Werner Koch <wk@g10code.com>
Released 0.10.4 development version.
2007-12-10 Werner Koch <wk@g10code.com>
Released 0.10.3 development version.
2007-11-12 Werner Koch <wk@g10code.com>
Released 0.10.2 development version.
2007-10-22 Werner Koch <wk@g10code.com>
Released 0.10.1 development version.
2007-10-12 Werner Koch <wk@g10code.com>
* po/POTFILES.in: Add more files.
2007-10-11 Werner Koch <wk@g10code.com>
Released 0.10.0 development version.
2007-10-05 Werner Koch <wk@g10code.com>
* doc/: New.
* doc/Makefile.am: New.
* doc/gpgol.texi: New.
* doc/gpl.texi: New.
2007-09-25 Werner Koch <wk@g10code.com>
* configure.ac: Check for libassuan.
2007-09-17 Werner Koch <wk@g10code.com>
* autogen.sh (FORCE): Add --force option.
2007-08-13 Marcus Brinkmann <marcus@g10code.de>
* configure.ac (svn_revision): Update to latest version from gnupg.
2006-12-13 Werner Koch <wk@g10code.com>
* po/LINGUAS: Added sv.
2006-12-13 Daniel Nylander <po@danielnylander.se> (wk)
* po/sv.po: New.
2006-10-13 Werner Koch <wk@g10code.com>
Released 0.9.91.
2006-08-28 Werner Koch <wk@g10code.com>
Released 0.9.90.
2006-04-25 Werner Koch <wk@g10code.com>
Released 0.9.10.
2006-04-24 Werner Koch <wk@g10code.com>
Released 0.9.9.
* configure.ac: Use M4 macros to get the actual SVN revision.
2006-03-28 Werner Koch <wk@g10code.com>
2006-03-21 Werner Koch <wk@g10code.com>
Released 0.9.7.
2006-01-26 Werner Koch <wk@g10code.com>
Released 0.9.6.
2005-12-07 Werner Koch <wk@g10code.com>
Released 0.9.5.
2005-12-06 Werner Koch <wk@g10code.com>
Released 0.9.4.
2005-12-02 Werner Koch <wk@g10code.com>
* Makefile.am (DISTCHECK_CONFIGURE_FLAGS): New.
2005-11-30 Werner Koch <wk@g10code.com>
* po/de.po: New.
* po/: New; created by autopoint.
* po/Makevars: New.
* m4/Makefile.am: Add new m4 files.
* autogen.sh: Detect gettext.
* configure.ac: Check for gettext.
2005-11-15 Werner Koch <wk@g10code.com>
* configure.ac (BUILD_TIMESTAMP): Include SVN revision.
(AM_INIT_AUTOMAKE): Fixed invocation.
2005-10-21 Marcus Brinkmann <marcus@g10code.de>
* m4/gpg-error.m4: New file.
* configure.ac: Also check for gpg-error.
2005-10-11 Werner Koch <wk@g10code.com>
* configure.ac: Use MS style bitfields.
2005-10-06 Marcus Brinkmann <marcus@g10code.de>
* configure.ac: Change AC_CONFIG_SRCDIR argument to src/gpgol.def.
2005-09-29 Werner Koch <wk@g10code.com>
Released 0.9.3.
2005-09-22 Werner Koch <wk@g10code.com>
Released 0.9.2.
2005-09-19 Werner Koch <wk@g10code.com>
Released 0.9.1.
2005-09-04 Werner Koch <wk@g10code.com>
Released 0.9.0.
2005-08-30 Werner Koch <wk@g10code.com>
Renamed project to gpgol.
2005-08-14 Werner Koch <wk@g10code.com>
* configure.ac: Build src/versioninfo.rc.
2005-08-10 Werner Koch <wk@g10code.com>
* configure.ac: Check for and define DLLTOOL.
diff --git a/NEWS b/NEWS
index dc403ff..44756dc 100644
--- a/NEWS
+++ b/NEWS
@@ -1,397 +1,401 @@
+Noteworthy changes for version 1.0.1 (not released)
+===================================================
+
+
Noteworthy changes for version 1.0.0 (2009-06-18)
=================================================
* Show a notice about potential problems.
* After about 2 years of development, the 1.0 version is now due.
Noteworthy changes for version 0.10.19 (2009-02-27)
===================================================
* Save the crypto settings in a message draft.
* Unnamed attachments are now shown with a suffix matching its MIME
type.
Noteworthy changes for version 0.10.18 (2009-01-28)
===================================================
* Handle OL created S/MIME messages.
Noteworthy changes for version 0.10.17 (2008-11-14)
===================================================
* Minor cleanups.
* All operations are now somewhat faster.
Noteworthy changes for version 0.10.16 (2008-11-11)
===================================================
* Fixed a regression in the last release with opaque signatures.
* Fixed PGP cleartext signature verification.
* Encryption of attachments is now much faster.
Noteworthy changes for version 0.10.15 (2008-08-06)
===================================================
* New option to present the body of a message as an attachment. This
is useful to make sure that the body will never show up as
plaintext in the message store.
* New menu item to remove all GpgOL created flags and attachments
from all messages in a folder.
* Icons are now installed for messages processed by GpgOL. For now
only for the German version of Outlook.
Noteworthy changes for version 0.10.14 (2008-05-28)
===================================================
* Minor fixes.
Noteworthy changes for version 0.10.13 (2008-05-06)
===================================================
* Properly handle the disposition of text attachments.
Noteworthy changes for version 0.10.12 (2008-04-16)
===================================================
* Added icons.
* Minor usuability changes.
Noteworthy changes for version 0.10.11 (2008-04-04)
===================================================
* Fixed a performance problem with signed+encrypted.
Noteworthy changes for version 0.10.10 (2008-04-02)
===================================================
* Visual cleanups.
* Changes to the I/O dispatcher.
Noteworthy changes for version 0.10.9 (2008-03-19)
==================================================
* Decrypt opaque signed and encrypted S/MIME mails.
* Handle old-style PGP message with attachments. Note that the
signature verification currently may indicate a bad signature.
Noteworthy changes for version 0.10.8 (2008-03-18)
==================================================
* Fixed a segv introduced with 0.10.6.
Noteworthy changes for version 0.10.7 (2008-03-11)
==================================================
* Changed the way sign+encrypt works to help the UI-server.
Noteworthy changes for version 0.10.6 (2008-03-10)
==================================================
* More tweaks to allow processing of opaque encrypted or signed
S/MIME.
* Shows an error message when trying to decrypt/verify messages not
signed or encrypted.
* Soft line breaks in QP encoded messages are now correctly
processed.
* The sender's address is send to the UI server to allow it to select
an appropriate signing key.
* Automatic protocol selection works now also with signing.
* Processing large messages is faster.
Noteworthy changes for version 0.10.5 (2008-02-18)
==================================================
* PGP inline encrypted mails are not anymore deleted after the first
decryption.
Noteworthy changes for version 0.10.4 (2008-02-06)
==================================================
* Sign and encrypt works now.
* Texts with embedded attachments are now concatenated.
* Encrypted message are now viewable in the sent messages folder.
Noteworthy changes for version 0.10.3 (2007-12-10)
==================================================
* Minor fixes.
Noteworthy changes for version 0.10.2 (2007-11-12)
==================================================
* New menu items to select the default protocol.
* Code cleanups.
Noteworthy changes for version 0.10.1 (2007-10-22)
==================================================
* Auto start the server.
* Code cleanups.
* Made all dialogs language neutral.
* The manual has some notes about the Registry usage and new MAPI
properties.
Noteworthy changes for version 0.10.0 (2007-10-11)
==================================================
* Basically a complete rewrite. A lot of things are still missing but
if might be useful to see the direction the development takes.
Noteworthy changes for version 0.9.91 (2006-10-13)
==================================================
* Fixed a crash in the recipients dialog.
Noteworthy changes for version 0.9.90 (2006-08-28)
==================================================
* Fix problem that message would be sent in clear
text if the user cancelled the operation.
* Cosmetic updates for some dialogs.
* Do not show the 'select signer dialog' when only
one secret key is available in the keyring.
* Fixes for the automatic key selection algorithm
used in the recipient key dialog.
Noteworthy changes for version 0.9.10 (2006-04-25)
==================================================
* Fixes for Umlaut problems.
Noteworthy changes for version 0.9.9 (2006-04-24)
=================================================
* Some cosmetic changes.
* Encryption to the default key works again.
Noteworthy changes for version 0.9.8 (2006-03-28)
=================================================
* PGP/MIME signature verification may now work in some cases.
* New option to prefer displaying of the HTML part.
Noteworthy changes for version 0.9.7 (2006-03-21)
=================================================
* Minor changes
Noteworthy changes for version 0.9.6 (2006-01-26)
=================================================
* Cosmetic fixes.
Noteworthy changes for version 0.9.5 (2005-12-07)
=================================================
* Fixed problems related to use on non-admin accounts.
* Print a warning if used with OL prior to OL2003 SP2.
Noteworthy changes for version 0.9.4 (2005-12-06)
=================================================
* Added translation framework. Provided German translation.
* New option to enable automatic decryption in the preview window.
* Removed deprecated options to configure gpg path and homedir.
* Default key from the option dialog works.
* Support for HTML mails.
Noteworthy changes for version 0.9.3 (2005-09-29)
=================================================
* Fixed bugs introduced with the last release.
* PGP/MIME decryption works now correctly with Latin-1 and utf-8.
* No more pop-ups to ask whether to save changes after just decrypting
a message.
* Fixed a couple of bugs possibly leading to crashes.
Noteworthy changes for version 0.9.2 (2005-09-22)
=================================================
* Saving attachments from PGP/MIME encrypted messages works.
Noteworthy changes for version 0.9.1 (2005-09-19)
=================================================
* Bug fixes
Noteworthy changes for version 0.9.0 (2005-09-04)
=================================================
* Major rewrite. Renamed the package to GPGol. Note, that there used
to be intermediate versions unter the name OutlGPG
* The package as been renamed to GPGol and consist of only one DLL
named "gpgol.dll". Installation of gpgme.dll and libgpg-error.dll
is required.
* It may by now only be build using the Mingw32 toolchain.
* GPGol now uses the standard GPGME.
Noteworthy changes for version 0.6.1 (unreleased)
=================================================
* Fix the problem that the user can just reply with
the encrypted text.
* Fixes for a lot of minor problems with NT5 based
systems and for Outlook version 2003.
* Support for handling HTML mails.
This includes the encryption of the contents and
the proper decryption without losing the special
(html) text attributes like colors.
* Support for '%ENV%' strings for the log file.
Noteworthy changes for version 0.5.5 (2005-07-12)
=================================================
* Support to sign all outgoing attachments.
* Support for logging.
* Fixed some memory leaks.
Noteworthy changes for version 0.5.4 (2005-07-03)
=================================================
* Support for securing attachments.
This means the all attachments will be encrypted
if encryption has been selected for the message.
* A new option to allow to save decrypted attachments
to the disk.
* Several bug fixes all over the place.
Noteworthy changes for version 0.5.3 (2005-06-16)
=================================================
* Allow to set a default key which is used automatically
for encryption.
* Handle old V3 keys in the signature verification dialog.
* Issue and error if the encrypt process returned invalid
recipients.
Noteworthy changes for version 0.5.2 (2005-06-05)
=================================================
* Differ between possible decryption failures.
- General errors.
- No secret key available.
* Add a 'encrypt-to' listbox to the decryption dialog
to know the recipients the message was encrypted for.
* Add some checks to report problems with permissions
related to the Registry.
* Fixed a format string problem which was possible for
crashes when the signature has been expired.
Noteworthy changes for version 0.5.1 (2005-05-29)
=================================================
* Issue a warning if the user cancels the sign or
encryption procedure.
* Support to read and write X- headers for messages.
* Fixed a problem which crashes Outlook if the keyManager
exe did not exist but was set in the registry.
Noteworthy changes for version 0.4.0 (2005-05-10)
=================================================
* Verify dialog is automatically shown whenever needed. Plus it
contains a hint-label whenever the signature is special. For
example the signature has expire or it was issued by a key which
is not trustworthy.
* Offer a GPG configuration dialog to set the path to GPG, the home
directory and an optional field to specify a key manager.
* Common dialogs for the following procedures:
- verify a clearsign signature
- decrypt a message (and verify a signature)
- encrypt a message (and sign the plaintext)
- clearsign a message
* Provide a class to encapsulate MAPI messages and high-level functions
for all crypto operations.
diff --git a/configure.ac b/configure.ac
index d8e4592..a4d67e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,271 +1,284 @@
# configure.ac - for GpgOL
# Copyright (C) 2005, 2006, 2007, 2008 g10 Code GmbH
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
min_automake_version="1.9.4"
# Remember to change the version number immediately *after* a release.
# Set my_issvn to "yes" for non-released code. Remember to run an
# "svn up" and "autogen.sh" right before creating a distribution.
-m4_define([my_version], [1.0.0])
-m4_define([my_issvn], [no])
+m4_define([my_version], [1.0.1])
+m4_define([my_issvn], [yes])
m4_define([svn_revision], m4_esyscmd([echo -n $( (svn info 2>/dev/null \
|| echo 'Revision: 0')|sed -n '/^Revision:/ {s/[^0-9]//gp;q;}')]))
AC_INIT([gpgol],
[my_version[]m4_if(my_issvn,[yes],[-svn[]svn_revision])],
[bug-gpgol@g10code.com])
NEED_GPG_ERROR_VERSION=1.4
NEED_GPGME_API=1
NEED_GPGME_VERSION=1.1.0
NEED_LIBASSUAN_API=1
NEED_LIBASSUAN_VERSION=1.0.4
SVN_REVISION=svn_revision
PACKAGE=$PACKAGE_NAME
PACKAGE_GT=${PACKAGE_NAME}
VERSION=$PACKAGE_VERSION
AC_CONFIG_SRCDIR(src/gpgol.def)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_TARGET()
AM_INIT_AUTOMAKE
AC_GNU_SOURCE
have_gpg_error=no
have_libassuan=no
AC_SUBST(PACKAGE)
AC_SUBST(PACKAGE_GT)
AC_SUBST(VERSION)
AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of this package])
AC_DEFINE_UNQUOTED(PACKAGE_GT, "$PACKAGE_GT",
[Name of this package for gettext])
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version of this package])
AC_DEFINE_UNQUOTED(PACKAGE_BUGREPORT, "$PACKAGE_BUGREPORT",
[Bug report address])
AC_DEFINE_UNQUOTED(NEED_GPGME_VERSION, "$NEED_GPGME_VERSION",
[Required version of GPGME])
AC_DEFINE_UNQUOTED(SVN_REVISION, ${SVN_REVISION}, [Current SVN revision])
BUILD_TIMESTAMP=`date --iso-8601=minutes`
AC_SUBST(BUILD_TIMESTAMP)
changequote(,)dnl
BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'`
changequote([,])dnl
BUILD_FILEVERSION="${BUILD_FILEVERSION}${SVN_REVISION}"
AC_SUBST(BUILD_FILEVERSION)
AH_BOTTOM([
/* Some global constants. */
/* Force using of NLS for W32 even if no libintl has been found. This is
okay because we have our own gettext implementation for W32. */
#if defined(HAVE_W32_SYSTEM) && !defined(ENABLE_NLS)
#define ENABLE_NLS 1
#endif
])
AM_MAINTAINER_MODE
# Checks for programs.
AC_PROG_MAKE_SET
AM_SANITY_CHECK
missing_dir=`cd $ac_aux_dir && pwd`
AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir)
AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir)
AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir)
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AC_PROG_CXX
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_RANLIB
AC_CHECK_TOOL(AR, ar, :)
AC_CHECK_TOOL(DLLTOOL, dlltool, :)
AC_CHECK_TOOL(WINDRES, windres, :)
have_dosish_system=no
have_w32_system=no
case "${host}" in
*-mingw32*)
# special stuff for Windoze NT
ac_cv_have_dev_random=no
AC_DEFINE(USE_ONLY_8DOT3,1,
[set this to limit filenames to the 8.3 format])
AC_DEFINE(HAVE_DRIVE_LETTERS,1,
[defined if we must run on a stupid file system])
have_dosish_system=yes
have_w32_system=yes
;;
*)
AC_MSG_ERROR([[
***
*** This software my only be build for W32 systems. Use
*** ./autogen.sh --build-w32
*** to prepare it for such a build.
***]])
;;
esac
if test "$have_dosish_system" = yes; then
AC_DEFINE(HAVE_DOSISH_SYSTEM,1,
[Defined if we run on some of the PCDOS like systems
(DOS, Windoze. OS/2) with special properties like
no file modes])
fi
AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes)
if test "$have_w32_system" = yes; then
AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system])
fi
AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes)
#
# Checks for libraries.
#
AM_PATH_GPGME("$NEED_GPGME_API:$NEED_GPGME_VERSION",
have_gpgme=yes,have_gpgme=no)
AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION",
have_gpg_error=yes,have_gpg_error=no)
AC_DEFINE(GPG_ERR_SOURCE_DEFAULT, GPG_ERR_SOURCE_USER_2,
[The default error source for GpgOL.])
AM_PATH_LIBASSUAN("$NEED_LIBASSUAN_API:$NEED_LIBASSUAN_VERSION",
have_libassuan=yes,have_libassuan=no)
AH_BOTTOM([
/* We don't want the old assuan codes anymore. */
#define _ASSUAN_ONLY_GPG_ERRORS 1
])
# Note, that autogen.sh greps for the next line.
AM_GNU_GETTEXT_VERSION(0.12.1)
AM_GNU_GETTEXT([external])
# We always want NLs, despite what the above macro figures out.
USE_NLS=yes
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(string.h unistd.h langinfo.h termio.h locale.h)
AC_CHECK_FUNCS(stpcpy)
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_C_INLINE
AC_TYPE_SIZE_T
AC_TYPE_SIGNAL
AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes)
# Add some extra libs here so that previous tests don't fail for
# mysterious reasons - the final link step should bail out.
if test "$have_w32_system" = yes; then
W32LIBS="-lwsock32"
fi
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -mms-bitfields"
CXXFLAGS="$CXXFLAGS -Wall -mms-bitfields"
if test "$USE_MAINTAINER_MODE" = "yes"; then
CFLAGS="$CFLAGS -Wcast-align -Wshadow -Wstrict-prototypes"
CFLAGS="$CFLAGS -Wno-format-y2k -Wformat-security"
CFLAGS="$CFLAGS -W -Wno-sign-compare"
CXXFLAGS="$CXXFLAGS -Wcast-align -Wshadow"
CXXFLAGS="$CXXFLAGS -Wno-format-y2k -Wformat-security"
CXXFLAGS="$CXXFLAGS -W -Wno-sign-compare"
AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers])
_gcc_cflags_save=$CFLAGS
CFLAGS="-Wno-missing-field-initializers"
AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]),_gcc_mfi=yes,_gcc_mfi=no)
AC_MSG_RESULT($_gcc_mfi)
CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_mfi" = xyes ; then
- CFLAGS="$CFLAGS -Wno-missing-field-initializers"
+ CFLAGS="$CFLAGS -Wno-missing-field-initializers"
CXXFLAGS="$CXXFLAGS -Wno-missing-field-initializers"
fi
fi
+
+ AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign])
+ _gcc_cflags_save=$CFLAGS
+ CFLAGS="-Wno-pointer-sign"
+ AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]),_gcc_psign=yes,_gcc_psign=no)
+ AC_MSG_RESULT($_gcc_psign)
+ CFLAGS=$_gcc_cflags_save;
+ if test x"$_gcc_psign" = xyes ; then
+ CFLAGS="$CFLAGS -Wno-pointer-sign"
+ CXXFLAGS="$CXXFLAGS -Wno-pointer-sign"
+ fi
+
+
fi
AC_SUBST(W32LIBS)
#
# Print errors here so that they are visible all
# together and the user can acquire them all together.
#
die=no
if test "$have_gpg_error" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libgpg-error to build this program.
** This library is for example available at
*** ftp://ftp.gnupg.org/pub/gcrypt/libgpg-error
*** (at least version $NEED_GPG_ERROR_VERSION is required.)
***]])
fi
if test "$have_gpgme" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need gpgme to build this program.
** This library is for example available at
*** ftp://ftp.gnupg.org/gcrypt/gpgme/
*** (at least version $NEED_GPGME_VERSION is required.)
***]])
fi
if test "$have_libassuan" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need libassuan to build this program.
*** This library is for example available at
*** ftp://ftp.gnupg.org/pub/gcrypt/libassuan/
*** (at least version $NEED_LIBASSUAN_VERSION is required).
***]])
fi
if test "$die" = "yes"; then
AC_MSG_ERROR([[
***
*** Required libraries not found. Please consult the above messages
*** and install them before running configure again.
***]])
fi
AC_CONFIG_FILES([ Makefile
src/Makefile
src/versioninfo.rc
forms/Makefile
doc/Makefile
po/Makefile.in
m4/Makefile
])
AC_OUTPUT
diff --git a/src/ChangeLog b/src/ChangeLog
index 8ad237a..94529a3 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,2351 +1,2359 @@
+2009-08-28 Werner Koch <wk@g10code.com>
+
+ * mimemaker.c (do_mime_sign): Comment cleanup.
+
+2009-08-27 Werner Koch <wk@g10code.com>
+
+ * mapihelp.cpp (mapi_get_sender): Add hack for Kleopatra.
+
2009-08-21 Werner Koch <wk@g10code.com>
* engine-assuan.c (async_worker_thread): Disable the use of
MsgWaitForMultipleObjects.
* common.h (struct compat): Add USE_MWFMO.
* main.c (read_options): Read that flag.
2009-08-19 Werner Koch <wk@g10code.com>
* message.cpp (ul_release): Add arg LNR. Change all callers.
* ol-ext-callback.cpp (ul_release): Add args FUNC and LNR. Change
all callers.
* item-events.cpp (ul_release): ditto.
* session-events.cpp (ul_release): Ditto.
2009-07-21 Werner Koch <wk@g10code.com>
* mapihelp.cpp (create_gpgol_tag, get_internetcharsetbody_tag)
(mapi_set_header): Release data returned from GetIDsFromNames.
* engine.h (ENGINE_FLAG_SIGN_FOLLOWS): New macro.
* engine.c (engine_encrypt_prepare): Add arg flags. Change callers.
* engine-assuan.c (op_assuan_encrypt): Ditto. send PREP_ENCRYPT
if the sign-follows flag is used.
* mimemaker.c (mime_sign_encrypt): Pass new flag.
2009-06-18 Werner Koch <wk@g10code.com>
* common.h (struct opt): Add ANNOUNCE_NUMBER.
* main.c (write_options, read_options): Store and load that number.
2009-02-26 Werner Koch <wk@g10code.com>
* mimeparser.c (start_attachment): Try to figure out a good file
name suffix for the FILENAME property.
* ext-commands.cpp (InstallCommands): Disable decrypt button for
non GpgOl messages.
* engine.c (engine_encrypt_prepare): Add arg SENDER.
* engine-assuan.c (op_assuan_encrypt): Ditto.
* mimemaker.c (do_mime_sign): Free sender string.
(mime_encrypt): Pass the sender address to the engine.
(mime_sign_encrypt): Ditto.
2009-02-25 Werner Koch <wk@g10code.com>
* mapihelp.cpp (get_gpgoldraftinfo_tag): New.
(mapi_get_gpgol_draft_info, mapi_set_gpgol_draft_info): New.
* ext-commands.cpp (DoCommand): Save encryption selection.
(InstallCommands): Get encryption selection from the draft info.
* mimemaker.c (finalize_message): Delete the property.
2009-01-28 Werner Koch <wk@g10code.com>
* mimeparser.c (t2body): Take care of x-pkcs7-mime as used by
native OL signed messages.
(mime_decrypt): Ditto.
2009-01-16 Werner Koch <wk@g10code.com>
* engine-assuan.c (attach_thread_input_wndw_proc)
(attach_thread_input): Enable code.
(async_worker_thread): Replace WFMO by MsgWaitForMultipleObjects
and add message dispatcher loops.
2008-11-27 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_get_sender): Extract the CN name if available.
2008-11-26 Werner Koch <wk@g10code.com>
* ext-commands.cpp (InstallCommands): Don't show status icon for
non-gpgol messages.
* config-dialog.c (start_key_manager): Remove.
* ext-commands.cpp (DoCommand): Do not use start_key_manager as a
fallback.
* olflange.cpp (GpgolExt): Show new version warning only once.
* dialogs.rc: Do not display the logo. Move version info around.
* dialogs.h (IDC_G10CODE_STRING): New.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Active product page by a
click on IDC_G10CODE_STRING.
(set_labels): Remove the build date from the Version field.
2008-11-14 Werner Koch <wk@g10code.com>
* dialogs.rc: Remove preview-decrypt check button.
* olflange-dlgs.cpp (set_labels): Ditto.
2008-11-13 Werner Koch <wk@g10code.com>
* engine.c (FILTER_BUFFER_SIZE): Increase from 4k to 128k.
(switch_threads, clear_switch_threads): Remove.
(engine_filter): Pulse event only if anything happened. Break
loop if input data has been put completely into the buffer. Add a
short delay for an idle loop.
* engine-assuan.c (work_item_s): Increase buffer from 1k to 8k.
(switch_threads, clear_switch_threads): Remove.
2008-11-10 Werner Koch <wk@g10code.com>
* engine.c (engine_init): Allow the user to try again if the
server did not come up.
* engine-assuan.c (connect_uiserver, op_assuan_init): Add a hack
to reset the retry counter.
2008-11-03 Werner Koch <wk@g10code.com>
* mimemaker.c (do_mime_sign): Add arg SESSION_NUMBER and pass it
to the sign operation. Set session title.
(mime_sign): Create a new session number.
(mime_sign_encrypt): Pass a session number and title to the engine.
(mime_encrypt): Ditto.
* engine-assuan.c (op_assuan_encrypt, op_assuan_sign): Send
session info.
2008-10-29 Werner Koch <wk@g10code.com>
* engine.c (engine_filter): Collect more data in the in buffer.
* mimemaker.c (write_b64): Buffer up to 2k of output.
2008-10-27 Werner Koch <wk@g10code.com>
* mimemaker.c (mime_encrypt): Check for an empty message before
creating the filter. Return a suitable error code.
(do_mime_sign): Ditto.
(mime_sign_encrypt): Ditto.
(mime_sign): Return an error code.
* message.cpp (sign_encrypt): Show an error message for empty
messages.
(message_sign): Ditto.
* olflange.cpp (install_forms): Add gpgol-cs.
* mapihelp.cpp (get_internetcharsetbody_tag): New.
(mapi_get_body_as_stream): Try the new tag first.
(get_msgcls_from_pgp_lines): Ditto. Remove the simple access
method.
2008-10-24 Werner Koch <wk@g10code.com>
* mimeparser.c (struct mime_context): Add flag MAY_BE_OPAQUE_SIGNED.
(t2body): Set that flag.
(is_cms_signed_data): New.
(mime_decrypt): Try to verify if the content is opaque signed
without proper MIME headers.
2008-10-23 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_delete_gpgol_body_attachment): New.
* message-events.cpp (OnWriteComplete): Remove a body attachment.
* message.cpp (message_display_handler): Do not display PGP
clearsigned messages.
(message_display_handler): Do not update GpgOLStatus; it is not
used anyway.
(pgp_mime_from_clearsigned): Fix bogus trailing white space
removal code. Insert an empty line.
* mimeparser.c (mime_verify): Add arg MIMEHACK.
(message_verify): Use it.
2008-10-17 Werner Koch <wk@g10code.com>
* recipient-dialog.c (load_rsetbox): Remove superfluous check on
negativness for an unsigned variable.
* mimeparser.c (mime_verify_opaque): Remove extra semicolon which
shortcuted most of the code. Why didn't gcc notice that? Bug
was introduced on 2008-06-12.
* engine-assuan.c (create_io_pipe, send_options)
(op_assuan_encrypt, op_assuan_sign, op_assuan_decrypt)
(op_assuan_verify): Replace use of long in snprint by int to
workaround a bug in mingw32. Doesn't matter on w32 anyway.
2008-10-16 Werner Koch <wk@g10code.com>
* mimeparser.c (start_attachment): Take care not to set the file
name "smime.p7m".
* mapihelp.cpp (is_really_cms_encrypted): Extend to detect unknown
message types.
(mapi_change_message_class): Adjust for this change.
(mapi_change_message_class): Factor code out to ...
(change_message_class_ipm_note)
(change_message_class_ipm_note_smime)
(change_message_class_ipm_note_smime_multipartsigned)
(change_message_class_ipm_note_secure_cex): New.
(get_first_attach_mime_tag): New.
(change_message_class_ipm_note_secure_cex): Use it here for CexSig.
(has_smime_filename): Also look at the long filename.
2008-10-15 Werner Koch <wk@g10code.com>
* engine-assuan.c (op_assuan_sign): Send the new --protocol option
to the server.
2008-09-30 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_change_message_class): Special handling for
MultipartSigned if S/MIME support is disabled.
2008-08-06 Werner Koch <wk@g10code.com>
* olflange-dlgs.cpp (GPGOptionsDlgProc): Disable S/MIME notice.
2008-08-05 Werner Koch <wk@g10code.com>
* ext-commands.cpp (InstallCommands): Display protocolicons only
for OL2007.
* olflange.cpp (get_ol_main_version): New.
* message.cpp (message_decrypt): Save a signature verification
result.
* mimeparser.c (mime_decrypt): Implement verification of included
signatures. This feature got lost during the removal of nested
crypto operations.
2008-08-04 Werner Koch <wk@g10code.com>
* olflange.cpp (install_forms): New.
(GpgolExt): Install forms if needed.
* common.c (get_data_dir): New.
* common.h (struct): Add field FORMS_REVISION.
* main.c (read_options, write_options): Read and write that option.
2008-07-31 Werner Koch <wk@g10code.com>
* ext-commands.h (class GpgolExtCommands): Add m_nCmdRevertFolder.
* ext-commands.cpp (GpgolExtCommands, InstallCommands): Ditto.
(DoCommand): Implement RevertFolder command.
* common.h (struct): Add variable DISABLE_GPGOL.
* session-events.cpp (OnDelivery): Make use of that variable.
* message.cpp (message_incoming_handler): Ditto.
* user-events.cpp (OnSelectionChange): Ditto
* message-events.cpp (OnRead, OnReadComplete, OnWrite)
(OnWriteComplete): Ditto
* mapihelp.cpp (mapi_get_int_prop): New.
* olflange.cpp (Install): Improve version check.
* revert.cpp, revert.h: New.
* mapihelp.cpp (mapi_attachment_to_body): New.
(mapi_get_old_message_class): New.
(mapi_change_message_class): Do not release newvalue when saving
the old class.
* olflange.cpp (parse_version_number, parse_version_string)
(compare_versions, gpgol_check_version): New.
* ext-commands.cpp (DoCommand): Support a "revert message class"
debug command.
2008-06-27 Werner Koch <wk@g10code.com>
* mapihelp.cpp (get_gpgololdmsgclass_tag): New.
(mapi_change_message_class): Save old message class.
2008-06-19 Werner Koch <wk@g10code.com>
* olflange-dlgs.cpp (GPGOptionsDlgProc): Change S/MIME enabled
message icon.
* olflange.cpp (GpgolExt): Ditto for the new version installed
notice.
2008-06-12 Werner Koch <wk@g10code.com>
* dialogs.rc: Add button for calling the engine's configuration.
* dialogs.h (IDC_GPG_CONF): New.
* engine.c (engine_start_confdialog): New.
* engine-assuan.c (op_assuan_start_confdialog): New.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Act upon the button.
* mapihelp.cpp (mapi_get_from_address): New.
* engine.c (engine_decrypt_start, engine_verify_start): Add new
arg FROM_ADDRESS.
* engine-assuan.c (op_assuan_verify, op_assuan_decrypt): Ditto.
* mimeparser.c (mime_verify, mime_verify_opaque, mime_decrypt):
Pass FROM_ADDRESS to the backend.
* olflange.cpp (DllUnregisterServer): Delete CLSIDs.
2008-06-05 Werner Koch <wk@g10code.com>
* mimeparser.c (mime_decrypt): Set session number and title.
(mime_verify_opaque, mime_verify): Ditto.
* mapihelp.cpp (mapi_get_subject): New.
* engine.c (engine_set_session_number, engine_set_session_title)
(engine_set_sender_address): New.
(struct engine_filter_s): Add fields session_number, session_title
and sender_address.
(engine_private_get_session_number): New.
(engine_private_get_session_title): New.
(release_filter): Release them.
(engine_new_session_number): New.
* engine-assuan.c (send_session_info): New
(op_assuan_decrypt, op_assuan_verify): Call it.
(op_assuan_sign): Use the end-of-option option for the SENDER command.
2008-05-28 Werner Koch <wk@g10code.com>
* dialogs.h (IDC_BODY_AS_ATTACHMENT): New.
* dialogs.rc: Add body-as-attachemnt checkbox to the otpion
dialog.
* olflange-dlgs.cpp (set_labels, GPGOptionsDlgProc): Add it.
* main.c (write_options, read_options): Handle bodyAsAttachment
registry key.
* mimeparser.c (start_attachment): Do not set the hidden flag if
the new option is used.
* mapihelp.cpp (mapi_test_attach_hidden): New.
(mapi_get_gpgol_body_attachment): Make use of that flag.
2008-05-23 Werner Koch <wk@g10code.com>
* mimemaker.c (struct sink_s): Add field ENC_COUNTER.
(write_buffer_for_cb): Update that.
(mime_encrypt): Bail out if no data has been encrypted.
(mime_sign_encrypt): Ditto.
2008-05-07 Werner Koch <wk@g10code.com>
* mimeparser.c (t2body): Fix last change.
2008-05-02 Werner Koch <wk@g10code.com>
* mimeparser.c (t2body): Detect non-inline text parts.
* rfc822parse.c (rfc822parse_query_parameter): Add special mode
for an ATTR of NULL.
2008-04-16 Werner Koch <wk@g10code.com>
* message-events.h (class GpgolMessageEvents): Add M_GOTINSPECTOR.
* message.cpp (message_incoming_handler): Change return type.
* ext-commands.cpp (check_toolbar, check_menu_toolbar): New.
(update_protocol_menu): Explicitly update the toolbar.
2008-04-15 Werner Koch <wk@g10code.com>
* Outlook.gpl: New.
2008-04-14 Werner Koch <wk@g10code.com>
* display.cpp (is_inspector_display): New.
(find_message_window): Rewrote.
* message-events.cpp (OnRead): Use it.
* message.cpp (message_incoming_handler): Add arg FORCE.
* message-events.cpp (OnRead): Pass false for FORCE.
* item-events.cpp (OnOpen): Ditto.
* ext-commands.cpp (DoCommand): Let CmdCryptoState process and
display the current message again.
(GpgolExtCommands): Remove m_nCmdCheckSig and m_nCmdDecrypt.
2008-04-10 Werner Koch <wk@g10code.com>
* ol-ext-callback.cpp (is_preview_pane_visible)
(show_preview_pane): New.
* display.cpp (update_display): Add arg IS_SENSITIVE and do not
use the OOM method if this is set.
* mapihelp.h (mapi_save_changes): New. Use if everywhere.
(mapi_delete_body_props): Use it to delete body parts.
* mapihelp.cpp (mapi_do_save_changes): New.
* mimemaker.c (finalize_message): Do no delete body parts because
mapi_save_changes does this now.
* mimeparser.c (finish_message): Remove the body property in
protect mode.
2008-04-04 Werner Koch <wk@g10code.com>
* engine-assuan.c (worker_start_read, worker_check_read): Factor
common code out to ..
(write_to_callback): .. new.
(async_worker_thread): Better comments and minor changes.
(enqueue_callback): Add arg INACTIVE.
(set_items_active): New.
(start_command): Set items active.
(op_assuan_encrypt): Create input and output items as inactive.
(async_worker_thread): Handle the inactive flag.
* common.c (gpgol_spawn_detached): Do not inherit handles.
2008-04-02 Werner Koch <wk@g10code.com>
* engine-assuan.c (destroy_command): Add arg FORCE.
(op_assuan_encrypt_bottom): Call destroy_command.
* mimeparser.c (struct mime_context): Use parser_error to return
gpg error codes.
* main.c (read_options): Allow names for debug flags.
* common.c (trim_spaces): New.
2008-03-31 Werner Koch <wk@g10code.com>
* engine-assuan.c (struct work_item_s): Add SWITCH_COUNTER.
(switch_threads, clear_switch_threads): New.
(worker_start_write): Use it.
* engine.c (struct engine_filter_s): Add SWITCH_COUNTER.
(switch_threads, clear_switch_threads): New.
(filter_gpgme_read_cb): Use it.
* ext-commands.h (class GpgolExtCommands): Add m_nCmdCryptoState.
* ext-commands.cpp (InstallCommands): Add a toolbar crypto state
button.
(DoCommand): Show a message when trying to select the disabled
S/MIME protocol.
* message.cpp (message_sign, message_verify, message_decrypt)
(sign_encrypt): Display message boxes only in debug mode.
* olflange-dlgs.cpp: Remove G-Data 2001 copyright because all that
old code has gone.
* dialogs.rc (IDD_EXT_OPTIONS): Remove option to select the key
manager.
(IDD_GPG_OPTIONS): Remove caching time, reorder options, add group
boxes.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Clean up accordingly.
* config-dialog.c (config_dlg_proc): Ditto.
(get_open_file_name, does_file_exist, error_box): Remove.
* ext-commands.cpp: Rename nCmdShowInfo to nCmdDebug0 and enable
it only in debug mode.
2008-03-26 Werner Koch <wk@g10code.com>
* engine-gpgme.c (cleanup): Implement.
(op_gpgme_init): Save thread handle.
(waiter_thread): Check shutdown flags.
* engine-assuan.c (get_uiserver_name): Fallback to GPA.
2008-03-19 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_change_message_class): Look into
multipart/mixed for PGP messages.
* mapihelp.cpp (mapi_get_attach): Add arg UNPROTECT and changed
all callers.
* common.h (DBG_MIME_PARSER, DBG_MIME_DATA): New.
* mimeparser.c (debug_mime_parser, debug_mime_data): New to
replace DEBUG_PARSER.
(struct mime_context): Add field is_opaque_signed.
(t2body): Set it.
(mime_decrypt): Handle an embedded opaque signed S/MIME part.
(mime_verify_opaque): Add arg INBUFER, INBUFFERLEN and
START_PART_COUNTER.
2008-03-18 Werner Koch <wk@g10code.com>
* mimeparser.c (message_cb): Clear all mimestruct fields. Fixes
segv introduced 2008-03-07.
* engine-assuan.c (async_worker_thread): Handle broken pipe.
2008-03-13 Werner Koch <wk@g10code.com>
* mimeparser.c (message_cb): Skip the OPEN event in non-MIME mode.
* rfc822parse.c (rfc822parse_open): Reset ERRNO.
2008-03-11 Werner Koch <wk@g10code.com>
* engine-assuan.c (op_assuan_encrypt): Factor some code out to ..
(op_assuan_encrypt_bottom): .. new.
(engine_assuan_encstate_s): New.
* engine.c (engine_encrypt_start): Split some code into ..
(engine_encrypt_prepare): .. new.
(engine_cancel): Cancel prepared encryption.
* mimemaker.c (mime_encrypt): Use engine_encrypt_prepare and _start.
(mime_sign_encrypt): Likewise, but do the _start only after
completing the signing.
2008-03-10 Werner Koch <wk@g10code.com>
* engine.c (FILTER_BUFFER_SIZE): Increase to 4k.
(engine_filter, engine_wait, engine_wait): Replace Sleep by
SwitchToThread.
* engine-assuan.c (struct work_item_s): Increase buffer to 1k.
(worker_start_write, async_worker_thread): Replace Sleep by
SwitchToThread.
2008-03-07 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_get_sender): New.
* mymapitags.h (PR_PRIMARY_SEND_ACCT): New.
* mimemaker.c (do_mime_sign): Pass the sender to the engine.
* common.h (opt): Add field SVN_REVISION.
* main.c (read_options, write_options): Set it.
* olflange.cpp (GpgolExt): Print a warning on program update.
* engine.c (struct engine_filter_s): Add field ADD_EXTRA_LF.
(engine_request_exra_lf): New.
(engine_wait): Implement that.
* mimeparser.c (mime_decrypt): Add arg SIMPLE_PGP and call
engine_request_exra_lf.
(struct mime_context): Add field NO_MAIL_HEADER.
(message_cb): Implement it.
* message.cpp (message_decrypt): Set that flag for old style PGP.
* common.h (DBG_COMMANDS, debug_commands): New.
* ext-commands.cpp: Use it.
2008-03-06 Werner Koch <wk@g10code.com>
* mimemaker.c (do_mime_sign): Figure out the protocol to use.
* engine.c (engine_sign_start): Add new args SENDER and R_PROTOCOL.
* engine-assuan.c (op_assuan_sign): Ditto. Send SENDER command.
2008-03-05 Werner Koch <wk@g10code.com>
* main.c (read_options): Insert the debug registry key.
(write_options): More debug output.
2008-02-28 Werner Koch <wk@g10code.com>
* olflange-dlgs.cpp (GPGOptionsDlgProc): Fix initial setting of
openpgp and smime state. I wish C would haved used := for
assignments.
2008-02-26 Werner Koch <wk@g10code.com>
* common.c (qp_decode): Add arg S_LBRK.
* mimeparser.c (plaintext_handler, ciphertext_handler): Handle
soft line breaks.
* mapihelp.cpp (mapi_change_message_class): Handle opaque S/MIME
messages without an smime-type parameter.
2008-02-25 Werner Koch <wk@g10code.com>
* message.cpp (message_verify): Show message boxes for non-signed
messages.
(message_decrypt): Likewise.
2008-02-19 Marcus Brinkmann <marcus@g10code.de>
* engine-assuan.c (get_uiserver_name): Change default uiserver
path and remove work-around.
2008-02-18 Werner Koch <wk@g10code.com>
* message.cpp (pgp_body_to_attachment): New.
(message_decrypt): Use it.
(message_wipe_body_cruft): Also wipe already processed PGP
encrypted messages. Factor common code out to ...
(do_wipe_body): .. new.
* mapihelp.h (ATTACHTYPE_PGPBODY): New.
2008-02-15 Werner Koch <wk@g10code.com>
* olflange-dlgs.cpp: Remove code for IDC_ENCRYPT_WITH_STANDARD_KEY
and IDC_ENCRYPT_TO.
* dialogs.rc: Ditto.
2008-02-13 Werner Koch <wk@g10code.com>
* mapihelp.cpp (get_gpgolcharset_tag, mapi_get_gpgol_charset)
(mapi_set_gpgol_charset): New.
(mapi_get_gpgol_body_attachment): Transcode from Latin-1.
* mimeparser.c (start_attachment): Set the charset property.
(struct mime_context): Remove is_utf8 field.
2008-02-11 Werner Koch <wk@g10code.com>
* common.h (tlvinfo_t): New.
* common.c (parse_tlv): New. Based on code from libksba.
* mapihelp.cpp (has_smime_filename): New.
(is_really_cms_encrypted): New.
(mapi_change_message_class): Use this here to work around a
CryptoEx bug.
2008-02-08 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_change_message_class): Improve detecion of
CryptoEx messages.
2008-02-07 Werner Koch <wk@g10code.com>
* engine.c (engine_verify_start): Enable opaque signature for the
assuan backend.
* engine-assuan.c (op_assuan_verify): New arg OUTDATA. Add
support for opaque signatures.
* mimeparser.c (mime_verify_opaque): New.
* message.cpp (message_verify): Handle opaque signed S/MIME.
* message.cpp (message_wipe_body_cruft): Delete only encrypted
messages.
2008-02-06 Werner Koch <wk@g10code.com>
* mimeparser.c (mime_decrypt): New arg IS_RFC822.
* message.cpp (message_decrypt): Add code to see whether to use
the new arg.
2008-02-01 Werner Koch <wk@g10code.com>
* mimeparser.c (ciphertext_handler, ciphermessage_cb)
(ciphermessage_t2body): New.
(mime_decrypt): Use an rfc822 parser to pass the message to the
engine.
* mapihelp.cpp (mapi_get_attach_as_stream): Add arg R_ATTACH.
(mapi_set_attach_hidden): New.
2008-01-31 Werner Koch <wk@g10code.com>
* message.cpp (message_verify): Check that the body attachment is
available before shortcutting the verification.
* user-events.cpp (OnSelectionChange): Change SMIME message
class.
* mapihelp.cpp (mapi_change_message_class): Add arg
SYNC_OVERRIDE. Changed all callers to pass false.
(mapi_test_sig_status): Take care of sent messages.
(mapi_get_gpgol_body_attachment): Change args to allow use as
testing fucntion too. Adjusted caller.
2008-01-29 Werner Koch <wk@g10code.com>
* mimemaker.c (do_mime_sign): Set CTE for SMIME.
(delete_all_attachments): Remove extra semicolon accidently
inserted with revision 916.
2008-01-18 Marcus Brinkmann <marcus@g10code.de>
* mimeparser.c (mime_verify): New variable sig_len, and pass it on
to engine_verify_start.
* engine.h (engine_verify_start): Add new argument sig_len.
* engine.c (engine_verify_start): Add new argument sig_len and
pass it on to op_assuan_verify and op_gpgme_verify.
* engine-assuan.h (op_asssuan_verify): Add new argument sig_len.
* engine-gpgme.c (op_gpgme_verify): New argument sig_len and use
it instead of string length of signature.
* engine-assuan.c (op_assuan_verify): Likewise.
2008-01-11 Werner Koch <wk@g10code.com>
* mimemaker.c (finalize_message): Add args PROTOCOL and ENCRYPT
and use them to set the override message class.
(mime_sign, mime_encrypt, mime_sign_encrypt): Pass this info via
the new args.
(do_mime_sign): Set micalg to sha1 for CMS.
* message.cpp (message_decrypt): Add hack fro seldgenerated messages.
2008-01-10 Werner Koch <wk@g10code.com>
* mapihelp.cpp (get_gpgolmsgclass_tag, mapi_set_gpgol_msg_class):
New.
(mapi_change_message_class, mapi_get_message_type): Try override
first.
* message.cpp (message_incoming_handler): Remove arg MSGTYE and
let the function retrieve it. Changed all callers. Retry after a
sucessful message class change.
* olflange.cpp (getMsgtype): Remove. The caching might lead to
problems and makes it all more complex. Changed all callers to
use mapi_get_message_type.
* mimemaker.c (create_top_encryption_header) <SMIME>: Write empty
line after header.
2008-01-09 Werner Koch <wk@g10code.com>
* mimeparser.c (finish_saved_body): New.
(finish_attachment): Keep the body attachment open.
(mime_decrypt, mime_verify): Close the saved body data.
(t2body): Continue body attachments.
* message.cpp (message_verify): Save changes.
* mapihelp.cpp (mapi_change_message_class): Handle case of
PGP/MIME signed with IPM.Note. Save only of really needed, use
FORCE_SAVE and keep it open for read and write.
2008-01-07 Marcus Brinkmann <marcus@g10code.de>
* engine-assuan.c (replace_dollar_s): Remove obsolete function.
(get_quoted_socket_name): Remove obsolete function.
(get_uiserver_name): Invoke GUI server with --daemon. Fix buglet
in assignment.
(destroy_command): Change return type to void to suppress compiler
warning.
2008-01-04 Werner Koch <wk@g10code.com>
* engine-assuan.c (send_options): Call AllowSetForegroundWindow.
* main.c (read_options): Allo other values than 1 for enableDebug.
* common.h (DBG_IOWORKER, DBG_IOWORKER_EXTRA, DBG_FILTER)
(DBG_FILTER): New.
* engine.c (debug_filter): Turn it into a macro.
(debug_filter_extra): New macro, used instead of checking the
value of debug_filter.
* engine-assuan.c (debug_ioworker, debug_ioworker_extra): new.
Use them all over the file to enable debugging.
* engine.c (engine_encrypt_start): Add arg HWND.
(engine_sign_start, engine_decrypt_start, engine_verify_start)
(engine_start_keymanager): Ditto.
* mimemaker.c (mime_encrypt, mime_sign_encrypt, mime_sign)
(do_mime_sign): Add arg HWND and pass it to the engine.
* mimeparser.c (mime_verify, mime_decrypt): Pass HWND.
* message.cpp (message_sign, sign_encrypt): Pass HWND.
(message_incoming_handler, message_verify, message_decrypt): Add
arg HWND and pass it on.
* message-events.cpp (OnRead): Pass HWND to message function.
* ext-commands.cpp (DoCommand): Ditto.
2008-01-03 Werner Koch <wk@g10code.com>
* mimemaker.c (mime_sign_encrypt): Fix result test of do_mime_sign.
(write_tempsign_attachment): Remove.
(do_mime_sign): Change last ark to a sink_t.
(mime_sign_encrypt): Rework to use a temporary stream instead of a
temporary attachment.
(create_mapi_attachment): Remove arg TEMPSIGN.
2007-12-18 Werner Koch <wk@g10code.com>
* mapihelp.cpp (get_msgcls_from_pgp_lines): Limit check to the
first 1 k and stop testing after the first PGP armor line.
(mapi_get_message_type): Return MSGTYPE_SMIME.
(mapi_change_message_class): Take care of CryptoEx signatures.
* mapihelp.h (MSGTYPE_SMIME): New.
* message.cpp (message_incoming_handler): Check message class for
unknown and unchecked messages. Take care MSGTYPE_SMIME.
* ext-commands.cpp (DoCommand): Add debug command change message class.
2007-12-07 Werner Koch <wk@g10code.com>
* ext-commands.cpp (InstallCommands): Removed toolbar button fro
decrypt as this is not anymore needed. Fixes bug#860.
(QueryHelpText): Ditto.
2007-11-12 Werner Koch <wk@g10code.com>
* olflange.h (class GpgolExt): Rename m_gpgSelectSmime to
m_protoSelection.
* message-events.cpp (OnWriteComplete): Use it accordingly.
* main.c (write_options, read_options): Load and save it.
* dialogs.rc: Add new check box for OpenPGP default protocol.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Adjust for above chnages.
2007-11-09 Werner Koch <wk@g10code.com>
* main.c (read_options): New option ENABLE_DEBUG to be enabled
only using the Registry.
(read_options): Show warning for certain option combinations.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Hide the Advanced options
button unless in debug mode.
* mapihelp.cpp (get_gpgollastdecrypted_tag): New.
(mapi_test_last_decrypted): New.
(mapi_has_last_decrypted): new.
* mimeparser.c (finish_message): Update the Last Decrypted property.
* message.cpp (message_decrypt): Use it here.
(message_wipe_body_cruft): New.
* main.c (do_log_window_hierarchy): Factor some code out to ..
(do_log_window_info): .. this.
(log_window_hierarchy): Log parent window info.
(get_64bit_session_marker): New.
(initialize_session_key): Init session marker.
* Makefile.am (gpgol_SOURCES): Remove item-events.cpp
* olflange.cpp (GpgolExt): Disable the GpgOLItemEvents as they can
only be used with the undocumented ECF file.
2007-10-29 Werner Koch <wk@g10code.com>
* mimemaker.c (create_top_signing_header): Add arg FIRST.
(mime_sign): Factor allmost all code out to ..
(do_mime_sign): .. new function.
(create_mapi_attachment): Add arg TEMPSIGN.
(delete_all_attachments): Adjust for that.
(mime_encrypt): Factor some code out to ..
(create_top_encryption_header): .. new.
(write_tempsign_attachment): New.
(mime_sign_encrypt): Implement.
2007-10-22 Werner Koch <wk@g10code.com>
* engine-assuan.c (connect_uiserver): Try to start the server.
(get_uiserver_name, replace_dollar_s, get_quoted_socket_name): New.
* main.c (REGKEY): Remove.
* common.h (GNUPG_REGKEY): New.
* common.c (default_homedir): Use it in place of a hard coded one.
(get_locale_dir): Ditto.
(gpgol_spawn_detached): New.
2007-10-18 Werner Koch <wk@g10code.com>
* common.c (get_system_check_bitmap): New.
* decrypt.bmp, encrypt.bmp, sign.bmp, key_mana.bmp: Change
background color to pink and voila Outlook presents them
transparent.
* logo.bmp: Cleaned up to use just one color.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Do not use LoadImage.
2007-10-16 Werner Koch <wk@g10code.com>
* myexchext.h (EECMDID_): Add a few more of these constants.
* ext-commands.cpp (check_menu): New.
(toolbar_add_menu): Rename to ..
(add_menu): .. this.
(toolbar_from_tbe): Remove.
(add_toolbar): New.
(InstallCommands): Use new toolbar helper.
(QueryButtonInfo): Changed to use new toolbar helper.
(~GpgolExtCommands): New.
* engine.c (engine_encrypt_start): Add arg R_PROTOCOL.
* engine-assuan.c (op_assuan_encrypt): Add arg R_USED_PROTOCOL and
ask the server for it.
* mimemaker.c (sink_encryption_write_b64): New.
(mime_encrypt): Add S/MIME support.
2007-10-15 Werner Koch <wk@g10code.com>
* engine-assuan.c (op_assuan_start_keymanager): New.
* engine.c (engine_start_keymanager): New.
* ext-commands.cpp (DoCommand): Call it.
2007-10-12 Werner Koch <wk@g10code.com>
* gpgol-rsrcs.rc: Remove.
* dialogs.rc: Renamed from olflange-rsrcs.rc.
* dialogs.h: Rename for olflange-ids.h. Changed all includers.
* Makefile.am: Adjust accordingly.
* verify-dialog.c (verify_dialog_box): Do not distinguish
languages.
(verify_dlg_set_labels): New.
(verify_dlg_proc): Call it.
* passphrase-dialog.c (passphrase_callback_box): Do not
distinguish languages.
(decrypt_key_dlg_set_labels): New.
(decrypt_key_dlg_proc): Call it.
(decrypt_key_ext_dlg_set_labels): New.
(decrypt_key_ext_dlg_proc): Call it.
* recipient-dialog.c (recipient_dialog_box): Do not distinguish
languages.
(recipient_dialog_box2): Ditto.
(recipient_dlg_set_labels): New.
(recipient_dlg_proc): Call it.
2007-10-11 Werner Koch <wk@g10code.com>
* ext-commands.cpp (toolbar_add_menu): USe "@" to indicate a
separator and "" to ski the entry. Changed callers.
* common.h (struct): Remove AUTO_SIGN_ATTACH. Add ENABLE_SMIME.
* main.c (read_options, write_options): Save the new option.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Implement new option and
print warning.
* mapihelp.cpp (mapi_change_message_class): Take care of enable_smime.
* olflange-ids.h: Renumbered.
* olflange-rsrcs.rc: Keep only the english dialog and changed all
text to dummy texts.
* olflange-dlgs.cpp (set_labels): New.
(GPGOptionsDlgProc): Call it.
* property-sheets.cpp (GetPages): Do not distinguish languages.
* config-dialog.c (config_dialog_box): Ditto.
(config_dlg_set_labels): New.
* gpgol-rsrcs.rc (IDD_OPT): Move to olflange-rsrcs.rc and rename
to IDD_EXT_OPTIONS.
(IDD_OPT_DE): Remove.
2007-10-10 Werner Koch <wk@g10code.com>
* main.c (read_options): Remove saveDecryptedAttachment. Add
smimeDefault.
2007-10-08 Werner Koch <wk@g10code.com>
* main.c (do_log): Remove trailing LF from w32 error message and
also print the numeric error code.
2007-09-25 Werner Koch <wk@g10code.com>
* Makefile.am (gpgol_LDADD): Link against libassuan.
* util.h (DIM, DIMof): New.
* engine.c (filter_gpgme_read_cb): Implement nonblock feature.
(filter_gpgme_write_cb): Ditto.
2007-09-24 Werner Koch <wk@g10code.com>
* common.c (standard_homedir, default_homedir): New.
(w32_shgetfolderpath): Make static.
2007-09-21 Werner Koch <wk@g10code.com>
* mimeparser.c (build_mimeinfo): New.
(finish_message): Store the mime structure.
* mapihelp.cpp (mapi_get_message_class): New.
(mapi_get_sig_status): New.
(get_gpgolprotectiv_tag, get_gpgolsigstatus_tag)
(get_gpgolattachtype_tag): Factored code out to ...
(create_gpgol_tag): .. New.
(get_gpgolmimeinfo_tag): New.
(mapi_get_mime_info): New.
2007-09-20 Werner Koch <wk@g10code.com>
* user-events.cpp, user-events.h: New.
* olflange.h (class GpgolExt): Add member for it.
* olflange.cpp (QueryInterface): Hook it in.
2007-09-19 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_has_sig_status): Return true if any sig
status is present.
(mapi_test_sig_status): New. Take semantics of the former
mapi_has_sig_status. Changed all callers.
(mapi_change_message_class): Set sign status to n/a for other
message classed.
* mimemaker.c (finalize_message): Mark created messages.
(write_buffer_for_cb): Rename from write_buffer_voidarg and return
number of bytes written.
(collect_signature): Return number of bytes written.
* mapihelp.h (struct mapi_attach_item_s): New member
PRIVATE_MAPITABLE.
* mapihelp.cpp (mapi_create_attach_table)
(mapi_release_attach_table): Keep the mapi table oben and put
PR_ATATCH_NUM into the MAPIPOS member.
(mapi_get_gpgol_body_attachment): Use PR_ATATCH_NUM.
* mimeparser.c (finish_message): New.
(mime_decrypt, mime_verify): Call it.
2007-09-17 Werner Koch <wk@g10code.com>
* olflange.cpp: Print gpgme version.
2007-09-14 Werner Koch <wk@g10code.com>
* engine-gpgme.c: Rewrote most of it.
2007-09-13 Werner Koch <wk@g10code.com>
* common.c (xrealloc): New.
2007-09-11 Werner Koch <wk@g10code.com>
* engine-gpgme.c (op_encrypt_data): New.
2007-09-08 Werner Koch <wk@g10code.com>
* engine.c: New.
* engine.h: Rewrite. Factor existing stuff out to ..
* engine-gpgme.h: .. new.
* engine-assuan.h, engine-assuan.c: New.
2007-09-07 Werner Koch <wk@g10code.com>
* common.c (qp_decode): Handle softe line breaks.
2007-09-06 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_get_body): New.
* mimeparser.c (protocol_t): Move to .. common.h.
2007-09-05 Werner Koch <wk@g10code.com>
* mimemaker.c, mimemaker.h: New.
2007-08-31 Werner Koch <wk@g10code.com>
* mapihelp.cpp (mapi_set_header): New.
* message.cpp (pgp_mime_from_clearsigned): New.
(message_verify): Use it.
* main.c: Call srand ().
* util.h (trailing_ws_p): New.
* common.c (generate_boundary): New.
2007-08-30 Werner Koch <wk@g10code.com>
* message-events.h (class GpgolMessageEvents): Rename M_IS_SMIME
to M_PROCESSED.
* message-events.cpp (OnRead, OnReadComplete): Ditto.
(OnReadComplete): Remove preview decryption stuff.
(OnWriteComplete): Remove GpgMsg based code.
* ext-commands.cpp (DoCommand): Ditto
(InstallCommands): Do not init the watcher.
* olflange.cpp (GpgolExt): Do not init the watcher.
(GpgolExt): Do not clanup the watcher.
* main.c (DllMain): Ditto.
* gpgmsg.cpp, gpgmsg.hh: Remove.
* watcher.cpp: Remove
* pgpmime.c, pgpmime.h: Remove.
* mapihelp.h (MSGTYPE_GPGOL_CLEAR_SIGNED)
(MSGTYPE_GPGOL_PGP_MESSAGE): New.
* mapihelp.cpp (mapi_get_message_type): Map them.
(get_msgcls_from_pgp_lines): New.
(mapi_change_message_class): Detect old old style PGP messages.
(mapi_get_body_as_stream): New.
* message.cpp (message_verify, message_decrypt): Handle them.
* message-events.cpp (OnRead): Ditto.
2007-08-29 Werner Koch <wk@g10code.com>
* ext-commands.h (class GpgolExtCommands): Add members
M_NCMDKEYMANAGER and M_NCMDDECRYPT.
* ext-commands.cpp (GpgolExtCommands): Initialize it.
(InstallCommands): Use them here instead of reusing another variable.
(DoCommand, Help, QueryHelpText, QueryButtonInfo): Restructured
for better readibility.
2007-08-21 Werner Koch <wk@g10code.com>
* w32-gettext.c (SUBLANG_BENGALI_BANGLADESH): Fix to 2 as per MSDN.
(SUBLANG_PUNJABI_PAKISTAN): Remove as it is not in MSDN.
(SUBLANG_ROMANIAN_MOLDOVA): Remove as it is not in MSDN.
(SUBLANG_ROMANIAN_ROMANIA): Change to value 1 as per MSDN.
2007-08-13 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (gpgol_SOURCES): Add common.h.
2007-08-06 Werner Koch <wk@g10code.com>
Lots of changes to support S/MIME and to revamp most of the old
code. More changes to follow. The list of changes below is not
complete as it does not identfy all newly written code.
* mimeparser.c: New. Based on pgpmime.c
* display.cpp (update_display): Removed unused arg MSG.
(update_display): Set the body to an empty string even if it is
not HTML.
* olflange.cpp (Install): Print OL version only once.
* verify-dialog.c (verify_dialog_box): Add arg PROTOCOL.
(load_sigbox): Ditto.
(verify_dlg_proc): Changed title acccording to used protocol.
* Makefile.am (gpgol_LDADD): Add libadvapi due to our use of
CryptGenRandom.
* main.c (get_crypt_random, initialize_session_key): New.
(DllMain): Initialize the session key.
(get_128bit_session_key, create_initialization_vector): New.
* serpent.c: New. Taken from libgcrypt 1.3.0 and stripped down to
fit our needs. Add CFB encryption API.
* serpent.h: New.
* mapihelp.cpp (mapi_mark_moss_attach): New.
* mymapitags.h (PR_ATTACHMENT_HIDDEN): New.
* display.cpp (open_inspector): New.
* mapihelp.cpp (mapi_get_binary_prop): New.
* engine-gpgme.c (op_verify_detached_sig_gpgme): Add arg
PROTOCOL. Changed all callers to pass the OPENPGP protocol.
* common.c (b64_decode): Renamed from base64_decode and use new
type for the context.
(b64_init): New.
* pgpmime.c (qp_decode, base64_decode): Move to common.c and make
public.
* common.h (STRICT): Remove.
* intern.h: Rename to common.h.
2007-07-20 Werner Koch <wk@g10code.com>
* myexchext.h (IOutlookExtItemEvents.): New.
* item-events.h, item-events.cpp: New.
* olflange.cpp (GpgolExt, QueryInterface): Hook it in.
2007-07-19 Werner Koch <wk@g10code.com>
* attached-file-events.cpp: Renamed from attach.c.
* attached-file-events.h: Renamed from attach.h. Removed some
inlines to the impl file.
* Makefile.am (gpgol_LDADD): Add libole32.
2007-07-18 Werner Koch <wk@g10code.com>
* mapihelp.c (log_mapi_property): Support STRIGN8 and UNICODE.
* myexchext.h (IExchExtUserEvents, IExchExtSessionEvents): New
declarations.
* session-events.cpp, session-events.h: New.
* olflange.cpp (GpgolExt, QueryInterface): Hook session-events in.
* olflange.cpp (DllRegisterServer): Register only for interfaces
we really use.
(ext_context_name): New. Factored out from several places.
(Install): Initialize for Session context.
Renamed all CGPGExchExt* classes to Gpgol*.
* olflange.h, olflange.cpp: Factor most code out to ..
* ext-commands.cpp, ext-commands.h, message-events.cpp
* message-events.h, property-sheets.cpp, property-sheets.h
* ol-ext-callback.cpp, ol-ext-callback.h: .. New.
2007-07-17 Werner Koch <wk@g10code.com>
* Makefile.am (gpgol_LDADD): Add ws2_32.
* main.c (log_window_hierarchy, do_log_window_hierarchy): New.
* olflange.cpp (show_window_hierarchy): Replace by
log_window_hierarchy.
(g_initdll): Make static.
(InstallCommands): Factor some code out to ..
(toolbar_from_tbe, toolbar_add_menu): .. new.
* mapihelp.c, mapihelp.h: New.
* olflange.cpp (show_mapi_property): Factor out to mapihelp.c
and rename to log_mapi_property.
2007-04-10 Werner Koch <wk@g10code.com>
* display.cpp (find_message_window): Add arg LEVEL for debugging.
Ignore MsoCommand* Windows. Fixes bug 735.
2006-10-14 Timo Schulz <ts@g10code.de>
* recipient-dialog.c (lv_get_item_param): New.
(copy_item): Use it here to copy the opaque param.
(recipient_dlg_proc): And here to avoid the hidden column.
(initialize_rsetbox): Localize column names.
* olflange.cpp (get_outlook_property): Free returned BSTR.
(InstallCommands): Likewise.
2009-09-06 Timo Schulz <ts@g10code.de>
* recipient-dialog.c (recipient_dialog2): Do not free
key array here.
2008-08-21 Timo Schulz <ts@g10code.de>
* engine-gpgme.c (op_lookup_keys): Only add useable keys
and add all invalid keys to unknown.
* recipient-dialog.c (copy_item): Rewritten.
(initialize_keybox): Add comment to clarify use of fnd_keys.
(recipient_dialog_box): Simplified.
(find_item): Support partial search.
2006-08-19 Timo Schulz <ts@g10code.de>
* olflange-rsrcs.rc: Correct some dialog sizes.
* passphrase-dialog.c (decrypt_key_dlg_proc): Automatically
select the secret key if only one is available.
* config-dialog.c (GPGOptionsDlgProc): Passphrase cache
time is now requested in minutes but still internally
stored as seconds.
2006-08-15 Timo Schulz <ts@g10code.de>
* decrypt.bmp, encrypt.bmp: Restore format.
* olflange.cpp (OnWriteComplete): Correct exit code handling.
* recipient-dialog.c (initialize_rsetbox): Correct column width.
(recipient_dlg_proc): Do not show the cancel error any longer.
* passphrase-dialog.c (decrypt_key_dlg_proc): Likewise.
(decrypt_key_ext_dlg_proc): Ditto.
* olgpgcore.def: Deleted unused file.
2006-06-14 Timo Schulz <ts@g10code.com>
* gpgol-rscs.rc (IDD_OPT): The English version of the dialog
has no log file item. Add it.
2006-05-22 Timo Schulz <ts@g10code.com>
* verify-dialog.c (load_sigbox): A sigsum of 0 also indicates
a valid (good) signature.
2006-04-25 Werner Koch <wk@g10code.com>
* xmalloc.h: New. Moved prototypes from util.h
* w32-gettext.h: Include it.
* common.c (utf8_to_wincp): Removed and replaced all callers by
utf8_to_native.
* common.c (wchar_to_utf8, utf8_to_wchar): Moved to ..
* w32-gettext.c: .. here.
(utf8_to_native): Make sure that we always return
a string and never NULL.
(native_to_utf8): New.
(native_to_wchar): New.
* gpgmsg.cpp (decrypt): Use native_to_utf8 for i18n strings
expected to be utf-8.
* pgpmime.c (pgpmime_decrypt, pgpmime_verify): Ditto.
2006-04-24 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (decrypt): New arg INFO_ONLY.
* olflange.cpp (OnReadComplete): Add code to call decrypt but with
INFO_ONLY if preview decryption has not been requested.
* main.c (read_options): New compatibility option no_preview_info.
* gpgmsg.cpp (getRecipients): Don't add the default key here.
(encrypt_and_sign): But do it here.
* engine-gpgme.c (op_get_one_key): New.
2006-04-22 Timo Schulz <ts@g10code.com>
* common.c (utf8_to_wincp): Corrected utf8 decoding.
* passphrase-dialog.c (load_recipbox): Likewise.
* olflange-dlg.cpp (GPGOptionsDlgProc): Activate the
'confirm' button when the dialog state has been changed.
* olflange-rsrcs.rc (IDD_GPG_OPTIONS_DE): Change description.
2006-03-28 Werner Koch <wk@g10code.com>
* olflange-rsrcs.rc (IDD_GPG_OPTIONS_DE): Add new control box.
(IDD_GPG_OPTIONS): Updated to match German version.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Ditto.
* gpgmsg.cpp (decrypt): Implemented PREFER_HTML option.
(get_long_attach_data): New.
2006-03-27 Werner Koch <wk@g10code.com>
* engine-gpgme.c (op_verify_detached_sig_gpgme): New.
* pgpmime.c (pgpmime_verify): New. First take on a PGP/MIME
signature verification.
* gpgmsg.cpp (is_pgpmime): Renamed to is_pgpmime_enc.
(class GpgMsgImpl): Niew members media_type. media_subtype and
ct_protocol.
(get_msg_content_type): New.
(decrypt): Show a warning for PGP/MIME signed messages.
2006-03-26 Werner Koch <wk@g10code.com>
* intern.h: New option PREFER_HTML.
* main.c (write_options, read_options): Ditto.
2006-03-20 Werner Koch <wk@g10code.com>
* olflange.cpp (Install): Also check major part of build version.
2006-03-17 Timo Schulz <ts@g10code.com>
* w32-gettext.c (utf8_to_native): Make it global.
* config-dialog.c (get_open_file_name): Make sure the selected
file really exists.
* passphrase-dialog.c (decrypt_dlg_proc): UTF8 conversion.
(passphrase_callback_box): Likewise.
* recipient-dialog.c (recipient_dlg_proc): Likewise.
* verify-dialog.c (load_akalist): Likewise.
(load_sigbox): Likewise.
* common.c (utf8_to_wincp): New.
2006-03-15 Werner Koch <wk@g10code.com>
* olflange.cpp (Install): Print gpgol version for debugging.
2006-03-14 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (decrypt_dlg_proc): When used
as a signing key selection dialog, use a different title.
* gpgol-rsrcs.rc: Use German titles for German dialog versions.
2006-02-23 Werner Koch <wk@g10code.com>
* main.c (read_options): Set default caching time to 10 minutes.
2006-01-16 Werner Koch <wk@g10code.com>
* verify-dialog.c (load_sigbox): Give a hint in case of a bad
signature.
* gpgol-rsrcs.rc (IDD_ENC_DE): Add an informational header.
2005-12-07 Werner Koch <wk@g10code.com>
* olflange.cpp (Install): Check the version and print a warning.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Simplified the default
key code.
* config-dialog.c (store_config_value): Create key if it does not
exists.
(load_config_value_ext): Removed.
2005-12-06 Werner Koch <wk@g10code.com>
* config-dialog.c (start_key_manager): Don't pass the options to
access.
2005-12-06 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (getRecipients): Add the default key to the list of
recipients.
* recipient-dialog.c (recipient_dlg_proc): Add the already found
keys to the selected ones.
* olflange.cpp (OnWriteComplete): Need to disable the deleting of
HTML bodys.
2005-12-05 Werner Koch <wk@g10code.com>
* Makefile.am (gpgol_LDADD): Add -loleaut32.
* engine-gpgme.c (op_verify_detached_sig_mem): New.
* olflange.cpp (OnWriteComplete): Pass HTML flag to sign call.
(put_outlook_property): Need to use a BSTR for the sake of putting
HTMLBody.
* gpgmsg.cpp (sign): Add arg WANT_HTML.
(free_attach_info): New. Use it in the destructor.
(createHtmlAttachment): New.
(encrypt_and_sign, sign): Use it here.
(writeAttestation): Don't write an empty attestation.
2005-12-02 Werner Koch <wk@g10code.com>
* verify-dialog.c (verify_dialog_box): Actually allow for German
dialog.
* recipient-dialog.c (recipient_dialog_box)
(recipient_dialog_box2): Ditto.
* passphrase-dialog.c (signer_dialog_box)
(passphrase_callback_box): Ditto.
* intern.h (struct): New field PREVIEW_DECRYPT. Use it instead os
the old compatibility flags.
* main.c (write_options, read_options): Store/load preview decrypt.
* config-dialog.c (config_dlg_proc): Removed homedir and gpgbinary
options as they are deprecated. Put logfile entry here.
* olflange-dlgs.cpp (GPGOptionsDlgProc): Remove logfile entry. Add
preview-decrypt checkbox.
* olflange.cpp (InstallCommands): Remove experimental preview
command.
* w32-gettext.c (gettext_localename): New.
* config-dialog.c (config_dialog_box): Use it here to match the
gettext behaviour.
(GetPages): Ditto.
2005-12-01 Werner Koch <wk@g10code.com>
* engine-gpgme.c (op_decrypt_stream_to_gpgme, decrypt_stream)
(op_decrypt): Add arg PREVIEW_MODE.
* pgpmime.c (pgpmime_decrypt): New arg PREVIEW_MODE.
(struct pgpmime_context): New field PREVIEW.
(message_cb, plaintext_handler): Handle preview mode.
* gpgmsg.cpp (class GpgMsgImpl): Renamed SILENT to PREVIEW.
(setSilent): Renamed to ..
(setPreview): .. this.
(decrypt): Handle preview mode. Display a string while decrypting
PGP/MIME messages.
* display.cpp (update_display): New arg TEXT.
* gpgmsg.cpp (class GpgMsgImpl): Removed BODY_PLAIN and BODY.
(getDisplayText): Removed.
(loadBody): Changes to return the allocated body.
(getOrigText): Removed.
(getMessageType): Rewritten to take the body text as argument.
(decrypt): Pass plaintext directly to update_display. Free
plaintext.
(sign, encrypt_and_sign): Likewise.
* olflange.cpp (OnWriteComplete): Always delete PR_BODY on error.
2005-11-30 Werner Koch <wk@g10code.com>
* gpgmsg.cpp: Made more strings translatable.
* olflange.cpp: Replaced all LoadStrings by gettext calls.
* olflange-ids.h: Removed the IDS_ constants.
* olflange-rsrcs.rc: Removed the stringtables.
* common.c (get_root_key, read_w32_registry_string): New. Taken
for libgpg-error.
* main.c (i18n_init): New.
(DllMain): Call it.
(get_locale_dir, drop_locale_dir): New.
* w32-gettext.c, w32-gettext.h: New. Taken form libgpg-error.
Slightly modified due to the fact that gpgol is W32-only.
* util.h (_, N_): Define standard i18n macros.
* display.cpp (set_message_body): Do not delete a RTF property.
* util.h (SRCNAME): New. Changed all __FILE__ to this.
* main.c (log_srcname): New.
2005-11-20 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (loadBody): For HTML try to read the HTMLBody from
the OOM as a last resort.
* olflange.cpp (get_outlook_property): New.
(put_outlook_property_int): New.
2005-11-15 Werner Koch <wk@g10code.com>
* Makefile.am (gpgol_LDADD): Remove -lintl for now.
* olflange.cpp (OnWriteComplete): Make sure that we don't sent out
unencrypted stuff on error.
* display.cpp (set_message_body): Add arg IS_HTML.
(update_display): Ditto.
* gpgmsg.cpp (loadBody): New arg WANT_HTML.
(getOrigText): Ditto.
* olflange.h (class CGPGExchExtMessageEvents): Add M_WANT_HTML.
* olflange.cpp (OnWrite): Set it.
(OnWriteComplete): Pass its value to the encrypt functions.
2005-11-10 Werner Koch <wk@g10code.com>
* config-dialog.c (start_key_manager): Changed invocation of
default keymanager.
2005-10-21 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (libgpgme.a, libgpgme.a): New targets.
(gpgol_DEPENDENCIES): Add libgpgme.a and libgpg-error.a.
(clean-local): Likewise.
(gpgol_LDADD): Link to these local versions statically.
2005-10-20 Marcus Brinkmann <marcus@g10code.de>
* mapi32.def: New file.
* Makefile.am (gpgol_DEPENDENCIES): New variable.
(libmapi32.a): New target.
(gpgol_LDADD): Replace mapi32.dll with "-L . -lmapi32".
(clean-local): New target.
2005-10-19 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (sign, encrypt_and_sign): Don't set the body first to
empty. If this is really required we should do this in
set_message_body.
(sign): Save changes. Set content type to text/plain.
(encrypt_and_sign): Save changes also for empty bodies.
2005-10-06 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (writeAttestation): Use gpgme_free for BUFFER.
* engine-gpgme.c (data_to_file): Ditto.
2005-10-06 Marcus Brinkmann <marcus@g10code.de>
* Makefile.am (gpgol_DEPENDENCIES): New variable.
(libmapi32.a): New target.
(gpgol_LDADD): Replace mapi32.dll with "-L . -lmapi32".
* Makefile.am (gpgol_LDADD): Prefix gpgol.def and mapi32.dll with
$(srcdir).
* Makefile.am (.rc.o): Invoke windres with "-I .".
* Makefile.am (.rc.o): Invoke windres with -I $(srcdir).
* Makefile.am (gpgol_SOURCES): Add util.h.
2005-09-29 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (encrypt_and_sign): Pass signing key to encryption
function.
* passphrase-dialog.c (signer_dialog_box): New arg encrypting.
* gpgmsg.cpp (encrypt_and_sign): Set content type.
* engine-gpgme.c (op_lookup_keys): Fixed multiple key detection.
2005-09-28 Werner Koch <wk@g10code.com>
* olflange.cpp (DoCommand): Catch close command and resend to
avoid the "save changes?".
* display.cpp (update_display): Take care of utf-8 issues.
* common.c (latin1_to_utf8): New.
* pgpmime.c (latin1_data_write): New.
(plaintext_handler): Use it here.
(message_cb): Detect utf-8 encoding.
* main.c (read_options): Make sure that compat flags are always
properly initialized.
* display.cpp (find_message_window): First try to find the window
by class name.
* common.c (wchar_to_utf8_2): New.
2005-09-27 Werner Koch <wk@g10code.com>
* pgpmime.c (pgpmime_decrypt): Pass a pseduo filename to the
decryption function.
* verify-dialog.c (load_sigbox): Get key direct from gpgme.
* passphrase-dialog.c (load_secbox, load_recipbox): Reworked.
(decrypt_key_dlg_proc, decrypt_key_ext_dlg_proc): Reworked.
(count_keys, release_keyarray): New.
(signer_dialog_box, passphrase_callback_box): Adjusted to above
changes.
* engine-gpgme.c (op_deinit): Remove keycache cleanup.
* Makefile.am (gpgol_SOURCES): Removed keycache.c, keycache.h.
* keycache.c, keycache.h: Removed.
* recipient-dialog.c (recipient_dialog_box)
(recipient_dialog_box2): Rewritten and changed interface.
(load_rsetbox): Removed keycache stuff and rewrote to make use of
the keyarray.
(copy_item, initialize_keybox, recipient_dlg_proc): Ditto.
(keycache_to_key_array): Removed.
* engine-gpgme.c (op_lookup_keys): Rewritten, changed interface.
* gpgmsg.cpp (count_recipients): Renamed to ..
(count_strings): .. this.
(count_keys): New.
(free_recipient_array): Renamed to ..
(free_string_array): .. this.
(encrypt_and_sign): Adjusted for changes in op_lookup_keys and
recipient_dialog_box2.
2005-09-26 Werner Koch <wk@g10code.com>
* passphrase-dialog.c (get_pubkey_algo_str): Add DSA and old Elgamal.
* gpgmsg.cpp (gatherAttachmentInfo): Ignore attachments we can't
open.
* main.c (write_options): Print message on error. Rearranged to
make use of a table for all options.
2005-09-23 Werner Koch <wk@g10code.com>
* recipient-dialog.c (recipient_dlg_proc): Removed
IDC_ENC_OPTARMOR stuff; it was not used.
(load_rsetbox): Fixed detection of encryption capability.
* gpgol-ids.h, gpgol-rsrcs.rc: Ditto.
2005-09-22 Werner Koch <wk@g10code.com>
* engine-gpgme.c (decrypt_stream): Use gpgme_op_decrypt_verify.
* gpgmsg.cpp (gatherAttachmentInfo): Ignore attestations when
checking for pgp/mime.
* pgpmime.c (pgpmime_decrypt): Added arg HWND.
(message_cb, plaintext_handler): Write attachments.
* pgpmime.c (base64_decode): New.
* rfc822parse.c (parse_field): Treat Content-Disposition special.
* gpgmsg.cpp (get_save_filename): Moved to ..
* common.c (get_save_filename): .. here.
2005-09-20 Timo Schulz <ts@g10code.com>
* attach.c: New.
* attach.h: New.
* olflange.cpp (CGPGExchExt): Allocate class for
attached file events.
(QueryInterface): Return interface pointer for
attached file events.
* passphrase-dialog.c (load_secbox): Use new GPGME
interface.
(get_pubkey_algo_str): New.
* verify-dialog.c (load_sigbox): Likewise.
* recipient-dialog.c (load_recipbox): Likewise.
2005-09-19 Werner Koch <wk@g10code.com>
* msgcache.c (flush_if_needed): New.
(msgcache_put): use it.
* intern.h (opt): New compatibility flags AUTO_DECRYPT and
NO_ATTESTATION.
* olflange.cpp (InstallCommands): Use watcher stuff only when this
option has been enabled.
* gpgmsg.cpp (decrypt): Take care of NO_ATTESTATION.
* main.c (DllMain): Removed debug output; this should not be
used before initialization!
* watcher.cpp: Include config.h. Removed weird line endings.
* gpgmsg.cpp (encrypt_and_sign): Call SaveChanges.
2005-09-15 Timo Schulz <ts@g10code.com>
* util.h: Provider watcher prototypes.
* watcher.cpp (watcher_init_hook): New.
(watcher_free_hook): New.
(watcher_set_callback_ctx): New.
(cbt_proc): New.
* display.cpp (find_message_window): Removed GpgMsg param.
Changed all callers.
2005-09-15 Werner Koch <wk@g10code.com>
* olflange.cpp (OnWriteComplete): Take care of EEME_FAILED.
(OnWrite): Check that we are encrypting only plain text messages.
* myexchext.h: Add flags used by OnReadComplete.
2005-09-14 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (writeAttestation): Add a content type.
(gatherAttachmentInfo): Detect whether we already have an attestation.
(decrypt): Don't create duplicate attestations.
(writeAttestation): Translate LF to CRLF.
2005-09-13 Werner Koch <wk@g10code.com>
* pgpmime.c (pgpmime_decrypt): New arg ATTESTATION.
* engine-gpgme.c (add_verify_attestation): New.
(op_decrypt, op_verify_detached_sig, op_verify_detached_sig)
(op_verify_detached_sig): Add new arg ATTESTATION. Changed all
callers.
(at_sig_summary, at_sig_validity, add_verify_attestation): New.
The code has been taken and modified from Mutt's crypt-gpgme.c and
entirely been writen by g10 Code.
(at_fingerprint): Ditto.
* gpgmsg.cpp (class GpgMsgImpl): New member ATTESTATION. Use it
in all calls to the functions above.
* gpgmsg.cpp (decryptAttachment, decrypt): Save plaintext back
into the MAPI if desired.
2004-09-08 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (lod_recipbox): Use gpgme directly
to resolve the keyids to userids.
* usermap.c, usermap.h: Removed.
* HashTable.cpp, HashTable.h: Removed.
2005-09-07 Timo Schulz <ts@g10code.com>
* common.c: Removed unused code.
* intern.h: Likewise.
2005-09-07 Timo Schulz <ts@g10code.com>
* olflange.cpp (ul_release): New. Wrapper with error checking
around UlRelease.
(InstallCommands): Separate test for type and if string is empty.
2005-09-06 Werner Koch <wk@g10code.com>
* engine-gpgme.c (op_decrypt_stream): Factored most code out to ..
(decrypt_stream): .. new.
(op_decrypt_stream_to_buffer): Simplified accordingly. Fixed
possible buffer overflow when trying to make it a string.
(op_decrypt_stream_to_gpgme): New.
* pgpmime.c, pgpmime.h: New.
* rfc822parse.h, rfc822parse.c: New. Taken from GnuPG 1.9.
2005-09-06 Timo Schulz <ts@g10code.com>
* config-dialog.c (get_open_file_name): Correctly terminated filter.
New parameter for the title. Changed all callers.
(get_folder): Likewise.
2005-09-01 Werner Koch <wk@g10code.com>
* gpgmsg.cpp (get_pgp_armor_type): New.
(gatherAttachmentInfo): Enhanced to detect text/plain pgp.
(verify): Removed.
(decrypt): Do clearsig verification here.
* gpgmsg.cpp (decrypt): Kludge to workaround OL not updating a
window.
* msgcache.c (msgcache_get_from_mapi): New.
* display.cpp (find_message_window): Don't use the GpgMsg function
to to the string compare.
* gpgmsg.cpp (matchesString): Removed.
* msgcache.c (msgcache_set_active): Removed.
(msgcache_get): Rewritten.
(msgcache_put): Now uses PR_CONVERSATION_INDEX as key. Update
existing entries.
* olflange.cpp (OnRead): Removed msgcache_set_active.
(InstallCommands): Now uses the ConversationIndex to match the
reply with the message.
2005-08-31 Werner Koch <wk@g10code.com>
* olflange.cpp (DllRegisterServer): Define a CLSID and set the
ThreadingModel.
2005-08-30 Werner Koch <wk@g10code.com>
Renamed from "outlgpg" to "gpgol".
More or less finished this major rewrite.
2005-08-26 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp, MapiGPGME.h: Removed.
* display.cpp, display.h: New.
2005-08-23 Werner Koch <wk@g10code.com>
* msgcache.c, msgcache.h: New.
2005-08-22 Werner Koch <wk@g10code.com>
* olflange.cpp: Major cleanups and partly rewrites.
2005-08-18 Werner Koch <wk@g10code.com>
* gpgmsg.cpp, gpgmsg.hh: New.
2005-08-17 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (setMessageAccess): Removed as it was only used at
one place.
(setRTFBody): Use the above code here directly..
(setWindow, setMessage): Removed. We can't use that because there
is only one instance of this class.
(decrypt): Add args HWND and MSG. Changed caller.
(getBody): Changed to ..
(get_body): .. plain function and add new arg MSG. Changed all
callers.
(isHtmlMessage): Likewise changed to ..
(is_html_message): .. plain function and add new arg MSG.
(doCmd): Removed.
* common.c (utf8_to_wchar): New.
* MapiGPGME.cpp (passphraseCallback): Removed.
(getPassphrase, clearPassphrase, storePassphrase): Removed.
(add_html_line_endings): Rewritten.
* engine-gpgme.c (op_sign_encrypt_start): Removed because it is
not used anywhere.
(op_sign): Renamed to ..
(do_sign): .. this and made local.
2005-08-16 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (signAttachment): Simplified.
* engine-gpgme.c (op_sign_file): Add arg TTL.
(op_sign_file_ext): Removed.
(op_sign_file_next): Renamed to ..
(do_sign_file): .. this and made local.
(do_sign_file): Updated to use new passphrase callback
semantics.
(op_decrypt_file): Ditto.
(free_recipients): Need to use gpgme_key_release and not just
free.
* engine-gpgme.c (do_decrypt): Factored some code out to ..
(update_passphrase_cache): .. new.
(op_sign_encrypt_file): Updated to use new passphrase callback
semantics.
* MapiGPGME.cpp (getBody): Properly distinguish property types.
(delete_buf): Removed macro. We now use malloc for the body
string. Changed other places to use delete directly for clarity.
(fail_if_null): Removed. Replaced by direct tests and a call to
out_of_core.
(setDefaultKey): Now use malloc/free instead of new/delete.
Changed at other places too.
(getDefaultKey): Changed to return a const char *.
* common.c (wchar_to_utf8): New.
(out_of_core): Made global and call abort after displaying the
message box.
2005-08-14 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (log_debug_w32): New.
(do_log): New arg W32ERR. Make sure to print a trailing LF.
* passphrase-dialog.c (passphrase_callback_box): Revamped.
(free_decrypt_key): Wipe out a passphrase. Remove superfluous
variable clearing.
* util.h (wipememory2, wipememory): New.
(wipestring): New.
* engine-gpgme.c (op_decrypt): Renamed to ..
(do_decrypt): .. this and made local.
(clear_error_if_cancel): Removed as we inlined the code.
(do_decrypt): Cleaned up.
* main.c (DllMain): Initialize passcaching subsystem.
* passcache.c, passcache.h: New.
* intern.h: Include it here.
* util.h: New.
* olflange.cpp (DllRegisterServer): Remove key for the old
versions of this plugin.
Merged olgpgmain.dll and olgpgcore.dll into outlgpg.dll.
* Makefile.am: Renamed target to outlgpg. Added required files.
* olflange.cpp, olflange.h: Renamed from GPGExch.cpp and
GPGExch.h. Removed all the MFC cruft as it is not required - it
was only used to get hands on the hInstance of the DLL; chnaged
that to use the glob_hinst which gets set by DllMain.
* outlgpg.def: New.
* olflange-def.h: New.
* olflange-dlgs.cpp: Renamed from GPGOptionsDlgs.cpp.
* olflange-ids.h: Renamed from ../olflange/resource.h
* olflange-rsrcs.rc: Renamed from ..olflange/olgpgmaindlgs.rc and
stripped off unneedded stuff.
* olgpgcoredlgs.rc: Renamed to ..
* outlgpg-rsrcs.rc: .. this and stripped of cruft.
* olgpgcoredlgs.h: Renamed to ..
* outlgpg-ids.h: .. this.
* versioninfo.rc.in: New.
2005-08-12 Timo Schulz <ts@g10code.com>
* config-dialog.c (sotre_extension_value, load_extension_value):
Adjust registry key.
2005-08-12 Werner Koch <wk@g10code.com>
* intern.h: Moved keycache prototypes to keycache.h.
* MapiGPGME.cpp (lock_log, unlock_log): New.
* engine-gpgme.c (op_init): Check GPGME version.
* main.c (outlook_gpg_get_version): Removed as it is now derived
from config.h.
(DllMain): Initializes gpgme and mapigpgme.
* MapiGPGME.cpp (initialize_mapi_gpgme): New.
* config-dialog.c (store_extension_value, load_extension_value):
Changed key to "OutlGPG".
* MapiGPGME.h (class MapiGPGME): New methods versionString and
showVersion. Breaks ABI but it doesn't matter as we are also
going to change the name of the project.
* Makefile.am: Renamed target to olgpgcore.
* resource.h: Renamed to ...
* olgpgcoredlgs.h: .. and removed cruft from generator.
* gpgmedlgs.rc: Renamed to ..
* olgpgcoredlgs.rc: ... and removed cruft.
* libgpgmedlgs.def: Renamed to ..
* olgpgcore.def: .. this.
2005-08-11 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (log_debug): New. Rewrote the whole log stuff.
It is not anymore per instance.
(logDebug): New version with va_list arg.
* MapiGPGME.cpp (passphraseCallback): Use gpgme_error_t becuase
C++ enforces enum types.
* engine-gpgme.c (op_lookup_keys): s/id/names/. id is a reved
word Obj-C and it is good style not to use it in plain C code.
(op_sign_file_next): Use gpgme_passphrase_cb_t in declaration.
(op_decrypt_next): Ditto.
* MapiGPGME.cpp (count_recipients): Renamed from countRecipients
method, made local and changed both callers.
(log_key_info): Changed output format. New arg PREFIX. Changed
callers.
(add_html_line_endings): Renamed from addHtmlLineEndings method
and made local.
(logDebug): Open in text mode. Removed all superfluous "\r" from
callers.
2005-08-09 Timo Schulz <ts@g10code.com>
* main.c (outlook_gpg_get_version): New.
Use same version as the Outlook GPG plugin.
* MapiGPGME.cpp [!__MINGW32__]: Changed sequence of include files.
2005-08-09 Werner Koch <wk@g10code.com>
* MapiGPGME.cpp (userid_from_key, keyid_from_key): New. Changed
all calls to the deprecated gpgme_key_get_string_attr function by
these.
* MapiGPGME.h, MapiGPGME.cpp: Splitted into interace and
implementation.
* HashTable.h (class HashTable): No need to dll export anything.
2005-08-08 Werner Koch <wk@g10code.com>
* common.c (w32_shgetfolderpath): Added.
* config-dialog.c (load_config_value_ext): use it here.
2005-07-21 Timo Schulz <twoaday@g10code.com>
* MapiGPGME.cpp (decrypt): Only return if no valid PGP
data was found and the message has no attachments.
* engine-gpgme.c (op_encrypt): Use --textmode to fix
problems when the recipient OS has different line endings.
For example Win32->Linux.
2005-07-20 Timo Schulz <twoaday@g10code.com>
* MapiGPGME.cpp (addHtmlLineEndings): New.
(encrypt):Use it here.
(signEncrypt): Likewise. Free memory in case of errors.
(decrypt): Free memory in case of the verify procedure.
Issue a warning when the text of the mail could not be
updated.
(isMessageEncrypted): New.
(countAttachments): Check for null pointers.
(clearPassphrase): Likewise.
* config-dialog.c (store_config_value): Support '%val%' input.
* verify-dialog.c (load_akalist): Return the number of user-ids
which were added.
* passphrase-dialog.c (load_secbox): Make sure we really start
to add the item data at the begin.
* intern.h: Fixed GCC compiler problem.
2005-07-19 Timo Schulz <twoaday@g10code.com>
* MapiGPGME.cpp (encrypt): Handle cancel.
(encryptAttachments): If no attachments exist, close the table.
(decryptAttachments): Likewise.
(signAttachments): Likewise.
(isHtmlBody): New.
(isHtmlMessage): New.
(setBody): Html support.
(encrypt): Figure out if message is html and encode the right body.
(signEncrypt): Likewise.
(verify): Always use the non-html body for GPG input.
(decrypt): Likewise.
* engine-gpgme.c (recipient_dialog_box2): Set cancel flag.
(op_sign_start): Handle cancel.
(recipient_dlg_proc): Make sure there is at least one selected
key. Disable armor checkbox.
2005-07-19 Timo Schulz <twoaday@g10code.com>
* MapiGPGME.cpp (~MapiGPGME): After releasing the
memory, set all pointers to NULL. It seems that NT5
bases operating systems are more pedantic with such
issues than 9X based systems.
(freeUnknownKeys): Likewise.
(freeRecipients): Likewise.
(encrypt): Outlook 2003 returns a body string which
is not NULL but \0 with a length of 0. Handle it.
(sign): Likewise.
(signEncrypt): Likewise.
* engine-gpgme.c (free_recipients): New.
(op_encrypt_start): Handle cancel and free memory.
2005-07-18 Timo Schulz <ts@g10code.com>
* gpgmedlgs.rc: Native language support for German.
2005-07-14 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp: Add some missing MAPI constants.
(passphrase_callback_box): Repair cancel button.
(decrypt): If the user cancels the operation, do not
alter the message text.
* config-dialog.c (expand_path): New. From WinPT.
(load_config_value): Support for REG_EXPAND_SZ.
Suggested by Sebastian.
2005-07-13 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp (sign): Ignore empty bodies.
(signEncrypt): Likewise. Free recipient memory.
(encrypt): Modify code so it really works.
(attachPublicKey): New.
* engine-gpgme.c (op_export_keys): New.
2005-07-12 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp (displayError): New.
(writeOptions): Use it here. Simplify the code.
(signAttachments): New.
(sign): Sign attachments. Noted by Ralf.
(processAttachment): Support for sign-only.
2005-07-08 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp (setEnableLogging): New.
(getEnableLogging): New.
(logDebug): Move all logging code to this function.
(readOptions): Automatically enable logging if the
'LogFile' registry entry is valid.
(prepareLogging): New.
(readOptions): Properly handle ""-strings.
* config-dialog.c (does_file_exist): Allow to have
parameters like '--keymanager' and cut them off before
checking the existence.
(start_key_manager): Free memory.
(config_dlg_proc): Initialize pointer to 'NULL'.
(SHFree): New. Special function to handle shell memory.
(get_folder): Free memory.
(does_folder_exist): New.
2005-07-06 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp: s/ATTR_/ATT_.
(saveDecryptedAttachment): Cut off the prefix.
(clearConfig): New.
(clearObject): New.
(MapiGPGME): Use it here.
Use ATT_PREFIX instead of a hardcoded string.
* engine-gpgme.c
(op_sign_file_next): New.
(op_sign_file): Call op_sign_file2.
(op_sign_file_ext): New.
2005-07-05 Timo Schulz <ts@g10code.de>
* MapiGPGME.cpp (readOptions, writeOptions):
Support for the new 'auto sign attachment' flag.
(signAttachment): New.
(setSignAttachments): New.
(getSignAttachments): New.
* engine-gpgme.c (op_sign_file): Enable armor.
2005-07-03 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (processAttachment): Implement
the decryption part and only use it if the 'save
decrypted attachment' flag is set.
(saveDecryptedAttachment): New.
(writeOptions): Save 'decrypt attachment' flag.
(readOptions): Load it here.
(cleanupTempFiles): Check handle.
(encrypt): If the message has no body skip the
procedure.
(op_decrypt_file): Set recipient callback.
(decryptAttachments): We do not alter the attachment
so there is no need to release it again.
Replace all 'free' with 'xfree'.
2005-07-01 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (processAttachment): Check that
the file has a valid PGP extension before we try
to decrypt the attachment.
(checkAttachmentExtension): Check if the file
extension is a vliad PGP extension.
2005-06-30 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (processAttachment): Use a unique
temp name to make a cleanup at the end easier.
(cleanupTempFiles): Delete possible left over
temp files.
(~MapiGPGME): Use it here.
2005-06-22 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (processAttachment): Close attachment
before we delete it.
(streamOnFile): Directly use the attachment.
(streamFromFile): Likewise.
(closeAttachment): Renamed to..
(releaseAttachment): ..this.
2005-06-21 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (getMessageType): Support all types.
(streamOnFile): More straight forward now.
(encryptAttachments): Get the attachment table first.
(getAttachPathname): New.
(processAttachment): Add new parameter for the position
of the attachment.
(streamFromFile): New.
(generateTempname): New.
* engine-gpgme.c (op_sign_encrypt_file): New.
2005-06-17 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (MapiGPGME): Initialize all attachment
components to zero. Thanks to Sebstian for pointing this out.
2005-06-16 Timo Schulz <ts@g10code.com>
* engine-gpgme.c (check_encrypt_result): New. Check if
the encrypt procedure returned some invalid recipients.
(op_encrypt): Use it here.
(op_encrypt_file): Likewise.
(op_sign_encrypt): Likewise.
* missing.h: Removed unused constants.
* verify-dialog.c (load_sigbox): Handle v3 RSA keys.
* passphrase-dialog.c (load_secbox): Make sure the index
from the first loop matche the second. Which means skip
all invalid keys also.
2005-06-13 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (writeOptions): New Option 'defaultKey'.
(readOptions): Likewise. Force overwrite 'addDefaultKey'.
2005-06-07 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (decrypt_key_dlg_proc): Add a
reference to the key object so it will be still valid
after the dialog is destroyed.
* MapiGPGME.cpp (freeUnknownKeys): Do not try to free
the context if no keys are available. Fixed a segv.
(signEncrypt): The new code now gets a persistent pointer
to the key.
* engine-gpgme.c (op_sign_encrypt): Load the right
dialog to request the passphrase.
2005-06-05 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (load_recipbox): Check ctx if null.
(passphrase_callback_box): Different dialogs for sign
and decrypt.
(signer_dialog_box): Do not zero the context too early.
* keycache.c (enum_gpg_seckeys): Also reload if ctx
is NULL.
* MapiGPGME.cpp (signEncrypt): Return if the user
cancelled the signer selection dialog.
* recipient-dialog.c (recipient_dlg_proc): Check 'Text Mode'
button.
* keycache.c (enum_gpg_keys, enum_gpg_seckeys): Fully
reset the keycache initializing it again. Thanks to Ralf.
2005-06-04 Timo Schulz <ts@g10code.com>
* verify-dialog.c (load_sigbox): Only issue a warning
if the key exists but is not valid.
Get pkalgo from the signature.
Fixed format string problem s/%d/%s.
* config-dialog.c (load_config_value): Close reg handle.
(config_dlg_proc): Show error if the values could not
be written to the registry.
2005-06-03 Timo Schulz <ts@g10code.com>
* mapuser.c (new_usermap): New.
(free_usermap): New.
* engine-gpgme.c (op_decrypt): Return 'No_Seckey' if
appropriate and not just 'Decrypt_Failed'.
Set the gpgme_ctx_t object in the callback to allow
to list the 'encrypt_to' entries.
* passphrase-dialog.c (decrypt_key_dlg_proc): Make sure
we only warn when there is a valid callback context.
(load_secbox): New parameter 'ctlid'. Changed all callers.
(load_recipbox): New. Use usermap to lookup user-id's.
(decrypt_key_ext_dlg_proc): New dialog procedure for
the callback mode.
* HashTable.cpp (HashTable_new): New. C-interface.
(HashTable_free): Likewise.
(HashTable_get): Likewise.
(HashTable_put): Likewise.
(HashTable_get_i): Likewise.
(HashTable_size): Likewise.
2005-05-29 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (decrypt_key_dlg_proc): Warning
if the user cancels the signing process.
Make the passphrase field invisible if the key is used
in selection mode.
(recipient_dlg_proc): Likewise.
(signer_dialog_box): Return -1 if the user cancelled the
dialog.
* engine-gpgme.c (op_sign): Set flags to '1' to indicate
signing process.
* config-dialog.c (does_file_exist): Use appropriate
length for xmalloc. Noted by Sebastian.
2005-05-24 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (setXHeader): New.
(getXHeader): New.
* engine-gpgme.c (op_sign_file): Implemented.
2005-05-22 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (createAttachment): New.
(deleteAttachment): New.
(encryptAttachments): New.
(encrypt): Also encrypt the attachments if possible.
(signEncrypt): Likewise.
(setEncodingFormat): New.
(getEncodingFormat): New.
(readOptions, writeOptions): Store encoding format.
(MapiGPGME): The default encoding is 'CLASSIC'.
2005-05-21 Timo Schulz <ts@g10code.com>
* HashTable.h: Export functions.
* MapiGPGME.cpp (freeAttachments): New.
(getAttachments): New.
(openAttachment): New.
(closeAttachment): New.
(processAttachment): New.
(hasAttachments): New.
(countAttachments): New.
(doCmdFile): New. Can handle files.
(doCmdAttach): New. Can handle attachment action types.
(signEncrypt): Release locusr key.
* engine-gpgme.c (op_sign_file): New. Dummy.
(op_sign_encrypt_file): New. Dummy.
2005-05-11 Timo Schulz <ts@g10code.com>
* common.c (cache_item_new): New.
(cache_item_free): New.
* engine-gpgme.c (op_decrypt_start_ext): Return an cache item and
not just the passphrase. Changed all caller.
* MapiGPGME.cpp (passphraseCallback): Support caching.
(decrypt): Likewise.
(storePassphrase): Likewise.
(getPassphrase): Likewise.
* HashTable.cpp: New.
2005-05-10 Timo Schulz <ts@g10code.com>
* passphrase-dialog.c (decrypt_key_dlg_proc): Reset 'hide state'.
Show some text when the user entered a wrong passphrase.
* MapiGPGME.cpp (findMessageWindow): New.
(setRTFBody): New.
(decrypt): Just change the window text, not the MAPI object.
Call verify() if this is a clearsigned message.
(getMessageType): New.
(getAttachmentExtension): New.
(verify): Extract text and set it.
* engine-gpgme.c (op_decrypt): Spawn verify dialog if the text
was also signed.
(op_decrypt_file): New.
* verify-dialog.c (load_sigbox): Avoid key cache and give more
information.
* passphrase-dialog.c (decrypt_key_dlg_proc): Handle 'x' clicks.
* logging.c (log_debug): Disable it for release versions.
2005-05-08 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (readOptions): Handle logfile.
(writeOptions): Likewise.
(storePassphrase): New.
(clearPassphrase): New.
(passphraseCallback): New. Needs to be static...
(decrypt): Store passphrase if requested.
(~MapiGPGME): Free memory.
(streamOnFile): New.
(getAttachMethod): New.
(getAttachFilename): New.
(setAttachMethod): New.
(getAttachFilename): New.
(getMessageHasAttachments): New.
(getMessageFlags): New.
* engine-gpgme.c (op_decrypt): New. Factoured out
code from...
(op_decrypt_start): ..here. Now call op_decrypt
with standard parameters.
(op_decrypt_next): Allow to use a pre-defined
passphrase callback. Needed for caching.
2005-05-02 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (fail_if_null, delete_buf): New
(doCmd): New.
(setBody): If the body to set is empty, do nothing.
(freeKeyArray): New.
(readOptions): New.
(writeOptions): New.
Implement all getters and setters for the config code.
* keycache.c: Now that we use a DLL, we create a
shared data segment for static data.
(load_keycache_objects): New.
2005-05-01 Timo Schulz <ts@g10code.com>
* MapiGPGME.cpp (rtfSync): New.
* logging.c (log_debug): New.
* common.c (xfree): New.
* engine-gpgme.c (op_lookup_keys): Corrected offsets.
2005-04-29 Timo Schulz <ts@g10code.com>
* engine-gpgme.c (op_encrypt_file): New.
2005-04-27 Timo Schulz <ts@g10code.com>
* config-dialog.c (config_dialog_box): Add dialog
item to select a GUI key manager.
Check that the entered files really exist.
(config_dlg_proc): Likewise.
(does_file_exist): New.
2005-04-24 Timo Schulz <ts@g10code.com>
* main.c (DllMain): New. With a static library it was not
possible to have a separate resource file. Thus we use a
DLL now which contains all needed dialogs.
* common.c (set_global_hinstance): New.
* libgpgmedlgs.def: New.
2005-04-18 Timo Schulz <ts@g10code.com>
* recipient-dialog.c (recipient_dialog_box2): New
way to show pre-selected keys.
(copy_item): New paramenter for the pos.
(find_item): Do not select the item.
* gpgmedlgs.rc (IDD_ENC): New label to describe
the listbox.
2005-04-15 Timo Schulz <ts@g10code.com>
* common.c (xmalloc, xcalloc, xstrdup): New.
(out_of_core): New.
* recipient-dialog.c (initialize_keybox): New.
(find_item): New.
* MapiGPGME.cpp (freeUnknownKeys): New.
(signEncrypt): Show dialog to select a key if no
default key was set.
* Replace all std-c alloc functions with x equivalents.
2005-04-13 Timo Schulz <ts@g10code.com>
* engine-gpgme.c (do_init): Alloc keycache objects.
(do_deinit): Cleanup the mess.
(op_lookup_keys): New. Allow to find keys via the email.
* GPGME.cpp (MapiGPGME): New. MAPI interface.
2005-04-07 Timo Schulz <ts@g10code.com>
* verify-dialog.c (load_akalist): New.
(load_sigbox): Handle bad signatures.
* keycache.c (enum_gpg_keys): Allow to reset the
enum context.
* config-dialog.c (get_open_file_name): Use MAX_PATH.
(get_folder): Likewise.
* recipient-dialog.c (load_rsetbox): Modify code to
add the last keycache item.
* passphrase-dialog.c (load_secbox): Likewise.
diff --git a/src/mapihelp.cpp b/src/mapihelp.cpp
index 454df66..0385bed 100644
--- a/src/mapihelp.cpp
+++ b/src/mapihelp.cpp
@@ -1,3180 +1,3191 @@
/* mapihelp.cpp - Helper functions for MAPI
* Copyright (C) 2005, 2007, 2008 g10 Code GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#include <windows.h>
#include "mymapi.h"
#include "mymapitags.h"
#include "common.h"
#include "rfc822parse.h"
#include "serpent.h"
#include "mapihelp.h"
#ifndef CRYPT_E_STREAM_INSUFFICIENT_DATA
#define CRYPT_E_STREAM_INSUFFICIENT_DATA 0x80091011
#endif
#ifndef CRYPT_E_ASN1_BADTAG
#define CRYPT_E_ASN1_BADTAG 0x8009310B
#endif
#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
SRCNAME, __func__, __LINE__); \
} while (0)
static int get_attach_method (LPATTACH obj);
static int has_smime_filename (LPATTACH obj);
static char *get_attach_mime_tag (LPATTACH obj);
/* Print a MAPI property to the log stream. */
void
log_mapi_property (LPMESSAGE message, ULONG prop, const char *propname)
{
HRESULT hr;
LPSPropValue propval = NULL;
size_t keylen;
void *key;
char *buf;
if (!message)
return; /* No message: Nop. */
hr = HrGetOneProp ((LPMAPIPROP)message, prop, &propval);
if (FAILED (hr))
{
log_debug ("%s:%s: HrGetOneProp(%s) failed: hr=%#lx\n",
SRCNAME, __func__, propname, hr);
return;
}
switch ( PROP_TYPE (propval->ulPropTag) )
{
case PT_BINARY:
keylen = propval->Value.bin.cb;
key = propval->Value.bin.lpb;
log_hexdump (key, keylen, "%s: %20s=", __func__, propname);
break;
case PT_UNICODE:
buf = wchar_to_utf8 (propval->Value.lpszW);
if (!buf)
log_debug ("%s:%s: error converting to utf8\n", SRCNAME, __func__);
else
log_debug ("%s: %20s=`%s'", __func__, propname, buf);
xfree (buf);
break;
case PT_STRING8:
log_debug ("%s: %20s=`%s'", __func__, propname, propval->Value.lpszA);
break;
case PT_LONG:
log_debug ("%s: %20s=%ld", __func__, propname, propval->Value.l);
break;
default:
log_debug ("%s:%s: HrGetOneProp(%s) property type %lu not supported\n",
SRCNAME, __func__, propname,
PROP_TYPE (propval->ulPropTag) );
return;
}
MAPIFreeBuffer (propval);
}
/* Helper to create a named property. */
static ULONG
create_gpgol_tag (LPMESSAGE message, wchar_t *name, const char *func)
{
HRESULT hr;
LPSPropTagArray proparr = NULL;
MAPINAMEID mnid, *pmnid;
/* {31805ab8-3e92-11dc-879c-00061b031004}: GpgOL custom properties. */
GUID guid = {0x31805ab8, 0x3e92, 0x11dc, {0x87, 0x9c, 0x00, 0x06,
0x1b, 0x03, 0x10, 0x04}};
ULONG result;
memset (&mnid, 0, sizeof mnid);
mnid.lpguid = &guid;
mnid.ulKind = MNID_STRING;
mnid.Kind.lpwstrName = name;
pmnid = &mnid;
hr = message->GetIDsFromNames (1, &pmnid, MAPI_CREATE, &proparr);
if (FAILED (hr))
proparr = NULL;
if (FAILED (hr) || !(proparr->aulPropTag[0] & 0xFFFF0000) )
{
log_error ("%s:%s: can't map GpgOL property: hr=%#lx\n",
SRCNAME, func, hr);
result = 0;
}
else
result = (proparr->aulPropTag[0] & 0xFFFF0000);
if (proparr)
MAPIFreeBuffer (proparr);
return result;
}
/* Return the property tag for GpgOL Msg Class. */
int
get_gpgolmsgclass_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL Msg Class", __func__)))
return -1;
*r_tag |= PT_STRING8;
return 0;
}
/* Return the property tag for GpgOL Old Msg Class. The Old Msg Class
saves the message class as seen before we changed it the first
time. */
int
get_gpgololdmsgclass_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL Old Msg Class", __func__)))
return -1;
*r_tag |= PT_STRING8;
return 0;
}
/* Return the property tag for GpgOL Attach Type. */
int
get_gpgolattachtype_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL Attach Type", __func__)))
return -1;
*r_tag |= PT_LONG;
return 0;
}
/* Return the property tag for GpgOL Sig Status. */
int
get_gpgolsigstatus_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL Sig Status", __func__)))
return -1;
*r_tag |= PT_STRING8;
return 0;
}
/* Return the property tag for GpgOL Protect IV. */
int
get_gpgolprotectiv_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL Protect IV", __func__)))
return -1;
*r_tag |= PT_BINARY;
return 0;
}
/* Return the property tag for GpgOL Last Decrypted. */
int
get_gpgollastdecrypted_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL Last Decrypted",__func__)))
return -1;
*r_tag |= PT_BINARY;
return 0;
}
/* Return the property tag for GpgOL MIME structure. */
int
get_gpgolmimeinfo_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL MIME Info", __func__)))
return -1;
*r_tag |= PT_STRING8;
return 0;
}
/* Return the property tag for GpgOL Charset. */
int
get_gpgolcharset_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL Charset", __func__)))
return -1;
*r_tag |= PT_STRING8;
return 0;
}
/* Return the property tag for GpgOL Draft Info. */
int
get_gpgoldraftinfo_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL Draft Info", __func__)))
return -1;
*r_tag |= PT_STRING8;
return 0;
}
/* Return the tag of the Internet Charset Body property which seems to
hold the PR_BODY as received and thus before charset
conversion. */
int
get_internetcharsetbody_tag (LPMESSAGE message, ULONG *r_tag)
{
HRESULT hr;
LPSPropTagArray proparr = NULL;
MAPINAMEID mnid, *pmnid;
/* {4E3A7680-B77A-11D0-9DA5-00C04FD65685} */
GUID guid = {0x4E3A7680, 0xB77A, 0x11D0, {0x9D, 0xA5, 0x00, 0xC0,
0x4F, 0xD6, 0x56, 0x85}};
int result;
memset (&mnid, 0, sizeof mnid);
mnid.lpguid = &guid;
mnid.ulKind = MNID_STRING;
mnid.Kind.lpwstrName = L"Internet Charset Body";
pmnid = &mnid;
hr = message->GetIDsFromNames (1, &pmnid, 0, &proparr);
if (FAILED (hr))
proparr = NULL;
if (FAILED (hr) || !(proparr->aulPropTag[0] & 0xFFFF0000) )
{
log_error ("%s:%s: can't get the Internet Charset Body property:"
" hr=%#lx\n", SRCNAME, __func__, hr);
result = -1;
}
else
{
result = 0;
*r_tag = ((proparr->aulPropTag[0] & 0xFFFF0000) | PT_BINARY);
}
if (proparr)
MAPIFreeBuffer (proparr);
return result;
}
/* A Wrapper around the SaveChanges method. This function should be
called indirect through the mapi_save_changes macro. Returns 0 on
success. */
int
mapi_do_save_changes (LPMESSAGE message, ULONG flags, int only_del_body,
const char *dbg_file, const char *dbg_func)
{
HRESULT hr;
SPropTagArray proparray;
int any = 0;
if (mapi_has_last_decrypted (message))
{
proparray.cValues = 1;
proparray.aulPropTag[0] = PR_BODY;
hr = message->DeleteProps (&proparray, NULL);
if (hr)
log_debug_w32 (hr, "%s:%s: deleting PR_BODY failed",
log_srcname (dbg_file), dbg_func);
else
any = 1;
proparray.cValues = 1;
proparray.aulPropTag[0] = PR_BODY_HTML;
hr = message->DeleteProps (&proparray, NULL);
if (hr)
log_debug_w32 (hr, "%s:%s: deleting PR_BODY_HTML failed",
log_srcname (dbg_file), dbg_func);
else
any = 1;
}
if (!only_del_body || any)
{
hr = message->SaveChanges (flags);
if (hr)
{
log_error ("%s:%s: SaveChanges(%lu) failed: hr=%#lx\n",
log_srcname (dbg_file), dbg_func,
(unsigned long)flags, hr);
return -1;
}
}
return 0;
}
/* Set an arbitary header in the message MSG with NAME to the value
VAL. */
int
mapi_set_header (LPMESSAGE msg, const char *name, const char *val)
{
HRESULT hr;
LPSPropTagArray pProps = NULL;
SPropValue pv;
MAPINAMEID mnid, *pmnid;
/* {00020386-0000-0000-C000-000000000046} -> GUID For X-Headers */
GUID guid = {0x00020386, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x46} };
int result;
if (!msg)
return -1;
memset (&mnid, 0, sizeof mnid);
mnid.lpguid = &guid;
mnid.ulKind = MNID_STRING;
mnid.Kind.lpwstrName = utf8_to_wchar (name);
pmnid = &mnid;
hr = msg->GetIDsFromNames (1, &pmnid, MAPI_CREATE, &pProps);
xfree (mnid.Kind.lpwstrName);
if (FAILED (hr))
{
pProps = NULL;
log_error ("%s:%s: can't get mapping for header `%s': hr=%#lx\n",
SRCNAME, __func__, name, hr);
result = -1;
}
else
{
pv.ulPropTag = (pProps->aulPropTag[0] & 0xFFFF0000) | PT_STRING8;
pv.Value.lpszA = (char *)val;
hr = HrSetOneProp(msg, &pv);
if (hr)
{
log_error ("%s:%s: can't set header `%s': hr=%#lx\n",
SRCNAME, __func__, name, hr);
result = -1;
}
else
result = 0;
}
if (pProps)
MAPIFreeBuffer (pProps);
return result;
}
/* Return the body as a new IStream object. Returns NULL on failure.
The stream returns the body as an ASCII stream (Use mapi_get_body
for an UTF-8 value). */
LPSTREAM
mapi_get_body_as_stream (LPMESSAGE message)
{
HRESULT hr;
ULONG tag;
LPSTREAM stream;
if (!message)
return NULL;
if (!get_internetcharsetbody_tag (message, &tag) )
{
/* The store knows about the Internet Charset Body property,
thus try to get the body from this property if it exists. */
hr = message->OpenProperty (tag, &IID_IStream, 0, 0,
(LPUNKNOWN*)&stream);
if (!hr)
return stream;
log_debug ("%s:%s: OpenProperty tag=%lx failed: hr=%#lx",
SRCNAME, __func__, tag, hr);
}
/* We try to get it as an ASCII body. If this fails we would either
need to implement some kind of stream filter to translated to
utf-8 or read everyting into a memory buffer and [provide an
istream from that memory buffer. */
hr = message->OpenProperty (PR_BODY_A, &IID_IStream, 0, 0,
(LPUNKNOWN*)&stream);
if (hr)
{
log_debug ("%s:%s: OpenProperty failed: hr=%#lx", SRCNAME, __func__, hr);
return NULL;
}
return stream;
}
/* Return the body of the message in an allocated buffer. The buffer
is guaranteed to be Nul terminated. The actual length (ie. the
strlen()) will be stored at R_NBYTES. The body will be returned in
UTF-8 encoding. Returns NULL if no body is available. */
char *
mapi_get_body (LPMESSAGE message, size_t *r_nbytes)
{
HRESULT hr;
LPSPropValue lpspvFEID = NULL;
LPSTREAM stream;
STATSTG statInfo;
ULONG nread;
char *body = NULL;
if (r_nbytes)
*r_nbytes = 0;
hr = HrGetOneProp ((LPMAPIPROP)message, PR_BODY, &lpspvFEID);
if (SUCCEEDED (hr)) /* Message is small enough to be retrieved directly. */
{
switch ( PROP_TYPE (lpspvFEID->ulPropTag) )
{
case PT_UNICODE:
body = wchar_to_utf8 (lpspvFEID->Value.lpszW);
if (!body)
log_debug ("%s: error converting to utf8\n", __func__);
break;
case PT_STRING8:
body = xstrdup (lpspvFEID->Value.lpszA);
break;
default:
log_debug ("%s: proptag=0x%08lx not supported\n",
__func__, lpspvFEID->ulPropTag);
break;
}
MAPIFreeBuffer (lpspvFEID);
}
else /* Message is large; use an IStream to read it. */
{
hr = message->OpenProperty (PR_BODY, &IID_IStream, 0, 0,
(LPUNKNOWN*)&stream);
if (hr)
{
log_debug ("%s:%s: OpenProperty failed: hr=%#lx",
SRCNAME, __func__, hr);
return NULL;
}
hr = stream->Stat (&statInfo, STATFLAG_NONAME);
if (hr)
{
log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
stream->Release ();
return NULL;
}
/* Fixme: We might want to read only the first 1k to decide
whether this is actually an OpenPGP message and only then
continue reading. */
body = (char*)xmalloc ((size_t)statInfo.cbSize.QuadPart + 2);
hr = stream->Read (body, (size_t)statInfo.cbSize.QuadPart, &nread);
if (hr)
{
log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
xfree (body);
stream->Release ();
return NULL;
}
body[nread] = 0;
body[nread+1] = 0;
if (nread != statInfo.cbSize.QuadPart)
{
log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
xfree (body);
stream->Release ();
return NULL;
}
stream->Release ();
{
char *tmp;
tmp = wchar_to_utf8 ((wchar_t*)body);
if (!tmp)
log_debug ("%s: error converting to utf8\n", __func__);
else
{
xfree (body);
body = tmp;
}
}
}
if (r_nbytes)
*r_nbytes = strlen (body);
return body;
}
/* Look at the body of the MESSAGE and try to figure out whether this
is a supported PGP message. Returns the new message class or NULL
if it does not look like a PGP message. */
static char *
get_msgcls_from_pgp_lines (LPMESSAGE message)
{
HRESULT hr;
LPSTREAM stream;
STATSTG statInfo;
ULONG nread;
size_t nbytes;
char *body = NULL;
char *p;
char *msgcls = NULL;
ULONG tag;
int is_binary = 0;
hr = 0;
if (!get_internetcharsetbody_tag (message, &tag) )
{
hr = message->OpenProperty (tag, &IID_IStream, 0, 0,
(LPUNKNOWN*)&stream);
if (!hr)
is_binary = 1;
}
if (hr)
{
tag = PR_BODY;
hr = message->OpenProperty (tag, &IID_IStream, 0, 0,
(LPUNKNOWN*)&stream);
}
if (hr)
{
log_debug ("%s:%s: OpenProperty(%lx) failed: hr=%#lx",
SRCNAME, __func__, tag, hr);
return NULL;
}
hr = stream->Stat (&statInfo, STATFLAG_NONAME);
if (hr)
{
log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
stream->Release ();
return NULL;
}
/* We read only the first 1k to decide whether this is actually an
OpenPGP armored message . */
nbytes = (size_t)statInfo.cbSize.QuadPart;
if (nbytes > 1024*2)
nbytes = 1024*2;
body = (char*)xmalloc (nbytes + 2);
hr = stream->Read (body, nbytes, &nread);
if (hr)
{
log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
xfree (body);
stream->Release ();
return NULL;
}
body[nread] = 0;
body[nread+1] = 0;
if (nread != nbytes)
{
log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
xfree (body);
stream->Release ();
return NULL;
}
stream->Release ();
if (!is_binary)
{
char *tmp;
tmp = wchar_to_utf8 ((wchar_t*)body);
if (!tmp)
log_debug ("%s: error converting to utf8\n", __func__);
else
{
xfree (body);
body = tmp;
}
}
/* The first ~1k of the body of the message is now available in the
utf-8 string BODY. Walk over it to figure out its type. */
for (p=body; p && *p; p = (p=strchr (p+1, '\n')? (p+1):NULL))
{
if (!strncmp (p, "-----BEGIN PGP ", 15))
{
if (!strncmp (p+15, "SIGNED MESSAGE-----", 19)
&& trailing_ws_p (p+15+19))
msgcls = xstrdup ("IPM.Note.GpgOL.ClearSigned");
else if (!strncmp (p+15, "MESSAGE-----", 12)
&& trailing_ws_p (p+15+12))
msgcls = xstrdup ("IPM.Note.GpgOL.PGPMessage");
break;
}
else if (!trailing_ws_p (p))
break; /* Text before the PGP message - don't take this as a
proper message. */
}
xfree (body);
return msgcls;
}
/* Check whether the message is really a CMS encrypted message.
We check here whether the message is really encrypted by looking at
the object identifier inside the CMS data. Returns:
-1 := Unknown message type,
0 := The message is signed,
1 := The message is encrypted.
This function is required for two reasons:
1. Due to a bug in CryptoEx which sometimes assignes the *.CexEnc
message class to signed messages and only updates the message
class after accessing them. Thus in old stores there may be a
lot of *.CexEnc message which are actually just signed.
2. If the smime-type parameter is missing we need another way to
decide whether to decrypt or to verify.
3. Some messages lack a PR_TRANSPORT_MESSAGE_HEADERS and thus it is
not possible to deduce the message type from the mail headers.
This function may be used to identify the message anyway.
*/
static int
is_really_cms_encrypted (LPMESSAGE message)
{
HRESULT hr;
SizedSPropTagArray (1L, propAttNum) = { 1L, {PR_ATTACH_NUM} };
LPMAPITABLE mapitable;
LPSRowSet mapirows;
unsigned int pos, n_attach;
int result = -1; /* Unknown. */
LPATTACH att = NULL;
LPSTREAM stream = NULL;
char buffer[24]; /* 24 bytes are more than enough to peek at.
Cf. ksba_cms_identify() from the libksba
package. */
const char *p;
ULONG nread;
size_t n;
tlvinfo_t ti;
hr = message->GetAttachmentTable (0, &mapitable);
if (FAILED (hr))
{
log_debug ("%s:%s: GetAttachmentTable failed: hr=%#lx",
SRCNAME, __func__, hr);
return -1;
}
hr = HrQueryAllRows (mapitable, (LPSPropTagArray)&propAttNum,
NULL, NULL, 0, &mapirows);
if (FAILED (hr))
{
log_debug ("%s:%s: HrQueryAllRows failed: hr=%#lx",
SRCNAME, __func__, hr);
mapitable->Release ();
return -1;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
if (n_attach != 1)
{
FreeProws (mapirows);
mapitable->Release ();
log_debug ("%s:%s: not just one attachment", SRCNAME, __func__);
return -1;
}
pos = 0;
if (mapirows->aRow[pos].cValues < 1)
{
log_error ("%s:%s: invalid row at pos %d", SRCNAME, __func__, pos);
goto leave;
}
if (mapirows->aRow[pos].lpProps[0].ulPropTag != PR_ATTACH_NUM)
{
log_error ("%s:%s: invalid prop at pos %d", SRCNAME, __func__, pos);
goto leave;
}
hr = message->OpenAttach (mapirows->aRow[pos].lpProps[0].Value.l,
NULL, MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment %d (%ld): hr=%#lx",
SRCNAME, __func__, pos,
mapirows->aRow[pos].lpProps[0].Value.l, hr);
goto leave;
}
if (!has_smime_filename (att))
{
log_debug ("%s:%s: no smime filename", SRCNAME, __func__);
goto leave;
}
if (get_attach_method (att) != ATTACH_BY_VALUE)
{
log_debug ("%s:%s: wrong attach method", SRCNAME, __func__);
goto leave;
}
hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream,
0, 0, (LPUNKNOWN*) &stream);
if (FAILED (hr))
{
log_error ("%s:%s: can't open data stream of attachment: hr=%#lx",
SRCNAME, __func__, hr);
goto leave;
}
hr = stream->Read (buffer, sizeof buffer, &nread);
if ( hr != S_OK )
{
log_error ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
goto leave;
}
if (nread < sizeof buffer)
{
log_error ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
goto leave;
}
p = buffer;
n = nread;
if (parse_tlv (&p, &n, &ti))
goto leave;
if (!(ti.cls == MY_ASN_CLASS_UNIVERSAL && ti.tag == MY_ASN_TAG_SEQUENCE
&& ti.is_cons) )
goto leave;
if (parse_tlv (&p, &n, &ti))
goto leave;
if (!(ti.cls == MY_ASN_CLASS_UNIVERSAL && ti.tag == MY_ASN_TAG_OBJECT_ID
&& !ti.is_cons && ti.length) || ti.length > n)
goto leave;
/* Now is this enveloped data (1.2.840.113549.1.7.3)
or signed data (1.2.840.113549.1.7.2) ? */
if (ti.length == 9)
{
if (!memcmp (p, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x03", 9))
result = 1; /* Encrypted. */
else if (!memcmp (p, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x02", 9))
result = 0; /* Signed. */
}
leave:
if (stream)
stream->Release ();
if (att)
att->Release ();
FreeProws (mapirows);
mapitable->Release ();
return result;
}
/* Return the content-type of the first and only attachment of MESSAGE
or NULL if it does not exists. Caller must free. */
static char *
get_first_attach_mime_tag (LPMESSAGE message)
{
HRESULT hr;
SizedSPropTagArray (1L, propAttNum) = { 1L, {PR_ATTACH_NUM} };
LPMAPITABLE mapitable;
LPSRowSet mapirows;
unsigned int pos, n_attach;
LPATTACH att = NULL;
char *result = NULL;
hr = message->GetAttachmentTable (0, &mapitable);
if (FAILED (hr))
{
log_debug ("%s:%s: GetAttachmentTable failed: hr=%#lx",
SRCNAME, __func__, hr);
return NULL;
}
hr = HrQueryAllRows (mapitable, (LPSPropTagArray)&propAttNum,
NULL, NULL, 0, &mapirows);
if (FAILED (hr))
{
log_debug ("%s:%s: HrQueryAllRows failed: hr=%#lx",
SRCNAME, __func__, hr);
mapitable->Release ();
return NULL;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
if (n_attach != 1)
{
FreeProws (mapirows);
mapitable->Release ();
log_debug ("%s:%s: not just one attachment", SRCNAME, __func__);
return NULL;
}
pos = 0;
if (mapirows->aRow[pos].cValues < 1)
{
log_error ("%s:%s: invalid row at pos %d", SRCNAME, __func__, pos);
goto leave;
}
if (mapirows->aRow[pos].lpProps[0].ulPropTag != PR_ATTACH_NUM)
{
log_error ("%s:%s: invalid prop at pos %d", SRCNAME, __func__, pos);
goto leave;
}
hr = message->OpenAttach (mapirows->aRow[pos].lpProps[0].Value.l,
NULL, MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment %d (%ld): hr=%#lx",
SRCNAME, __func__, pos,
mapirows->aRow[pos].lpProps[0].Value.l, hr);
goto leave;
}
/* Note: We do not expect a filename. */
if (get_attach_method (att) != ATTACH_BY_VALUE)
{
log_debug ("%s:%s: wrong attach method", SRCNAME, __func__);
goto leave;
}
result = get_attach_mime_tag (att);
leave:
if (att)
att->Release ();
FreeProws (mapirows);
mapitable->Release ();
return result;
}
/* Helper for mapi_change_message_class. Returns the new message
class as an allocated string.
Most message today are of the message class "IPM.Note". However a
PGP/MIME encrypted message also has this class. We need to see
whether we can detect such a mail right here and change the message
class accordingly. */
static char *
change_message_class_ipm_note (LPMESSAGE message)
{
char *newvalue = NULL;
char *ct, *proto;
ct = mapi_get_message_content_type (message, &proto, NULL);
if (ct)
{
log_debug ("%s:%s: content type is '%s'", SRCNAME, __func__, ct);
if (proto)
{
log_debug ("%s:%s: protocol is '%s'", SRCNAME, __func__, proto);
if (!strcmp (ct, "multipart/encrypted")
&& !strcmp (proto, "application/pgp-encrypted"))
{
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartEncrypted");
}
else if (!strcmp (ct, "multipart/signed")
&& !strcmp (proto, "application/pgp-signature"))
{
/* Sometimes we receive a PGP/MIME signed message with a
class IPM.Note. */
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
}
xfree (proto);
}
else if (!strcmp (ct, "text/plain"))
{
newvalue = get_msgcls_from_pgp_lines (message);
}
else if (!strcmp (ct, "multipart/mixed"))
{
/* It is quite common to have a multipart/mixed mail with
separate encrypted PGP parts. Look at the body to
decide. */
newvalue = get_msgcls_from_pgp_lines (message);
}
xfree (ct);
}
else
log_debug ("%s:%s: message has no content type", SRCNAME, __func__);
return newvalue;
}
/* Helper for mapi_change_message_class. Returns the new message
class as an allocated string.
This function is used for the message class "IPM.Note.SMIME". It
indicates an S/MIME opaque encrypted or signed message. This may
also be an PGP/MIME mail. */
static char *
change_message_class_ipm_note_smime (LPMESSAGE message)
{
char *newvalue = NULL;
char *ct, *proto, *smtype;
ct = mapi_get_message_content_type (message, &proto, &smtype);
if (ct)
{
log_debug ("%s:%s: content type is '%s'", SRCNAME, __func__, ct);
if (proto
&& !strcmp (ct, "multipart/signed")
&& !strcmp (proto, "application/pgp-signature"))
{
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
}
else if (!opt.enable_smime)
; /* S/MIME not enabled; thus no further checks. */
else if (smtype)
{
log_debug ("%s:%s: smime-type is '%s'", SRCNAME, __func__, smtype);
if (!strcmp (ct, "application/pkcs7-mime")
|| !strcmp (ct, "application/x-pkcs7-mime"))
{
if (!strcmp (smtype, "signed-data"))
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueSigned");
else if (!strcmp (smtype, "enveloped-data"))
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
}
}
else
{
/* No smime type. The filename parameter is often not
reliable, thus we better look into the message to see if
it is encrypted and assume an opaque signed one if this
is not the case. */
switch (is_really_cms_encrypted (message))
{
case 0:
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueSigned");
break;
case 1:
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
break;
}
}
xfree (smtype);
xfree (proto);
xfree (ct);
}
else
{
log_debug ("%s:%s: message has no content type", SRCNAME, __func__);
/* CryptoEx (or the Toltec Connector) create messages without
the transport headers property and thus we don't know the
content type. We try to detect the message type anyway by
looking into the first and only attachments. */
switch (is_really_cms_encrypted (message))
{
case 0:
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueSigned");
break;
case 1:
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
break;
default: /* Unknown. */
break;
}
}
/* If we did not found anything but let's change the class anyway. */
if (!newvalue && opt.enable_smime)
newvalue = xstrdup ("IPM.Note.GpgOL");
return newvalue;
}
/* Helper for mapi_change_message_class. Returns the new message
class as an allocated string.
This function is used for the message class
"IPM.Note.SMIME.MultipartSigned". This is an S/MIME message class
but smime support is not enabled. We need to check whether this is
actually a PGP/MIME message. */
static char *
change_message_class_ipm_note_smime_multipartsigned (LPMESSAGE message)
{
char *newvalue = NULL;
char *ct, *proto;
ct = mapi_get_message_content_type (message, &proto, NULL);
if (ct)
{
log_debug ("%s:%s: content type is '%s'", SRCNAME, __func__, ct);
if (proto
&& !strcmp (ct, "multipart/signed")
&& !strcmp (proto, "application/pgp-signature"))
{
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
}
xfree (proto);
xfree (ct);
}
else
log_debug ("%s:%s: message has no content type", SRCNAME, __func__);
return newvalue;
}
/* Helper for mapi_change_message_class. Returns the new message
class as an allocated string.
This function is used for the message classes
"IPM.Note.Secure.CexSig" and "IPM.Note.Secure.Cexenc" (in the
latter case IS_CEXSIG is true). These are CryptoEx generated
signature or encryption messages. */
static char *
change_message_class_ipm_note_secure_cex (LPMESSAGE message, int is_cexenc)
{
char *newvalue = NULL;
char *ct, *smtype, *proto;
ct = mapi_get_message_content_type (message, &proto, &smtype);
if (ct)
{
log_debug ("%s:%s: content type is '%s'", SRCNAME, __func__, ct);
if (smtype)
log_debug ("%s:%s: smime-type is '%s'", SRCNAME, __func__, smtype);
if (proto)
log_debug ("%s:%s: protocol is '%s'", SRCNAME, __func__, proto);
if (smtype)
{
if (!strcmp (ct, "application/pkcs7-mime")
|| !strcmp (ct, "application/x-pkcs7-mime"))
{
if (!strcmp (smtype, "signed-data"))
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueSigned");
else if (!strcmp (smtype, "enveloped-data"))
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
}
}
if (!newvalue && proto)
{
if (!strcmp (ct, "multipart/signed")
&& (!strcmp (proto, "application/pkcs7-signature")
|| !strcmp (proto, "application/x-pkcs7-signature")))
{
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
}
else if (!strcmp (ct, "multipart/signed")
&& (!strcmp (proto, "application/pgp-signature")))
{
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
}
}
if (!newvalue && !strcmp (ct, "text/plain"))
{
newvalue = get_msgcls_from_pgp_lines (message);
}
if (!newvalue)
{
switch (is_really_cms_encrypted (message))
{
case 0:
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueSigned");
break;
case 1:
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
break;
}
}
xfree (smtype);
xfree (proto);
xfree (ct);
}
else
{
log_debug ("%s:%s: message has no content type", SRCNAME, __func__);
if (is_cexenc)
{
switch (is_really_cms_encrypted (message))
{
case 0:
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueSigned");
break;
case 1:
newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
break;
}
}
else
{
char *mimetag;
mimetag = get_first_attach_mime_tag (message);
if (mimetag && !strcmp (mimetag, "multipart/signed"))
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
xfree (mimetag);
}
if (!newvalue)
{
newvalue = get_msgcls_from_pgp_lines (message);
}
}
if (!newvalue)
newvalue = xstrdup ("IPM.Note.GpgOL");
return newvalue;
}
/* This function checks whether MESSAGE requires processing by us and
adjusts the message class to our own. By passing true for
SYNC_OVERRIDE the actual MAPI message class will be updated to our
own message class overide. Return true if the message was
changed. */
int
mapi_change_message_class (LPMESSAGE message, int sync_override)
{
HRESULT hr;
ULONG tag;
SPropValue prop;
LPSPropValue propval = NULL;
char *newvalue = NULL;
int need_save = 0;
int have_override = 0;
if (!message)
return 0; /* No message: Nop. */
if (get_gpgolmsgclass_tag (message, &tag) )
return 0; /* Ooops. */
hr = HrGetOneProp ((LPMAPIPROP)message, tag, &propval);
if (FAILED (hr))
{
hr = HrGetOneProp ((LPMAPIPROP)message, PR_MESSAGE_CLASS_A, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: HrGetOneProp() failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return 0;
}
}
else
{
have_override = 1;
log_debug ("%s:%s: have override message class\n", SRCNAME, __func__);
}
if ( PROP_TYPE (propval->ulPropTag) == PT_STRING8 )
{
const char *s = propval->Value.lpszA;
int cexenc = 0;
log_debug ("%s:%s: checking message class `%s'",
SRCNAME, __func__, s);
if (!strcmp (s, "IPM.Note"))
{
newvalue = change_message_class_ipm_note (message);
}
else if (!strcmp (s, "IPM.Note.SMIME"))
{
newvalue = change_message_class_ipm_note_smime (message);
}
else if (opt.enable_smime
&& !strncmp (s, "IPM.Note.SMIME", 14) && (!s[14]||s[14] =='.'))
{
/* This is "IPM.Note.SMIME.foo" (where ".foo" is optional
but the previous condition has already taken care of
this). Note that we can't just insert a new part and
keep the SMIME; we need to change the SMIME part of the
class name so that Outlook does not process it as an
SMIME message. */
newvalue = (char*)xmalloc (strlen (s) + 1);
strcpy (stpcpy (newvalue, "IPM.Note.GpgOL"), s+14);
}
else if (!strcmp (s, "IPM.Note.SMIME.MultipartSigned"))
{
/* This is an S/MIME message class but smime support is not
enabled. We need to check whether this is actually a
PGP/MIME message. */
newvalue = change_message_class_ipm_note_smime_multipartsigned
(message);
}
else if (opt.enable_smime && sync_override && have_override
&& !strncmp (s, "IPM.Note.GpgOL", 14) && (!s[14]||s[14] =='.'))
{
/* In case the original message class is not yet an GpgOL
class we set it here. This is needed to convince Outlook
not to do any special processing for IPM.Note.SMIME etc. */
LPSPropValue propval2 = NULL;
hr = HrGetOneProp ((LPMAPIPROP)message, PR_MESSAGE_CLASS_A,
&propval2);
if (SUCCEEDED (hr) && PROP_TYPE (propval2->ulPropTag) == PT_STRING8
&& propval2->Value.lpszA && strcmp (propval2->Value.lpszA, s))
newvalue = (char*)xstrdup (s);
MAPIFreeBuffer (propval2);
}
else if (opt.enable_smime
&& (!strcmp (s, "IPM.Note.Secure.CexSig")
|| (cexenc = !strcmp (s, "IPM.Note.Secure.CexEnc"))))
{
newvalue = change_message_class_ipm_note_secure_cex
(message, cexenc);
}
}
if (!newvalue)
{
/* We use our Sig-Status property to mark messages which passed
this function. This helps us to avoid later tests. */
if (!mapi_has_sig_status (message))
{
mapi_set_sig_status (message, "#");
need_save = 1;
}
}
else
{
/* Save old message class if not yet done. (The second
condition is just a failsafe check). */
if (!get_gpgololdmsgclass_tag (message, &tag)
&& PROP_TYPE (propval->ulPropTag) == PT_STRING8)
{
LPSPropValue propval2 = NULL;
hr = HrGetOneProp ((LPMAPIPROP)message, tag, &propval2);
if (!FAILED (hr))
MAPIFreeBuffer (propval2);
else
{
/* No such property - save it. */
log_debug ("%s:%s: saving old message class\n",
SRCNAME, __func__);
prop.ulPropTag = tag;
prop.Value.lpszA = propval->Value.lpszA;
hr = message->SetProps (1, &prop, NULL);
if (hr)
{
log_error ("%s:%s: can't save old message class: hr=%#lx\n",
SRCNAME, __func__, hr);
MAPIFreeBuffer (propval);
return 0;
}
need_save = 1;
}
}
/* Change message class. */
log_debug ("%s:%s: setting message class to `%s'\n",
SRCNAME, __func__, newvalue);
prop.ulPropTag = PR_MESSAGE_CLASS_A;
prop.Value.lpszA = newvalue;
hr = message->SetProps (1, &prop, NULL);
xfree (newvalue);
if (hr)
{
log_error ("%s:%s: can't set message class: hr=%#lx\n",
SRCNAME, __func__, hr);
MAPIFreeBuffer (propval);
return 0;
}
need_save = 1;
}
MAPIFreeBuffer (propval);
if (need_save)
{
if (mapi_save_changes (message, KEEP_OPEN_READWRITE|FORCE_SAVE))
return 0;
}
return 1;
}
/* Return the message class. This function will never return NULL so
it is mostly useful for debugging. Caller needs to release the
returned string. */
char *
mapi_get_message_class (LPMESSAGE message)
{
HRESULT hr;
LPSPropValue propval = NULL;
char *retstr;
if (!message)
return xstrdup ("[No message]");
hr = HrGetOneProp ((LPMAPIPROP)message, PR_MESSAGE_CLASS_A, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: HrGetOneProp() failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return xstrdup (hr == MAPI_E_NOT_FOUND?
"[No message class property]":
"[Error getting message class property]");
}
if ( PROP_TYPE (propval->ulPropTag) == PT_STRING8 )
retstr = xstrdup (propval->Value.lpszA);
else
retstr = xstrdup ("[Invalid message class property]");
MAPIFreeBuffer (propval);
return retstr;
}
/* Return the old message class. This function returns NULL if no old
message class has been saved. Caller needs to release the returned
string. */
char *
mapi_get_old_message_class (LPMESSAGE message)
{
HRESULT hr;
ULONG tag;
LPSPropValue propval = NULL;
char *retstr;
if (!message)
return NULL;
if (get_gpgololdmsgclass_tag (message, &tag))
return NULL;
hr = HrGetOneProp ((LPMAPIPROP)message, tag, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: HrGetOneProp() failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return NULL;
}
if ( PROP_TYPE (propval->ulPropTag) == PT_STRING8 )
retstr = xstrdup (propval->Value.lpszA);
else
retstr = NULL;
MAPIFreeBuffer (propval);
return retstr;
}
/* Return the sender of the message. According to the specs this is
an UTF-8 string; we rely on that the UI server handles
internationalized domain names. */
char *
mapi_get_sender (LPMESSAGE message)
{
HRESULT hr;
LPSPropValue propval = NULL;
char *buf;
char *p0, *p;
if (!message)
return NULL; /* No message: Nop. */
hr = HrGetOneProp ((LPMAPIPROP)message, PR_PRIMARY_SEND_ACCT, &propval);
if (FAILED (hr))
{
log_debug ("%s:%s: HrGetOneProp failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return NULL;
}
if (PROP_TYPE (propval->ulPropTag) != PT_UNICODE)
{
log_debug ("%s:%s: HrGetOneProp returns invalid type %lu\n",
SRCNAME, __func__, PROP_TYPE (propval->ulPropTag) );
MAPIFreeBuffer (propval);
return NULL;
}
buf = wchar_to_utf8 (propval->Value.lpszW);
MAPIFreeBuffer (propval);
if (!buf)
{
log_error ("%s:%s: error converting to utf8\n", SRCNAME, __func__);
return NULL;
}
/* The PR_PRIMARY_SEND_ACCT property seems to be divided into fields
using Ctrl-A as delimiter. The first field looks like the ascii
formatted number of fields to follow, the second field like the
email account and the third seems to be a textual description of
that account. We return the second field. */
p = strchr (buf, '\x01');
if (!p)
{
log_error ("%s:%s: unknown format of the value `%s'\n",
SRCNAME, __func__, buf);
xfree (buf);
return NULL;
}
for (p0=buf, p++; *p && *p != '\x01';)
*p0++ = *p++;
*p0 = 0;
/* When using an Exchange account this is an X.509 address and not
an SMTP address. We try to detect this here and extract only the
CN RDN. Note that there are two CNs. This is just a simple
approach and not a real parser. A better way to do this would be
to ask MAPI to resolve the X.500 name to an SMTP name. */
if (strstr (buf, "/o=") && strstr (buf, "/ou=") &&
(p = strstr (buf, "/cn=Recipients")) && (p = strstr (p+1, "/cn=")))
{
log_debug ("%s:%s: orig address is `%s'\n", SRCNAME, __func__, buf);
memmove (buf, p+4, strlen (p+4)+1);
+ if (!strchr (buf, '@'))
+ {
+ /* Some Exchange accounts return only the accoutn name and
+ no rfc821 mail address. Kleopatra chokes on that, thus
+ we append a domain name. Thisis a bad hack. */
+ char *newbuf = (char *)xmalloc (strlen (buf) + 6 + 1);
+ strcpy (stpcpy (newbuf, buf), "@local");
+ xfree (buf);
+ buf = newbuf;
+ }
+
}
log_debug ("%s:%s: address is `%s'\n", SRCNAME, __func__, buf);
return buf;
}
/* Return the from address of the message as a malloced UTF-8 string.
Returns NULL if that address is not available. */
char *
mapi_get_from_address (LPMESSAGE message)
{
HRESULT hr;
LPSPropValue propval = NULL;
char *buf;
if (!message)
return xstrdup ("[no message]"); /* Ooops. */
hr = HrGetOneProp ((LPMAPIPROP)message, PR_SENDER_EMAIL_ADDRESS_W, &propval);
if (FAILED (hr))
{
log_debug ("%s:%s: HrGetOneProp failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return NULL;
}
if (PROP_TYPE (propval->ulPropTag) != PT_UNICODE)
{
log_debug ("%s:%s: HrGetOneProp returns invalid type %lu\n",
SRCNAME, __func__, PROP_TYPE (propval->ulPropTag) );
MAPIFreeBuffer (propval);
return NULL;
}
buf = wchar_to_utf8 (propval->Value.lpszW);
MAPIFreeBuffer (propval);
if (!buf)
{
log_error ("%s:%s: error converting to utf8\n", SRCNAME, __func__);
return NULL;
}
return buf;
}
/* Return the subject of the message as a malloced UTF-8 string.
Returns a replacement string if a subject is missing. */
char *
mapi_get_subject (LPMESSAGE message)
{
HRESULT hr;
LPSPropValue propval = NULL;
char *buf;
if (!message)
return xstrdup ("[no message]"); /* Ooops. */
hr = HrGetOneProp ((LPMAPIPROP)message, PR_SUBJECT_W, &propval);
if (FAILED (hr))
{
log_debug ("%s:%s: HrGetOneProp failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return xstrdup (_("[no subject]"));
}
if (PROP_TYPE (propval->ulPropTag) != PT_UNICODE)
{
log_debug ("%s:%s: HrGetOneProp returns invalid type %lu\n",
SRCNAME, __func__, PROP_TYPE (propval->ulPropTag) );
MAPIFreeBuffer (propval);
return xstrdup (_("[no subject]"));
}
buf = wchar_to_utf8 (propval->Value.lpszW);
MAPIFreeBuffer (propval);
if (!buf)
{
log_error ("%s:%s: error converting to utf8\n", SRCNAME, __func__);
return xstrdup (_("[no subject]"));
}
return buf;
}
/* Return the message type. This function knows only about our own
message types. Returns MSGTYPE_UNKNOWN for any MESSAGE we have
no special support for. */
msgtype_t
mapi_get_message_type (LPMESSAGE message)
{
HRESULT hr;
ULONG tag;
LPSPropValue propval = NULL;
msgtype_t msgtype = MSGTYPE_UNKNOWN;
if (!message)
return msgtype;
if (get_gpgolmsgclass_tag (message, &tag) )
return msgtype; /* Ooops */
hr = HrGetOneProp ((LPMAPIPROP)message, tag, &propval);
if (FAILED (hr))
{
hr = HrGetOneProp ((LPMAPIPROP)message, PR_MESSAGE_CLASS_A, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: HrGetOneProp(PR_MESSAGE_CLASS) failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return msgtype;
}
}
else
log_debug ("%s:%s: have override message class\n", SRCNAME, __func__);
if ( PROP_TYPE (propval->ulPropTag) == PT_STRING8 )
{
const char *s = propval->Value.lpszA;
if (!strncmp (s, "IPM.Note.GpgOL", 14) && (!s[14] || s[14] =='.'))
{
s += 14;
if (!*s)
msgtype = MSGTYPE_GPGOL;
else if (!strcmp (s, ".MultipartSigned"))
msgtype = MSGTYPE_GPGOL_MULTIPART_SIGNED;
else if (!strcmp (s, ".MultipartEncrypted"))
msgtype = MSGTYPE_GPGOL_MULTIPART_ENCRYPTED;
else if (!strcmp (s, ".OpaqueSigned"))
msgtype = MSGTYPE_GPGOL_OPAQUE_SIGNED;
else if (!strcmp (s, ".OpaqueEncrypted"))
msgtype = MSGTYPE_GPGOL_OPAQUE_ENCRYPTED;
else if (!strcmp (s, ".ClearSigned"))
msgtype = MSGTYPE_GPGOL_CLEAR_SIGNED;
else if (!strcmp (s, ".PGPMessage"))
msgtype = MSGTYPE_GPGOL_PGP_MESSAGE;
else
log_debug ("%s:%s: message class `%s' not supported",
SRCNAME, __func__, s-14);
}
else if (!strncmp (s, "IPM.Note.SMIME", 14) && (!s[14] || s[14] =='.'))
msgtype = MSGTYPE_SMIME;
}
MAPIFreeBuffer (propval);
return msgtype;
}
/* This function is pretty useless because IConverterSession won't
take attachments into account. Need to write our own version. */
int
mapi_to_mime (LPMESSAGE message, const char *filename)
{
HRESULT hr;
LPCONVERTERSESSION session;
LPSTREAM stream;
hr = CoCreateInstance (CLSID_IConverterSession, NULL, CLSCTX_INPROC_SERVER,
IID_IConverterSession, (void **) &session);
if (FAILED (hr))
{
log_error ("%s:%s: can't create new IConverterSession object: hr=%#lx",
SRCNAME, __func__, hr);
return -1;
}
hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
(STGM_CREATE | STGM_READWRITE),
(char*)filename, NULL, &stream);
if (FAILED (hr))
{
log_error ("%s:%s: can't create file `%s': hr=%#lx\n",
SRCNAME, __func__, filename, hr);
hr = -1;
}
else
{
hr = session->MAPIToMIMEStm (message, stream, CCSF_SMTP);
if (FAILED (hr))
{
log_error ("%s:%s: MAPIToMIMEStm failed: hr=%#lx",
SRCNAME, __func__, hr);
stream->Revert ();
hr = -1;
}
else
{
stream->Commit (0);
hr = 0;
}
stream->Release ();
}
session->Release ();
return hr;
}
/* Return a binary property in a malloced buffer with its length stored
at R_NBYTES. Returns NULL on error. */
char *
mapi_get_binary_prop (LPMESSAGE message, ULONG proptype, size_t *r_nbytes)
{
HRESULT hr;
LPSPropValue propval = NULL;
char *data;
*r_nbytes = 0;
hr = HrGetOneProp ((LPMAPIPROP)message, proptype, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: error getting property %#lx: hr=%#lx",
SRCNAME, __func__, proptype, hr);
return NULL;
}
switch ( PROP_TYPE (propval->ulPropTag) )
{
case PT_BINARY:
/* This is a binary object but we know that it must be plain
ASCII due to the armored format. */
data = (char*)xmalloc (propval->Value.bin.cb + 1);
memcpy (data, propval->Value.bin.lpb, propval->Value.bin.cb);
data[propval->Value.bin.cb] = 0;
*r_nbytes = propval->Value.bin.cb;
break;
default:
log_debug ("%s:%s: requested property %#lx has unknown tag %#lx\n",
SRCNAME, __func__, proptype, propval->ulPropTag);
data = NULL;
break;
}
MAPIFreeBuffer (propval);
return data;
}
/* Return an integer property at R_VALUE. On error the function
returns -1 and sets R_VALUE to 0, on success 0 is returned. */
int
mapi_get_int_prop (LPMAPIPROP object, ULONG proptype, LONG *r_value)
{
int rc = -1;
HRESULT hr;
LPSPropValue propval = NULL;
*r_value = 0;
hr = HrGetOneProp (object, proptype, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: error getting property %#lx: hr=%#lx",
SRCNAME, __func__, proptype, hr);
return -1;
}
switch ( PROP_TYPE (propval->ulPropTag) )
{
case PT_LONG:
*r_value = propval->Value.l;
rc = 0;
break;
default:
log_debug ("%s:%s: requested property %#lx has unknown tag %#lx\n",
SRCNAME, __func__, proptype, propval->ulPropTag);
break;
}
MAPIFreeBuffer (propval);
return rc;
}
/* Return the attachment method for attachment OBJ. In case of error
we return 0 which happens not to be defined. */
static int
get_attach_method (LPATTACH obj)
{
HRESULT hr;
LPSPropValue propval = NULL;
int method ;
hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_METHOD, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: error getting attachment method: hr=%#lx",
SRCNAME, __func__, hr);
return 0;
}
/* We don't bother checking whether we really get a PT_LONG ulong
back; if not the system is seriously damaged and we can't do
further harm by returning a possible random value. */
method = propval->Value.l;
MAPIFreeBuffer (propval);
return method;
}
/* Return the filename from the attachment as a malloced string. The
encoding we return will be UTF-8, however the MAPI docs declare
that MAPI does only handle plain ANSI and thus we don't really care
later on. In fact we would need to convert the filename back to
wchar and use the Unicode versions of the file API. Returns NULL
on error or if no filename is available. */
static char *
get_attach_filename (LPATTACH obj)
{
HRESULT hr;
LPSPropValue propval;
char *name = NULL;
hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_LONG_FILENAME, &propval);
if (FAILED(hr))
hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_FILENAME, &propval);
if (FAILED(hr))
{
log_debug ("%s:%s: no filename property found", SRCNAME, __func__);
return NULL;
}
switch ( PROP_TYPE (propval->ulPropTag) )
{
case PT_UNICODE:
name = wchar_to_utf8 (propval->Value.lpszW);
if (!name)
log_debug ("%s:%s: error converting to utf8\n", SRCNAME, __func__);
break;
case PT_STRING8:
name = xstrdup (propval->Value.lpszA);
break;
default:
log_debug ("%s:%s: proptag=%#lx not supported\n",
SRCNAME, __func__, propval->ulPropTag);
name = NULL;
break;
}
MAPIFreeBuffer (propval);
return name;
}
/* Return the content-type of the attachment OBJ or NULL if it does
not exists. Caller must free. */
static char *
get_attach_mime_tag (LPATTACH obj)
{
HRESULT hr;
LPSPropValue propval = NULL;
char *name;
hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_MIME_TAG_A, &propval);
if (FAILED (hr))
{
if (hr != MAPI_E_NOT_FOUND)
log_error ("%s:%s: error getting attachment's MIME tag: hr=%#lx",
SRCNAME, __func__, hr);
return NULL;
}
switch ( PROP_TYPE (propval->ulPropTag) )
{
case PT_UNICODE:
name = wchar_to_utf8 (propval->Value.lpszW);
if (!name)
log_debug ("%s:%s: error converting to utf8\n", SRCNAME, __func__);
break;
case PT_STRING8:
name = xstrdup (propval->Value.lpszA);
break;
default:
log_debug ("%s:%s: proptag=%#lx not supported\n",
SRCNAME, __func__, propval->ulPropTag);
name = NULL;
break;
}
MAPIFreeBuffer (propval);
return name;
}
/* Return the GpgOL Attach Type for attachment OBJ. Tag needs to be
the tag of that property. */
static attachtype_t
get_gpgolattachtype (LPATTACH obj, ULONG tag)
{
HRESULT hr;
LPSPropValue propval = NULL;
attachtype_t retval;
hr = HrGetOneProp ((LPMAPIPROP)obj, tag, &propval);
if (FAILED (hr))
{
if (hr != MAPI_E_NOT_FOUND)
log_error ("%s:%s: error getting GpgOL Attach Type: hr=%#lx",
SRCNAME, __func__, hr);
return ATTACHTYPE_UNKNOWN;
}
retval = (attachtype_t)propval->Value.l;
MAPIFreeBuffer (propval);
return retval;
}
/* Gather information about attachments and return a new table of
attachments. Caller must release the returned table.s The routine
will return NULL in case of an error or if no attachments are
available. With FAST set only some information gets collected. */
mapi_attach_item_t *
mapi_create_attach_table (LPMESSAGE message, int fast)
{
HRESULT hr;
SizedSPropTagArray (1L, propAttNum) = { 1L, {PR_ATTACH_NUM} };
LPMAPITABLE mapitable;
LPSRowSet mapirows;
mapi_attach_item_t *table;
unsigned int pos, n_attach;
ULONG moss_tag;
if (get_gpgolattachtype_tag (message, &moss_tag) )
return NULL;
/* Open the attachment table. */
hr = message->GetAttachmentTable (0, &mapitable);
if (FAILED (hr))
{
log_debug ("%s:%s: GetAttachmentTable failed: hr=%#lx",
SRCNAME, __func__, hr);
return NULL;
}
hr = HrQueryAllRows (mapitable, (LPSPropTagArray)&propAttNum,
NULL, NULL, 0, &mapirows);
if (FAILED (hr))
{
log_debug ("%s:%s: HrQueryAllRows failed: hr=%#lx",
SRCNAME, __func__, hr);
mapitable->Release ();
return NULL;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
log_debug ("%s:%s: message has %u attachments\n",
SRCNAME, __func__, n_attach);
if (!n_attach)
{
FreeProws (mapirows);
mapitable->Release ();
return NULL;
}
/* Allocate our own table. */
table = (mapi_attach_item_t *)xcalloc (n_attach+1, sizeof *table);
for (pos=0; pos < n_attach; pos++)
{
LPATTACH att;
if (mapirows->aRow[pos].cValues < 1)
{
log_error ("%s:%s: invalid row at pos %d", SRCNAME, __func__, pos);
table[pos].mapipos = -1;
continue;
}
if (mapirows->aRow[pos].lpProps[0].ulPropTag != PR_ATTACH_NUM)
{
log_error ("%s:%s: invalid prop at pos %d", SRCNAME, __func__, pos);
table[pos].mapipos = -1;
continue;
}
table[pos].mapipos = mapirows->aRow[pos].lpProps[0].Value.l;
hr = message->OpenAttach (table[pos].mapipos, NULL,
MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment %d (%d): hr=%#lx",
SRCNAME, __func__, pos, table[pos].mapipos, hr);
table[pos].mapipos = -1;
continue;
}
table[pos].method = get_attach_method (att);
table[pos].filename = fast? NULL : get_attach_filename (att);
table[pos].content_type = fast? NULL : get_attach_mime_tag (att);
if (table[pos].content_type)
{
char *p = strchr (table[pos].content_type, ';');
if (p)
{
*p++ = 0;
trim_trailing_spaces (table[pos].content_type);
while (strchr (" \t\r\n", *p))
p++;
trim_trailing_spaces (p);
table[pos].content_type_parms = p;
}
}
table[pos].attach_type = get_gpgolattachtype (att, moss_tag);
att->Release ();
}
table[0].private_mapitable = mapitable;
FreeProws (mapirows);
table[pos].end_of_table = 1;
mapitable = NULL;
if (fast)
{
log_debug ("%s:%s: attachment info: not shown due to fast flag\n",
SRCNAME, __func__);
}
else
{
log_debug ("%s:%s: attachment info:\n", SRCNAME, __func__);
for (pos=0; !table[pos].end_of_table; pos++)
{
log_debug ("\t%d mt=%d fname=`%s' ct=`%s' ct_parms=`%s'\n",
table[pos].mapipos,
table[pos].attach_type,
table[pos].filename, table[pos].content_type,
table[pos].content_type_parms);
}
}
return table;
}
/* Release a table as created by mapi_create_attach_table. */
void
mapi_release_attach_table (mapi_attach_item_t *table)
{
unsigned int pos;
LPMAPITABLE mapitable;
if (!table)
return;
mapitable = (LPMAPITABLE)table[0].private_mapitable;
if (mapitable)
mapitable->Release ();
for (pos=0; !table[pos].end_of_table; pos++)
{
xfree (table[pos].filename);
xfree (table[pos].content_type);
}
xfree (table);
}
/* Return an attachment as a new IStream object. Returns NULL on
failure. If R_ATTACH is not NULL the actual attachment will not be
released but stored at that address; the caller needs to release it
in this case. */
LPSTREAM
mapi_get_attach_as_stream (LPMESSAGE message, mapi_attach_item_t *item,
LPATTACH *r_attach)
{
HRESULT hr;
LPATTACH att;
LPSTREAM stream;
if (r_attach)
*r_attach = NULL;
if (!item || item->end_of_table || item->mapipos == -1)
return NULL;
hr = message->OpenAttach (item->mapipos, NULL, MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment at %d: hr=%#lx",
SRCNAME, __func__, item->mapipos, hr);
return NULL;
}
if (item->method != ATTACH_BY_VALUE)
{
log_error ("%s:%s: attachment: method not supported", SRCNAME, __func__);
att->Release ();
return NULL;
}
hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream,
0, 0, (LPUNKNOWN*) &stream);
if (FAILED (hr))
{
log_error ("%s:%s: can't open data stream of attachment: hr=%#lx",
SRCNAME, __func__, hr);
att->Release ();
return NULL;
}
if (r_attach)
*r_attach = att;
else
att->Release ();
return stream;
}
/* Return a malloced buffer with the content of the attachment. If
R_NBYTES is not NULL the number of bytes will get stored there.
ATT must have an attachment method of ATTACH_BY_VALUE. Returns
NULL on error. If UNPROTECT is set and the appropriate crypto
attribute is available, the function returns the unprotected
version of the atatchment. */
static char *
attach_to_buffer (LPATTACH att, size_t *r_nbytes, int unprotect,
int *r_was_protected)
{
HRESULT hr;
LPSTREAM stream;
STATSTG statInfo;
ULONG nread;
char *buffer;
symenc_t symenc = NULL;
if (r_was_protected)
*r_was_protected = 0;
if (unprotect)
{
ULONG tag;
char *iv;
size_t ivlen;
if (!get_gpgolprotectiv_tag ((LPMESSAGE)att, &tag)
&& (iv = mapi_get_binary_prop ((LPMESSAGE)att, tag, &ivlen)))
{
symenc = symenc_open (get_128bit_session_key (), 16, iv, ivlen);
xfree (iv);
if (!symenc)
log_error ("%s:%s: can't open encryption context",
SRCNAME, __func__);
}
}
hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream,
0, 0, (LPUNKNOWN*) &stream);
if (FAILED (hr))
{
log_error ("%s:%s: can't open data stream of attachment: hr=%#lx",
SRCNAME, __func__, hr);
return NULL;
}
hr = stream->Stat (&statInfo, STATFLAG_NONAME);
if ( hr != S_OK )
{
log_error ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
stream->Release ();
return NULL;
}
/* Allocate one byte more so that we can terminate the string. */
buffer = (char*)xmalloc ((size_t)statInfo.cbSize.QuadPart + 1);
hr = stream->Read (buffer, (size_t)statInfo.cbSize.QuadPart, &nread);
if ( hr != S_OK )
{
log_error ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
xfree (buffer);
stream->Release ();
return NULL;
}
if (nread != statInfo.cbSize.QuadPart)
{
log_error ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
xfree (buffer);
buffer = NULL;
}
stream->Release ();
if (buffer && symenc)
{
symenc_cfb_decrypt (symenc, buffer, buffer, nread);
if (nread < 16 || memcmp (buffer, "GpgOL attachment", 16))
{
xfree (buffer);
buffer = native_to_utf8
(_("[The content of this message is not visible because it has "
"been decrypted by another Outlook session. Use the "
"\"decrypt/verify\" command to make it visible]"));
nread = strlen (buffer);
}
else
{
memmove (buffer, buffer+16, nread-16);
nread -= 16;
if (r_was_protected)
*r_was_protected = 1;
}
}
/* Make sure that the buffer is a C string. */
if (buffer)
buffer[nread] = 0;
symenc_close (symenc);
if (r_nbytes)
*r_nbytes = nread;
return buffer;
}
/* Return an attachment as a malloced buffer. The size of the buffer
will be stored at R_NBYTES. If unprotect is true, the atatchment
will be unprotected. Returns NULL on failure. */
char *
mapi_get_attach (LPMESSAGE message, int unprotect,
mapi_attach_item_t *item, size_t *r_nbytes)
{
HRESULT hr;
LPATTACH att;
char *buffer;
if (!item || item->end_of_table || item->mapipos == -1)
return NULL;
hr = message->OpenAttach (item->mapipos, NULL, MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment at %d: hr=%#lx",
SRCNAME, __func__, item->mapipos, hr);
return NULL;
}
if (item->method != ATTACH_BY_VALUE)
{
log_error ("%s:%s: attachment: method not supported", SRCNAME, __func__);
att->Release ();
return NULL;
}
buffer = attach_to_buffer (att, r_nbytes, unprotect, NULL);
att->Release ();
return buffer;
}
/* Mark this attachment as the original MOSS message. We set a custom
property as well as the hidden flag. */
int
mapi_mark_moss_attach (LPMESSAGE message, mapi_attach_item_t *item)
{
int retval = -1;
HRESULT hr;
LPATTACH att;
SPropValue prop;
if (!item || item->end_of_table || item->mapipos == -1)
return -1;
hr = message->OpenAttach (item->mapipos, NULL, MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment at %d: hr=%#lx",
SRCNAME, __func__, item->mapipos, hr);
return -1;
}
if (get_gpgolattachtype_tag (message, &prop.ulPropTag) )
goto leave;
prop.Value.l = ATTACHTYPE_MOSS;
hr = HrSetOneProp (att, &prop);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Attach Type", hr);
return false;
}
prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
prop.Value.b = TRUE;
hr = HrSetOneProp (att, &prop);
if (hr)
{
log_error ("%s:%s: can't set hidden attach flag: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
hr = att->SaveChanges (KEEP_OPEN_READWRITE);
if (hr)
{
log_error ("%s:%s: SaveChanges(attachment) failed: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
retval = 0;
leave:
att->Release ();
return retval;
}
/* If the hidden property has not been set on ATTACH, set it and save
the changes. */
int
mapi_set_attach_hidden (LPATTACH attach)
{
int retval = -1;
HRESULT hr;
LPSPropValue propval;
SPropValue prop;
hr = HrGetOneProp ((LPMAPIPROP)attach, PR_ATTACHMENT_HIDDEN, &propval);
if (SUCCEEDED (hr)
&& PROP_TYPE (propval->ulPropTag) == PT_BOOLEAN
&& propval->Value.b)
return 0;/* Already set to hidden. */
prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
prop.Value.b = TRUE;
hr = HrSetOneProp (attach, &prop);
if (hr)
{
log_error ("%s:%s: can't set hidden attach flag: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
hr = attach->SaveChanges (KEEP_OPEN_READWRITE);
if (hr)
{
log_error ("%s:%s: SaveChanges(attachment) failed: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
retval = 0;
leave:
return retval;
}
/* Returns true if ATTACH has the hidden flag set to true. */
int
mapi_test_attach_hidden (LPATTACH attach)
{
HRESULT hr;
LPSPropValue propval = NULL;
int result = 0;
hr = HrGetOneProp ((LPMAPIPROP)attach, PR_ATTACHMENT_HIDDEN, &propval);
if (FAILED (hr))
return result; /* No. */
if (PROP_TYPE (propval->ulPropTag) == PT_BOOLEAN && propval->Value.b)
result = 1; /* Yes. */
MAPIFreeBuffer (propval);
return result;
}
/* Returns True if MESSAGE has the GpgOL Sig Status property. */
int
mapi_has_sig_status (LPMESSAGE msg)
{
HRESULT hr;
LPSPropValue propval = NULL;
ULONG tag;
int yes;
if (get_gpgolsigstatus_tag (msg, &tag) )
return 0; /* Error: Assume No. */
hr = HrGetOneProp ((LPMAPIPROP)msg, tag, &propval);
if (FAILED (hr))
return 0; /* No. */
if (PROP_TYPE (propval->ulPropTag) == PT_STRING8)
yes = 1;
else
yes = 0;
MAPIFreeBuffer (propval);
return yes;
}
/* Returns True if MESSAGE has a GpgOL Sig Status property and that it
is not set to unchecked. */
int
mapi_test_sig_status (LPMESSAGE msg)
{
HRESULT hr;
LPSPropValue propval = NULL;
ULONG tag;
int yes;
if (get_gpgolsigstatus_tag (msg, &tag) )
return 0; /* Error: Assume No. */
hr = HrGetOneProp ((LPMAPIPROP)msg, tag, &propval);
if (FAILED (hr))
return 0; /* No. */
/* We return False if we have an unknown signature status (?) or the
message has been sent by us and not yet checked (@). */
if (PROP_TYPE (propval->ulPropTag) == PT_STRING8)
yes = !(propval->Value.lpszA && (!strcmp (propval->Value.lpszA, "?")
|| !strcmp (propval->Value.lpszA, "@")));
else
yes = 0;
MAPIFreeBuffer (propval);
return yes;
}
/* Return the signature status as an allocated string. Will never
return NULL. */
char *
mapi_get_sig_status (LPMESSAGE msg)
{
HRESULT hr;
LPSPropValue propval = NULL;
ULONG tag;
char *retstr;
if (get_gpgolsigstatus_tag (msg, &tag) )
return xstrdup ("[Error getting tag for sig status]");
hr = HrGetOneProp ((LPMAPIPROP)msg, tag, &propval);
if (FAILED (hr))
return xstrdup ("");
if (PROP_TYPE (propval->ulPropTag) == PT_STRING8)
retstr = xstrdup (propval->Value.lpszA);
else
retstr = xstrdup ("[Sig status has an invalid type]");
MAPIFreeBuffer (propval);
return retstr;
}
/* Set the signature status property to STATUS_STRING. There are a
few special values:
"#" The message is not of interest to us.
"@" The message has been created and signed or encrypted by us.
"?" The signature status has not been checked.
"!" The signature verified okay
"~" The signature was not fully verified.
"-" The signature is bad
Note that this function does not call SaveChanges. */
int
mapi_set_sig_status (LPMESSAGE message, const char *status_string)
{
HRESULT hr;
SPropValue prop;
if (get_gpgolsigstatus_tag (message, &prop.ulPropTag) )
return -1;
prop.Value.lpszA = xstrdup (status_string);
hr = HrSetOneProp (message, &prop);
xfree (prop.Value.lpszA);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Sig Status", hr);
return -1;
}
return 0;
}
/* When sending a message we need to fake the message class so that OL
processes it according to our needs. However, if we later try to
get the message class from the sent message, OL still has the SMIME
message class and tries to hide this by trying to decrypt the
message and return the message class from the plaintext. To
mitigate the problem we define our own msg class override
property. */
int
mapi_set_gpgol_msg_class (LPMESSAGE message, const char *name)
{
HRESULT hr;
SPropValue prop;
if (get_gpgolmsgclass_tag (message, &prop.ulPropTag) )
return -1;
prop.Value.lpszA = xstrdup (name);
hr = HrSetOneProp (message, &prop);
xfree (prop.Value.lpszA);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Msg Class", hr);
return -1;
}
return 0;
}
/* Return the charset as assigned by GpgOL to an attachment. This may
return NULL it is has not been assigned or is the standard
(UTF-8). */
char *
mapi_get_gpgol_charset (LPMESSAGE obj)
{
HRESULT hr;
LPSPropValue propval = NULL;
ULONG tag;
char *retstr;
if (get_gpgolcharset_tag (obj, &tag) )
return NULL; /* Error. */
hr = HrGetOneProp ((LPMAPIPROP)obj, tag, &propval);
if (FAILED (hr))
return NULL;
if (PROP_TYPE (propval->ulPropTag) == PT_STRING8)
{
if (!strcmp (propval->Value.lpszA, "utf-8"))
retstr = NULL;
else
retstr = xstrdup (propval->Value.lpszA);
}
else
retstr = NULL;
MAPIFreeBuffer (propval);
return retstr;
}
/* Set the GpgOl charset property to an attachment.
Note that this function does not call SaveChanges. */
int
mapi_set_gpgol_charset (LPMESSAGE obj, const char *charset)
{
HRESULT hr;
SPropValue prop;
char *p;
/* Note that we lowercase the value and cut it to a max of 32
characters. The latter is required to make sure that
HrSetOneProp will always work. */
if (get_gpgolcharset_tag (obj, &prop.ulPropTag) )
return -1;
prop.Value.lpszA = xstrdup (charset);
for (p=prop.Value.lpszA; *p; p++)
*p = tolower (*(unsigned char*)p);
if (strlen (prop.Value.lpszA) > 32)
prop.Value.lpszA[32] = 0;
hr = HrSetOneProp ((LPMAPIPROP)obj, &prop);
xfree (prop.Value.lpszA);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Charset", hr);
return -1;
}
return 0;
}
/* Return GpgOL's draft info string as an allocated string. If no
draft info is available, NULL is returned. */
char *
mapi_get_gpgol_draft_info (LPMESSAGE msg)
{
HRESULT hr;
LPSPropValue propval = NULL;
ULONG tag;
char *retstr;
if (get_gpgoldraftinfo_tag (msg, &tag) )
return NULL;
hr = HrGetOneProp ((LPMAPIPROP)msg, tag, &propval);
if (FAILED (hr))
return NULL;
if (PROP_TYPE (propval->ulPropTag) == PT_STRING8)
retstr = xstrdup (propval->Value.lpszA);
else
retstr = NULL;
MAPIFreeBuffer (propval);
return retstr;
}
/* Set GpgOL's draft info string to STRING. This string is defined as:
Character 1: 'E' = encrypt selected,
'e' = encrypt not selected.
'-' = don't care
Character 2: 'S' = sign selected,
's' = sign not selected.
'-' = don't care
Character 3: 'A' = Auto protocol
'P' = OpenPGP protocol
'X' = S/MIME protocol
'-' = don't care
If string is NULL, the property will get deleted.
Note that this function does not call SaveChanges. */
int
mapi_set_gpgol_draft_info (LPMESSAGE message, const char *string)
{
HRESULT hr;
SPropValue prop;
SPropTagArray proparray;
if (get_gpgoldraftinfo_tag (message, &prop.ulPropTag) )
return -1;
if (string)
{
prop.Value.lpszA = xstrdup (string);
hr = HrSetOneProp (message, &prop);
xfree (prop.Value.lpszA);
}
else
{
proparray.cValues = 1;
proparray.aulPropTag[0] = prop.ulPropTag;
hr = message->DeleteProps (&proparray, NULL);
}
if (hr)
{
log_error ("%s:%s: can't %s %s property: hr=%#lx\n",
SRCNAME, __func__, string?"set":"delete",
"GpgOL Draft Info", hr);
return -1;
}
return 0;
}
/* Return the MIME info as an allocated string. Will never return
NULL. */
char *
mapi_get_mime_info (LPMESSAGE msg)
{
HRESULT hr;
LPSPropValue propval = NULL;
ULONG tag;
char *retstr;
if (get_gpgolmimeinfo_tag (msg, &tag) )
return xstrdup ("[Error getting tag for MIME info]");
hr = HrGetOneProp ((LPMAPIPROP)msg, tag, &propval);
if (FAILED (hr))
return xstrdup ("");
if (PROP_TYPE (propval->ulPropTag) == PT_STRING8)
retstr = xstrdup (propval->Value.lpszA);
else
retstr = xstrdup ("[MIME info has an invalid type]");
MAPIFreeBuffer (propval);
return retstr;
}
/* Helper for mapi_get_msg_content_type() */
static int
get_message_content_type_cb (void *dummy_arg,
rfc822parse_event_t event, rfc822parse_t msg)
{
(void)dummy_arg;
(void)msg;
if (event == RFC822PARSE_T2BODY)
return 42; /* Hack to stop the parsing after having read the
outer headers. */
return 0;
}
/* Return Content-Type of the current message. This one is taken
directly from the rfc822 header. If R_PROTOCOL is not NULL a
string with the protocol parameter will be stored at this address,
if no protocol is given NULL will be stored. If R_SMTYPE is not
NULL a string with the smime-type parameter will be stored there.
Caller must release all returned strings. */
char *
mapi_get_message_content_type (LPMESSAGE message,
char **r_protocol, char **r_smtype)
{
HRESULT hr;
LPSPropValue propval = NULL;
rfc822parse_t msg;
const char *header_lines, *s;
rfc822parse_field_t ctx;
size_t length;
char *retstr = NULL;
if (r_protocol)
*r_protocol = NULL;
if (r_smtype)
*r_smtype = NULL;
hr = HrGetOneProp ((LPMAPIPROP)message,
PR_TRANSPORT_MESSAGE_HEADERS_A, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: error getting the headers lines: hr=%#lx",
SRCNAME, __func__, hr);
return NULL;
}
if (PROP_TYPE (propval->ulPropTag) != PT_STRING8)
{
/* As per rfc822, header lines must be plain ascii, so no need
to cope with unicode etc. */
log_error ("%s:%s: proptag=%#lx not supported\n",
SRCNAME, __func__, propval->ulPropTag);
MAPIFreeBuffer (propval);
return NULL;
}
header_lines = propval->Value.lpszA;
/* Read the headers into an rfc822 object. */
msg = rfc822parse_open (get_message_content_type_cb, NULL);
if (!msg)
{
log_error ("%s:%s: rfc822parse_open failed\n", SRCNAME, __func__);
MAPIFreeBuffer (propval);
return NULL;
}
while ((s = strchr (header_lines, '\n')))
{
length = (s - header_lines);
if (length && s[-1] == '\r')
length--;
rfc822parse_insert (msg, (const unsigned char*)header_lines, length);
header_lines = s+1;
}
/* Parse the content-type field. */
ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
if (ctx)
{
const char *s1, *s2;
s1 = rfc822parse_query_media_type (ctx, &s2);
if (s1)
{
retstr = (char*)xmalloc (strlen (s1) + 1 + strlen (s2) + 1);
strcpy (stpcpy (stpcpy (retstr, s1), "/"), s2);
if (r_protocol)
{
s = rfc822parse_query_parameter (ctx, "protocol", 0);
if (s)
*r_protocol = xstrdup (s);
}
if (r_smtype)
{
s = rfc822parse_query_parameter (ctx, "smime-type", 0);
if (s)
*r_smtype = xstrdup (s);
}
}
rfc822parse_release_field (ctx);
}
rfc822parse_close (msg);
MAPIFreeBuffer (propval);
return retstr;
}
/* Returns True if MESSAGE has a GpgOL Last Decrypted property with any value.
This indicates that there should be no PR_BODY tag. */
int
mapi_has_last_decrypted (LPMESSAGE message)
{
HRESULT hr;
LPSPropValue propval = NULL;
ULONG tag;
int yes = 0;
if (get_gpgollastdecrypted_tag (message, &tag) )
return 0; /* No. */
hr = HrGetOneProp ((LPMAPIPROP)message, tag, &propval);
if (FAILED (hr))
return 0; /* No. */
if (PROP_TYPE (propval->ulPropTag) == PT_BINARY)
yes = 1;
MAPIFreeBuffer (propval);
return yes;
}
/* Returns True if MESSAGE has a GpgOL Last Decrypted property and
that matches the current session. */
int
mapi_test_last_decrypted (LPMESSAGE message)
{
HRESULT hr;
LPSPropValue propval = NULL;
ULONG tag;
int yes = 0;
if (get_gpgollastdecrypted_tag (message, &tag) )
goto leave; /* No. */
hr = HrGetOneProp ((LPMAPIPROP)message, tag, &propval);
if (FAILED (hr))
goto leave; /* No. */
if (PROP_TYPE (propval->ulPropTag) == PT_BINARY
&& propval->Value.bin.cb == 8
&& !memcmp (propval->Value.bin.lpb, get_64bit_session_marker (), 8) )
yes = 1;
MAPIFreeBuffer (propval);
leave:
log_debug ("%s:%s: message decrypted during this session: %s\n",
SRCNAME, __func__, yes?"yes":"no");
return yes;
}
/* Helper for mapi_get_gpgol_body_attachment. */
static int
has_gpgol_body_name (LPATTACH obj)
{
HRESULT hr;
LPSPropValue propval;
int yes = 0;
hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_FILENAME, &propval);
if (FAILED(hr))
return 0;
if ( PROP_TYPE (propval->ulPropTag) == PT_UNICODE)
{
if (!wcscmp (propval->Value.lpszW, L"gpgol000.txt"))
yes = 1;
else if (!wcscmp (propval->Value.lpszW, L"gpgol000.htm"))
yes = 2;
}
else if ( PROP_TYPE (propval->ulPropTag) == PT_STRING8)
{
if (!strcmp (propval->Value.lpszA, "gpgol000.txt"))
yes = 1;
else if (!strcmp (propval->Value.lpszA, "gpgol000.htm"))
yes = 2;
}
MAPIFreeBuffer (propval);
return yes;
}
/* Helper to check whether the file name of OBJ is "smime.p7m".
Returns on true if so. */
static int
has_smime_filename (LPATTACH obj)
{
HRESULT hr;
LPSPropValue propval;
int yes = 0;
hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_FILENAME, &propval);
if (FAILED(hr))
{
hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_LONG_FILENAME, &propval);
if (FAILED(hr))
return 0;
}
if ( PROP_TYPE (propval->ulPropTag) == PT_UNICODE)
{
if (!wcscmp (propval->Value.lpszW, L"smime.p7m"))
yes = 1;
}
else if ( PROP_TYPE (propval->ulPropTag) == PT_STRING8)
{
if (!strcmp (propval->Value.lpszA, "smime.p7m"))
yes = 1;
}
MAPIFreeBuffer (propval);
return yes;
}
/* Return the content of the body attachment of MESSAGE. The body
attachment is a hidden attachment created by us for later display.
If R_NBYTES is not NULL the number of bytes in the returned buffer
is stored there. If R_ISHTML is not NULL a flag indicating whether
the HTML is html formatted is stored there. If R_PROTECTED is not
NULL a flag indicating whether the message was protected is stored
there. If no body attachment can be found or on any other error an
error codes is returned and NULL is stored at R_BODY. Caller must
free the returned string. If NULL is passed for R_BODY, the
function will only test whether a body attachment is available and
return an error code if not. R_IS_HTML and R_PROTECTED are not
defined in this case. */
int
mapi_get_gpgol_body_attachment (LPMESSAGE message,
char **r_body, size_t *r_nbytes,
int *r_ishtml, int *r_protected)
{
HRESULT hr;
SizedSPropTagArray (1L, propAttNum) = { 1L, {PR_ATTACH_NUM} };
LPMAPITABLE mapitable;
LPSRowSet mapirows;
unsigned int pos, n_attach;
ULONG moss_tag;
char *body = NULL;
int bodytype;
int found = 0;
if (r_body)
*r_body = NULL;
if (r_ishtml)
*r_ishtml = 0;
if (r_protected)
*r_protected = 0;
if (get_gpgolattachtype_tag (message, &moss_tag) )
return -1;
hr = message->GetAttachmentTable (0, &mapitable);
if (FAILED (hr))
{
log_debug ("%s:%s: GetAttachmentTable failed: hr=%#lx",
SRCNAME, __func__, hr);
return -1;
}
hr = HrQueryAllRows (mapitable, (LPSPropTagArray)&propAttNum,
NULL, NULL, 0, &mapirows);
if (FAILED (hr))
{
log_debug ("%s:%s: HrQueryAllRows failed: hr=%#lx",
SRCNAME, __func__, hr);
mapitable->Release ();
return -1;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
if (!n_attach)
{
FreeProws (mapirows);
mapitable->Release ();
log_debug ("%s:%s: No attachments at all", SRCNAME, __func__);
return -1;
}
log_debug ("%s:%s: message has %u attachments\n",
SRCNAME, __func__, n_attach);
for (pos=0; pos < n_attach; pos++)
{
LPATTACH att;
if (mapirows->aRow[pos].cValues < 1)
{
log_error ("%s:%s: invalid row at pos %d", SRCNAME, __func__, pos);
continue;
}
if (mapirows->aRow[pos].lpProps[0].ulPropTag != PR_ATTACH_NUM)
{
log_error ("%s:%s: invalid prop at pos %d", SRCNAME, __func__, pos);
continue;
}
hr = message->OpenAttach (mapirows->aRow[pos].lpProps[0].Value.l,
NULL, MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment %d (%ld): hr=%#lx",
SRCNAME, __func__, pos,
mapirows->aRow[pos].lpProps[0].Value.l, hr);
continue;
}
if ((bodytype=has_gpgol_body_name (att))
&& get_gpgolattachtype (att, moss_tag) == ATTACHTYPE_FROMMOSS)
{
found = 1;
if (!r_body)
; /* Body content has not been requested. */
else if (opt.body_as_attachment && !mapi_test_attach_hidden (att))
{
/* The body is to be shown as an attachment. */
body = native_to_utf8
(bodytype == 2
? ("[Open the attachment \"gpgol000.htm\""
" to view the message.]")
: ("[Open the attachment \"gpgol000.txt\""
" to view the message.]"));
found = 1;
}
else
{
char *charset;
if (get_attach_method (att) == ATTACH_BY_VALUE)
body = attach_to_buffer (att, r_nbytes, 1, r_protected);
if (body && (charset = mapi_get_gpgol_charset ((LPMESSAGE)att)))
{
/* We only support transcoding from Latin-1 for now. */
if (strcmp (charset, "iso-8859-1")
&& !strcmp (charset, "latin-1"))
log_debug ("%s:%s: Using Latin-1 instead of %s",
SRCNAME, __func__, charset);
xfree (charset);
charset = latin1_to_utf8 (body);
xfree (body);
body = charset;
}
}
att->Release ();
if (r_ishtml)
*r_ishtml = (bodytype == 2);
break;
}
att->Release ();
}
FreeProws (mapirows);
mapitable->Release ();
if (!found)
{
log_error ("%s:%s: no suitable body attachment found", SRCNAME,__func__);
if (r_body)
*r_body = native_to_utf8
(_("[The content of this message is not visible"
" due to an processing error in GpgOL.]"));
return -1;
}
if (r_body)
*r_body = body;
else
xfree (body); /* (Should not happen.) */
return 0;
}
/* Delete a possible body atatchment. Returns true if an atatchment
has been deleted. */
int
mapi_delete_gpgol_body_attachment (LPMESSAGE message)
{
HRESULT hr;
SizedSPropTagArray (1L, propAttNum) = { 1L, {PR_ATTACH_NUM} };
LPMAPITABLE mapitable;
LPSRowSet mapirows;
unsigned int pos, n_attach;
ULONG moss_tag;
int found = 0;
if (get_gpgolattachtype_tag (message, &moss_tag) )
return 0;
hr = message->GetAttachmentTable (0, &mapitable);
if (FAILED (hr))
{
log_debug ("%s:%s: GetAttachmentTable failed: hr=%#lx",
SRCNAME, __func__, hr);
return 0;
}
hr = HrQueryAllRows (mapitable, (LPSPropTagArray)&propAttNum,
NULL, NULL, 0, &mapirows);
if (FAILED (hr))
{
log_debug ("%s:%s: HrQueryAllRows failed: hr=%#lx",
SRCNAME, __func__, hr);
mapitable->Release ();
return 0;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
if (!n_attach)
{
FreeProws (mapirows);
mapitable->Release ();
return 0; /* No Attachments. */
}
for (pos=0; pos < n_attach; pos++)
{
LPATTACH att;
if (mapirows->aRow[pos].cValues < 1)
{
log_error ("%s:%s: invalid row at pos %d", SRCNAME, __func__, pos);
continue;
}
if (mapirows->aRow[pos].lpProps[0].ulPropTag != PR_ATTACH_NUM)
{
log_error ("%s:%s: invalid prop at pos %d", SRCNAME, __func__, pos);
continue;
}
hr = message->OpenAttach (mapirows->aRow[pos].lpProps[0].Value.l,
NULL, MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment %d (%ld): hr=%#lx",
SRCNAME, __func__, pos,
mapirows->aRow[pos].lpProps[0].Value.l, hr);
continue;
}
if (has_gpgol_body_name (att)
&& get_gpgolattachtype (att, moss_tag) == ATTACHTYPE_FROMMOSS)
{
att->Release ();
hr = message->DeleteAttach (mapirows->aRow[pos].lpProps[0].Value.l,
0, NULL, 0);
if (hr)
log_error ("%s:%s: DeleteAttach failed: hr=%#lx\n",
SRCNAME, __func__, hr);
else
{
log_debug ("%s:%s: body attachment deleted\n",
SRCNAME, __func__);
found = 1;
}
break;
}
att->Release ();
}
FreeProws (mapirows);
mapitable->Release ();
return found;
}
/* Copy the attachment ITEM of the message MESSAGE verbatim to the
PR_BODY property. Returns 0 on success. This function does not
call SaveChanges. */
int
mapi_attachment_to_body (LPMESSAGE message, mapi_attach_item_t *item)
{
int result = -1;
HRESULT hr;
LPATTACH att = NULL;
LPSTREAM instream = NULL;
LPSTREAM outstream = NULL;
LPUNKNOWN punk;
if (!message || !item || item->end_of_table || item->mapipos == -1)
return -1; /* Error. */
hr = message->OpenAttach (item->mapipos, NULL, MAPI_BEST_ACCESS, &att);
if (FAILED (hr))
{
log_error ("%s:%s: can't open attachment at %d: hr=%#lx",
SRCNAME, __func__, item->mapipos, hr);
goto leave;
}
if (item->method != ATTACH_BY_VALUE)
{
log_error ("%s:%s: attachment: method not supported", SRCNAME, __func__);
goto leave;
}
hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream,
0, 0, (LPUNKNOWN*) &instream);
if (FAILED (hr))
{
log_error ("%s:%s: can't open data stream of attachment: hr=%#lx",
SRCNAME, __func__, hr);
goto leave;
}
punk = (LPUNKNOWN)outstream;
hr = message->OpenProperty (PR_BODY_A, &IID_IStream, 0,
MAPI_CREATE|MAPI_MODIFY, &punk);
if (FAILED (hr))
{
log_error ("%s:%s: can't open body stream for update: hr=%#lx",
SRCNAME, __func__, hr);
goto leave;
}
outstream = (LPSTREAM)punk;
{
ULARGE_INTEGER cb;
cb.QuadPart = 0xffffffffffffffffll;
hr = instream->CopyTo (outstream, cb, NULL, NULL);
}
if (hr)
{
log_error ("%s:%s: can't copy streams: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
hr = outstream->Commit (0);
if (hr)
{
log_error ("%s:%s: commiting output stream failed: hr=%#lx",
SRCNAME, __func__, hr);
goto leave;
}
result = 0;
leave:
if (outstream)
{
if (result)
outstream->Revert ();
outstream->Release ();
}
if (instream)
instream->Release ();
if (att)
att->Release ();
return result;
}
diff --git a/src/mimemaker.c b/src/mimemaker.c
index 41b1986..c370bda 100644
--- a/src/mimemaker.c
+++ b/src/mimemaker.c
@@ -1,1948 +1,1961 @@
/* mimemaker.c - Construct MIME message out of a MAPI
* Copyright (C) 2007, 2008 g10 Code GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#define COBJMACROS
#include <windows.h>
#include <objidl.h>
#include "mymapi.h"
#include "mymapitags.h"
#include "common.h"
#include "engine.h"
#include "mapihelp.h"
#include "mimemaker.h"
#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
SRCNAME, __func__, __LINE__); \
} while (0)
static const char oid_mimetag[] =
{0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
/* The base-64 list used for base64 encoding. */
static unsigned char bintoasc[64+1] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/");
/* The object we use instead of IStream. It allows us to have a
callback method for output and thus for processing stuff
recursively. */
struct sink_s;
typedef struct sink_s *sink_t;
struct sink_s
{
void *cb_data;
sink_t extrasink;
int (*writefnc)(sink_t sink, const void *data, size_t datalen);
unsigned long enc_counter; /* Used by write_buffer_for_cb. */
/* struct { */
/* int idx; */
/* unsigned char inbuf[4]; */
/* int quads; */
/* } b64; */
};
/* Object used to collect data in a memory buffer. */
struct databuf_s
{
size_t len; /* Used length. */
size_t size; /* Allocated length of BUF. */
char *buf; /* Malloced buffer. */
};
/*** local prototypes ***/
static int write_multistring (sink_t sink, const char *text1,
...) GPGOL_GCC_A_SENTINEL(0);
/* Standard write method used with a sink_t object. */
static int
sink_std_write (sink_t sink, const void *data, size_t datalen)
{
HRESULT hr;
LPSTREAM stream = sink->cb_data;
if (!stream)
{
log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
return -1;
}
if (!data)
return 0; /* Flush - nothing to do here. */
hr = IStream_Write (stream, data, datalen, NULL);
if (hr)
{
log_error ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
return -1;
}
return 0;
}
/* Make sure that PROTOCOL is usable or return a suitable protocol.
On error PROTOCOL_UNKNOWN is returned. */
static protocol_t
check_protocol (protocol_t protocol)
{
switch (protocol)
{
case PROTOCOL_UNKNOWN:
return PROTOCOL_UNKNOWN;
case PROTOCOL_OPENPGP:
case PROTOCOL_SMIME:
return protocol;
}
log_error ("%s:%s: BUG", SRCNAME, __func__);
return PROTOCOL_UNKNOWN;
}
/* Create a new MAPI attchment for MESSAGE which will be used to
prepare the MIME message. On sucess the stream to write the data
to is stored at STREAM and the attachment object itself is
returned. The caller needs to call SaveChanges. Returns NULL on
failure in which case STREAM will be set to NULL. */
static LPATTACH
create_mapi_attachment (LPMESSAGE message, sink_t sink)
{
HRESULT hr;
ULONG pos;
SPropValue prop;
LPATTACH att = NULL;
LPUNKNOWN punk;
sink->cb_data = NULL;
sink->writefnc = NULL;
hr = IMessage_CreateAttach (message, NULL, 0, &pos, &att);
if (hr)
{
log_error ("%s:%s: can't create attachment: hr=%#lx\n",
SRCNAME, __func__, hr);
return NULL;
}
prop.ulPropTag = PR_ATTACH_METHOD;
prop.Value.ul = ATTACH_BY_VALUE;
hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach method: hr=%#lx\n",
SRCNAME, __func__, hr);
goto failure;
}
/* Mark that attachment so that we know why it has been created. */
if (get_gpgolattachtype_tag (message, &prop.ulPropTag) )
goto failure;
prop.Value.l = ATTACHTYPE_MOSSTEMPL;
hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Attach Type", hr);
goto failure;
}
/* We better insert a short filename. */
prop.ulPropTag = PR_ATTACH_FILENAME_A;
prop.Value.lpszA = MIMEATTACHFILENAME;
hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
SRCNAME, __func__, hr);
goto failure;
}
/* Even for encrypted messages we need to set the MAPI property to
multipart/signed. This seems to be a part of the trigger which
leads OL to process such a message in a special way. */
prop.ulPropTag = PR_ATTACH_TAG;
prop.Value.bin.cb = sizeof oid_mimetag;
prop.Value.bin.lpb = (LPBYTE)oid_mimetag;
hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
if (!hr)
{
prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
prop.Value.lpszA = "multipart/signed";
hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
}
if (hr)
{
log_error ("%s:%s: can't set attach mime tag: hr=%#lx\n",
SRCNAME, __func__, hr);
goto failure;
}
punk = NULL;
hr = IAttach_OpenProperty (att, PR_ATTACH_DATA_BIN, &IID_IStream, 0,
(MAPI_CREATE|MAPI_MODIFY), &punk);
if (FAILED (hr))
{
log_error ("%s:%s: can't create output stream: hr=%#lx\n",
SRCNAME, __func__, hr);
goto failure;
}
sink->cb_data = (LPSTREAM)punk;
sink->writefnc = sink_std_write;
return att;
failure:
IAttach_Release (att);
return NULL;
}
/* Write data to a sink_t. */
static int
write_buffer (sink_t sink, const void *data, size_t datalen)
{
if (!sink || !sink->writefnc)
{
log_error ("%s:%s: sink not properly setup", SRCNAME, __func__);
return -1;
}
return sink->writefnc (sink, data, datalen);
}
/* Same as above but used for passing as callback function. This
fucntion does not return an error code but the number of bytes
written. */
static int
write_buffer_for_cb (void *opaque, const void *data, size_t datalen)
{
sink_t sink = opaque;
sink->enc_counter += datalen;
return write_buffer (sink, data, datalen) ? -1 : datalen;
}
/* Write the string TEXT to the IStream STREAM. Returns 0 on sucsess,
prints an error message and returns -1 on error. */
static int
write_string (sink_t sink, const char *text)
{
return write_buffer (sink, text, strlen (text));
}
/* Write the string TEXT1 and all folloing arguments of type (const
char*) to the SINK. The list of argumens needs to be terminated
with a NULL. Returns 0 on sucsess, prints an error message and
returns -1 on error. */
static int
write_multistring (sink_t sink, const char *text1, ...)
{
va_list arg_ptr;
int rc;
const char *s;
va_start (arg_ptr, text1);
s = text1;
do
rc = write_string (sink, s);
while (!rc && (s=va_arg (arg_ptr, const char *)));
va_end (arg_ptr);
return rc;
}
/* Helper to write a boundary to the output sink. The leading LF
will be written as well. */
static int
write_boundary (sink_t sink, const char *boundary, int lastone)
{
int rc = write_string (sink, "\r\n--");
if (!rc)
rc = write_string (sink, boundary);
if (!rc)
rc = write_string (sink, lastone? "--\r\n":"\r\n");
return rc;
}
/* Write DATALEN bytes of DATA to SINK in base64 encoding. This
creates a complete Base64 chunk including the trailing fillers. */
static int
write_b64 (sink_t sink, const void *data, size_t datalen)
{
int rc;
const unsigned char *p;
unsigned char inbuf[4];
int idx, quads;
char outbuf[2048];
size_t outlen;
log_debug (" writing base64 of length %d\n", (int)datalen);
idx = quads = 0;
outlen = 0;
for (p = data; datalen; p++, datalen--)
{
inbuf[idx++] = *p;
if (idx > 2)
{
/* We need space for a quad and a possible CR,LF. */
if (outlen+4+2 >= sizeof outbuf)
{
if ((rc = write_buffer (sink, outbuf, outlen)))
return rc;
outlen = 0;
}
outbuf[outlen++] = bintoasc[(*inbuf>>2)&077];
outbuf[outlen++] = bintoasc[(((*inbuf<<4)&060)
|((inbuf[1] >> 4)&017))&077];
outbuf[outlen++] = bintoasc[(((inbuf[1]<<2)&074)
|((inbuf[2]>>6)&03))&077];
outbuf[outlen++] = bintoasc[inbuf[2]&077];
idx = 0;
if (++quads >= (64/4))
{
quads = 0;
outbuf[outlen++] = '\r';
outbuf[outlen++] = '\n';
}
}
}
/* We need space for a quad and a final CR,LF. */
if (outlen+4+2 >= sizeof outbuf)
{
if ((rc = write_buffer (sink, outbuf, outlen)))
return rc;
outlen = 0;
}
if (idx)
{
outbuf[outlen++] = bintoasc[(*inbuf>>2)&077];
if (idx == 1)
{
outbuf[outlen++] = bintoasc[((*inbuf<<4)&060)&077];
outbuf[outlen++] = '=';
outbuf[outlen++] = '=';
}
else
{
outbuf[outlen++] = bintoasc[(((*inbuf<<4)&060)
|((inbuf[1]>>4)&017))&077];
outbuf[outlen++] = bintoasc[((inbuf[1]<<2)&074)&077];
outbuf[outlen++] = '=';
}
if ((rc = write_buffer (sink, outbuf, 4)))
return rc;
++quads;
}
if (quads)
{
outbuf[outlen++] = '\r';
outbuf[outlen++] = '\n';
}
if (outlen)
{
if ((rc = write_buffer (sink, outbuf, outlen)))
return rc;
}
return 0;
}
/* Write DATALEN bytes of DATA to SINK in quoted-prinable encoding. */
static int
write_qp (sink_t sink, const void *data, size_t datalen)
{
int rc;
const unsigned char *p;
char outbuf[80]; /* We only need 76 octect + 2 for the lineend. */
int outidx;
/* Check whether the current character is followed by a line ending.
Note that the end of the etxt also counts as a lineending */
#define nextlf_p() ((datalen > 2 && p[1] == '\r' && p[2] == '\n') \
|| (datalen > 1 && p[1] == '\n') \
|| datalen == 1 )
/* Macro to insert a soft line break if needed. */
# define do_softlf(n) \
do { \
if (outidx + (n) > 76 \
|| (outidx + (n) == 76 && !nextlf_p())) \
{ \
outbuf[outidx++] = '='; \
outbuf[outidx++] = '\r'; \
outbuf[outidx++] = '\n'; \
if ((rc = write_buffer (sink, outbuf, outidx))) \
return rc; \
outidx = 0; \
} \
} while (0)
log_debug (" writing qp of length %d\n", (int)datalen);
outidx = 0;
for (p = data; datalen; p++, datalen--)
{
if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
{
/* Line break. */
outbuf[outidx++] = '\r';
outbuf[outidx++] = '\n';
if ((rc = write_buffer (sink, outbuf, outidx)))
return rc;
outidx = 0;
if (*p == '\r')
{
p++;
datalen--;
}
}
else if (*p == '\t' || *p == ' ')
{
/* Check whether tab or space is followed by a line break
which forbids verbatim encoding. If we are already at
the end of the buffer we take that as a line end too. */
if (nextlf_p())
{
do_softlf (3);
outbuf[outidx++] = '=';
outbuf[outidx++] = tohex ((*p>>4)&15);
outbuf[outidx++] = tohex (*p&15);
}
else
{
do_softlf (1);
outbuf[outidx++] = *p;
}
}
else if (!outidx && *p == '.' && nextlf_p () )
{
/* We better protect a line with just a single dot. */
outbuf[outidx++] = '=';
outbuf[outidx++] = tohex ((*p>>4)&15);
outbuf[outidx++] = tohex (*p&15);
}
else if (!outidx && datalen >= 5 && !memcmp (p, "From ", 5))
{
/* Protect the 'F' so that MTAs won't prefix the "From "
with an '>' */
outbuf[outidx++] = '=';
outbuf[outidx++] = tohex ((*p>>4)&15);
outbuf[outidx++] = tohex (*p&15);
}
else if (*p >= '!' && *p <= '~' && *p != '=')
{
do_softlf (1);
outbuf[outidx++] = *p;
}
else
{
do_softlf (3);
outbuf[outidx++] = '=';
outbuf[outidx++] = tohex ((*p>>4)&15);
outbuf[outidx++] = tohex (*p&15);
}
}
if (outidx)
{
outbuf[outidx++] = '\r';
outbuf[outidx++] = '\n';
if ((rc = write_buffer (sink, outbuf, outidx)))
return rc;
}
# undef do_softlf
# undef nextlf_p
return 0;
}
/* Write DATALEN bytes of DATA to SINK in plain ascii encoding. */
static int
write_plain (sink_t sink, const void *data, size_t datalen)
{
int rc;
const unsigned char *p;
char outbuf[100];
int outidx;
log_debug (" writing ascii of length %d\n", (int)datalen);
outidx = 0;
for (p = data; datalen; p++, datalen--)
{
if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
{
outbuf[outidx++] = '\r';
outbuf[outidx++] = '\n';
if ((rc = write_buffer (sink, outbuf, outidx)))
return rc;
outidx = 0;
if (*p == '\r')
{
p++;
datalen--;
}
}
else if (!outidx && *p == '.'
&& ( (datalen > 2 && p[1] == '\r' && p[2] == '\n')
|| (datalen > 1 && p[1] == '\n')
|| datalen == 1))
{
/* Better protect a line with just a single dot. We do
this by adding a space. */
outbuf[outidx++] = *p;
outbuf[outidx++] = ' ';
}
else if (outidx > 80)
{
/* We should never be called for too long lines - QP should
have been used. */
log_error ("%s:%s: BUG: line longer than exepcted",
SRCNAME, __func__);
return -1;
}
else
outbuf[outidx++] = *p;
}
if (outidx)
{
outbuf[outidx++] = '\r';
outbuf[outidx++] = '\n';
if ((rc = write_buffer (sink, outbuf, outidx)))
return rc;
}
return 0;
}
/* Infer the conent type from DATA and FILENAME. The return value is
a static string there won't be an error return. In case Bae 64
encoding is required for the type true will be stored at FORCE_B64;
however, this is only a shortcut and if that is not set, the caller
should infer the encoding by otehr means. */
static const char *
infer_content_type (const char *data, size_t datalen, const char *filename,
int is_mapibody, int *force_b64)
{
static struct {
char b64;
const char *suffix;
const char *ct;
} suffix_table[] =
{
{ 1, "3gp", "video/3gpp" },
{ 1, "abw", "application/x-abiword" },
{ 1, "ai", "application/postscript" },
{ 1, "au", "audio/basic" },
{ 1, "bin", "application/octet-stream" },
{ 1, "class", "application/java-vm" },
{ 1, "cpt", "application/mac-compactpro" },
{ 0, "css", "text/css" },
{ 0, "csv", "text/comma-separated-values" },
{ 1, "deb", "application/x-debian-package" },
{ 1, "dl", "video/dl" },
{ 1, "doc", "application/msword" },
{ 1, "dv", "video/dv" },
{ 1, "dvi", "application/x-dvi" },
{ 1, "eml", "message/rfc822" },
{ 1, "eps", "application/postscript" },
{ 1, "fig", "application/x-xfig" },
{ 1, "flac", "application/x-flac" },
{ 1, "fli", "video/fli" },
{ 1, "gif", "image/gif" },
{ 1, "gl", "video/gl" },
{ 1, "gnumeric", "application/x-gnumeric" },
{ 1, "hqx", "application/mac-binhex40" },
{ 1, "hta", "application/hta" },
{ 0, "htm", "text/html" },
{ 0, "html", "text/html" },
{ 0, "ics", "text/calendar" },
{ 1, "jar", "application/java-archive" },
{ 1, "jpeg", "image/jpeg" },
{ 1, "jpg", "image/jpeg" },
{ 1, "js", "application/x-javascript" },
{ 1, "latex", "application/x-latex" },
{ 1, "lha", "application/x-lha" },
{ 1, "lzh", "application/x-lzh" },
{ 1, "lzx", "application/x-lzx" },
{ 1, "m3u", "audio/mpegurl" },
{ 1, "m4a", "audio/mpeg" },
{ 1, "mdb", "application/msaccess" },
{ 1, "midi", "audio/midi" },
{ 1, "mov", "video/quicktime" },
{ 1, "mp2", "audio/mpeg" },
{ 1, "mp3", "audio/mpeg" },
{ 1, "mp4", "video/mp4" },
{ 1, "mpeg", "video/mpeg" },
{ 1, "mpega", "audio/mpeg" },
{ 1, "mpg", "video/mpeg" },
{ 1, "mpga", "audio/mpeg" },
{ 1, "msi", "application/x-msi" },
{ 1, "mxu", "video/vnd.mpegurl" },
{ 1, "nb", "application/mathematica" },
{ 1, "oda", "application/oda" },
{ 1, "odb", "application/vnd.oasis.opendocument.database" },
{ 1, "odc", "application/vnd.oasis.opendocument.chart" },
{ 1, "odf", "application/vnd.oasis.opendocument.formula" },
{ 1, "odg", "application/vnd.oasis.opendocument.graphics" },
{ 1, "odi", "application/vnd.oasis.opendocument.image" },
{ 1, "odm", "application/vnd.oasis.opendocument.text-master" },
{ 1, "odp", "application/vnd.oasis.opendocument.presentation" },
{ 1, "ods", "application/vnd.oasis.opendocument.spreadsheet" },
{ 1, "odt", "application/vnd.oasis.opendocument.text" },
{ 1, "ogg", "application/ogg" },
{ 1, "otg", "application/vnd.oasis.opendocument.graphics-template" },
{ 1, "oth", "application/vnd.oasis.opendocument.text-web" },
{ 1, "otp", "application/vnd.oasis.opendocument.presentation-template"},
{ 1, "ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
{ 1, "ott", "application/vnd.oasis.opendocument.text-template" },
{ 1, "pdf", "application/pdf" },
{ 1, "png", "image/png" },
{ 1, "pps", "application/vnd.ms-powerpoint" },
{ 1, "ppt", "application/vnd.ms-powerpoint" },
{ 1, "prf", "application/pics-rules" },
{ 1, "ps", "application/postscript" },
{ 1, "qt", "video/quicktime" },
{ 1, "rar", "application/rar" },
{ 1, "rdf", "application/rdf+xml" },
{ 1, "rpm", "application/x-redhat-package-manager" },
{ 0, "rss", "application/rss+xml" },
{ 1, "ser", "application/java-serialized-object" },
{ 0, "sh", "application/x-sh" },
{ 0, "shtml", "text/html" },
{ 1, "sid", "audio/prs.sid" },
{ 0, "smil", "application/smil" },
{ 1, "snd", "audio/basic" },
{ 0, "svg", "image/svg+xml" },
{ 1, "tar", "application/x-tar" },
{ 0, "texi", "application/x-texinfo" },
{ 0, "texinfo", "application/x-texinfo" },
{ 1, "tif", "image/tiff" },
{ 1, "tiff", "image/tiff" },
{ 1, "torrent", "application/x-bittorrent" },
{ 1, "tsp", "application/dsptype" },
{ 0, "vrml", "model/vrml" },
{ 1, "vsd", "application/vnd.visio" },
{ 1, "wp5", "application/wordperfect5.1" },
{ 1, "wpd", "application/wordperfect" },
{ 0, "xhtml", "application/xhtml+xml" },
{ 1, "xlb", "application/vnd.ms-excel" },
{ 1, "xls", "application/vnd.ms-excel" },
{ 1, "xlt", "application/vnd.ms-excel" },
{ 0, "xml", "application/xml" },
{ 0, "xsl", "application/xml" },
{ 0, "xul", "application/vnd.mozilla.xul+xml" },
{ 1, "zip", "application/zip" },
{ 0, NULL, NULL }
};
int i;
char suffix_buffer[12+1];
const char *suffix;
*force_b64 = 0;
suffix = filename? strrchr (filename, '.') : NULL;
if (suffix && strlen (suffix) < sizeof suffix_buffer -1 )
{
suffix++;
for (i=0; i < sizeof suffix_buffer - 1; i++)
suffix_buffer[i] = tolower (*(const unsigned char*)suffix);
suffix_buffer[i] = 0;
for (i=0; suffix_table[i].suffix; i++)
if (!strcmp (suffix_table[i].suffix, suffix_buffer))
{
if (suffix_table[i].b64)
*force_b64 = 1;
return suffix_table[i].ct;
}
}
/* Not found via filename, look at the content. */
if (is_mapibody)
{
/* Fixme: This is too simple. */
if (datalen > 6 && (!memcmp (data, "<html>", 6)
||!memcmp (data, "<HTML>", 6)))
return "text/html";
return "text/plain";
}
return "application/octet-stream";
}
/* Figure out the best encoding to be used for the part. Return values are
0: Plain ASCII.
1: Quoted Printable
2: Base64 */
static int
infer_content_encoding (const void *data, size_t datalen)
{
const unsigned char *p;
int need_qp;
size_t len, maxlen, highbin, lowbin, ntotal;
ntotal = datalen;
len = maxlen = lowbin = highbin = 0;
need_qp = 0;
for (p = data; datalen; p++, datalen--)
{
len++;
if ((*p & 0x80))
highbin++;
else if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
{
len--;
if (len > maxlen)
maxlen = len;
len = 0;
}
else if (*p == '\r')
{
/* CR not followed by a linefeed. */
lowbin++;
}
else if (*p == '\t' || *p == ' ' || *p == '\f')
;
else if (*p < ' ' || *p == 127)
lowbin++;
else if (len == 1 && datalen > 2
&& *p == '-' && p[1] == '-' && p[2] == ' '
&& ( (datalen > 4 && p[3] == '\r' && p[4] == '\n')
|| (datalen > 3 && p[3] == '\n')
|| datalen == 3))
{
/* This is a "-- \r\n" line, thus it indicates the usual
signature line delimiter. We need to protect the
trailing space. */
need_qp = 1;
}
else if (len == 1 && datalen > 5 && !memcmp (p, "--=-=", 5))
{
/* This look pretty much like a our own boundary.
We better protect it by forcing QP encoding. */
need_qp = 1;
}
else if (len == 1 && datalen >= 5 && !memcmp (p, "From ", 5))
{
/* The usual From hack is required so that MTAs do not
prefix it with an '>'. */
need_qp = 1;
}
}
if (len > maxlen)
maxlen = len;
if (maxlen <= 76 && !lowbin && !highbin && !need_qp)
return 0; /* Plain ASCII is sufficient. */
/* Somewhere in the Outlook documentation 20% is mentioned as
discriminating value for Base64. Though our counting won't be
identical we use that value to behave closely to it. */
if (ntotal && ((float)(lowbin+highbin))/ntotal < 0.20)
return 1; /* Use quoted printable. */
return 2; /* Use base64. */
}
/* Write a MIME part to SINK. First the BOUNDARY is written (unless
it is NULL) then the DATA is analyzed and appropriate headers are
written. If FILENAME is given it will be added to the part's
header. IS_MAPIBODY should be passed as true if the data has been
retrieved from the body property. */
static int
write_part (sink_t sink, const char *data, size_t datalen,
const char *boundary, const char *filename, int is_mapibody)
{
int rc;
const char *ct;
int use_b64, use_qp, is_text;
if (filename)
{
/* If there is a filename strip the directory part. Take care
- that there might be slashes of backslashes. */
+ that there might be slashes or backslashes. */
const char *s1 = strrchr (filename, '/');
const char *s2 = strrchr (filename, '\\');
if (!s1)
s1 = s2;
else if (s1 && s2 && s2 > s1)
s1 = s2;
if (s1)
filename = s1;
if (*filename && filename[1] == ':')
filename += 2;
if (!*filename)
filename = NULL;
}
log_debug ("Writing part of length %d%s filename=`%s'\n",
(int)datalen, is_mapibody? " (body)":"",
filename?filename:"[none]");
ct = infer_content_type (data, datalen, filename, is_mapibody, &use_b64);
use_qp = 0;
if (!use_b64)
{
switch (infer_content_encoding (data, datalen))
{
case 0: break;
case 1: use_qp = 1; break;
default: use_b64 = 1; break;
}
}
is_text = !strncmp (ct, "text/", 5);
if (boundary)
if ((rc = write_boundary (sink, boundary, 0)))
return rc;
if ((rc=write_multistring (sink,
"Content-Type: ", ct,
(is_text || filename? ";\r\n" :"\r\n"),
NULL)))
return rc;
/* OL inserts a charset parameter in many cases, so we do it right
away for all text parts. We can assume us-ascii if no special
encoding is required. */
if (is_text)
if ((rc=write_multistring (sink,
"\tcharset=\"",
(!use_qp && !use_b64? "us-ascii" : "utf-8"),
filename ? "\";\r\n" : "\"\r\n",
NULL)))
return rc;
if (filename)
if ((rc=write_multistring (sink,
"\tname=\"", filename, "\"\r\n",
NULL)))
return rc;
/* Note that we need to output even 7bit because OL inserts that
anyway. */
if ((rc = write_multistring (sink,
"Content-Transfer-Encoding: ",
(use_b64? "base64\r\n":
use_qp? "quoted-printable\r\n":"7bit\r\n"),
NULL)))
return rc;
if (filename)
if ((rc=write_multistring (sink,
"Content-Disposition: attachment;\r\n"
"\tfilename=\"", filename, "\"\r\n",
NULL)))
return rc;
/* Write delimiter. */
if ((rc = write_string (sink, "\r\n")))
return rc;
/* Write the content. */
if (use_b64)
rc = write_b64 (sink, data, datalen);
else if (use_qp)
rc = write_qp (sink, data, datalen);
else
rc = write_plain (sink, data, datalen);
return rc;
}
/* Return the number of attachments in TABLE to be put into the MIME
message. */
static int
count_usable_attachments (mapi_attach_item_t *table)
{
int idx, count = 0;
if (table)
for (idx=0; !table[idx].end_of_table; idx++)
if (table[idx].attach_type == ATTACHTYPE_UNKNOWN
&& table[idx].method == ATTACH_BY_VALUE)
count++;
return count;
}
/* Write out all attachments from TABLE separated by BOUNDARY to SINK.
This function needs to be syncronized with count_usable_attachments. */
static int
write_attachments (sink_t sink,
LPMESSAGE message, mapi_attach_item_t *table,
const char *boundary)
{
int idx, rc;
char *buffer;
size_t buflen;
if (table)
for (idx=0; !table[idx].end_of_table; idx++)
if (table[idx].attach_type == ATTACHTYPE_UNKNOWN
&& table[idx].method == ATTACH_BY_VALUE)
{
buffer = mapi_get_attach (message, 0, table+idx, &buflen);
if (!buffer)
log_debug ("Attachment at index %d not found\n", idx);
else
log_debug ("Attachment at index %d: length=%d\n", idx, (int)buflen);
if (!buffer)
return -1;
rc = write_part (sink, buffer, buflen, boundary,
table[idx].filename, 0);
xfree (buffer);
}
return 0;
}
/* Delete all attachments from TABLE except for the one we just created */
static int
delete_all_attachments (LPMESSAGE message, mapi_attach_item_t *table)
{
HRESULT hr;
int idx;
if (table)
for (idx=0; !table[idx].end_of_table; idx++)
{
if (table[idx].attach_type == ATTACHTYPE_MOSSTEMPL
&& table[idx].filename
&& !strcmp (table[idx].filename, MIMEATTACHFILENAME))
continue;
hr = IMessage_DeleteAttach (message, table[idx].mapipos, 0, NULL, 0);
if (hr)
{
log_error ("%s:%s: DeleteAttach failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return -1;
}
}
return 0;
}
/* Commit changes to the attachment ATTACH and release the object.
SINK needs to be passed as well and will also be closed. Note that
the address of ATTACH is expected so that the fucntion can set it
to NULL. */
static int
close_mapi_attachment (LPATTACH *attach, sink_t sink)
{
HRESULT hr;
LPSTREAM stream = sink? sink->cb_data : NULL;
if (!stream)
{
log_error ("%s:%s: sink not setup", SRCNAME, __func__);
return -1;
}
hr = IStream_Commit (stream, 0);
if (hr)
{
log_error ("%s:%s: Commiting output stream failed: hr=%#lx",
SRCNAME, __func__, hr);
return -1;
}
IStream_Release (stream);
sink->cb_data = NULL;
hr = IAttach_SaveChanges (*attach, 0);
if (hr)
{
log_error ("%s:%s: SaveChanges of the attachment failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return -1;
}
IAttach_Release (*attach);
*attach = NULL;
return 0;
}
/* Cancel changes to the attachment ATTACH and release the object.
SINK needs to be passed as well and will also be closed. Note that
the address of ATTACH is expected so that the fucntion can set it
to NULL. */
static void
cancel_mapi_attachment (LPATTACH *attach, sink_t sink)
{
LPSTREAM stream = sink? sink->cb_data : NULL;
if (stream)
{
IStream_Revert (stream);
IStream_Release (stream);
sink->cb_data = NULL;
}
if (*attach)
{
/* Fixme: Should we try to delete it or is there a Revert method? */
IAttach_Release (*attach);
*attach = NULL;
}
}
/* Do the final processing for a message. */
static int
finalize_message (LPMESSAGE message, mapi_attach_item_t *att_table,
protocol_t protocol, int encrypt)
{
HRESULT hr;
SPropValue prop;
/* Set the message class. */
prop.ulPropTag = PR_MESSAGE_CLASS_A;
prop.Value.lpszA = "IPM.Note.SMIME.MultipartSigned";
hr = IMessage_SetProps (message, 1, &prop, NULL);
if (hr)
{
log_error ("%s:%s: error setting the message class: hr=%#lx\n",
SRCNAME, __func__, hr);
return -1;
}
/* Set a special property so that we are later able to identify
messages signed or encrypted by us. */
if (mapi_set_sig_status (message, "@"))
return -1;
/* We also need to set the message class into our custom
property. This override is at leas required for encrypted
messages. */
if (mapi_set_gpgol_msg_class (message,
(encrypt?
(protocol == PROTOCOL_SMIME?
"IPM.Note.GpgOL.OpaqueEncrypted" :
"IPM.Note.GpgOL.MultipartEncrypted") :
"IPM.Note.GpgOL.MultipartSigned")))
return -1;
/* Now delete all parts of the MAPI message except for the one
attachment we just created. */
if (delete_all_attachments (message, att_table))
return -1;
/* Remove the draft info so that we don't leak the information on
whether the message has been signed etc. */
mapi_set_gpgol_draft_info (message, NULL);
return mapi_save_changes (message, KEEP_OPEN_READWRITE|FORCE_SAVE);
}
/* Sink write method used by mime_sign. We write the data to the
filter and also to the EXTRASINK but we don't pass a flush request
to EXTRASINK. */
static int
sink_hashing_write (sink_t hashsink, const void *data, size_t datalen)
{
int rc;
engine_filter_t filter = hashsink->cb_data;
if (!filter || !hashsink->extrasink)
{
log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
return -1;
}
rc = engine_filter (filter, data, datalen);
if (!rc && data && datalen)
write_buffer (hashsink->extrasink, data, datalen);
return rc;
}
/* This function is called by the filter to collect the output which
is a detached signature. */
static int
collect_signature (void *opaque, const void *data, size_t datalen)
{
struct databuf_s *db = opaque;
if (db->len + datalen >= db->size)
{
db->size += datalen + 1024;
db->buf = xrealloc (db->buf, db->size);
}
memcpy (db->buf + db->len, data, datalen);
db->len += datalen;
return datalen;
}
/* Helper to create the signing header. This includes enough space
for later fixup of the micalg parameter. The MIME version is only
written if FIRST is set. */
static void
create_top_signing_header (char *buffer, size_t buflen, protocol_t protocol,
int first, const char *boundary, const char *micalg)
{
snprintf (buffer, buflen,
"%s"
"Content-Type: multipart/signed;\r\n"
"\tprotocol=\"application/%s\";\r\n"
"\tmicalg=%-15.15s;\r\n"
"\tboundary=\"%s\"\r\n"
"\r\n",
first? "MIME-Version: 1.0\r\n":"",
(protocol==PROTOCOL_OPENPGP? "pgp-signature":"pkcs7-signature"),
micalg, boundary);
}
/* Main body of mime_sign without the the code to delete the original
attachments. On success the function returns the current
attachment table at R_ATT_TABLE or sets this to NULL on error. If
TMPSINK is set no attachment will be created but the output
written to that sink. */
static int
do_mime_sign (LPMESSAGE message, HWND hwnd, protocol_t protocol,
mapi_attach_item_t **r_att_table, sink_t tmpsink,
unsigned int session_number)
{
int result = -1;
int rc;
LPATTACH attach = NULL;
struct sink_s sinkmem;
sink_t sink = &sinkmem;
struct sink_s hashsinkmem;
sink_t hashsink = &hashsinkmem;
char boundary[BOUNDARYSIZE+1];
char inner_boundary[BOUNDARYSIZE+1];
mapi_attach_item_t *att_table = NULL;
char *body = NULL;
int n_att_usable;
char top_header[BOUNDARYSIZE+200];
engine_filter_t filter = NULL;
struct databuf_s sigbuffer;
char *sender = NULL;
*r_att_table = NULL;
memset (sink, 0, sizeof *sink);
memset (hashsink, 0, sizeof *hashsink);
memset (&sigbuffer, 0, sizeof sigbuffer);
if (tmpsink)
{
attach = NULL;
sink = tmpsink;
}
else
{
attach = create_mapi_attachment (message, sink);
if (!attach)
return -1;
}
/* Get the attachment info and the body. */
body = mapi_get_body (message, NULL);
if (body && !*body)
{
xfree (body);
body = NULL;
}
att_table = mapi_create_attach_table (message, 0);
n_att_usable = count_usable_attachments (att_table);
if (!n_att_usable && !body)
{
log_debug ("%s:%s: can't sign an empty message\n", SRCNAME, __func__);
result = gpg_error (GPG_ERR_NO_DATA);
goto failure;
}
/* Prepare the signing. */
if (engine_create_filter (&filter, collect_signature, &sigbuffer))
goto failure;
if (session_number)
{
engine_set_session_number (filter, session_number);
{
char *tmp = mapi_get_subject (message);
engine_set_session_title (filter, tmp);
xfree (tmp);
}
}
sender = mapi_get_sender (message);
if (engine_sign_start (filter, hwnd, protocol, sender, &protocol))
goto failure;
protocol = check_protocol (protocol);
if (protocol == PROTOCOL_UNKNOWN)
{
log_error ("%s:%s: no protocol selected", SRCNAME, __func__);
goto failure;
}
/* Write the top header. */
generate_boundary (boundary);
create_top_signing_header (top_header, sizeof top_header,
protocol, 1, boundary, "xxx");
if ((rc = write_string (sink, top_header)))
goto failure;
/* Create the inner boundary if we have a body and at least one
attachment or more than one attachment. */
if ((body && n_att_usable) || n_att_usable > 1)
generate_boundary (inner_boundary);
else
*inner_boundary = 0;
/* Write the boundary so that it is not included in the hashing. */
if ((rc = write_boundary (sink, boundary, 0)))
goto failure;
/* Create a new sink for hashing and write/hash our content. */
hashsink->cb_data = filter;
hashsink->extrasink = sink;
hashsink->writefnc = sink_hashing_write;
/* Note that OL2003 will add an extra line after the multipart
header, thus we do the same to avoid running all through an
IConverterSession first. */
if (*inner_boundary
&& (rc=write_multistring (hashsink,
"Content-Type: multipart/mixed;\r\n",
"\tboundary=\"", inner_boundary, "\"\r\n",
"\r\n", /* <-- extra line */
NULL)))
goto failure;
if (body)
rc = write_part (hashsink, body, strlen (body),
*inner_boundary? inner_boundary : NULL, NULL, 1);
if (!rc && n_att_usable)
rc = write_attachments (hashsink, message, att_table,
*inner_boundary? inner_boundary : NULL);
if (rc)
goto failure;
xfree (body);
body = NULL;
/* Finish the possible multipart/mixed. */
if (*inner_boundary && (rc = write_boundary (hashsink, inner_boundary, 1)))
goto failure;
/* Here we are ready with the hashing. Flush the filter and wait
for the signing process to finish. */
if ((rc = write_buffer (hashsink, NULL, 0)))
goto failure;
if ((rc = engine_wait (filter)))
goto failure;
filter = NULL; /* Not valid anymore. */
hashsink->cb_data = NULL; /* Not needed anymore. */
/* Write signature attachment. */
if ((rc = write_boundary (sink, boundary, 0)))
goto failure;
- if ((rc=write_string (sink,
- (protocol == PROTOCOL_OPENPGP
- ? "Content-Type: application/pgp-signature\r\n"
- : ("Content-Transfer-Encoding: base64\r\n"
- "Content-Type: application/pkcs7-signature\r\n")
- ))))
- goto failure;
+ if (protocol == PROTOCOL_OPENPGP)
+ {
+ rc = write_string (sink,
+ "Content-Type: application/pgp-signature\r\n");
+ }
+ else
+ {
+ rc = write_string (sink,
+ "Content-Transfer-Encoding: base64\r\n"
+ "Content-Type: application/pkcs7-signature\r\n");
+ /* rc = write_string (sink, */
+ /* "Content-Type: application/x-pkcs7-signature\r\n" */
+ /* "\tname=\"smime.p7s\"\r\n" */
+ /* "Content-Transfer-Encoding: base64\r\n" */
+ /* "Content-Disposition: attachment;\r\n" */
+ /* "\tfilename=\"smime.p7s\"\r\n"); */
- /* If we would add "Content-Transfer-Encoding: 7bit\r\n" to this
+ }
+ /* About the above code:
+ If we would add "Content-Transfer-Encoding: 7bit\r\n" to this
attachment, Outlooks does not proceed with sending and even does
not return any error. A wild guess is that while OL adds this
header itself, it detects that it already exists and somehow gets
into a problem. It is not a problem with the other parts,
though. Hmmm, triggered by the top levels CT protocol parameter?
Anyway, it is not required that we add it as we won't hash it.
Note, that this only holds for OpenPGP; for S/MIME we need to set
set CTE. We even write it before the CT because that is the same
as Outlook would do it for a missing CTE. */
+ if (rc)
+ goto failure;
if ((rc = write_string (sink, "\r\n")))
goto failure;
/* Write the signature. We add an extra CR,LF which should not harm
and a terminating 0. */
collect_signature (&sigbuffer, "\r\n", 3);
if ((rc = write_string (sink, sigbuffer.buf)))
goto failure;
/* Write the final boundary and finish the attachment. */
if ((rc = write_boundary (sink, boundary, 1)))
goto failure;
/* Fixup the micalg parameter. */
{
HRESULT hr;
LARGE_INTEGER off;
LPSTREAM stream = sink->cb_data;
off.QuadPart = 0;
hr = IStream_Seek (stream, off, STREAM_SEEK_SET, NULL);
if (hr)
{
log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
SRCNAME, __func__, hr);
goto failure;
}
create_top_signing_header (top_header, sizeof top_header,
protocol, 1, boundary,
protocol == PROTOCOL_SMIME? "sha1":"pgp-sha1");
hr = IStream_Write (stream, top_header, strlen (top_header), NULL);
if (hr)
{
log_error ("%s:%s: writing fixed micalg failed: hr=%#lx",
SRCNAME, __func__, hr);
goto failure;
}
/* Better seek again to the end. */
off.QuadPart = 0;
hr = IStream_Seek (stream, off, STREAM_SEEK_END, NULL);
if (hr)
{
log_error ("%s:%s: seeking back to the end failed: hr=%#lx",
SRCNAME, __func__, hr);
goto failure;
}
}
if (attach)
{
if (close_mapi_attachment (&attach, sink))
goto failure;
}
result = 0; /* Everything is fine, fall through the cleanup now. */
failure:
engine_cancel (filter);
if (attach)
cancel_mapi_attachment (&attach, sink);
xfree (body);
if (result)
mapi_release_attach_table (att_table);
else
*r_att_table = att_table;
xfree (sigbuffer.buf);
xfree (sender);
return result;
}
/* Sign the MESSAGE using PROTOCOL. If PROTOCOL is PROTOCOL_UNKNOWN
the engine decides what protocol to use. On return MESSAGE is
modified so that sending it will result in a properly MOSS (that is
PGP or S/MIME) signed message. On failure the function tries to
keep the original message intact but there is no 100% guarantee for
it. */
int
mime_sign (LPMESSAGE message, HWND hwnd, protocol_t protocol)
{
int result = -1;
mapi_attach_item_t *att_table;
result = do_mime_sign (message, hwnd, protocol, &att_table, 0,
engine_new_session_number ());
if (!result)
{
if (!finalize_message (message, att_table, protocol, 0))
result = 0;
}
mapi_release_attach_table (att_table);
return result;
}
/* Sink write method used by mime_encrypt. */
static int
sink_encryption_write (sink_t encsink, const void *data, size_t datalen)
{
engine_filter_t filter = encsink->cb_data;
if (!filter)
{
log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
return -1;
}
return engine_filter (filter, data, datalen);
}
#if 0 /* Not used. */
/* Sink write method used by mime_encrypt for writing Base64. */
static int
sink_encryption_write_b64 (sink_t encsink, const void *data, size_t datalen)
{
engine_filter_t filter = encsink->cb_data;
int rc;
const unsigned char *p;
unsigned char inbuf[4];
int idx, quads;
char outbuf[6];
size_t outbuflen;
if (!filter)
{
log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
return -1;
}
idx = encsink->b64.idx;
assert (idx < 4);
memcpy (inbuf, encsink->b64.inbuf, idx);
quads = encsink->b64.quads;
if (!data) /* Flush. */
{
outbuflen = 0;
if (idx)
{
outbuf[0] = bintoasc[(*inbuf>>2)&077];
if (idx == 1)
{
outbuf[1] = bintoasc[((*inbuf<<4)&060)&077];
outbuf[2] = '=';
outbuf[3] = '=';
}
else
{
outbuf[1] = bintoasc[(((*inbuf<<4)&060)|
((inbuf[1]>>4)&017))&077];
outbuf[2] = bintoasc[((inbuf[1]<<2)&074)&077];
outbuf[3] = '=';
}
outbuflen = 4;
quads++;
}
if (quads)
{
outbuf[outbuflen++] = '\r';
outbuf[outbuflen++] = '\n';
}
if (outbuflen && (rc = engine_filter (filter, outbuf, outbuflen)))
return rc;
/* Send the flush command to the filter. */
if ((rc = engine_filter (filter, data, datalen)))
return rc;
}
else
{
for (p = data; datalen; p++, datalen--)
{
inbuf[idx++] = *p;
if (idx > 2)
{
idx = 0;
outbuf[0] = bintoasc[(*inbuf>>2)&077];
outbuf[1] = bintoasc[(((*inbuf<<4)&060)
|((inbuf[1] >> 4)&017))&077];
outbuf[2] = bintoasc[(((inbuf[1]<<2)&074)
|((inbuf[2]>>6)&03))&077];
outbuf[3] = bintoasc[inbuf[2]&077];
outbuflen = 4;
if (++quads >= (64/4))
{
quads = 0;
outbuf[4] = '\r';
outbuf[5] = '\n';
outbuflen += 2;
}
if ((rc = engine_filter (filter, outbuf, outbuflen)))
return rc;
}
}
}
encsink->b64.idx = idx;
memcpy (encsink->b64.inbuf, inbuf, idx);
encsink->b64.quads = quads;
return 0;
}
#endif /*Not used.*/
/* Helper from mime_encrypt. BOUNDARY is a buffer of at least
BOUNDARYSIZE+1 bytes which will be set on return from that
function. */
static int
create_top_encryption_header (sink_t sink, protocol_t protocol, char *boundary)
{
int rc;
if (protocol == PROTOCOL_SMIME)
{
*boundary = 0;
rc = write_multistring (sink,
"MIME-Version: 1.0\r\n"
"Content-Type: application/pkcs7-mime;\r\n"
"\tsmime-type=enveloped-data;\r\n"
"\tname=\"smime.p7m\"\r\n"
"Content-Transfer-Encoding: base64\r\n"
"\r\n",
NULL);
}
else
{
generate_boundary (boundary);
rc = write_multistring (sink,
"MIME-Version: 1.0\r\n"
"Content-Type: multipart/encrypted;\r\n"
"\tprotocol=\"application/pgp-encrypted\";\r\n",
"\tboundary=\"", boundary, "\"\r\n",
NULL);
if (rc)
return rc;
/* Write the PGP/MIME encrypted part. */
rc = write_boundary (sink, boundary, 0);
if (rc)
return rc;
rc = write_multistring (sink,
"Content-Type: application/pgp-encrypted\r\n"
"\r\n"
"Version: 1\r\n", NULL);
if (rc)
return rc;
/* And start the second part. */
rc = write_boundary (sink, boundary, 0);
if (rc)
return rc;
rc = write_multistring (sink,
"Content-Type: application/octet-stream\r\n"
"\r\n", NULL);
}
return rc;
}
/* Encrypt the MESSAGE. */
int
mime_encrypt (LPMESSAGE message, HWND hwnd,
protocol_t protocol, char **recipients)
{
int result = -1;
int rc;
LPATTACH attach;
struct sink_s sinkmem;
sink_t sink = &sinkmem;
struct sink_s encsinkmem;
sink_t encsink = &encsinkmem;
char boundary[BOUNDARYSIZE+1];
char inner_boundary[BOUNDARYSIZE+1];
mapi_attach_item_t *att_table = NULL;
char *body = NULL;
int n_att_usable;
engine_filter_t filter = NULL;
char *sender = NULL;
memset (sink, 0, sizeof *sink);
memset (encsink, 0, sizeof *encsink);
attach = create_mapi_attachment (message, sink);
if (!attach)
return -1;
/* Get the attachment info and the body. We need to do this before
creating the engine's filter sue problem sending the cancel to
the engine with nothing for the engine to process. This is
actually a bug in our engine code but we better avoid triggering
this bug because the engine sometimes hangs. Fixme: Needs a
proper fix. */
body = mapi_get_body (message, NULL);
if (body && !*body)
{
xfree (body);
body = NULL;
}
att_table = mapi_create_attach_table (message, 0);
n_att_usable = count_usable_attachments (att_table);
if (!n_att_usable && !body)
{
log_debug ("%s:%s: can't encrypt an empty message\n", SRCNAME, __func__);
result = gpg_error (GPG_ERR_NO_DATA);
goto failure;
}
/* Prepare the encryption. We do this early as it is quite common
that some recipient keys are not available and thus the
encryption will fail early. */
if (engine_create_filter (&filter, write_buffer_for_cb, sink))
goto failure;
engine_set_session_number (filter, engine_new_session_number ());
{
char *tmp = mapi_get_subject (message);
engine_set_session_title (filter, tmp);
xfree (tmp);
}
sender = mapi_get_sender (message);
if (engine_encrypt_prepare (filter, hwnd, protocol, 0,
sender, recipients, &protocol))
goto failure;
if (engine_encrypt_start (filter, 0))
goto failure;
protocol = check_protocol (protocol);
if (protocol == PROTOCOL_UNKNOWN)
goto failure;
/* Write the top header. */
rc = create_top_encryption_header (sink, protocol, boundary);
if (rc)
goto failure;
/* Create a new sink for encrypting the following stuff. */
encsink->cb_data = filter;
encsink->writefnc = sink_encryption_write;
if ((body && n_att_usable) || n_att_usable > 1)
{
/* A body and at least one attachment or more than one attachment */
generate_boundary (inner_boundary);
if ((rc=write_multistring (encsink,
"Content-Type: multipart/mixed;\r\n",
"\tboundary=\"", inner_boundary, "\"\r\n",
NULL)))
goto failure;
}
else /* Only one part. */
*inner_boundary = 0;
if (body)
rc = write_part (encsink, body, strlen (body),
*inner_boundary? inner_boundary : NULL, NULL, 1);
if (!rc && n_att_usable)
rc = write_attachments (encsink, message, att_table,
*inner_boundary? inner_boundary : NULL);
if (rc)
goto failure;
xfree (body);
body = NULL;
/* Finish the possible multipart/mixed. */
if (*inner_boundary && (rc = write_boundary (encsink, inner_boundary, 1)))
goto failure;
/* Flush the encryption sink and wait for the encryption to get
ready. */
if ((rc = write_buffer (encsink, NULL, 0)))
goto failure;
if ((rc = engine_wait (filter)))
goto failure;
filter = NULL; /* Not valid anymore. */
encsink->cb_data = NULL; /* Not needed anymore. */
if (!sink->enc_counter)
{
log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
goto failure;
}
/* Write the final boundary (for OpenPGP) and finish the attachment. */
if (*boundary && (rc = write_boundary (sink, boundary, 1)))
goto failure;
if (close_mapi_attachment (&attach, sink))
goto failure;
if (finalize_message (message, att_table, protocol, 1))
goto failure;
result = 0; /* Everything is fine, fall through the cleanup now. */
failure:
engine_cancel (filter);
cancel_mapi_attachment (&attach, sink);
xfree (body);
mapi_release_attach_table (att_table);
xfree (sender);
return result;
}
/* Sign and Encrypt the MESSAGE. */
int
mime_sign_encrypt (LPMESSAGE message, HWND hwnd,
protocol_t protocol, char **recipients)
{
int result = -1;
int rc = 0;
HRESULT hr;
LPATTACH attach;
LPSTREAM tmpstream = NULL;
struct sink_s sinkmem;
sink_t sink = &sinkmem;
struct sink_s encsinkmem;
sink_t encsink = &encsinkmem;
struct sink_s tmpsinkmem;
sink_t tmpsink = &tmpsinkmem;
char boundary[BOUNDARYSIZE+1];
mapi_attach_item_t *att_table = NULL;
engine_filter_t filter = NULL;
unsigned int session_number;
char *sender = NULL;
memset (sink, 0, sizeof *sink);
memset (encsink, 0, sizeof *encsink);
memset (tmpsink, 0, sizeof *tmpsink);
attach = create_mapi_attachment (message, sink);
if (!attach)
return -1;
/* First check that we are not trying to process an empty message
which might lock up our engine. Unfortunately we need to
duplicate the code we use in do_mime_sign here. FIXME: The
engine should be fixed instead of using such a workaround. */
{
char *body;
body = mapi_get_body (message, NULL);
if (body && !*body)
{
xfree (body);
body = NULL;
}
att_table = mapi_create_attach_table (message, 0);
if (!count_usable_attachments (att_table) && !body)
result = gpg_error (GPG_ERR_NO_DATA);
xfree (body);
if (att_table)
{
mapi_release_attach_table (att_table);
att_table = NULL;
}
if (gpg_err_code (result) == GPG_ERR_NO_DATA)
{
log_debug ("%s:%s: can't sign+encrypt an empty message\n",
SRCNAME, __func__);
goto failure;
}
}
/* Create a temporary sink to construct the signed data. */
hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
(SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
| STGM_CREATE | STGM_READWRITE),
NULL, "GPG", &tmpstream);
if (FAILED (hr))
{
log_error ("%s:%s: can't create temp file: hr=%#lx\n",
SRCNAME, __func__, hr);
rc = -1;
goto failure;
}
tmpsink->cb_data = tmpstream;
tmpsink->writefnc = sink_std_write;
/* Prepare the encryption. We do this early as it is quite common
that some recipients are not available and thus the encryption
will fail early. This is also required to allow the UIserver to
figure out the protocol to use if we have not forced one. */
if (engine_create_filter (&filter, write_buffer_for_cb, sink))
goto failure;
session_number = engine_new_session_number ();
engine_set_session_number (filter, session_number);
{
char *tmp = mapi_get_subject (message);
engine_set_session_title (filter, tmp);
xfree (tmp);
}
sender = mapi_get_sender (message);
if ((rc=engine_encrypt_prepare (filter, hwnd,
protocol, ENGINE_FLAG_SIGN_FOLLOWS,
sender, recipients, &protocol)))
goto failure;
protocol = check_protocol (protocol);
if (protocol == PROTOCOL_UNKNOWN)
goto failure;
/* Now sign the message. This creates another attachment with the
complete MIME object of the signed message. We can't do the
encryption in streaming mode while running the encryption because
we need to fix up that ugly micalg parameter after having created
the signature. Note that the protocol to use is taken from the
encryption operation. */
if (do_mime_sign (message, hwnd, protocol, &att_table, tmpsink,
session_number))
goto failure;
/* Now send the actual ENCRYPT command. This split up between
prepare and start is necessary to help with the implementarion of
the UI-server. If we would send the ENCRYPT command immediately
the UI-server might block while reading from the input stream
because we are first going to do a sign operation which in trun
needs the attention of the UI server. A more robust but
complicated approach to the UI-server would be to delay the
reading (and thus the start of the underlying encrypt operation)
until the first byte has been received. */
if ((rc=engine_encrypt_start (filter, 0)))
goto failure;
/* Write the top header. */
rc = create_top_encryption_header (sink, protocol, boundary);
if (rc)
goto failure;
/* Create a new sink for encrypting the temporary attachment with
the signed message. */
encsink->cb_data = filter;
encsink->writefnc = sink_encryption_write;
/* Copy the temporary stream to the encryption sink. */
{
LARGE_INTEGER off;
ULONG nread;
char buffer[4096];
off.QuadPart = 0;
hr = IStream_Seek (tmpstream, off, STREAM_SEEK_SET, NULL);
if (hr)
{
log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
SRCNAME, __func__, hr);
rc = gpg_error (GPG_ERR_EIO);
goto failure;
}
for (;;)
{
hr = IStream_Read (tmpstream, buffer, sizeof buffer, &nread);
if (hr)
{
log_error ("%s:%s: IStream::Read failed: hr=%#lx",
SRCNAME, __func__, hr);
rc = gpg_error (GPG_ERR_EIO);
goto failure;
}
if (!nread)
break; /* EOF */
rc = write_buffer (encsink, buffer, nread);
if (rc)
{
log_error ("%s:%s: writing tmpstream to encsink failed: %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
}
}
/* Flush the encryption sink and wait for the encryption to get
ready. */
if ((rc = write_buffer (encsink, NULL, 0)))
goto failure;
if ((rc = engine_wait (filter)))
goto failure;
filter = NULL; /* Not valid anymore. */
encsink->cb_data = NULL; /* Not needed anymore. */
if (!sink->enc_counter)
{
log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
goto failure;
}
/* Write the final boundary (for OpenPGP) and finish the attachment. */
if (*boundary && (rc = write_boundary (sink, boundary, 1)))
goto failure;
if (close_mapi_attachment (&attach, sink))
goto failure;
if (finalize_message (message, att_table, protocol, 1))
goto failure;
result = 0; /* Everything is fine, fall through the cleanup now. */
failure:
if (result)
log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
gpg_strerror (rc), gpg_strsource (rc));
engine_cancel (filter);
if (tmpstream)
IStream_Release (tmpstream);
mapi_release_attach_table (att_table);
xfree (sender);
return result;
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 30, 5:54 PM (1 d, 21 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e9/df/4915018d127ff1504b866af95df8

Event Timeline