diff --git a/configure.ac b/configure.ac
index 2d1fab6..c94f968 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,323 +1,311 @@
# configure.ac - for GpgOL
# Copyright (C) 2005, 2006, 2007, 2008, 2011 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.14"
# To build a release you need to create a tag with the version number
# (git tag -s gpgol-k.n.m) and run "./autogen.sh --force". Please
# bump the version number immediately *after* the release and do
# another commit and push so that the git magic is able to work.
m4_define([mym4_version], [2.1.2])
# Below is m4 magic to extract and compute the git revision number,
# the decimalized short revision number, a beta version string and a
# flag indicating a development version (mym4_isgit). Note that the
# m4 processing is done by autoconf and not during the configure run.
m4_define([mym4_revision],
m4_esyscmd([git rev-parse --short HEAD | tr -d '\n\r']))
m4_define([mym4_revision_dec],
m4_esyscmd_s([echo $((0x$(echo ]mym4_revision[|head -c 4)))]))
m4_define([mym4_betastring],
m4_esyscmd_s([git describe --match 'gpgol-[0-9].[0-9].*[0-9]' \
--long | awk -F- '$3!=0{print"-beta"$3}']))
m4_define([mym4_isgit],m4_if(mym4_betastring,[],[no],[yes]))
m4_define([mym4_full_version],[mym4_version[]mym4_betastring])
AC_INIT([gpgol],[mym4_full_version], [http://bugs.gnupg.org])
# Bump up this value if you changed any form.
GPGOL_FORMS_REVISION=335
NEED_GPG_ERROR_VERSION=1.9
NEED_GPGME_API=1
NEED_GPGME_VERSION=1.11.0
NEED_LIBASSUAN_API=2
NEED_LIBASSUAN_VERSION=2.0.0
GIT_COMMIT=0x[]mym4_revision
GIT_COMMIT_DECIMAL=0x[]mym4_revision_dec
PACKAGE=$PACKAGE_NAME
PACKAGE_GT=${PACKAGE_NAME}
VERSION=$PACKAGE_VERSION
AC_CONFIG_AUX_DIR([build-aux])
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(GIT_COMMIT, ${GIT_COMMIT}, [Current GIT commit])
AC_DEFINE_UNQUOTED(GPGOL_FORMS_REVISION, ${GPGOL_FORMS_REVISION},
[Current Forms 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}${GIT_COMMIT_DECIMAL}"
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_NOTICE([[
***
*** This software is only useful for W32 systems. Use
*** ./autogen.sh --build-w32
*** to prepare it for such a build.
***
*** For your current host only tests will be built!
***]])
;;
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)
AM_CONDITIONAL(BUILD_W64, test "$host" = "x86_64-w64-mingw32")
#
# Checks for libraries.
#
AM_PATH_GPGME("$NEED_GPGME_API:$NEED_GPGME_VERSION",
have_gpgme=yes,have_gpgme=no)
AM_PATH_GPGMEPP(have_gpgmepp=yes,have_gpgmepp=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)
# 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)
# Check for functions
AC_REPLACE_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 -fno-strict-aliasing"
CXXFLAGS="$CXXFLAGS -Wall -mms-bitfields -fno-strict-aliasing"
# Hardening flags
# Stack protection
# -fstack-protector-all -Wstack-protector --param ssp-buffer-size=4
# causes gpgol not to be loaded by Outlook due to a runtime error.
# This needs to be analysed but could be an incompatibility between
# gcc's stack protection and COM / Outlook system calls.
if test "$have_w32_system" = yes; then
HARDENING="-Wl,--dynamicbase -Wl,--nxcompat -fno-exceptions -D_FORTIFY_SOURCE=2 -O0"
else
HARDENING="-fPIC"
fi
CFLAGS="$CFLAGS $HARDENING"
CXXFLAGS="$CXXFLAGS $HARDENING"
if test "$USE_MAINTAINER_MODE" = "yes"; then
CFLAGS="$CFLAGS -Werror -Wcast-align -Wshadow -Wstrict-prototypes"
CFLAGS="$CFLAGS -Wformat-security"
CFLAGS="$CFLAGS -W -Wno-sign-compare"
CXXFLAGS="$CXXFLAGS -Werror -Wcast-align -Wshadow"
CXXFLAGS="$CXXFLAGS -Wformat-security"
CXXFLAGS="$CXXFLAGS -W -Wno-sign-compare"
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"
fi
fi
AC_SUBST(W32LIBS)
-#
-# This should be a temporary option that will eventually become the
-# default.
-#
-AC_ARG_ENABLE(mime-send,
- AC_HELP_STRING([--enable-mime-send], [Enable experimental send support for mime messages]),
- mime_send=$enableval, mime_send=no)
-if test "$mime_send" != "no"; then
- AC_DEFINE(MIME_SEND, 1,
- [Sending Mime messages will be supported.])
-fi
-
#
# 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_gpgmepp" = "no"; then
die=yes
AC_MSG_NOTICE([[
***
*** You need the C++ language binding for gpgme to build this program.
** Ensure that GPGME was compiled with --enabled-languages=cpp
***]])
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/icons/Makefile
src/versioninfo.rc
forms/Makefile
doc/Makefile
po/Makefile.in
m4/Makefile
tests/Makefile
])
AC_OUTPUT
diff --git a/src/addin-options.cpp b/src/addin-options.cpp
index 6677a30..8384efc 100644
--- a/src/addin-options.cpp
+++ b/src/addin-options.cpp
@@ -1,243 +1,207 @@
/* addin-options.cpp - Options for the Ol >= 2010 Addin
* Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation 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 .
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include "dialogs.h"
#include "common.h"
#include "engine.h"
#include
#include
#include
/* To avoid writing a dialog template for each language we use gettext
for the labels and hope that there is enough space in the dialog to
fit the longest translation.. */
static void
set_labels (HWND dlg)
{
static struct { int itemid; const char *label; } labels[] = {
{ IDC_G_GENERAL, N_("General")},
{ IDC_ENABLE_SMIME, N_("Enable the S/MIME support")},
-#ifndef MIME_SEND
- { IDC_MIME_UI, N_("Enable simplified interface (experimental)")},
-#endif
{ IDC_G_SEND, N_("Message sending")},
{ IDC_ENCRYPT_DEFAULT, N_("&Encrypt new messages by default")},
{ IDC_SIGN_DEFAULT, N_("&Sign new messages by default")},
{ IDC_INLINE_PGP, N_("&Send OpenPGP mails without "
"attachments as PGP/Inline")},
{ IDC_REPLYCRYPT, N_("S&elect crypto settings automatically "
"for reply and forward")},
{ IDC_AUTORRESOLVE, N_("&Resolve recipient keys automatically")},
{ IDC_GPG_OPTIONS, N_("Debug...")},
{ IDC_GPG_CONF, N_("Configure GnuPG")},
{ IDC_VERSION_INFO, N_("Version ")VERSION},
{ 0, NULL}
};
int i;
for (i=0; labels[i].itemid; i++)
SetDlgItemText (dlg, labels[i].itemid, _(labels[i].label));
}
-
-/** Enable or disable options that are only relvant for mime ui */
-static void
-enable_disable_opts (HWND hDlg)
-{
- BOOL enable = opt.mime_ui ? TRUE : FALSE;
- EnableWindow (GetDlgItem (hDlg, IDC_ENCRYPT_DEFAULT), enable);
- EnableWindow (GetDlgItem (hDlg, IDC_SIGN_DEFAULT), enable);
- char *uiserver = get_uiserver_name ();
- if (!uiserver && !opt.enable_smime)
- {
- EnableWindow (GetDlgItem (hDlg, IDC_AUTORRESOLVE), FALSE);
- }
- xfree (uiserver);
-}
-
static void
launch_kleo_config (HWND hDlg)
{
char *uiserver = get_uiserver_name ();
bool showError = false;
if (uiserver)
{
std::string path (uiserver);
xfree (uiserver);
if (path.find("kleopatra.exe") != std::string::npos)
{
size_t dpos;
if ((dpos = path.find(" --daemon")) != std::string::npos)
{
path.erase(dpos, strlen(" --daemon"));
}
auto ctx = GpgME::Context::createForEngine(GpgME::SpawnEngine);
if (!ctx)
{
log_error ("%s:%s: No spawn engine.",
SRCNAME, __func__);
}
std::string parentWid = std::to_string ((int) (intptr_t) hDlg);
const char *argv[] = {path.c_str(),
"--config",
"--parent-windowid",
parentWid.c_str(),
NULL };
log_debug ("%s:%s: Starting %s %s %s",
SRCNAME, __func__, path.c_str(), argv[1], argv[2]);
GpgME::Data d(GpgME::Data::null);
ctx->spawnAsync(path.c_str(), argv, d, d,
d, (GpgME::Context::SpawnFlags) (
GpgME::Context::SpawnAllowSetFg |
GpgME::Context::SpawnShowWindow));
}
else
{
showError = true;
}
}
else
{
showError = true;
}
if (showError)
{
MessageBox (NULL,
_("Could not find Kleopatra.\n"
"Please reinstall Gpg4win with the Kleopatra component enabled."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
}
}
static INT_PTR CALLBACK
options_window_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
(void)lParam;
switch (uMsg)
{
case WM_INITDIALOG:
{
SendDlgItemMessage (hDlg, IDC_ENABLE_SMIME, BM_SETCHECK,
!!opt.enable_smime, 0L);
SendDlgItemMessage (hDlg, IDC_ENCRYPT_DEFAULT, BM_SETCHECK,
!!opt.encrypt_default, 0L);
SendDlgItemMessage (hDlg, IDC_SIGN_DEFAULT, BM_SETCHECK,
!!opt.sign_default, 0L);
-#ifndef MIME_SEND
- SendDlgItemMessage (hDlg, IDC_MIME_UI, BM_SETCHECK,
- !!opt.mime_ui, 0L);
-#endif
SendDlgItemMessage (hDlg, IDC_INLINE_PGP, BM_SETCHECK,
!!opt.inline_pgp, 0L);
SendDlgItemMessage (hDlg, IDC_REPLYCRYPT, BM_SETCHECK,
!!opt.reply_crypt, 0L);
SendDlgItemMessage (hDlg, IDC_AUTORRESOLVE, BM_SETCHECK,
!!opt.autoresolve, 0L);
- enable_disable_opts (hDlg);
set_labels (hDlg);
ShowWindow (GetDlgItem (hDlg, IDC_GPG_OPTIONS),
opt.enable_debug ? SW_SHOW : SW_HIDE);
}
return 1;
case WM_LBUTTONDOWN:
{
return 1;
}
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDOK:
{
opt.enable_smime = !!SendDlgItemMessage
(hDlg, IDC_ENABLE_SMIME, BM_GETCHECK, 0, 0L);
opt.encrypt_default = !!SendDlgItemMessage
(hDlg, IDC_ENCRYPT_DEFAULT, BM_GETCHECK, 0, 0L);
opt.sign_default = !!SendDlgItemMessage
(hDlg, IDC_SIGN_DEFAULT, BM_GETCHECK, 0, 0L);
-#ifndef MIME_SEND
- int mime_ui_old = opt.mime_ui;
- opt.mime_ui = !!SendDlgItemMessage
- (hDlg, IDC_MIME_UI, BM_GETCHECK, 0, 0L);
- if (opt.mime_ui != mime_ui_old)
- {
- MessageBox (NULL,
- _("Changing the interface requires a restart of Outlook."),
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- }
-#endif
opt.inline_pgp = !!SendDlgItemMessage
(hDlg, IDC_INLINE_PGP, BM_GETCHECK, 0, 0L);
opt.reply_crypt = !!SendDlgItemMessage
(hDlg, IDC_REPLYCRYPT, BM_GETCHECK, 0, 0L);
opt.autoresolve = !!SendDlgItemMessage
(hDlg, IDC_AUTORRESOLVE, BM_GETCHECK, 0, 0L);
write_options ();
EndDialog (hDlg, TRUE);
break;
}
case IDC_GPG_CONF:
launch_kleo_config (hDlg);
break;
case IDC_GPG_OPTIONS:
config_dialog_box (hDlg);
break;
}
case WM_SYSCOMMAND:
switch (LOWORD (wParam))
{
case SC_CLOSE:
EndDialog (hDlg, TRUE);
}
break;
}
return 0;
}
void
options_dialog_box (HWND parent)
{
int resid;
resid = IDD_ADDIN_OPTIONS;
if (!parent)
parent = GetDesktopWindow ();
DialogBoxParam (glob_hinst, MAKEINTRESOURCE (resid), parent,
options_window_proc, 0);
}
diff --git a/src/common.h b/src/common.h
index bd55a13..177bf6a 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,165 +1,159 @@
/* common.h - Common declarations for GpgOL
* Copyright (C) 2004 Timo Schulz
* Copyright (C) 2005, 2006, 2007, 2008 g10 Code GmbH
* Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation 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
* 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 .
*/
#ifndef GPGOL_COMMON_H
#define GPGOL_COMMON_H
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "common_indep.h"
#include
-#ifdef MIME_SEND
-# define MIME_UI_DEFAULT 1
-#else
-# define MIME_UI_DEFAULT 0
-#endif
-
/* Registry path to store plugin settings */
#define GPGOL_REGPATH "Software\\GNU\\GpgOL"
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
extern HINSTANCE glob_hinst;
extern UINT this_dll;
/*-- common.c --*/
void set_global_hinstance (HINSTANCE hinst);
void center_window (HWND childwnd, HWND style);
HBITMAP get_system_check_bitmap (int checked);
char *get_save_filename (HWND root, const char *srcname);
char *get_open_filename (HWND root, const char *title);
char *utf8_to_wincp (const char *string);
const char *default_homedir (void);
char *get_data_dir (void);
char *get_gpg4win_dir (void);
int store_extension_value (const char *key, const char *val);
int store_extension_subkey_value (const char *subkey, const char *key,
const char *val);
int load_extension_value (const char *key, char **val);
/* Get a temporary filename with and its name */
wchar_t *get_tmp_outfile (wchar_t *name, HANDLE *outHandle);
wchar_t *get_pretty_attachment_name (wchar_t *path, protocol_t protocol,
int signature);
/*-- recipient-dialog.c --*/
unsigned int recipient_dialog_box (gpgme_key_t **ret_rset);
unsigned int recipient_dialog_box2 (gpgme_key_t *fnd, char **unknown,
gpgme_key_t **ret_rset);
/*-- passphrase-dialog.c --*/
int signer_dialog_box (gpgme_key_t *r_key, char **r_passwd, int encrypting);
gpgme_error_t passphrase_callback_box (void *opaque, const char *uid_hint,
const char *pass_info,
int prev_was_bad, int fd);
void free_decrypt_key (struct passphrase_cb_s *ctx);
const char *get_pubkey_algo_str (gpgme_pubkey_algo_t id);
/*-- config-dialog.c --*/
void config_dialog_box (HWND parent);
/*-- verify-dialog.c --*/
int verify_dialog_box (gpgme_protocol_t protocol,
gpgme_verify_result_t res,
const char *filename);
/*-- inspectors.cpp --*/
int initialize_inspectors (void);
#if __GNUC__ >= 4
# define GPGOL_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a)))
#else
# define GPGOL_GCC_A_SENTINEL(a)
#endif
/* i18n stuff */
#include "w32-gettext.h"
#define _(a) gettext (a)
#define N_(a) gettext_noop (a)
/*-- common.c --*/
void fatal_error (const char *format, ...);
char *wchar_to_utf8_2 (const wchar_t *string, size_t len);
wchar_t *utf8_to_wchar2 (const char *string, size_t len);
char *read_w32_registry_string (const char *root, const char *dir,
const char *name);
char *percent_escape (const char *str, const char *extra);
void fix_linebreaks (char *str, int *len);
/* Format a date from gpgme (seconds since epoch)
with windows system locale. */
char *format_date_from_gpgme (unsigned long time);
/* Get the name of the uiserver */
char *get_uiserver_name (void);
int is_elevated (void);
/*-- main.c --*/
const void *get_128bit_session_key (void);
const void *get_64bit_session_marker (void);
void *create_initialization_vector (size_t nbytes);
void read_options (void);
int write_options (void);
extern int g_ol_version_major;
void log_window_hierarchy (HWND window, const char *fmt,
...) __attribute__ ((format (printf,2,3)));
void bring_to_front (HWND wid);
int gpgol_message_box (HWND parent, const char *utf8_text,
const char *utf8_caption, UINT type);
/* Show a bug message with the code. */
void gpgol_bug (HWND parent, int code);
#define ERR_CRYPT_RESOLVER_FAILED 1
#define ERR_WANTS_SEND_MIME_BODY 2
#define ERR_WANTS_SEND_INLINE_BODY 3
#define ERR_INLINE_BODY_TO_BODY 4
#define ERR_INLINE_BODY_INV_STATE 5
#ifdef __cplusplus
}
#endif
#endif /*GPGOL_COMMON_H*/
diff --git a/src/common_indep.h b/src/common_indep.h
index adfc8bf..7332dbd 100644
--- a/src/common_indep.h
+++ b/src/common_indep.h
@@ -1,408 +1,407 @@
#ifndef COMMON_INDEP_H
#define COMMON_INDEP_H
/* common_indep.h - Common, platform indepentent routines used by GpgOL
* Copyright (C) 2005, 2007, 2008 g10 Code GmbH
* Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation 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
* 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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "xmalloc.h"
#ifdef HAVE_W32_SYSTEM
/* Not so independenent ;-) need this for logging HANDLE */
# include
#endif
/* The Registry key used by Gpg4win. */
#ifdef _WIN64
# define GPG4WIN_REGKEY_2 "Software\\Wow6432Node\\GNU\\GnuPG"
#else
# define GPG4WIN_REGKEY_2 "Software\\GNU\\GnuPG"
#endif
#ifdef _WIN64
# define GPG4WIN_REGKEY_3 "Software\\Wow6432Node\\Gpg4win"
#else
# define GPG4WIN_REGKEY_3 "Software\\Gpg4win"
#endif
/* Identifiers for the protocol. We use different one than those use
by gpgme. FIXME: We might want to define an unknown protocol to
non-null and define such a value also in gpgme. */
typedef enum
{
PROTOCOL_UNKNOWN = 0,
PROTOCOL_OPENPGP = 1000,
PROTOCOL_SMIME = 1001
}
protocol_t;
/* Possible options for the recipient dialog. */
enum
{
OPT_FLAG_TEXT = 2,
OPT_FLAG_FORCE = 4,
OPT_FLAG_CANCEL = 8
};
typedef enum
{
GPG_FMT_NONE = 0, /* do not encrypt attachments */
GPG_FMT_CLASSIC = 1, /* encrypt attachments without any encoding */
GPG_FMT_PGP_PEF = 2 /* use the PGP partioned encoding format (PEF) */
}
gpgol_format_t;
/* Type of a message. */
typedef enum
{
OPENPGP_NONE = 0,
OPENPGP_MSG,
OPENPGP_SIG,
OPENPGP_CLEARSIG,
OPENPGP_PUBKEY, /* Note, that this type is only partly supported */
OPENPGP_SECKEY /* Note, that this type is only partly supported */
}
openpgp_t;
/* The list of message types we support in GpgOL. */
typedef enum
{
MSGTYPE_UNKNOWN = 0,
MSGTYPE_SMIME, /* Original SMIME class. */
MSGTYPE_GPGOL,
MSGTYPE_GPGOL_MULTIPART_SIGNED,
MSGTYPE_GPGOL_MULTIPART_ENCRYPTED,
MSGTYPE_GPGOL_OPAQUE_SIGNED,
MSGTYPE_GPGOL_OPAQUE_ENCRYPTED,
MSGTYPE_GPGOL_CLEAR_SIGNED,
MSGTYPE_GPGOL_PGP_MESSAGE,
MSGTYPE_GPGOL_WKS_CONFIRMATION
}
msgtype_t;
typedef enum
{
ATTACHTYPE_UNKNOWN = 0,
ATTACHTYPE_MOSS = 1, /* The original MOSS message (ie. a
S/MIME or PGP/MIME message. */
ATTACHTYPE_FROMMOSS = 2, /* Attachment created from MOSS. */
ATTACHTYPE_MOSSTEMPL = 3, /* Attachment has been created in the
course of sending a message */
ATTACHTYPE_PGPBODY = 4, /* Attachment contains the original
PGP message body of PGP inline
encrypted messages. */
ATTACHTYPE_FROMMOSS_DEC = 5 /* A FROMMOSS attachment that has been
temporarily decrypted and needs to be
encrypted before it is written back
into storage. */
}
attachtype_t;
/* An object to collect information about one MAPI attachment. */
struct mapi_attach_item_s
{
int end_of_table; /* True if this is the last plus one entry of
the table. */
void *private_mapitable; /* Only for use by mapi_release_attach_table. */
int mapipos; /* The position which needs to be passed to
MAPI to open the attachment. -1 means that
there is no valid attachment. */
int method; /* MAPI attachment method. */
char *filename; /* Malloced filename of this attachment or NULL. */
/* Malloced string with the MIME attrib or NULL. Parameters are
stripped off thus a compare against "type/subtype" is
sufficient. */
char *content_type;
/* If not NULL the parameters of the content_type. */
const char *content_type_parms;
/* If not NULL the content_id */
char *content_id;
/* The attachment type from Property GpgOL Attach Type. */
attachtype_t attach_type;
};
typedef struct mapi_attach_item_s mapi_attach_item_t;
/* Passphrase callback structure. */
struct passphrase_cb_s
{
gpgme_key_t signer;
gpgme_ctx_t ctx;
char keyid[16+1];
char *user_id;
char *pass;
int opts;
int ttl; /* TTL of the passphrase. */
unsigned int decrypt_cmd:1; /* 1 = show decrypt dialog, otherwise secret key
selection. */
unsigned int hide_pwd:1;
unsigned int last_was_bad:1;
};
/* Global options - initialized to default by main.c. */
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
#ifdef __cplusplus
extern
#endif
struct
{
int enable_debug; /* Enable extra debug options. Values
larger than 1 increases the debug log
verbosity. */
int enable_smime; /* Enable S/MIME support. */
int passwd_ttl; /* Time in seconds the passphrase is stored. */
protocol_t default_protocol;/* The default protocol. */
int encrypt_default; /* Encrypt by default. */
int sign_default; /* Sign by default. */
int enc_format; /* Encryption format for attachments. */
char *default_key; /* The key we want to always encrypt to. */
int enable_default_key; /* Enable the use of DEFAULT_KEY. */
int preview_decrypt; /* Decrypt in preview window. */
int prefer_html; /* Prefer html in html/text alternatives. */
int body_as_attachment; /* Present encrypted message as attachment. */
- int mime_ui; /* Only for Addin. Use the PGP/MIME ui */
int inline_pgp; /* Only for Addin. Use Inline PGP by default. */
int autoresolve; /* Autresolve keys with --locate-keys. */
int reply_crypt; /* Only for Addin. Encrypt / Sign based on cryptostatus. */
int deprecation_shown; /* Flag to save if deprecation warning was shown */
/* The compatibility flags. */
struct
{
unsigned int no_msgcache:1;
unsigned int no_pgpmime:1;
unsigned int no_oom_write:1; /* Don't write using Outlooks object model. */
unsigned int no_preview_info:1; /* No preview info about PGP/MIME. */
unsigned int old_reply_hack: 1; /* See gpgmsg.cpp:decrypt. */
unsigned int auto_decrypt: 1; /* Try to decrypt when clicked. */
unsigned int no_attestation: 1; /* Don't create an attestation. */
unsigned int use_mwfmo: 1; /* Use MsgWaitForMultipleObjects. */
} compat;
/* The current git commit id. */
unsigned int git_commit;
/* The forms revision number of the binary. */
int forms_revision;
/* The stored number of the binary which showed the last announcement. */
int announce_number;
/* Disable message processing until restart. This is required to
implement message reverting as a perparation to remove GpgOL. */
int disable_gpgol;
} opt;
/* The state object used by b64_decode. */
struct b64_state_s
{
int idx;
unsigned char val;
int stop_seen;
int invalid_encoding;
};
typedef struct b64_state_s b64_state_t;
/* Bit values used for extra log file verbosity. Value 1 is reserved
to enable debug menu options. */
#define DBG_IOWORKER (1<<1)
#define DBG_IOWORKER_EXTRA (1<<2)
#define DBG_FILTER (1<<3)
#define DBG_FILTER_EXTRA (1<<4)
#define DBG_MEMORY (1<<5)
#define DBG_COMMANDS (1<<6)
#define DBG_MIME_PARSER (1<<7)
#define DBG_MIME_DATA (1<<8)
#define DBG_OOM (1<<9)
#define DBG_OOM_EXTRA (1<<10)
/* Macros to used in conditionals to enable debug output. */
#define debug_commands (opt.enable_debug & DBG_COMMANDS)
size_t qp_decode (char *buffer, size_t length, int *r_slbrk);
char *qp_encode (const char *input, size_t length, size_t* outlen);
void b64_init (b64_state_t *state);
size_t b64_decode (b64_state_t *state, char *buffer, size_t length);
char * b64_encode (const char *input, size_t length);
char *latin1_to_utf8 (const char *string);
char *mem2str (char *dest, const void *src, size_t n);
char *trim_spaces (char *string);
char *trim_trailing_spaces (char *string);
/* To avoid that a compiler optimizes certain memset calls away, these
macros may be used instead. */
#define wipememory2(_ptr,_set,_len) do { \
volatile char *_vptr=(volatile char *)(_ptr); \
size_t _vlen=(_len); \
while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \
} while(0)
#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len)
#define wipestring(_ptr) do { \
volatile char *_vptr=(volatile char *)(_ptr); \
while(*_vptr) { *_vptr=0; _vptr++; } \
} while(0)
#define debug_oom (opt.enable_debug & DBG_OOM)
#define debug_oom_extra (opt.enable_debug & DBG_OOM_EXTRA)
void log_debug (const char *fmt, ...) __attribute__ ((format (printf,1,2)));
void log_error (const char *fmt, ...) __attribute__ ((format (printf,1,2)));
void log_vdebug (const char *fmt, va_list a);
void log_debug_w32 (int w32err, const char *fmt,
...) __attribute__ ((format (printf,2,3)));
void log_error_w32 (int w32err, const char *fmt,
...) __attribute__ ((format (printf,2,3)));
void log_hexdump (const void *buf, size_t buflen, const char *fmt,
...) __attribute__ ((format (printf,3,4)));
#define log_oom if (opt.enable_debug & DBG_OOM) log_debug
#define log_oom_extra if (opt.enable_debug & DBG_OOM_EXTRA) log_debug
#define log_mime_parser if (opt.enable_debug & DBG_MIME_PARSER) log_debug
#define log_mime_data if (opt.enable_debug & DBG_MIME_DATA) log_debug
#define gpgol_release(X) \
{ \
if (X && opt.enable_debug & DBG_OOM_EXTRA) \
{ \
log_debug ("%s:%s: Object: %p released ref: %lu \n", \
SRCNAME, __func__, X, X->Release()); \
} \
else if (X) \
{ \
X->Release(); \
} \
}
const char *log_srcname (const char *s);
#define SRCNAME log_srcname (__FILE__)
#define TRACEPOINT log_debug ("%s:%s:%d: tracepoint\n", \
SRCNAME, __func__, __LINE__);
const char *get_log_file (void);
void set_log_file (const char *name);
void set_default_key (const char *name);
/*-- Convenience macros. -- */
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
#define DIMof(type,member) DIM(((type *)0)->member)
/*-- Macros to replace ctype ones to avoid locale problems. --*/
#define spacep(p) (*(p) == ' ' || *(p) == '\t')
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
#define hexdigitp(a) (digitp (a) \
|| (*(a) >= 'A' && *(a) <= 'F') \
|| (*(a) >= 'a' && *(a) <= 'f'))
/* Note this isn't identical to a C locale isspace() without \f and
\v, but works for the purposes used here. */
#define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
/* The atoi macros assume that the buffer has only valid digits. */
#define atoi_1(p) (*(p) - '0' )
#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
#define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2))
#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
#define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a'))
/***** Inline functions. ****/
/* Return true if LINE consists only of white space (up to and
including the LF). */
static inline int
trailing_ws_p (const char *line)
{
for ( ; *line && *line != '\n'; line++)
if (*line != ' ' && *line != '\t' && *line != '\r')
return 0;
return 1;
}
/* An strcmp variant with the compare ending at the end of B. */
static inline int
tagcmp (const char *a, const char *b)
{
return strncmp (a, b, strlen (b));
}
#ifdef HAVE_W32_SYSTEM
extern HANDLE log_mutex;
#endif
/***** Missing functions. ****/
#ifndef HAVE_STPCPY
static inline char *
_gpgol_stpcpy (char *a, const char *b)
{
while (*b)
*a++ = *b++;
*a = 0;
return a;
}
#define stpcpy(a,b) _gpgol_stpcpy ((a), (b))
#endif /*!HAVE_STPCPY*/
#ifdef _WIN64
#define SIZE_T_FORMAT "%I64u"
#else
# ifdef HAVE_W32_SYSTEM
# define SIZE_T_FORMAT "%u"
# else
# define SIZE_T_FORMAT "%lu"
# endif
#endif
/* The length of the boundary - the buffer needs to be allocated one
byte larger. */
#define BOUNDARYSIZE 20
char *generate_boundary (char *buffer);
#ifdef __cplusplus
}
#endif
#endif // COMMON_INDEP_H
diff --git a/src/dialogs.h b/src/dialogs.h
index be662ca..217b0d0 100644
--- a/src/dialogs.h
+++ b/src/dialogs.h
@@ -1,140 +1,78 @@
/* dialogs.h
Resouces IDs for the dialogs.
*/
#ifndef DIALOGS_H
#define DIALOGS_H
-/* Ids used for bitmaps. There is some magic in the identifiers: In
- the code we only use the first ID value and add 1 to find the mask.
- */
-#define IDB_ENCRYPT_16 0x1000
-#define IDB_ENCRYPT_16M 0x1001
-
-#define IDB_SIGN_16 0x1010
-#define IDB_SIGN_16M 0x1011
-
-#define IDB_KEY_MANAGER_16 0x1020
-#define IDB_KEY_MANAGER_16M 0x1021
-
-#define IDB_DECRYPT_16 0x1030
-#define IDB_DECRYPT_16M 0x1031
-
-#define IDB_VERIFY_16 0x1040
-#define IDB_VERIFY_16M 0x1041
-
-#define IDB_DECRYPT_VERIFY_16 0x1050
-#define IDB_DECRYPT_VERIFY_16M 0x1051
-
#define IDB_LOGO 0x1060
/* Ids for the extended options dialog. */
#define IDD_EXT_OPTIONS 0x4110
#define IDC_T_DEBUG_LOGFILE 0x4120
#define IDC_DEBUG_LOGFILE 0x4130
-/* Ids for the recipient selection dialog. */
-#define IDD_ENC 0x4210
-#define IDC_ENC_RSET1 0x4220
-#define IDC_ENC_RSET2_T 0x4230
-#define IDC_ENC_RSET2 0x4240
-#define IDC_ENC_NOTFOUND_T 0x4250
-#define IDC_ENC_NOTFOUND 0x4260
-
-
-/* Ids for the two decryption dialogs. */
-#define IDD_DEC 0x4310
-#define IDD_DECEXT 0x4320
-#define IDC_DEC_KEYLIST 0x4330
-#define IDC_DEC_HINT 0x4340
-#define IDC_DEC_PASSINF 0x4350
-#define IDC_DEC_PASS 0x4360
-#define IDC_DEC_HIDE 0x4370
-#define IDC_DECEXT_RSET_T 0x4380
-#define IDC_DECEXT_RSET 0x4390
-#define IDC_DECEXT_KEYLIST 0x43A0
-#define IDC_DECEXT_HINT 0x43B0
-#define IDC_DECEXT_PASSINF 0x43C0
-#define IDC_DECEXT_PASS 0x43D0
-#define IDC_DECEXT_HIDE 0x43E0
-
-
-/* Ids for the verification dialog. */
-#define IDD_VRY 0x4410
-#define IDC_VRY_TIME_T 0x4420
-#define IDC_VRY_TIME 0x4430
-#define IDC_VRY_PKALGO_T 0x4440
-#define IDC_VRY_PKALGO 0x4450
-#define IDC_VRY_KEYID_T 0x4460
-#define IDC_VRY_KEYID 0x4470
-#define IDC_VRY_STATUS 0x4480
-#define IDC_VRY_ISSUER_T 0x4490
-#define IDC_VRY_ISSUER 0x44A0
-#define IDC_VRY_AKALIST_T 0x44B0
-#define IDC_VRY_AKALIST 0x44C0
-#define IDC_VRY_HINT 0x44D0
-
/* Ids used for the main config dialog. */
#define IDD_GPG_OPTIONS 0x5000
#define IDD_ADDIN_OPTIONS 0x5001
#define IDC_TIME_PHRASES 0x5010
#define IDC_ENCRYPT_DEFAULT 0x5020
#define IDC_SIGN_DEFAULT 0x5030
#define IDC_ENCRYPT_WITH_STANDARD_KEY 0x5040
#define IDC_OPENPGP_DEFAULT 0x5050
#define IDC_SMIME_DEFAULT 0x5060
#define IDC_GPG_OPTIONS 0x5070
#define IDC_ADDIN_OPTIONS 0x5071
#define IDC_BITMAP 0x5080
#define IDC_VERSION_INFO 0x5090
#define IDC_ENCRYPT_TO 0x50A0
#define IDC_ENABLE_SMIME 0x50B0
#define IDC_PREVIEW_DECRYPT 0x50C0
#define IDC_PREFER_HTML 0x50D0
#define IDC_G_GENERAL 0x50E0
#define IDC_G_SEND 0x50F0
#define IDC_G_RECV 0x5100
#define IDC_BODY_AS_ATTACHMENT 0x5110
#define IDC_GPG_CONF 0x5120
#define IDC_G10CODE_STRING 0x5130
#define IDC_GPG4WIN_STRING 0x5131
#define IDC_START_CERTMAN 0x5132
#define IDC_MIME_UI 0x5133
#define IDC_INLINE_PGP 0x5134
#define IDC_AUTORRESOLVE 0x5135
#define IDC_REPLYCRYPT 0x5136
/* Ids for PNG Images */
#define IDI_ENCRYPT_16_PNG 0x6000
#define IDI_ENCRYPT_48_PNG 0x6010
#define IDI_DECRYPT_16_PNG 0x6020
#define IDI_DECRYPT_48_PNG 0x6030
#define IDI_ENCSIGN_FILE_48_PNG 0x6050
#define IDI_SIGN_48_PNG 0x6060
#define IDI_VERIFY_48_PNG 0x6070
#define IDI_EMBLEM_WARNING_64_PNG 0x6071
#define IDI_EMBLEM_QUESTION_64_PNG 0x6074
#define IDI_SIGN_ENCRYPT_40_PNG 0x6075
#define IDI_ENCRYPT_20_PNG 0x6076
#define IDI_SIGN_20_PNG 0x6077
#define IDI_GPGOL_LOCK_ICON 0x6078
/* Status icons */
#define ENCRYPT_ICON_OFFSET 0x10
#define IDI_LEVEL_0 0x6080
#define IDI_LEVEL_1 0x6081
#define IDI_LEVEL_2 0x6082
#define IDI_LEVEL_3 0x6083
#define IDI_LEVEL_4 0x6084
#define IDI_LEVEL_0_ENC (IDI_LEVEL_0 + ENCRYPT_ICON_OFFSET)
#define IDI_LEVEL_1_ENC (IDI_LEVEL_1 + ENCRYPT_ICON_OFFSET)
#define IDI_LEVEL_2_ENC (IDI_LEVEL_2 + ENCRYPT_ICON_OFFSET)
#define IDI_LEVEL_3_ENC (IDI_LEVEL_3 + ENCRYPT_ICON_OFFSET)
#define IDI_LEVEL_4_ENC (IDI_LEVEL_4 + ENCRYPT_ICON_OFFSET)
#endif /*DIALOGS_H*/
diff --git a/src/dialogs.rc b/src/dialogs.rc
index 4ee8f3b..5687041 100644
--- a/src/dialogs.rc
+++ b/src/dialogs.rc
@@ -1,362 +1,194 @@
/* dialogs.rc - GpgOL dialog resources. -*- c -*-
* Copyright (C) 2004, 2005, 2006, 2007 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 .
*/
#include "dialogs.h"
#include "config.h"
#include "afxres.h"
IDI_ENCRYPT_16_PNG RCDATA "icons/encrypt-16.png"
IDI_ENCRYPT_48_PNG RCDATA "icons/encrypt-48.png"
IDI_SIGN_48_PNG RCDATA "icons/sign-48.png"
IDI_DECRYPT_16_PNG RCDATA "icons/decrypt-16.png"
IDI_DECRYPT_48_PNG RCDATA "icons/decrypt-48.png"
IDI_VERIFY_48_PNG RCDATA "icons/verify-48.png"
IDI_ENCSIGN_FILE_48_PNG RCDATA "icons/encrypt-sign-file-48.png"
IDI_LEVEL_0_ENC RCDATA "icons/level-0-enc.png"
IDI_LEVEL_1_ENC RCDATA "icons/level-1-enc.png"
/* We use the same icon for level 2 and 3 */
IDI_LEVEL_2_ENC RCDATA "icons/level-3-enc.png"
IDI_LEVEL_3_ENC RCDATA "icons/level-3-enc.png"
IDI_LEVEL_4_ENC RCDATA "icons/level-4-enc.png"
IDI_LEVEL_0 RCDATA "icons/level-0.png"
IDI_LEVEL_1 RCDATA "icons/level-1.png"
IDI_LEVEL_2 RCDATA "icons/level-3.png"
IDI_LEVEL_3 RCDATA "icons/level-3.png"
IDI_LEVEL_4 RCDATA "icons/level-4.png"
IDI_SIGN_ENCRYPT_40_PNG RCDATA "icons/sign-enc-40.png"
IDI_ENCRYPT_20_PNG RCDATA "icons/encrypt-20.png"
IDI_SIGN_20_PNG RCDATA "icons/sign-20.png"
IDI_GPGOL_LOCK_ICON ICON DISCARDABLE "icons/lock.ico"
IDB_LOGO BITMAP DISCARDABLE "icons/logo.bmp"
IDD_GPG_OPTIONS DIALOG DISCARDABLE 0, 0, 266, 274
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "GpgOL"
FONT 8, "MS Sans Serif"
BEGIN
/* General options box. */
GROUPBOX "general-options", IDC_G_GENERAL,
9, 9, 250, 25
CONTROL "enable-smime", IDC_ENABLE_SMIME,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 19, 215, 10
/* Send options box. */
GROUPBOX "send-options", IDC_G_SEND,
9, 40, 250, 38
CONTROL "encrypt-by-default", IDC_ENCRYPT_DEFAULT,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 50, 215, 10
CONTROL "sign-by-default", IDC_SIGN_DEFAULT,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 61, 215, 10
/* Receive options box. */
GROUPBOX "recv-options", IDC_G_RECV,
9, 82, 250, 36
/* We have no reliable way to detect the preview window, thus we
don't show this option. */
/* CONTROL "preview-decrypt", IDC_PREVIEW_DECRYPT, */
/* "Button", BS_AUTOCHECKBOX | WS_TABSTOP, */
/* 24, 114, 215, 10 */
CONTROL "prefer-html", IDC_PREFER_HTML,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 94, 215, 10
CONTROL "body-as-attachment", IDC_BODY_AS_ATTACHMENT,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 103, 215, 10
/* Stuff at the lower left corner. */
LTEXT "GpgOL by g10 Code GmbH", IDC_G10CODE_STRING,
8, 229, 100, 8
LTEXT "Version x ", IDC_VERSION_INFO,
8, 240, 100, 9
/* No more logo due to problems with the background colour. */
/* CONTROL IDB_BANNER, IDC_BITMAP, */
/* "Static", SS_BITMAP | SS_REALSIZEIMAGE, */
/* 8, 212, 150, 64 */
PUSHBUTTON "advanced", IDC_GPG_OPTIONS,
130, 240, 50, 14
PUSHBUTTON "gpgconf", IDC_GPG_CONF,
190, 240, 70, 14
END
IDD_EXT_OPTIONS DIALOG DISCARDABLE 0, 0, 155, 70
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
CAPTION "GpgOL - Debug Options"
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "debug-logfile", IDC_T_DEBUG_LOGFILE,
8, 10, 122, 8
EDITTEXT IDC_DEBUG_LOGFILE,
8, 20, 138, 12, ES_AUTOHSCROLL
DEFPUSHBUTTON "&OK", IDOK,
90, 50, 50, 14
END
-/*
- The dialog to select recipient keys
- */
-IDD_ENC DIALOG DISCARDABLE 0, 0, 332, 215
-STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "GpgOL - Select Recipients"
-FONT 8, "MS Sans Serif"
-BEGIN
- CONTROL "List1", IDC_ENC_RSET1,
- "SysListView32", LVS_REPORT | LVS_NOSORTHEADER |
- WS_BORDER | WS_TABSTOP,
- 8, 4, 314, 92
-
- LTEXT "Ausgewählte Empfänger:", IDC_ENC_RSET2_T,
- 8, 98, 130, 8
-
- CONTROL "List2", IDC_ENC_RSET2,
- "SysListView32", LVS_REPORT | LVS_NOSORTHEADER |
- WS_BORDER | WS_TABSTOP,
- 8, 110, 313, 49
-
- LTEXT "Recipient which were NOT found", IDC_ENC_NOTFOUND_T,
- 8, 161, 128, 8
-
- LISTBOX IDC_ENC_NOTFOUND,
- 8, 170, 313, 22, LBS_SORT | LBS_NOINTEGRALHEIGHT |
- WS_VSCROLL | WS_TABSTOP
-
- DEFPUSHBUTTON "&OK", IDOK,
- 221, 196, 50, 14
-
- PUSHBUTTON "&Cancel-btn", IDCANCEL,
- 273, 196, 50, 14
-END
-
-
-/*
- The decryption dialog used to select the secret key.
-*/
-IDD_DEC DIALOG DISCARDABLE 0, 0, 225, 101
-STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "Secret Key Selection"
-FONT 8, "MS Sans Serif"
-BEGIN
- COMBOBOX IDC_DEC_KEYLIST,
- 9, 7, 209, 58, CBS_DROPDOWNLIST | WS_VSCROLL |
- WS_TABSTOP
-
- LTEXT "", IDC_DEC_HINT,
- 9, 25, 201, 10
-
- LTEXT "enter-passphrase", IDC_DEC_PASSINF,
- 9, 37, 158, 8
-
- EDITTEXT IDC_DEC_PASS,
- 9, 51, 207, 12, ES_PASSWORD | ES_AUTOHSCROLL
-
- CONTROL "&Hide Typing", IDC_DEC_HIDE,
- "Button", BS_AUTOCHECKBOX | WS_TABSTOP,
- 138, 64, 81, 10
-
- DEFPUSHBUTTON "&OK", IDOK,
- 115, 83, 50, 14
-
- PUSHBUTTON "&Cancel-btn", IDCANCEL,
- 167, 83, 50, 14
-END
-
-
-/*
- The extended decryption dialog used to select the secret key.
-*/
-IDD_DECEXT DIALOG DISCARDABLE 0, 0, 207, 134
-STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "GpgOL - Secret Key Selection"
-FONT 8, "MS Sans Serif"
-BEGIN
- LTEXT "encrypted-to",IDC_DECEXT_RSET_T,
- 7, 4, 128, 8
-
- LISTBOX IDC_DECEXT_RSET,
- 7, 16, 193, 25, LBS_NOINTEGRALHEIGHT |
- WS_VSCROLL | WS_TABSTOP
-
- COMBOBOX IDC_DECEXT_KEYLIST,
- 8, 54, 191, 14, CBS_DROPDOWN | CBS_SORT |
- WS_DISABLED | WS_VSCROLL | WS_TABSTOP
-
- LTEXT "", IDC_DECEXT_HINT,
- 7, 73, 193, 8
-
- LTEXT "enter-passphrase", IDC_DECEXT_PASSINF,
- 7, 85, 158, 8
-
- EDITTEXT IDC_DECEXT_PASS,
- 7, 96, 192, 12, ES_PASSWORD | ES_AUTOHSCROLL
-
- CONTROL "hide-typing", IDC_DECEXT_HIDE,
- "Button", BS_AUTOCHECKBOX | WS_TABSTOP,
- 122, 108, 81, 10
-
- DEFPUSHBUTTON "OK", IDOK,
- 98, 125, 50, 14
-
- PUSHBUTTON "&Cancel-btn", IDCANCEL,
- 149, 125, 50, 14
-END
-
-
-/*
- The dialog to display verification results.
-*/
-IDD_VRY DIALOG DISCARDABLE 0, 0, 253, 116
-STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
-CAPTION "GpgOL - Signature Verification Result"
-FONT 8, "MS Sans Serif"
-BEGIN
- LTEXT "made-at", IDC_VRY_TIME_T,
- 3, 6, 50, 8
-
- EDITTEXT IDC_VRY_TIME,
- 55, 4, 76, 12, ES_AUTOHSCROLL | ES_READONLY
-
- LTEXT "using", IDC_VRY_PKALGO_T,
- 132, 6, 18, 8
-
- EDITTEXT IDC_VRY_PKALGO,
- 152, 4, 20, 12, ES_AUTOHSCROLL | ES_READONLY
-
- LTEXT "key-id", IDC_VRY_KEYID_T,
- 174, 7, 50, 8
-
- EDITTEXT IDC_VRY_KEYID,
- 196, 4, 51, 12, ES_AUTOHSCROLL | ES_READONLY
-
- EDITTEXT IDC_VRY_STATUS,
- 3, 29, 92, 12, ES_AUTOHSCROLL | ES_READONLY
-
- LTEXT "from", IDC_VRY_ISSUER_T,
- 98, 31, 14, 8
-
- EDITTEXT IDC_VRY_ISSUER,
- 117, 29, 131, 13, ES_AUTOHSCROLL | ES_READONLY
-
- LTEXT "aka", IDC_VRY_AKALIST_T,
- 3, 47, 56, 8
-
- LISTBOX IDC_VRY_AKALIST,
- 3, 56, 245, 25, LBS_SORT | LBS_NOINTEGRALHEIGHT |
- WS_VSCROLL | WS_TABSTOP
-
- LTEXT "", IDC_VRY_HINT,
- 3, 83, 245, 10
-
- DEFPUSHBUTTON "&OK", IDOK,
- 198, 96, 50, 14
-END
-
IDD_ADDIN_OPTIONS DIALOGEX DISCARDABLE 300, 300, 286, 190
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_SHELLFONT | DS_SETFONT
CAPTION "GpgOL"
FONT 8, "MS Shell Dlg"
BEGIN
/* General options box. */
-#ifdef MIME_SEND
GROUPBOX "general-options", IDC_G_GENERAL,
9, 9, 270, 25
-#else
- GROUPBOX "general-options", IDC_G_GENERAL,
- 9, 9, 270, 35
-#endif
CONTROL "enable-smime", IDC_ENABLE_SMIME,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 19, 235, 10
-#ifndef MIME_SEND
- CONTROL "enable-mime-ui", IDC_MIME_UI,
- "Button", BS_AUTOCHECKBOX | WS_TABSTOP,
- 24, 29, 235, 10
-#endif
/* Send options box. */
GROUPBOX "send-options", IDC_G_SEND,
9, 50, 270, 67
CONTROL "encrypt-by-default", IDC_ENCRYPT_DEFAULT,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 60, 235, 10
CONTROL "sign-by-default", IDC_SIGN_DEFAULT,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 71, 235, 10
CONTROL "inline-pgp", IDC_INLINE_PGP,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 82, 235, 10
CONTROL "replycrypt", IDC_REPLYCRYPT,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 93, 235, 10
CONTROL "autoresolve", IDC_AUTORRESOLVE,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP,
24, 104, 235, 10
/* Stuff at the lower left corner. */
CONTROL IDB_LOGO, IDC_BITMAP,
"Static", SS_BITMAP | SS_REALSIZEIMAGE,
10, 125, 128, 80
LTEXT "Version x ", IDC_VERSION_INFO,
10, 175, 100, 9
PUSHBUTTON "advanced", IDC_GPG_OPTIONS,
180, 140, 90, 14
PUSHBUTTON "gpgconf", IDC_GPG_CONF,
180, 155, 90, 14
DEFPUSHBUTTON "&OK", IDOK,
180, 170, 90, 14
END
diff --git a/src/gpgoladdin.cpp b/src/gpgoladdin.cpp
index c163647..0cadbc8 100644
--- a/src/gpgoladdin.cpp
+++ b/src/gpgoladdin.cpp
@@ -1,1489 +1,1129 @@
/* gpgoladdin.cpp - Connect GpgOL to Outlook as an addin
* Copyright (C) 2013 Intevation GmbH
* 2015 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation 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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include "common.h"
#include "gpgoladdin.h"
#include "mymapi.h"
#include "mymapitags.h"
#include "engine.h"
#include "engine-assuan.h"
#include "mapihelp.h"
#include "oomhelp.h"
#include "olflange.h"
#include "gpgol-ids.h"
#include "ribbon-callbacks.h"
#include "eventsinks.h"
#include "eventsink.h"
#include "windowmessages.h"
#include "mail.h"
#include "addin-options.h"
#include
#include
#define ICON_SIZE_LARGE 32
#define ICON_SIZE_NORMAL 16
/* We use UTF-8 internally. */
#undef _
#define _(a) utf8_gettext (a)
ULONG addinLocks = 0;
bool can_unload = false;
-/* Invalidating the interface does not take a nice effect so we store
- this option in a global variable. */
-bool use_mime_ui = false;
-
static std::list g_ribbon_uis;
static GpgolAddin * addin_instance = NULL;
/* This is the main entry point for the addin
Outlook uses this function to query for an Object implementing
the IClassFactory interface.
*/
STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppvObj)
{
if (!ppvObj)
return E_POINTER;
*ppvObj = NULL;
if (rclsid != CLSID_GPGOL)
return CLASS_E_CLASSNOTAVAILABLE;
/* Let the factory give the requested interface. */
GpgolAddinFactory* factory = new GpgolAddinFactory();
if (!factory)
return E_OUTOFMEMORY;
HRESULT hr = factory->QueryInterface (riid, ppvObj);
if(FAILED(hr))
{
*ppvObj = NULL;
delete factory;
}
return hr;
}
STDAPI DllCanUnloadNow()
{
/* This is called regularly to check if memory can be freed
by unloading the dll. The following unload will not call
any addin methods like disconnect etc. It will just
unload the Library. Any callbacks will become invalid.
So we _only_ say it's ok to unload if we were disconnected.
For the epic story behind the next line see GnuPG-Bug-Id 1837 */
return can_unload ? S_OK : S_FALSE;
}
/* Class factory */
STDMETHODIMP GpgolAddinFactory::QueryInterface (REFIID riid, LPVOID* ppvObj)
{
HRESULT hr = S_OK;
*ppvObj = NULL;
if ((IID_IUnknown == riid) || (IID_IClassFactory == riid))
*ppvObj = static_cast(this);
else
{
hr = E_NOINTERFACE;
LPOLESTR sRiid = NULL;
StringFromIID (riid, &sRiid);
/* Should not happen */
log_debug ("GpgolAddinFactory queried for unknown interface: %S \n", sRiid);
}
if (*ppvObj)
((LPUNKNOWN)*ppvObj)->AddRef();
return hr;
}
/* This actually creates the instance of our COM object */
STDMETHODIMP GpgolAddinFactory::CreateInstance (LPUNKNOWN punk, REFIID riid,
LPVOID* ppvObj)
{
(void)punk;
*ppvObj = NULL;
GpgolAddin* obj = GpgolAddin::get_instance();
if (NULL == obj)
return E_OUTOFMEMORY;
HRESULT hr = obj->QueryInterface (riid, ppvObj);
if (FAILED(hr))
{
LPOLESTR sRiid = NULL;
StringFromIID (riid, &sRiid);
fprintf(stderr, "failed to create instance for: %S", sRiid);
}
return hr;
}
/* GpgolAddin definition */
/* Constructor of GpgolAddin
Initializes members and creates the interface objects for the new
context. Does the DLL initialization if it has not been done
before.
The ref count is set by the factory after creation.
*/
GpgolAddin::GpgolAddin (void) : m_lRef(0),
m_application(nullptr),
m_addin(nullptr),
m_applicationEventSink(nullptr),
m_explorersEventSink(nullptr),
m_disabled(false),
m_hook(nullptr)
{
read_options ();
- use_mime_ui = opt.mime_ui;
/* RibbonExtender is it's own object to avoid the pitfalls of
multiple inheritance
*/
m_ribbonExtender = new GpgolRibbonExtender();
}
GpgolAddin::~GpgolAddin (void)
{
if (m_disabled)
{
return;
}
log_debug ("%s:%s: Releasing Application Event Sink;",
SRCNAME, __func__);
gpgol_release (m_explorersEventSink);
gpgol_release (m_applicationEventSink);
engine_deinit ();
write_options ();
UnhookWindowsHookEx (m_hook);
addin_instance = NULL;
log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__);
}
STDMETHODIMP
GpgolAddin::QueryInterface (REFIID riid, LPVOID* ppvObj)
{
HRESULT hr = S_OK;
*ppvObj = NULL;
if (m_disabled)
return E_NOINTERFACE;
if ((riid == IID_IUnknown) || (riid == IID_IDTExtensibility2) ||
(riid == IID_IDispatch))
{
*ppvObj = (LPUNKNOWN) this;
}
else if (riid == IID_IRibbonExtensibility)
{
return m_ribbonExtender->QueryInterface (riid, ppvObj);
}
else
{
hr = E_NOINTERFACE;
#if 0
LPOLESTR sRiid = NULL;
StringFromIID(riid, &sRiid);
log_debug ("%s:%s: queried for unimplmented interface: %S",
SRCNAME, __func__, sRiid);
#endif
}
if (*ppvObj)
((LPUNKNOWN)*ppvObj)->AddRef();
return hr;
}
static void
addGpgOLToReg (const std::string &path)
{
HKEY h;
int err = RegOpenKeyEx (HKEY_CURRENT_USER, path.c_str(), 0,
KEY_ALL_ACCESS, &h);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: no DoNotDisableAddinList entry '%s' creating it",
SRCNAME, __func__, path.c_str ());
err = RegCreateKeyEx (HKEY_CURRENT_USER, path.c_str (), 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&h, NULL);
}
if (err != ERROR_SUCCESS)
{
log_error ("%s:%s: failed to create key.",
SRCNAME, __func__);
return;
}
DWORD type;
err = RegQueryValueEx (h, GPGOL_PROGID, NULL, &type, NULL, NULL);
if (err == ERROR_SUCCESS)
{
log_debug ("%s:%s: Found gpgol reg key. Leaving it unchanged.",
SRCNAME, __func__);
RegCloseKey (h);
return;
}
// No key exists. Create one.
DWORD dwTemp = 1;
err = RegSetValueEx (h, GPGOL_PROGID, 0, REG_DWORD, (BYTE*)&dwTemp, 4);
RegCloseKey (h);
if (err != ERROR_SUCCESS)
{
log_error ("%s:%s: failed to set registry value.",
SRCNAME, __func__);
}
else
{
log_debug ("%s:%s: added gpgol to %s",
SRCNAME, __func__, path.c_str ());
}
}
/* This is a bit evil as we basically disable outlooks resiliency
for us. But users are still able to manually disable the addon
or change the donotdisable setting to zero and we won't change
it.
It has been much requested by users that we do this automatically.
*/
static void
setupDoNotDisable ()
{
std::string path = "Software\\Microsoft\\Office\\";
path += std::to_string (g_ol_version_major);
path += ".0\\Outlook\\Resiliency\\DoNotDisableAddinList";
addGpgOLToReg (path);
path = "Software\\Microsoft\\Office\\";
path += std::to_string (g_ol_version_major);
path += ".0\\Outlook\\Resiliency\\AddinList";
addGpgOLToReg (path);
}
STDMETHODIMP
GpgolAddin::OnConnection (LPDISPATCH Application, ext_ConnectMode ConnectMode,
LPDISPATCH AddInInst, SAFEARRAY ** custom)
{
(void)custom;
char* version;
log_debug ("%s:%s: this is GpgOL %s\n",
SRCNAME, __func__, PACKAGE_VERSION);
can_unload = false;
m_application = Application;
m_application->AddRef();
m_addin = AddInInst;
version = get_oom_string (Application, "Version");
log_debug ("%s:%s: using GPGME %s\n",
SRCNAME, __func__, gpgme_check_version (NULL));
log_debug ("%s:%s: in Outlook %s\n",
SRCNAME, __func__, version);
g_ol_version_major = atoi (version);
if (!version || !strlen (version) ||
(strncmp (version, "14", 2) &&
strncmp (version, "15", 2) &&
strncmp (version, "16", 2)))
{
m_disabled = true;
log_debug ("%s:%s: Disabled addin for unsupported version.",
SRCNAME, __func__);
xfree (version);
return S_OK;
}
engine_init ();
setupDoNotDisable ();
if (ConnectMode != ext_cm_Startup)
{
OnStartupComplete (custom);
}
return S_OK;
}
STDMETHODIMP
GpgolAddin::OnDisconnection (ext_DisconnectMode RemoveMode,
SAFEARRAY** custom)
{
(void)custom;
(void)RemoveMode;
log_debug ("%s:%s: cleaning up GpgolAddin object;",
SRCNAME, __func__);
/* Doing the wipe in the dtor is too late. Outlook
does not allow us any OOM calls then and only returns
"Unexpected error" in that case. Weird. */
if (Mail::close_all_mails ())
{
MessageBox (NULL,
"Failed to remove plaintext from at least one message.\n\n"
"Until GpgOL is activated again it is possible that the "
"plaintext of messages decrypted in this Session is saved "
"or transfered back to your mailserver.",
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
}
write_options();
can_unload = true;
return S_OK;
}
STDMETHODIMP
GpgolAddin::OnAddInsUpdate (SAFEARRAY** custom)
{
(void)custom;
return S_OK;
}
static void
check_html_preferred()
{
/* Check if HTML Mail should be enabled. */
HKEY h;
std::string path = "Software\\Microsoft\\Office\\";
path += std::to_string (g_ol_version_major);
path += ".0\\Outlook\\Options\\Mail";
opt.prefer_html = 1;
int err = RegOpenKeyEx (HKEY_CURRENT_USER, path.c_str() , 0, KEY_READ, &h);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: no mail options under %s",
SRCNAME, __func__, path.c_str());
return;
}
else
{
DWORD type;
err = RegQueryValueEx (h, "ReadAsPlain", NULL, &type, NULL, NULL);
if (err != ERROR_SUCCESS || type != REG_DWORD)
{
log_debug ("%s:%s: No type or key for ReadAsPlain",
SRCNAME, __func__);
return;
}
else
{
DWORD data;
DWORD size = sizeof (DWORD);
err = RegQueryValueEx (h, "ReadAsPlain", NULL, NULL, (LPBYTE)&data,
&size);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: Failed to find out ReadAsPlain",
SRCNAME, __func__);
return;
}
opt.prefer_html = data ? 0 : 1;
return;
}
}
}
static LPDISPATCH
install_explorer_sinks (LPDISPATCH application)
{
LPDISPATCH explorers = get_oom_object (application, "Explorers");
if (!explorers)
{
log_error ("%s:%s: No explorers object",
SRCNAME, __func__);
return nullptr;
}
int count = get_oom_int (explorers, "Count");
for (int i = 1; i <= count; i++)
{
std::string item = "Item(";
item += std::to_string (i) + ")";
LPDISPATCH explorer = get_oom_object (explorers, item.c_str());
if (!explorer)
{
log_error ("%s:%s: failed to get explorer %i",
SRCNAME, __func__, i);
continue;
}
/* Explorers delete themself in the close event of the explorer. */
LPDISPATCH sink = install_ExplorerEvents_sink (explorer);
if (!sink)
{
log_error ("%s:%s: failed to create eventsink for explorer %i",
SRCNAME, __func__, i);
}
else
{
log_oom_extra ("%s:%s: created sink %p for explorer %i",
SRCNAME, __func__, sink, i);
}
add_explorer (explorer);
gpgol_release (explorer);
}
/* Now install the event sink to handle new explorers */
return install_ExplorersEvents_sink (explorers);
}
STDMETHODIMP
GpgolAddin::OnStartupComplete (SAFEARRAY** custom)
{
(void)custom;
TRACEPOINT;
if (!create_responder_window())
{
log_error ("%s:%s: Failed to create the responder window;",
SRCNAME, __func__);
}
if (!m_application)
{
/* Should not happen as OnConnection should be called before */
log_error ("%s:%s: no application set;",
SRCNAME, __func__);
return E_NOINTERFACE;
}
if (!(m_hook = create_message_hook ()))
{
log_error ("%s:%s: Failed to create messagehook. ",
SRCNAME, __func__);
}
/* Set up categories */
const char *decCategory = _("GpgOL: Encrypted Message");
const char *verifyCategory = _("GpgOL: Trusted Sender Address");
ensure_category_exists (m_application, decCategory, 8);
ensure_category_exists (m_application, verifyCategory, 5);
install_forms ();
m_applicationEventSink = install_ApplicationEvents_sink (m_application);
m_explorersEventSink = install_explorer_sinks (m_application);
check_html_preferred ();
return S_OK;
}
STDMETHODIMP
GpgolAddin::OnBeginShutdown (SAFEARRAY * * custom)
{
(void)custom;
TRACEPOINT;
return S_OK;
}
STDMETHODIMP
GpgolAddin::GetTypeInfoCount (UINT *r_count)
{
*r_count = 0;
TRACEPOINT; /* Should not happen */
return S_OK;
}
STDMETHODIMP
GpgolAddin::GetTypeInfo (UINT iTypeInfo, LCID lcid,
LPTYPEINFO *r_typeinfo)
{
(void)iTypeInfo;
(void)lcid;
(void)r_typeinfo;
TRACEPOINT; /* Should not happen */
return S_OK;
}
STDMETHODIMP
GpgolAddin::GetIDsOfNames (REFIID riid, LPOLESTR *rgszNames,
UINT cNames, LCID lcid,
DISPID *rgDispId)
{
(void)riid;
(void)rgszNames;
(void)cNames;
(void)lcid;
(void)rgDispId;
TRACEPOINT; /* Should not happen */
return E_NOINTERFACE;
}
STDMETHODIMP
GpgolAddin::Invoke (DISPID dispid, REFIID riid, LCID lcid,
WORD flags, DISPPARAMS *parms, VARIANT *result,
EXCEPINFO *exepinfo, UINT *argerr)
{
USE_INVOKE_ARGS
TRACEPOINT; /* Should not happen */
return DISP_E_MEMBERNOTFOUND;
}
/* Definition of GpgolRibbonExtender */
GpgolRibbonExtender::GpgolRibbonExtender (void) : m_lRef(0)
{
}
GpgolRibbonExtender::~GpgolRibbonExtender (void)
{
log_debug ("%s:%s: cleaning up GpgolRibbonExtender object;",
SRCNAME, __func__);
log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__);
}
STDMETHODIMP
GpgolRibbonExtender::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
HRESULT hr = S_OK;
*ppvObj = NULL;
if ((riid == IID_IUnknown) || (riid == IID_IRibbonExtensibility) ||
(riid == IID_IDispatch))
{
*ppvObj = (LPUNKNOWN) this;
}
else
{
LPOLESTR sRiid = NULL;
StringFromIID (riid, &sRiid);
log_debug ("%s:%s: queried for unknown interface: %S",
SRCNAME, __func__, sRiid);
}
if (*ppvObj)
((LPUNKNOWN)*ppvObj)->AddRef();
return hr;
}
STDMETHODIMP
GpgolRibbonExtender::GetTypeInfoCount (UINT *r_count)
{
*r_count = 0;
TRACEPOINT; /* Should not happen */
return S_OK;
}
STDMETHODIMP
GpgolRibbonExtender::GetTypeInfo (UINT iTypeInfo, LCID lcid,
LPTYPEINFO *r_typeinfo)
{
(void)iTypeInfo;
(void)lcid;
(void)r_typeinfo;
TRACEPOINT; /* Should not happen */
return S_OK;
}
/* Good documentation of what this function is supposed to do can
be found at: http://msdn.microsoft.com/en-us/library/cc237568.aspx
There is also a very good blog explaining how Ribbon Extensibility
is supposed to work.
http://blogs.msdn.com/b/andreww/archive/2007/03/09/
why-is-it-so-hard-to-shim-iribbonextensibility.aspx
*/
#define ID_MAPPER(name,id) \
if (!wcscmp (rgszNames[i], name)) \
{ \
found = true; \
rgDispId[i] = id; \
break; \
} \
STDMETHODIMP
GpgolRibbonExtender::GetIDsOfNames (REFIID riid, LPOLESTR *rgszNames,
UINT cNames, LCID lcid,
DISPID *rgDispId)
{
(void)riid;
(void)lcid;
bool found = false;
if (!rgszNames || !cNames || !rgDispId)
{
return E_POINTER;
}
for (unsigned int i = 0; i < cNames; i++)
{
log_debug ("%s:%s: GetIDsOfNames for: %S",
SRCNAME, __func__, rgszNames[i]);
/* How this is supposed to work with cNames > 1 is unknown,
but we can just say that we won't support callbacks with
different parameters and just match the name (the first element)
and we give it one of our own dispIds's that are later handled in
the invoke part */
ID_MAPPER (L"attachmentDecryptCallback", ID_CMD_DECRYPT)
- ID_MAPPER (L"encryptSelection", ID_CMD_ENCRYPT_SELECTION)
- ID_MAPPER (L"decryptSelection", ID_CMD_DECRYPT_SELECTION)
- ID_MAPPER (L"startCertManager", ID_CMD_CERT_MANAGER)
- ID_MAPPER (L"btnCertManager", ID_BTN_CERTMANAGER)
ID_MAPPER (L"btnDecrypt", ID_BTN_DECRYPT)
ID_MAPPER (L"btnDecryptLarge", ID_BTN_DECRYPT_LARGE)
ID_MAPPER (L"btnEncrypt", ID_BTN_ENCRYPT)
ID_MAPPER (L"btnEncryptLarge", ID_BTN_ENCRYPT_LARGE)
ID_MAPPER (L"btnEncryptSmall", IDI_ENCRYPT_20_PNG)
ID_MAPPER (L"btnSignSmall", IDI_SIGN_20_PNG)
ID_MAPPER (L"btnSignEncryptLarge", IDI_SIGN_ENCRYPT_40_PNG)
ID_MAPPER (L"btnEncryptFileLarge", ID_BTN_ENCSIGN_LARGE)
ID_MAPPER (L"btnSignLarge", ID_BTN_SIGN_LARGE)
ID_MAPPER (L"btnVerifyLarge", ID_BTN_VERIFY_LARGE)
ID_MAPPER (L"btnSigstateLarge", ID_BTN_SIGSTATE_LARGE)
- ID_MAPPER (L"encryptBody", ID_CMD_ENCRYPT_BODY)
- ID_MAPPER (L"decryptBody", ID_CMD_DECRYPT_BODY)
- ID_MAPPER (L"addEncSignedAttachment", ID_CMD_ATT_ENCSIGN_FILE)
- ID_MAPPER (L"addEncAttachment", ID_CMD_ATT_ENC_FILE)
- ID_MAPPER (L"signBody", ID_CMD_SIGN_BODY)
- ID_MAPPER (L"verifyBody", ID_CMD_VERIFY_BODY)
/* MIME support: */
ID_MAPPER (L"encryptMime", ID_CMD_MIME_ENCRYPT)
ID_MAPPER (L"encryptMimeEx", ID_CMD_MIME_ENCRYPT_EX)
ID_MAPPER (L"signMime", ID_CMD_MIME_SIGN)
ID_MAPPER (L"signMimeEx", ID_CMD_MIME_SIGN_EX)
ID_MAPPER (L"encryptSignMime", ID_CMD_SIGN_ENCRYPT_MIME)
ID_MAPPER (L"encryptSignMimeEx", ID_CMD_SIGN_ENCRYPT_MIME_EX)
ID_MAPPER (L"getEncryptPressed", ID_GET_ENCRYPT_PRESSED)
ID_MAPPER (L"getEncryptPressedEx", ID_GET_ENCRYPT_PRESSED_EX)
ID_MAPPER (L"getSignPressed", ID_GET_SIGN_PRESSED)
ID_MAPPER (L"getSignPressedEx", ID_GET_SIGN_PRESSED_EX)
ID_MAPPER (L"getSignEncryptPressed", ID_GET_SIGN_ENCRYPT_PRESSED)
ID_MAPPER (L"getSignEncryptPressedEx", ID_GET_SIGN_ENCRYPT_PRESSED_EX)
ID_MAPPER (L"ribbonLoaded", ID_ON_LOAD)
ID_MAPPER (L"openOptions", ID_CMD_OPEN_OPTIONS)
ID_MAPPER (L"getSigLabel", ID_GET_SIG_LABEL)
ID_MAPPER (L"getSigSTip", ID_GET_SIG_STIP)
ID_MAPPER (L"getSigTip", ID_GET_SIG_TTIP)
ID_MAPPER (L"launchDetails", ID_LAUNCH_CERT_DETAILS)
ID_MAPPER (L"getIsDetailsEnabled", ID_GET_IS_DETAILS_ENABLED)
ID_MAPPER (L"getIsCrypto", ID_GET_IS_CRYPTO_MAIL)
ID_MAPPER (L"printDecrypted", ID_CMD_PRINT_DECRYPTED)
}
if (cNames > 1)
{
log_debug ("More then one name provided. Should not happen");
}
return found ? S_OK : E_NOINTERFACE;
}
STDMETHODIMP
GpgolRibbonExtender::Invoke (DISPID dispid, REFIID riid, LCID lcid,
WORD flags, DISPPARAMS *parms, VARIANT *result,
EXCEPINFO *exepinfo, UINT *argerr)
{
USE_INVOKE_ARGS
log_debug ("%s:%s: enter with dispid: %x",
SRCNAME, __func__, (int)dispid);
if (!(flags & DISPATCH_METHOD))
{
log_debug ("%s:%s: not called in method mode. Bailing out.",
SRCNAME, __func__);
return DISP_E_MEMBERNOTFOUND;
}
switch (dispid)
{
case ID_CMD_DECRYPT:
/* We can assume that this points to an implementation of
IRibbonControl as we know the callback dispid. */
return decryptAttachments (parms->rgvarg[0].pdispVal);
- case ID_CMD_ENCRYPT_SELECTION:
- return encryptSelection (parms->rgvarg[0].pdispVal);
- case ID_CMD_DECRYPT_SELECTION:
- return decryptSelection (parms->rgvarg[0].pdispVal);
- case ID_CMD_CERT_MANAGER:
- return startCertManager (parms->rgvarg[0].pdispVal);
- case ID_CMD_ENCRYPT_BODY:
- return encryptBody (parms->rgvarg[0].pdispVal);
- case ID_CMD_DECRYPT_BODY:
- return decryptBody (parms->rgvarg[0].pdispVal);
- case ID_CMD_ATT_ENCSIGN_FILE:
- return addEncSignedAttachment (parms->rgvarg[0].pdispVal);
- case ID_CMD_ATT_ENC_FILE:
- return addEncAttachment (parms->rgvarg[0].pdispVal);
- case ID_CMD_SIGN_BODY:
- return signBody (parms->rgvarg[0].pdispVal);
- case ID_CMD_VERIFY_BODY:
- return verifyBody (parms->rgvarg[0].pdispVal);
case ID_CMD_SIGN_ENCRYPT_MIME:
return mark_mime_action (parms->rgvarg[1].pdispVal,
OP_SIGN|OP_ENCRYPT, false);
case ID_CMD_SIGN_ENCRYPT_MIME_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal,
OP_SIGN|OP_ENCRYPT, true);
case ID_CMD_MIME_ENCRYPT:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_ENCRYPT,
false);
case ID_CMD_MIME_SIGN:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_SIGN,
false);
case ID_GET_ENCRYPT_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_ENCRYPT,
result, false);
case ID_GET_SIGN_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN,
result, false);
case ID_GET_SIGN_ENCRYPT_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal,
OP_SIGN | OP_ENCRYPT,
result, false);
case ID_CMD_MIME_SIGN_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_SIGN, true);
case ID_CMD_MIME_ENCRYPT_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_ENCRYPT, true);
case ID_GET_ENCRYPT_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_ENCRYPT,
result, true);
case ID_GET_SIGN_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN,
result, true);
case ID_GET_SIGN_ENCRYPT_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN | OP_ENCRYPT,
result, true);
case ID_GET_SIG_STIP:
return get_sig_stip (parms->rgvarg[0].pdispVal, result);
case ID_GET_SIG_TTIP:
return get_sig_ttip (parms->rgvarg[0].pdispVal, result);
case ID_GET_SIG_LABEL:
return get_sig_label (parms->rgvarg[0].pdispVal, result);
case ID_LAUNCH_CERT_DETAILS:
return launch_cert_details (parms->rgvarg[0].pdispVal);
case ID_GET_IS_DETAILS_ENABLED:
return get_is_details_enabled (parms->rgvarg[0].pdispVal, result);
case ID_ON_LOAD:
{
g_ribbon_uis.push_back (parms->rgvarg[0].pdispVal);
return S_OK;
}
case ID_CMD_OPEN_OPTIONS:
{
options_dialog_box (NULL);
return S_OK;
}
case ID_CMD_PRINT_DECRYPTED:
return print_decrypted (parms->rgvarg[0].pdispVal);
case ID_GET_IS_CRYPTO_MAIL:
return get_is_crypto_mail (parms->rgvarg[0].pdispVal, result);
- case ID_BTN_CERTMANAGER:
case ID_BTN_ENCRYPT:
case ID_BTN_DECRYPT:
case ID_BTN_DECRYPT_LARGE:
case ID_BTN_ENCRYPT_LARGE:
case ID_BTN_ENCSIGN_LARGE:
case ID_BTN_SIGN_LARGE:
case ID_BTN_VERIFY_LARGE:
case IDI_SIGN_ENCRYPT_40_PNG:
case IDI_ENCRYPT_20_PNG:
case IDI_SIGN_20_PNG:
return getIcon (dispid, result);
case ID_BTN_SIGSTATE_LARGE:
return get_crypto_icon (parms->rgvarg[0].pdispVal, result);
}
log_debug ("%s:%s: leave", SRCNAME, __func__);
return DISP_E_MEMBERNOTFOUND;
}
/* Returns the XML markup for the various RibbonID's
The custom ui syntax is documented at:
http://msdn.microsoft.com/en-us/library/dd926139%28v=office.12%29.aspx
The outlook specific elements are documented at:
http://msdn.microsoft.com/en-us/library/office/ee692172%28v=office.14%29.aspx
*/
static STDMETHODIMP
GetCustomUI_MIME (BSTR RibbonID, BSTR * RibbonXml)
{
char * buffer = NULL;
/* const char *certManagerTTip =
_("Start the Certificate Management Software");
const char *certManagerSTip =
_("Open GPA or Kleopatra to manage your certificates. "
"You can use this you to generate your "
"own certificates. ");*/
const char *encryptTTip =
_("Encrypt the message");
const char *encryptSTip =
_("Encrypts the message and all attachments before sending");
const char *signTTip =
_("Sign the message");
const char *signSTip =
_("Sign the message and all attachments before sending");
const char *secureTTip =
_("Sign and encrypt the message");
const char *secureSTip =
_("Encrypting and cryptographically signing a message means that the "
"recipients can be sure that no one modified the message and only the "
"recipients can read it");
const char *optsSTip =
_("Open the settings dialog for GpgOL");
log_debug ("%s:%s: GetCustomUI_MIME for id: %ls", SRCNAME, __func__, RibbonID);
if (!RibbonXml || !RibbonID)
return E_POINTER;
if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Compose"))
{
gpgrt_asprintf (&buffer,
""
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"", _("GpgOL"),
_("Secure"), secureTTip, secureSTip,
_("Sign"), signTTip, signSTip,
_("Encrypt"), encryptTTip, encryptSTip,
optsSTip
);
}
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Read"))
{
gpgrt_asprintf (&buffer,
""
" "
" "
" "
" "
" "
" "
" "
" "
""
" "
" "
" "
""
"",
_("GpgOL"),
optsSTip,
_("Decrypt")
);
}
/* We don't use this code currently because calling the send
event for Inline Response mailitems fails. */
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer") && g_ol_version_major > 14)
{
gpgrt_asprintf (&buffer,
""
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"",
_("GpgOL"),
optsSTip,
_("GpgOL"),
_("Secure"), secureTTip, secureSTip,
_("Sign"), signTTip, signSTip,
_("Encrypt"), encryptTTip, encryptSTip,
optsSTip,
_("&Print decrypted")
);
}
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer"))
{
// No TabComposeTools in Outlook 2010
gpgrt_asprintf (&buffer,
""
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"",
_("GpgOL"),
optsSTip
);
}
if (buffer)
{
wchar_t *wbuf = utf8_to_wchar2 (buffer, strlen(buffer));
xfree (buffer);
*RibbonXml = SysAllocString (wbuf);
xfree (wbuf);
}
else
*RibbonXml = NULL;
return S_OK;
}
-/* This is the old pre-mime adding UI code. It will be removed once we have a
- stable version that can also send mime messages.
-*/
-static STDMETHODIMP
-GetCustomUI_old (BSTR RibbonID, BSTR * RibbonXml)
-{
- char *buffer = NULL;
- const char *certManagerTTip =
- _("Start the Certificate Management Software");
- const char *certManagerSTip =
- _("Open GPA or Kleopatra to manage your certificates. "
- "You can use this you to generate your "
- "own certificates. ");
- const char *encryptTextTTip =
- _("Encrypt the text of the message");
- const char *encryptTextSTip =
- _("Choose the certificates for which the message "
- "should be encrypted and replace the text "
- "with the encrypted message.");
- const char *encryptFileTTip =
- _("Add a file as an encrypted attachment");
- const char *encryptFileSTip =
- _("Encrypts a file and adds it as an attachment to the "
- "message. ");
- const char *encryptSignFileTTip =
- _("Add a file as an encrypted attachment with a signature");
- const char *encryptSignFileSTip =
- _("Encrypts a file, signs it and adds both the encrypted file "
- "and the signature as attachments to the message. ");
- const char *decryptTextTTip=
- _("Decrypt the message");
- const char *decryptTextSTip =
- _("Look for PGP or S/MIME encrypted data in the message text "
- "and decrypt it.");
- const char *signTextTTip =
- _("Add a signature of the message");
- const char *signTextSTip =
- _("Appends a signed copy of the message text in an opaque signature. "
- "An opaque signature ensures that the signed text is not modified by "
- "embedding it in the signature itself. "
- "The combination of the signed message text and your signature is "
- "added below the plain text. "
- "The message will not be encrypted!");
- const char *optsSTip =
- _("Open the settings dialog for GpgOL.");
-
- log_debug ("%s:%s: GetCustomUI for id: %ls", SRCNAME, __func__, RibbonID);
-
- if (!RibbonXml)
- return E_POINTER;
-
- if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Compose"))
- {
- gpgrt_asprintf (&buffer,
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- ""
- "", _("GpgOL"), _("General"),
- _("Start Certificate Manager"), certManagerTTip, certManagerSTip,
- optsSTip,
- _("Textbody"),
- _("Encrypt"), encryptTextTTip, encryptTextSTip,
- _("Decrypt"), decryptTextTTip, decryptTextSTip,
- _("Sign"), signTextTTip, signTextSTip,
- _("Verify"),
- _("Attachments"),
- _("Encrypted file"), encryptFileTTip, encryptFileSTip,
- _("Encrypted file and Signature"), encryptSignFileTTip, encryptSignFileSTip,
- _("Encrypt"), _("Decrypt")
- );
- }
- else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Read"))
- {
- gpgrt_asprintf (&buffer,
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- ""
- ""
- " "
- " "
- " "
- " "
- " "
- ""
- "",
- _("GpgOL"), _("General"),
- _("Start Certificate Manager"), certManagerTTip, certManagerSTip,
- optsSTip,
- _("Textbody"),
- _("Decrypt"), decryptTextTTip, decryptTextSTip,
- _("Verify"),
- _("GpgOL"), _("Save and decrypt"),
- _("Decrypt"),
- _("Decrypt"));
- }
- else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer"))
- {
- gpgrt_asprintf (&buffer,
- ""
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- /* This would be totally nice but Outlook
- saves the decrypted text aftewards automatically.
- Yay,..
- " "
- " "
- " "
- */
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- /*
- There appears to be no way to access the word editor
- / get the selected text from that Context.
- " "
- " "
- " "
- */
- " "
- " "
- " "
- " "
- "",
- _("GpgOL"), _("General"),
- _("Start Certificate Manager"), certManagerTTip, certManagerSTip,
- optsSTip,
- /*_("Mail Body"), _("Decrypt"),*/
- _("GpgOL"), _("Save and decrypt"),/*_("Decrypt"), */
- _("Save and decrypt"));
- }
-
- if (buffer)
- {
- wchar_t *wbuf = utf8_to_wchar2 (buffer, strlen(buffer));
- xfree (buffer);
- *RibbonXml = SysAllocString (wbuf);
- xfree (wbuf);
- }
- else
- *RibbonXml = NULL;
-
- return S_OK;
-}
-
STDMETHODIMP
GpgolRibbonExtender::GetCustomUI (BSTR RibbonID, BSTR * RibbonXml)
{
- if (use_mime_ui)
- {
- return GetCustomUI_MIME (RibbonID, RibbonXml);
- }
- else
- {
- return GetCustomUI_old (RibbonID, RibbonXml);
- }
+ return GetCustomUI_MIME (RibbonID, RibbonXml);
}
/* RibbonUi elements are created on demand but they are reused
in different inspectors. So far and from all documentation
I could find RibbonUi elments are never
deleted. When they are created the onLoad callback is called
to register them.
The callbacks registered in the XML description are only
executed on Load. So to have different information depending
on the available mails we have to invalidate the UI ourself.
This means that the callbacks will be reevaluated and the UI
Updated. Sadly we don't know which ribbon_ui needs updates
so we have to invalidate everything.
*/
void gpgoladdin_invalidate_ui ()
{
std::list::iterator it;
for (it = g_ribbon_uis.begin(); it != g_ribbon_uis.end(); ++it)
{
log_debug ("%s:%s: Invalidating ribbon: %p",
SRCNAME, __func__, *it);
invoke_oom_method (*it, "Invalidate", NULL);
}
}
GpgolAddin *
GpgolAddin::get_instance ()
{
if (!addin_instance)
{
addin_instance = new GpgolAddin ();
}
return addin_instance;
}
diff --git a/src/main.c b/src/main.c
index ddb54eb..186620e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,512 +1,503 @@
/* main.c - DLL entry point
* 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
* 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 .
*/
#include
#include
#include
#include
#include
#include
#include "mymapi.h"
#include "mymapitags.h"
#include "common.h"
#include "mymapi.h"
/* Local function prototypes. */
static char *get_locale_dir (void);
static void drop_locale_dir (char *locale_dir);
/* The major version of Outlook we are attached to */
int g_ol_version_major;
/* Initialization of gloabl options. These are merely the defaults
and will get updated later from the Registry. That is done later
at the time Outlook calls its entry point the first time. */
static void
init_options (void)
{
opt.enc_format = GPG_FMT_CLASSIC;
}
/* For certain operations we need to acquire a log on the logging
functions. This lock is controlled by this Mutex. */
HANDLE log_mutex;
/* Early initialization of this module. This is done right at startup
with only one thread running. Should be called only once. Returns
0 on success. */
static int
initialize_main (void)
{
SECURITY_ATTRIBUTES sa;
memset (&sa, 0, sizeof sa);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof sa;
log_mutex = CreateMutex (&sa, FALSE, NULL);
return log_mutex? 0 : -1;
}
/* Return nbytes of cryptographic strong random. Caller needs to free
the returned buffer. */
static char *
get_crypt_random (size_t nbytes)
{
HCRYPTPROV prov;
char *buffer;
if (!CryptAcquireContext (&prov, NULL, NULL, PROV_RSA_FULL,
(CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) )
return NULL;
buffer = xmalloc (nbytes);
if (!CryptGenRandom (prov, nbytes, buffer))
{
xfree (buffer);
buffer = NULL;
}
CryptReleaseContext (prov, 0);
return buffer;
}
static void
i18n_init (void)
{
char *locale_dir;
#ifdef ENABLE_NLS
# ifdef HAVE_LC_MESSAGES
setlocale (LC_TIME, "");
setlocale (LC_MESSAGES, "");
# else
setlocale (LC_ALL, "" );
# endif
#endif
locale_dir = get_locale_dir ();
if (locale_dir)
{
bindtextdomain (PACKAGE_GT, locale_dir);
drop_locale_dir (locale_dir);
}
textdomain (PACKAGE_GT);
}
static char *
get_gpgme_w32_inst_dir (void)
{
char *gpg4win_dir = get_gpg4win_dir ();
char *tmp;
gpgrt_asprintf (&tmp, "%s\\bin\\gpgme-w32spawn.exe", gpg4win_dir);
if (!access(tmp, R_OK))
{
xfree (tmp);
gpgrt_asprintf (&tmp, "%s\\bin", gpg4win_dir);
xfree (gpg4win_dir);
return tmp;
}
xfree (tmp);
gpgrt_asprintf (&tmp, "%s\\gpgme-w32spawn.exe", gpg4win_dir);
if (!access(tmp, R_OK))
{
xfree (tmp);
return gpg4win_dir;
}
OutputDebugString("Failed to find gpgme-w32spawn.exe!");
return NULL;
}
/* Entry point called by DLL loader. */
int WINAPI
DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
{
(void)reserved;
if (reason == DLL_PROCESS_ATTACH)
{
set_global_hinstance (hinst);
gpg_err_init ();
/* Set the installation directory for GpgME so that
it can find tools like gpgme-w32-spawn correctly. */
char *instdir = get_gpgme_w32_inst_dir();
gpgme_set_global_flag ("w32-inst-dir", instdir);
xfree (instdir);
/* The next call initializes subsystems of gpgme and should be
done as early as possible. The actual return value (the
version string) is not used here. It may be called at any
time later for this. */
gpgme_check_version (NULL);
/* Early initializations of our subsystems. */
if (initialize_main ())
return FALSE;
i18n_init ();
init_options ();
}
else if (reason == DLL_PROCESS_DETACH)
{
gpg_err_deinit (0);
}
return TRUE;
}
/* Return a new allocated IV of size NBYTES. Caller must free it. On
error NULL is returned. */
void *
create_initialization_vector (size_t nbytes)
{
return get_crypt_random (nbytes);
}
static char *
get_locale_dir (void)
{
char *instdir;
char *p;
char *dname;
instdir = get_gpg4win_dir();
if (!instdir)
return NULL;
/* Build the key: "/share/locale". */
#define SLDIR "\\share\\locale"
dname = malloc (strlen (instdir) + strlen (SLDIR) + 1);
if (!dname)
{
free (instdir);
return NULL;
}
p = dname;
strcpy (p, instdir);
p += strlen (instdir);
strcpy (p, SLDIR);
free (instdir);
return dname;
}
static void
drop_locale_dir (char *locale_dir)
{
free (locale_dir);
}
/* Read option settings from the Registry. */
void
read_options (void)
{
static int warnings_shown;
char *val = NULL;
/* Set the log file first so that output from this function is
logged too. */
load_extension_value ("logFile", &val);
set_log_file (val);
xfree (val); val = NULL;
/* Parse the debug flags. */
load_extension_value ("enableDebug", &val);
opt.enable_debug = 0;
if (val)
{
char *p, *pend;
trim_spaces (val);
for (p = val; p; p = pend)
{
pend = strpbrk (p, ", \t\n\r\f");
if (pend)
{
*pend++ = 0;
pend += strspn (pend, ", \t\n\r\f");
}
if (isascii (*p) && isdigit (*p))
opt.enable_debug |= strtoul (p, NULL, 0);
else if (!strcmp (p, "ioworker"))
opt.enable_debug |= DBG_IOWORKER;
else if (!strcmp (p, "ioworker-extra"))
opt.enable_debug |= DBG_IOWORKER_EXTRA;
else if (!strcmp (p, "filter"))
opt.enable_debug |= DBG_FILTER;
else if (!strcmp (p, "filter-extra"))
opt.enable_debug |= DBG_FILTER_EXTRA;
else if (!strcmp (p, "memory"))
opt.enable_debug |= DBG_MEMORY;
else if (!strcmp (p, "commands"))
opt.enable_debug |= DBG_COMMANDS;
else if (!strcmp (p, "mime-parser"))
opt.enable_debug |= DBG_MIME_PARSER;
else if (!strcmp (p, "mime-data"))
opt.enable_debug |= DBG_MIME_DATA;
else if (!strcmp (p, "oom"))
opt.enable_debug |= DBG_OOM;
else if (!strcmp (p, "oom-extra"))
opt.enable_debug |= DBG_OOM_EXTRA;
else
log_debug ("invalid debug flag `%s' ignored", p);
}
}
else
{
/* To help the user enable debugging make sure that the registry
key exists. Note that the other registry keys are stored
after using the configuration dialog. */
store_extension_value ("enableDebug", "0");
}
xfree (val); val = NULL;
if (opt.enable_debug)
log_debug ("enabled debug flags:%s%s%s%s%s%s%s%s%s%s\n",
(opt.enable_debug & DBG_IOWORKER)? " ioworker":"",
(opt.enable_debug & DBG_IOWORKER_EXTRA)? " ioworker-extra":"",
(opt.enable_debug & DBG_FILTER)? " filter":"",
(opt.enable_debug & DBG_FILTER_EXTRA)? " filter-extra":"",
(opt.enable_debug & DBG_MEMORY)? " memory":"",
(opt.enable_debug & DBG_COMMANDS)? " commands":"",
(opt.enable_debug & DBG_MIME_PARSER)? " mime-parser":"",
(opt.enable_debug & DBG_MIME_DATA)? " mime-data":"",
(opt.enable_debug & DBG_OOM)? " oom":"",
(opt.enable_debug & DBG_OOM_EXTRA)? " oom-extra":""
);
load_extension_value ("enableSmime", &val);
opt.enable_smime = !val ? 0 : atoi (val);
xfree (val); val = NULL;
/* load_extension_value ("defaultProtocol", &val); */
/* switch ((!val || *val == '0')? 0 : atol (val)) */
/* { */
/* case 1: opt.default_protocol = PROTOCOL_OPENPGP; break; */
/* case 2: opt.default_protocol = PROTOCOL_SMIME; break; */
/* case 0: */
/* default: opt.default_protocol = PROTOCOL_UNKNOWN /\*(auto*)*\/; break; */
/* } */
/* xfree (val); val = NULL; */
opt.default_protocol = PROTOCOL_UNKNOWN; /* (auto)*/
load_extension_value ("encryptDefault", &val);
opt.encrypt_default = val == NULL || *val != '1'? 0 : 1;
xfree (val); val = NULL;
load_extension_value ("signDefault", &val);
opt.sign_default = val == NULL || *val != '1'? 0 : 1;
xfree (val); val = NULL;
load_extension_value ("previewDecrypt", &val);
opt.preview_decrypt = val == NULL || *val != '1'? 0 : 1;
xfree (val); val = NULL;
load_extension_value ("enableDefaultKey", &val);
opt.enable_default_key = val == NULL || *val != '1' ? 0 : 1;
xfree (val); val = NULL;
load_extension_value ("encodingFormat", &val);
opt.enc_format = val == NULL? GPG_FMT_CLASSIC : atol (val);
xfree (val); val = NULL;
load_extension_value ("defaultKey", &val);
set_default_key (val);
xfree (val); val = NULL;
load_extension_value ("gitCommit", &val);
opt.git_commit = val? strtoul (val, NULL, 16) : 0;
xfree (val); val = NULL;
load_extension_value ("formsRevision", &val);
opt.forms_revision = val? atol (val) : 0;
xfree (val); val = NULL;
load_extension_value ("announceNumber", &val);
opt.announce_number = val? atol (val) : 0;
xfree (val); val = NULL;
load_extension_value ("bodyAsAttachment", &val);
opt.body_as_attachment = val == NULL || *val != '1'? 0 : 1;
xfree (val); val = NULL;
-#ifdef MIME_SEND
- opt.mime_ui = 1;
-#else
- load_extension_value ("mimeUI", &val);
- opt.mime_ui = val == NULL || *val != '1'? 0 : 1;
- xfree (val); val = NULL;
-#endif
-
load_extension_value ("inlinePGP", &val);
opt.inline_pgp = val == NULL || *val != '1'? 0 : 1;
xfree (val); val = NULL;
load_extension_value ("autoresolve", &val);
opt.autoresolve = val == NULL ? 1 : *val != '1' ? 0 : 1;
xfree (val); val = NULL;
load_extension_value ("replyCrypt", &val);
opt.reply_crypt = val == NULL ? 1 : *val != '1' ? 0 : 1;
xfree (val); val = NULL;
load_extension_value ("deprecationShown", &val);
opt.deprecation_shown = val == NULL || *val != '1'? 0 : 1;
xfree (val); val = NULL;
/* Note, that on purpose these flags are only Registry changeable.
The format of the entry is a string of of "0" and "1" digits; see
the switch below for a description. */
memset (&opt.compat, 0, sizeof opt.compat);
load_extension_value ("compatFlags", &val);
if (val)
{
const char *s = val;
int i, x;
for (s=val, i=0; *s; s++, i++)
{
x = *s == '1';
switch (i)
{
case 0: opt.compat.no_msgcache = x; break;
case 1: opt.compat.no_pgpmime = x; break;
case 2: opt.compat.no_oom_write = x; break;
case 3: opt.compat.no_preview_info = x; break;
case 4: opt.compat.old_reply_hack = x; break;
case 5: opt.compat.auto_decrypt = x; break;
case 6: opt.compat.no_attestation = x; break;
case 7: opt.compat.use_mwfmo = x; break;
}
}
log_debug ("Note: using compatibility flags: %s", val);
}
if (!warnings_shown)
{
char tmpbuf[512];
warnings_shown = 1;
if (val && *val)
{
snprintf (tmpbuf, sizeof tmpbuf,
_("Note: Using compatibility flags: %s"), val);
MessageBox (NULL, tmpbuf, _("GpgOL"), MB_ICONWARNING|MB_OK);
}
}
xfree (val); val = NULL;
}
/* Write current options back to the Registry. */
int
write_options (void)
{
struct
{
const char *name;
int mode;
int value;
char *s_val;
} table[] = {
{"enableSmime", 0, opt.enable_smime, NULL},
/* {"defaultProtocol", 3, opt.default_protocol}, */
{"encryptDefault", 0, opt.encrypt_default, NULL},
{"signDefault", 0, opt.sign_default, NULL},
{"previewDecrypt", 0, opt.preview_decrypt, NULL},
{"encodingFormat", 1, opt.enc_format, NULL},
{"logFile", 2, 0, (char*) get_log_file ()},
{"defaultKey", 2, 0, opt.default_key},
{"enableDefaultKey", 0, opt.enable_default_key, NULL},
{"gitCommit", 4, opt.git_commit, NULL},
{"formsRevision", 1, opt.forms_revision, NULL},
{"announceNumber", 1, opt.announce_number, NULL},
{"bodyAsAttachment", 0, opt.body_as_attachment, NULL},
- {"mimeUI", MIME_UI_DEFAULT, opt.mime_ui, NULL},
{"inlinePGP", 0, opt.inline_pgp, NULL},
{"autoresolve", 0, opt.autoresolve, NULL},
{"replyCrypt", 0, opt.reply_crypt, NULL},
{"deprecationShown", 0, opt.deprecation_shown, NULL},
{NULL, 0, 0, NULL}
};
char buf[32];
int rc, i;
const char *string;
for (i=0; table[i].name; i++)
{
switch (table[i].mode)
{
case 0:
string = table[i].value? "1": "0";
log_debug ("storing option `%s' value=`%s'\n",
table[i].name, string);
rc = store_extension_value (table[i].name, string);
break;
case 1:
sprintf (buf, "%d", table[i].value);
log_debug ("storing option `%s' value=`%s'\n",
table[i].name, buf);
rc = store_extension_value (table[i].name, buf);
break;
case 2:
string = table[i].s_val? table[i].s_val : "";
log_debug ("storing option `%s' value=`%s'\n",
table[i].name, string);
rc = store_extension_value (table[i].name, string);
break;
/* case 3: */
/* buf[0] = '0'; */
/* buf[1] = 0; */
/* switch (opt.default_protocol) */
/* { */
/* case PROTOCOL_UNKNOWN: buf[0] = '0'; /\* auto *\/ break; */
/* case PROTOCOL_OPENPGP: buf[0] = '1'; break; */
/* case PROTOCOL_SMIME: buf[0] = '2'; break; */
/* } */
/* log_debug ("storing option `%s' value=`%s'\n", */
/* table[i].name, buf); */
/* rc = store_extension_value (table[i].name, buf); */
/* break; */
case 4:
sprintf (buf, "0x%x", table[i].value);
log_debug ("storing option `%s' value=`%s'\n",
table[i].name, buf);
rc = store_extension_value (table[i].name, buf);
break;
default:
rc = -1;
break;
}
if (rc)
log_error ("error storing option `%s': rc = %d\n", table[i].name, rc);
}
return 0;
}
diff --git a/src/mapihelp.cpp b/src/mapihelp.cpp
index 1acae9e..2cb451d 100644
--- a/src/mapihelp.cpp
+++ b/src/mapihelp.cpp
@@ -1,3849 +1,3844 @@
/* 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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "mymapi.h"
#include "mymapitags.h"
#include "common.h"
#include "rfc822parse.h"
#include "mapihelp.h"
#include "parsetlv.h"
#include "gpgolstr.h"
#include "oomhelp.h"
#include
#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
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, const wchar_t *name, const char *func)
{
HRESULT hr;
LPSPropTagArray proparr = NULL;
MAPINAMEID mnid, *pmnid;
GpgOLStr propname(name);
/* {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 = propname;
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}};
GpgOLStr propname (L"Internet Charset Body");
int result;
memset (&mnid, 0, sizeof mnid);
mnid.lpguid = &guid;
mnid.ulKind = MNID_STRING;
mnid.Kind.lpwstrName = propname;
pmnid = &mnid;
hr = message->GetIDsFromNames (1, &pmnid, 0, &proparr);
if (FAILED (hr))
proparr = NULL;
if (FAILED (hr) || !(proparr->aulPropTag[0] & 0xFFFF0000) )
{
log_debug ("%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;
}
/* Return the property tag for GpgOL UUID Info. */
static int
get_gpgoluid_tag (LPMESSAGE message, ULONG *r_tag)
{
if (!(*r_tag = create_gpgol_tag (message, L"GpgOL UID", __func__)))
return -1;
*r_tag |= PT_UNICODE;
return 0;
}
char *
mapi_get_uid (LPMESSAGE msg)
{
/* If the UUID is not in OOM maybe we find it in mapi. */
if (!msg)
{
log_error ("%s:%s: Called without message",
SRCNAME, __func__);
return NULL;
}
ULONG tag;
if (get_gpgoluid_tag (msg, &tag))
{
log_debug ("%s:%s: Failed to get tag for '%p'",
SRCNAME, __func__, msg);
return NULL;
}
LPSPropValue propval = NULL;
HRESULT hr = HrGetOneProp ((LPMAPIPROP)msg, tag, &propval);
if (hr)
{
log_debug ("%s:%s: Failed to get prop for '%p'",
SRCNAME, __func__, msg);
return NULL;
}
char *ret = NULL;
if (PROP_TYPE (propval->ulPropTag) == PT_UNICODE)
{
ret = wchar_to_utf8 (propval->Value.lpszW);
log_debug ("%s:%s: Fund uuid in MAPI for %p",
SRCNAME, __func__, msg);
}
else if (PROP_TYPE (propval->ulPropTag) == PT_STRING8)
{
ret = strdup (propval->Value.lpszA);
log_debug ("%s:%s: Fund uuid in MAPI for %p",
SRCNAME, __func__, msg);
}
MAPIFreeBuffer (propval);
return ret;
}
/* 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)
{
int i;
for (i = 0, hr = 0; hr && i < 10; i++)
{
hr = message->SaveChanges (flags);
if (hr)
{
log_debug ("%s:%s: Failed try to save.",
SRCNAME, __func__);
Sleep (1000);
}
}
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 headers as ASCII string. Returns empty
string on failure. */
std::string
mapi_get_header (LPMESSAGE message)
{
HRESULT hr;
LPSTREAM stream;
ULONG bRead;
std::string ret;
if (!message)
return ret;
hr = message->OpenProperty (PR_TRANSPORT_MESSAGE_HEADERS_A, &IID_IStream, 0, 0,
(LPUNKNOWN*)&stream);
if (hr)
{
log_debug ("%s:%s: OpenProperty failed: hr=%#lx", SRCNAME, __func__, hr);
return ret;
}
char buf[8192];
while ((hr = stream->Read (buf, 8192, &bRead)) == S_OK ||
hr == S_FALSE)
{
if (!bRead)
{
// EOF
break;
}
ret += std::string (buf, bRead);
}
gpgol_release (stream);
return ret;
}
/* 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);
gpgol_release (stream);
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);
gpgol_release (stream);
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);
gpgol_release (stream);
return NULL;
}
gpgol_release (stream);
{
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.
If r_nobody is not null it is set to true if no body was found.
*/
static char *
get_msgcls_from_pgp_lines (LPMESSAGE message, bool *r_nobody = nullptr)
{
HRESULT hr;
LPSTREAM stream;
STATSTG statInfo;
ULONG nread;
size_t nbytes;
char *body = NULL;
char *p;
char *msgcls = NULL;
int is_wchar = 0;
- if (!opt.mime_ui)
- {
- return NULL;
- }
-
if (r_nobody)
{
*r_nobody = false;
}
stream = mapi_get_body_as_stream (message);
if (!stream)
{
log_debug ("%s:%s: Failed to get body ASCII stream.",
SRCNAME, __func__);
hr = message->OpenProperty (PR_BODY_W, &IID_IStream, 0, 0,
(LPUNKNOWN*)&stream);
if (hr)
{
log_error ("%s:%s: Failed to get w_body stream. : hr=%#lx",
SRCNAME, __func__, hr);
if (r_nobody)
{
*r_nobody = true;
}
return NULL;
}
else
{
is_wchar = 1;
}
}
hr = stream->Stat (&statInfo, STATFLAG_NONAME);
if (hr)
{
log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
gpgol_release (stream);
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);
gpgol_release (stream);
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);
gpgol_release (stream);
return NULL;
}
gpgol_release (stream);
if (is_wchar)
{
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))
{
/* Enabling clearsigned detection for Outlook 2010 and later
would result in data loss as the signature is not reverted. */
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))
{
/* We have text before the message. In that case we need
to break because some bad MUA's like Outlook do not insert
quote characters before a replied to message. In that case
the reply to an inline Mail from an Outlook without GpgOL
enabled could cause the behavior that we would detect
the original message.
*/
log_debug ("%s:%s: Detected non whitespace %c before a PGP Marker",
SRCNAME, __func__, *p);
break;
}
}
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);
gpgol_release (mapitable);
return -1;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
if (n_attach != 1)
{
FreeProws (mapirows);
gpgol_release (mapitable);
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 == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
&& ti.is_cons) )
goto leave;
if (parse_tlv (&p, &n, &ti))
goto leave;
if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_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)
gpgol_release (stream);
if (att)
gpgol_release (att);
FreeProws (mapirows);
gpgol_release (mapitable);
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);
gpgol_release (mapitable);
return NULL;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
if (n_attach < 1)
{
FreeProws (mapirows);
gpgol_release (mapitable);
log_debug ("%s:%s: less then 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)
gpgol_release (att);
FreeProws (mapirows);
gpgol_release (mapitable);
return result;
}
/* Look at the first attachment's content type to determine the
messageclass. */
static char *
get_msgcls_from_first_attachment (LPMESSAGE message)
{
char *ret = nullptr;
char *attach_mime = get_first_attach_mime_tag (message);
if (!attach_mime)
{
return nullptr;
}
if (!strcmp (attach_mime, "application/pgp-encrypted"))
{
ret = xstrdup ("IPM.Note.GpgOL.MultipartEncrypted");
xfree (attach_mime);
}
else if (!strcmp (attach_mime, "application/pgp-signature"))
{
ret = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
xfree (attach_mime);
}
return ret;
}
/* 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);
log_debug ("%s:%s: content type is '%s'", SRCNAME, __func__,
ct ? ct : "null");
if (ct && 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 (ct && !strcmp (ct, "application/ms-tnef"))
{
/* ms-tnef can either be inline PGP or PGP/MIME. First check
for inline and then look at the attachments if they look
like PGP /MIME .*/
newvalue = get_msgcls_from_pgp_lines (message);
if (!newvalue)
{
/* So no PGP Inline. Lets look at the attachment. */
newvalue = get_msgcls_from_first_attachment (message);
}
}
else if (!ct || !strcmp (ct, "text/plain") ||
!strcmp (ct, "multipart/mixed") ||
!strcmp (ct, "multipart/alternative") ||
!strcmp (ct, "multipart/related") ||
!strcmp (ct, "text/html"))
{
bool has_no_body = false;
/* It is quite common to have a multipart/mixed or alternative
mail with separate encrypted PGP parts. Look at the body to
decide. */
newvalue = get_msgcls_from_pgp_lines (message, &has_no_body);
if (!newvalue && has_no_body && ct && !strcmp (ct, "multipart/mixed"))
{
/* This is uncommon. But some Exchanges might break a PGP/MIME mail
this way. Let's take a look at the attachments. Maybe it's
a PGP/MIME mail. */
log_debug ("%s:%s: Multipart mixed without body found. Looking at attachments.",
SRCNAME, __func__);
newvalue = get_msgcls_from_first_attachment (message);
}
}
xfree (ct);
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 (ct && !strcmp (ct, "application/ms-tnef"))
{
/* So no PGP Inline. Lets look at the attachment. */
char *attach_mime = get_first_attach_mime_tag (message);
if (!attach_mime)
{
xfree (ct);
xfree (proto);
return nullptr;
}
if (!strcmp (attach_mime, "multipart/signed"))
{
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
xfree (attach_mime);
}
}
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");
}
else if (!strcmp (ct, "wks.confirmation.mail"))
{
newvalue = xstrdup ("IPM.Note.GpgOL.WKSConfirmation");
}
else if (ct && !strcmp (ct, "application/ms-tnef"))
{
/* So no PGP Inline. Lets look at the attachment. */
char *attach_mime = get_first_attach_mime_tag (message);
if (!attach_mime)
{
xfree (ct);
xfree (proto);
return nullptr;
}
if (!strcmp (attach_mime, "multipart/signed"))
{
newvalue = xstrdup ("IPM.Note.GpgOL.MultipartSigned");
xfree (attach_mime);
}
}
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") ||
!strcmp (ct, "multipart/alternative") ||
!strcmp (ct, "multipart/mixed")))
{
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;
}
static msgtype_t
string_to_type (const char *s)
{
if (!s || strlen (s) < 14)
{
return MSGTYPE_UNKNOWN;
}
if (!strncmp (s, "IPM.Note.GpgOL", 14) && (!s[14] || s[14] =='.'))
{
s += 14;
if (!*s)
return MSGTYPE_GPGOL;
else if (!strcmp (s, ".MultipartSigned"))
return MSGTYPE_GPGOL_MULTIPART_SIGNED;
else if (!strcmp (s, ".MultipartEncrypted"))
return MSGTYPE_GPGOL_MULTIPART_ENCRYPTED;
else if (!strcmp (s, ".OpaqueSigned"))
return MSGTYPE_GPGOL_OPAQUE_SIGNED;
else if (!strcmp (s, ".OpaqueEncrypted"))
return MSGTYPE_GPGOL_OPAQUE_ENCRYPTED;
else if (!strcmp (s, ".ClearSigned"))
return MSGTYPE_GPGOL_CLEAR_SIGNED;
else if (!strcmp (s, ".PGPMessage"))
return MSGTYPE_GPGOL_PGP_MESSAGE;
else if (!strcmp (s, ".WKSConfirmation"))
return MSGTYPE_GPGOL_WKS_CONFIRMATION;
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] =='.'))
return MSGTYPE_SMIME;
return MSGTYPE_UNKNOWN;
}
/* 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,
msgtype_t *r_type)
{
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 (opt.enable_smime && !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. */
char *tmp = change_message_class_ipm_note_smime_multipartsigned
(message);
/* This case happens even for PGP/MIME mails but that is ok
as we later fiddle out the protocol. But we have to
check if this is a WKS Mail now so that we can do the
special handling for that. */
if (tmp && !strcmp (tmp, "IPM.Note.GpgOL.WKSConfirmation"))
{
newvalue = tmp;
}
else
{
xfree (tmp);
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 (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))
{
log_debug ("%s:%s: Failed to get PR_MESSAGE_CLASS_A property.",
SRCNAME, __func__);
}
else if (PROP_TYPE (propval2->ulPropTag) != PT_STRING8)
{
log_debug ("%s:%s: PR_MESSAGE_CLASS_A is not string.",
SRCNAME, __func__);
}
else if (!propval2->Value.lpszA)
{
log_debug ("%s:%s: PR_MESSAGE_CLASS_A is null.",
SRCNAME, __func__);
}
else if (!strcmp (propval2->Value.lpszA, s))
{
log_debug ("%s:%s: PR_MESSAGE_CLASS_A is already the same.",
SRCNAME, __func__);
}
else
{
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 (r_type && !newvalue)
{
*r_type = string_to_type (s);
}
}
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
{
if (r_type)
{
*r_type = string_to_type (newvalue);
}
/* 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;
}
static char *
resolve_ex_from_address (LPMESSAGE message)
{
HRESULT hr;
char *sender_entryid;
size_t entryidlen;
LPMAPISESSION session;
ULONG utype;
LPUNKNOWN user;
LPSPropValue propval = NULL;
char *buf;
if (g_ol_version_major < 14)
{
log_debug ("%s:%s: Not implemented for Ol < 14", SRCNAME, __func__);
return NULL;
}
sender_entryid = mapi_get_binary_prop (message, PR_SENDER_ENTRYID,
&entryidlen);
if (!sender_entryid)
{
log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
return NULL;
}
session = get_oom_mapi_session ();
if (!session)
{
log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
xfree (sender_entryid);
return NULL;
}
hr = session->OpenEntry (entryidlen, (LPENTRYID)sender_entryid,
&IID_IMailUser,
MAPI_BEST_ACCESS | MAPI_CACHE_ONLY,
&utype, (IUnknown**)&user);
if (FAILED (hr))
{
log_debug ("%s:%s: Failed to open cached entry. Fallback to uncached.",
SRCNAME, __func__);
hr = session->OpenEntry (entryidlen, (LPENTRYID)sender_entryid,
&IID_IMailUser,
MAPI_BEST_ACCESS,
&utype, (IUnknown**)&user);
}
gpgol_release (session);
if (FAILED (hr))
{
log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
return NULL;
}
hr = HrGetOneProp ((LPMAPIPROP)user, PR_SMTP_ADDRESS_W, &propval);
if (FAILED (hr))
{
log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
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);
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;
ULONG try_props[3] = {PidTagSenderSmtpAddress_W,
PR_SENT_REPRESENTING_SMTP_ADDRESS_W,
PR_SENDER_EMAIL_ADDRESS_W};
if (!message)
return xstrdup ("[no message]"); /* Ooops. */
for (int i = 0; i < 3; i++)
{
/* We try to get different properties first as they contain
the SMTP address of the sender. EMAIL address can be
some LDAP stuff for exchange. */
hr = HrGetOneProp ((LPMAPIPROP)message, try_props[i],
&propval);
if (!FAILED (hr))
{
break;
}
}
/* This is the last result that should always work but not necessarily
contain an SMTP Address. */
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;
}
if (strstr (buf, "/o="))
{
char *buf2;
/* If both SMTP Address properties are not set
we need to fallback to resolve the address
through the address book */
log_debug ("%s:%s: resolving exchange address.",
SRCNAME, __func__);
buf2 = resolve_ex_from_address (message);
if (buf2)
{
xfree (buf);
return buf2;
}
}
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 )
{
msgtype = string_to_type (propval->Value.lpszA);
}
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;
}
gpgol_release (stream);
}
gpgol_release (session);
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-id of the attachment OBJ or NULL if it does
not exists. Caller must free. */
static char *
get_attach_content_id (LPATTACH obj)
{
HRESULT hr;
LPSPropValue propval = NULL;
char *name;
hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_CONTENT_ID, &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 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. */
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);
gpgol_release (mapitable);
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);
gpgol_release (mapitable);
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);
table[pos].content_id = fast? NULL : get_attach_content_id (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);
gpgol_release (att);
}
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)
gpgol_release (mapitable);
for (pos=0; !table[pos].end_of_table; pos++)
{
xfree (table[pos].filename);
xfree (table[pos].content_type);
xfree (table[pos].content_id);
}
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__);
gpgol_release (att);
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);
gpgol_release (att);
return NULL;
}
if (r_attach)
*r_attach = att;
else
gpgol_release (att);
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)
{
HRESULT hr;
LPSTREAM stream;
STATSTG statInfo;
ULONG nread;
char *buffer;
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);
gpgol_release (stream);
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);
gpgol_release (stream);
return NULL;
}
if (nread != statInfo.cbSize.QuadPart)
{
log_error ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
xfree (buffer);
buffer = NULL;
}
gpgol_release (stream);
/* Make sure that the buffer is a C string. */
if (buffer)
buffer[nread] = 0;
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,
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__);
gpgol_release (att);
return NULL;
}
buffer = attach_to_buffer (att, r_nbytes);
gpgol_release (att);
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:
gpgol_release (att);
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 around mapi_get_gpgol_draft_info to avoid
the string handling.
Return values are:
0 -> Do nothing
1 -> Encrypt
2 -> Sign
3 -> Encrypt & Sign*/
int
get_gpgol_draft_info_flags (LPMESSAGE message)
{
char *buf = mapi_get_gpgol_draft_info (message);
int ret = 0;
if (!buf)
{
return 0;
}
if (buf[0] == 'E')
{
ret |= 1;
}
if (buf[1] == 'S')
{
ret |= 2;
}
xfree (buf);
return ret;
}
/* Sets the draft info flags. Protocol is always Auto.
flags should be the same as defined by
get_gpgol_draft_info_flags
*/
int
set_gpgol_draft_info_flags (LPMESSAGE message, int flags)
{
char buf[4];
buf[3] = '\0';
buf[2] = 'A'; /* Protocol */
buf[1] = flags & 2 ? 'S' : 's';
buf[0] = flags & 1 ? 'E' : 'e';
return mapi_set_gpgol_draft_info (message, buf);
}
/* 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)
{
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;
/* 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",
SRCNAME, __func__);
return NULL;
}
const std::string hdrStr = mapi_get_header (message);
if (hdrStr.empty())
{
log_error ("%s:%s: failed to get headers",
SRCNAME, __func__);
return NULL;
}
header_lines = hdrStr.c_str();
while ((s = strchr (header_lines, '\n')))
{
length = (s - header_lines);
if (length && s[-1] == '\r')
length--;
if (!strncmp ("Wks-Phase: confirm", header_lines,
std::max (18, (int) length)))
{
log_debug ("%s:%s: detected wks confirmation mail",
SRCNAME, __func__);
retstr = xstrdup ("wks.confirmation.mail");
rfc822parse_close (msg);
return retstr;
}
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);
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;
}
/* 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);
gpgol_release (mapitable);
return -1;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
if (!n_attach)
{
FreeProws (mapirows);
gpgol_release (mapitable);
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);
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;
}
}
gpgol_release (att);
if (r_ishtml)
*r_ishtml = (bodytype == 2);
break;
}
gpgol_release (att);
}
FreeProws (mapirows);
gpgol_release (mapitable);
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);
gpgol_release (mapitable);
return 0;
}
n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
if (!n_attach)
{
FreeProws (mapirows);
gpgol_release (mapitable);
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)
{
gpgol_release (att);
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;
}
gpgol_release (att);
}
FreeProws (mapirows);
gpgol_release (mapitable);
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 ();
gpgol_release (outstream);
}
if (instream)
gpgol_release (instream);
if (att)
gpgol_release (att);
return result;
}
/* Copy the MAPI body to a PGPBODY type attachment. */
int
mapi_body_to_attachment (LPMESSAGE message)
{
HRESULT hr;
LPSTREAM instream;
ULONG newpos;
LPATTACH newatt = NULL;
SPropValue prop;
LPSTREAM outstream = NULL;
LPUNKNOWN punk;
GpgOLStr body_filename (PGPBODYFILENAME);
instream = mapi_get_body_as_stream (message);
if (!instream)
return -1;
hr = message->CreateAttach (NULL, 0, &newpos, &newatt);
if (hr)
{
log_error ("%s:%s: can't create attachment: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
prop.ulPropTag = PR_ATTACH_METHOD;
prop.Value.ul = ATTACH_BY_VALUE;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach method: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
/* Mark that attachment so that we know why it has been created. */
if (get_gpgolattachtype_tag (message, &prop.ulPropTag) )
goto leave;
prop.Value.l = ATTACHTYPE_PGPBODY;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set %s property: hr=%#lx\n",
SRCNAME, __func__, "GpgOL Attach Type", hr);
goto leave;
}
prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
prop.Value.b = TRUE;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set hidden attach flag: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
prop.ulPropTag = PR_ATTACH_FILENAME_A;
prop.Value.lpszA = body_filename;
hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
if (hr)
{
log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
punk = (LPUNKNOWN)outstream;
hr = newatt->OpenProperty (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 leave;
}
outstream = (LPSTREAM)punk;
/* Insert a blank line so that our mime parser skips over the mail
headers. */
hr = outstream->Write ("\r\n", 2, NULL);
if (hr)
{
log_error ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
goto leave;
}
{
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;
}
gpgol_release (outstream);
outstream = NULL;
hr = newatt->SaveChanges (0);
if (hr)
{
log_error ("%s:%s: SaveChanges of the attachment failed: hr=%#lx\n",
SRCNAME, __func__, hr);
goto leave;
}
gpgol_release (newatt);
newatt = NULL;
hr = mapi_save_changes (message, KEEP_OPEN_READWRITE);
leave:
if (outstream)
{
outstream->Revert ();
gpgol_release (outstream);
}
if (newatt)
gpgol_release (newatt);
gpgol_release (instream);
return hr? -1:0;
}
int
mapi_mark_or_create_moss_attach (LPMESSAGE message, msgtype_t msgtype)
{
int i;
if (msgtype == MSGTYPE_UNKNOWN ||
msgtype == MSGTYPE_GPGOL)
{
return 0;
}
/* First check if we already have one marked. */
mapi_attach_item_t *table = mapi_create_attach_table (message, 0);
int part1 = 0,
part2 = 0;
for (i = 0; table && !table[i].end_of_table; i++)
{
if (table[i].attach_type == ATTACHTYPE_PGPBODY ||
table[i].attach_type == ATTACHTYPE_MOSS ||
table[i].attach_type == ATTACHTYPE_MOSSTEMPL)
{
if (!part1)
{
part1 = i + 1;
}
else if (!part2)
{
/* If we have two MOSS attachments we use
the second one. */
part2 = i + 1;
break;
}
}
}
if (part1 || part2)
{
/* Found existing moss attachment */
mapi_release_attach_table (table);
/* Remark to ensure that it is hidden. As our revert
code must unhide it so that it is not stored in winmail.dat
but used as the mosstmpl. */
mapi_attach_item_t *item = table - 1 + (part2 ? part2 : part1);
LPATTACH att;
if (message->OpenAttach (item->mapipos, NULL, MAPI_BEST_ACCESS, &att) != S_OK)
{
log_error ("%s:%s: can't open attachment at %d",
SRCNAME, __func__, item->mapipos);
return -1;
}
if (!mapi_test_attach_hidden (att))
{
mapi_set_attach_hidden (att);
}
gpgol_release (att);
if (part2)
return part2;
return part1;
}
if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED ||
msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
{
/* Inline message we need to create body attachment so that we
are able to restore the content. */
if (mapi_body_to_attachment (message))
{
log_error ("%s:%s: Failed to create body attachment.",
SRCNAME, __func__);
return 0;
}
log_debug ("%s:%s: Created body attachment. Repeating lookup.",
SRCNAME, __func__);
/* The position of the MOSS attach might change depending on
the attachment count of the mail. So repeat the check to get
the right position. */
return mapi_mark_or_create_moss_attach (message, msgtype);
}
if (!table)
{
log_debug ("%s:%s: Neither pgp inline nor an attachment table.",
SRCNAME, __func__);
return 0;
}
/* MIME Mails check for S/MIME first. */
for (i = 0; !table[i].end_of_table; i++)
{
if (table[i].content_type
&& (!strcmp (table[i].content_type, "application/pkcs7-mime")
|| !strcmp (table[i].content_type,
"application/x-pkcs7-mime"))
&& table[i].filename
&& !strcmp (table[i].filename, "smime.p7m"))
break;
}
if (!table[i].end_of_table)
{
mapi_mark_moss_attach (message, table + i);
mapi_release_attach_table (table);
return i + 1;
}
/* PGP/MIME or S/MIME stuff. */
/* Multipart/encrypted message: We expect 2 attachments.
The first one with the version number and the second one
with the ciphertext. As we don't know wether we are
called the first time, we first try to find these
attachments by looking at all attachments. Only if this
fails we identify them by their order (i.e. the first 2
attachments) and mark them as part1 and part2. */
for (i = 0; !table[i].end_of_table; i++); /* Count entries */
if (i >= 2)
{
int part1_idx = -1,
part2_idx = -1;
/* At least 2 attachments but none are marked. Thus we
assume that this is the first time we see this
message and we will set the mark now if we see
appropriate content types. */
if (table[0].content_type
&& !strcmp (table[0].content_type,
"application/pgp-encrypted"))
part1_idx = 0;
if (table[1].content_type
&& !strcmp (table[1].content_type,
"application/octet-stream"))
part2_idx = 1;
if (part1_idx != -1 && part2_idx != -1)
{
mapi_mark_moss_attach (message, table+part1_idx);
mapi_mark_moss_attach (message, table+part2_idx);
mapi_release_attach_table (table);
return 2;
}
}
if (!table[0].end_of_table && table[1].end_of_table)
{
/* No MOSS flag found in the table but there is only one
attachment. Due to the message type we know that this is
the original MOSS message. We mark this attachment as
hidden, so that it won't get displayed. We further mark
it as our original MOSS attachment so that after parsing
we have a mean to find it again (see above). */
mapi_mark_moss_attach (message, table + 0);
mapi_release_attach_table (table);
return 1;
}
mapi_release_attach_table (table);
return 0; /* No original attachment - this should not happen. */
}
diff --git a/src/ribbon-callbacks.cpp b/src/ribbon-callbacks.cpp
index db0042f..f1184d4 100644
--- a/src/ribbon-callbacks.cpp
+++ b/src/ribbon-callbacks.cpp
@@ -1,1834 +1,815 @@
/* ribbon-callbacks.h - Callbacks for the ribbon extension interface
* Copyright (C) 2013 Intevation GmbH
* Software engineering by Intevation 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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include "ribbon-callbacks.h"
#include "gpgoladdin.h"
#include "common.h"
#include "mymapi.h"
#include "mymapitags.h"
#include "common.h"
#include "engine.h"
#include "engine-assuan.h"
#include "mapihelp.h"
#include "mimemaker.h"
#include "filetype.h"
-#include "gpgolstr.h"
#include "mail.h"
#include
#include
using namespace GpgME;
-#define OPAQUE_SIGNED_MARKER "-----BEGIN PGP MESSAGE-----"
-
/* Gets the context of a ribbon control. And prints some
useful debug output */
HRESULT getContext (LPDISPATCH ctrl, LPDISPATCH *context)
{
*context = get_oom_object (ctrl, "get_Context");
if (*context)
{
char *name = get_object_name (*context);
log_debug ("%s:%s: contextObj: %s",
SRCNAME, __func__, name);
xfree (name);
}
return context ? S_OK : E_FAIL;
}
#define OP_ENCRYPT 1 /* Encrypt the data */
#define OP_SIGN 2 /* Sign the data */
#define OP_DECRYPT 1 /* Decrypt the data */
#define OP_VERIFY 2 /* Verify the data */
#define DATA_BODY 4 /* Use text body as data */
#define DATA_SELECTION 8 /* Use selection as data */
-/* Read hfile in chunks of 4KB and writes them to the sink */
-static int
-copyFileToSink (HANDLE hFile, sink_t sink)
-{
- char buf[4096];
- DWORD bytesRead = 0;
- do
- {
- if (!ReadFile (hFile, buf, sizeof buf, &bytesRead, NULL))
- {
- log_error ("%s:%s: Could not read source file.",
- SRCNAME, __func__);
- return -1;
- }
- if (write_buffer (sink, bytesRead ? buf : NULL, bytesRead))
- {
- log_error ("%s:%s: Could not write out buffer",
- SRCNAME, __func__);
- return -1;
- }
- }
- while (bytesRead);
- return 0;
-}
-
-static int
-attachSignature (LPDISPATCH mailItem, char *subject, HANDLE hFileToSign,
- protocol_t protocol, unsigned int session_number,
- HWND curWindow, wchar_t *fileNameToSign, char *sender)
-{
- wchar_t *sigName = NULL;
- wchar_t *sigFileName = NULL;
- HANDLE hSigFile = NULL;
- int rc = 0;
- struct sink_s encsinkmem;
- sink_t encsink = &encsinkmem;
- struct sink_s sinkmem;
- sink_t sink = &sinkmem;
- engine_filter_t filter = NULL;
-
- memset (encsink, 0, sizeof *encsink);
- memset (sink, 0, sizeof *sink);
-
- /* Prepare a fresh filter */
- if ((rc = engine_create_filter (&filter, write_buffer_for_cb, sink)))
- {
- goto failure;
- }
- encsink->cb_data = filter;
- encsink->writefnc = sink_encryption_write;
- engine_set_session_number (filter, session_number);
- engine_set_session_title (filter, subject ? subject :_("GpgOL"));
-
- if (engine_sign_start (filter, curWindow, protocol, sender, &protocol))
- goto failure;
-
- sigName = get_pretty_attachment_name (fileNameToSign, protocol, 1);
-
- /* If we are unlucky the number of temporary file artifacts might
- differ for the signature and the encrypted file but we have
- to live with that. */
- sigFileName = get_tmp_outfile (sigName, &hSigFile);
- sink->cb_data = hSigFile;
- sink->writefnc = sink_file_write;
-
- if (!sigFileName)
- {
- log_error ("%s:%s: Could not get a decent attachment name",
- SRCNAME, __func__);
- goto failure;
- }
-
- /* Reset the file to sign handle to the beginning of the file and
- copy it to the signature buffer */
- SetFilePointer (hFileToSign, 0, NULL, 0);
- if ((rc=copyFileToSink (hFileToSign, encsink)))
- goto failure;
-
- /* Lets hope the user did not select a huge file. We are hanging
- here until encryption is completed.. */
- if ((rc = engine_wait (filter)))
- goto failure;
-
- filter = NULL; /* Not valid anymore. */
- encsink->cb_data = NULL; /* Not needed anymore. */
-
- if (!sink->enc_counter)
- {
- log_error ("%s:%s: nothing received from engine", SRCNAME, __func__);
- goto failure;
- }
-
- /* Now we have an encrypted file behind encryptedFile. Let's add it */
- add_oom_attachment (mailItem, sigFileName, nullptr);
-
-failure:
- xfree (sigFileName);
- xfree (sigName);
- if (hSigFile)
- {
- CloseHandle (hSigFile);
- DeleteFileW (sigFileName);
- }
- return rc;
-}
-
-/* do_composer_action
- Encrypts / Signs text in an IInspector context.
- Depending on the flags either the
- active selection or the full body is encrypted.
- Combine OP_ENCRYPT and OP_SIGN if you want both.
-*/
-
-HRESULT
-do_composer_action (LPDISPATCH ctrl, int flags)
-{
- LPDISPATCH context = NULL;
- LPDISPATCH selection = NULL;
- LPDISPATCH wordEditor = NULL;
- LPDISPATCH application = NULL;
- LPDISPATCH mailItem = NULL;
- LPDISPATCH sender = NULL;
- LPDISPATCH recipients = NULL;
-
- struct sink_s encsinkmem;
- sink_t encsink = &encsinkmem;
- struct sink_s sinkmem;
- sink_t sink = &sinkmem;
- char* senderAddr = NULL;
- char** recipientAddrs = NULL;
- LPSTREAM tmpstream = NULL;
- engine_filter_t filter = NULL;
- char* plaintext = NULL;
- int rc = 0;
- HRESULT hr;
- HWND curWindow;
- protocol_t protocol;
- unsigned int session_number;
- int i;
- STATSTG tmpStat;
-
- log_debug ("%s:%s: enter", SRCNAME, __func__);
-
- hr = getContext (ctrl, &context);
- if (FAILED(hr))
- return hr;
-
- memset (encsink, 0, sizeof *encsink);
- memset (sink, 0, sizeof *sink);
-
- curWindow = get_oom_context_window (context);
-
- wordEditor = get_oom_object (context, "WordEditor");
- application = get_oom_object (wordEditor, "get_Application");
- selection = get_oom_object (application, "get_Selection");
- mailItem = get_oom_object (context, "CurrentItem");
- sender = get_oom_object (mailItem, "Session.CurrentUser");
- recipients = get_oom_object (mailItem, "Recipients");
-
- if (!wordEditor || !application || !selection || !mailItem ||
- !sender || !recipients)
- {
- MessageBox (NULL,
- "Internal error in GpgOL.\n"
- "Could not find all objects.",
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- log_error ("%s:%s: Could not find all objects.",
- SRCNAME, __func__);
- goto failure;
- }
-
- if (flags & DATA_SELECTION)
- {
- plaintext = get_oom_string (selection, "Text");
-
- if (!plaintext || strlen (plaintext) <= 1)
- {
- MessageBox (NULL,
- _("Please select text to encrypt."),
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- goto failure;
- }
- }
- else if (flags & DATA_BODY)
- {
- plaintext = get_oom_string (mailItem, "Body");
- if (!plaintext || strlen (plaintext) <= 1)
- {
- MessageBox (NULL,
- _("Textbody empty."),
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- goto failure;
- }
- }
-
- /* Create a temporary sink to construct the encrypted data. */
- hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
- (SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
- | STGM_CREATE | STGM_READWRITE),
- NULL, GpgOLStr("GPG"), &tmpstream);
-
- if (FAILED (hr))
- {
- log_error ("%s:%s: can't create temp file: hr=%#lx\n",
- SRCNAME, __func__, hr);
- rc = -1;
- goto failure;
- }
-
- sink->cb_data = tmpstream;
- sink->writefnc = sink_std_write;
-
- /* Now lets prepare our encryption */
- session_number = engine_new_session_number ();
-
- /* Prepare the encryption sink */
-
- if (engine_create_filter (&filter, write_buffer_for_cb, sink))
- {
- goto failure;
- }
-
- encsink->cb_data = filter;
- encsink->writefnc = sink_encryption_write;
-
- engine_set_session_number (filter, session_number);
- engine_set_session_title (filter, _("GpgOL"));
-
- senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
-
- if (flags & OP_ENCRYPT)
- {
- recipientAddrs = get_oom_recipients (recipients);
-
- if (!recipientAddrs || !(*recipientAddrs))
- {
- MessageBox (NULL,
- _("Please add at least one recipent."),
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- goto failure;
- }
-
- if ((rc=engine_encrypt_prepare (filter, curWindow,
- PROTOCOL_UNKNOWN,
- (flags & OP_SIGN) ?
- ENGINE_FLAG_SIGN_FOLLOWS : 0,
- senderAddr, recipientAddrs,
- &protocol)))
- {
- log_error ("%s:%s: engine encrypt prepare failed : %s",
- SRCNAME, __func__, gpg_strerror (rc));
- goto failure;
- }
-
- if ((rc=engine_encrypt_start (filter, 0)))
- {
- log_error ("%s:%s: engine encrypt start failed: %s",
- SRCNAME, __func__, gpg_strerror (rc));
- goto failure;
- }
- }
- else
- {
- /* We could do some kind of clearsign / sign text as attachment here
- but it is error prone */
- if ((rc=engine_sign_opaque_start (filter, curWindow, PROTOCOL_UNKNOWN,
- senderAddr, &protocol)))
- {
- log_error ("%s:%s: engine sign start failed: %s",
- SRCNAME, __func__, gpg_strerror (rc));
- goto failure;
- }
- }
-
- /* Write the text in the encryption sink. */
- rc = write_buffer (encsink, plaintext, strlen (plaintext));
-
- 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;
- }
-
- /* Check the size of the encrypted data */
- tmpstream->Stat (&tmpStat, 0);
-
- if (tmpStat.cbSize.QuadPart > UINT_MAX)
- {
- log_error ("%s:%s: No one should write so large mails.",
- SRCNAME, __func__);
- goto failure;
- }
-
- /* Copy the encrypted stream to the message editor. */
- {
- LARGE_INTEGER off;
- ULONG nread;
-
- char buffer[(unsigned int)tmpStat.cbSize.QuadPart + 1];
-
- memset (buffer, 0, sizeof buffer);
-
- off.QuadPart = 0;
- hr = tmpstream->Seek (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;
- }
- hr = tmpstream->Read (buffer, sizeof (buffer) - 1, &nread);
- if (hr)
- {
- log_error ("%s:%s: IStream::Read failed: hr=%#lx",
- SRCNAME, __func__, hr);
- rc = gpg_error (GPG_ERR_EIO);
- goto failure;
- }
- if (strlen (buffer) > 1)
- {
- if (flags & OP_SIGN)
- {
- /* When signing we append the signature after the body */
- unsigned int combinedSize = strlen (buffer) +
- strlen (plaintext) + 5;
- char combinedBody[combinedSize];
- memset (combinedBody, 0, combinedSize);
- snprintf (combinedBody, combinedSize, "%s\r\n\r\n%s", plaintext,
- buffer);
- if (flags & DATA_SELECTION)
- put_oom_string (selection, "Text", combinedBody);
- else if (flags & DATA_BODY)
- put_oom_string (mailItem, "Body", combinedBody);
-
- }
- else if (protocol == PROTOCOL_SMIME)
- {
- unsigned int enclosedSize = strlen (buffer) + 34 + 31 + 1;
- char enclosedData[enclosedSize];
- snprintf (enclosedData, sizeof enclosedData,
- "-----BEGIN ENCRYPTED MESSAGE-----\r\n"
- "%s"
- "-----END ENCRYPTED MESSAGE-----\r\n", buffer);
- if (flags & DATA_SELECTION)
- put_oom_string (selection, "Text", enclosedData);
- else if (flags & DATA_BODY)
- put_oom_string (mailItem, "Body", enclosedData);
-
- }
- else
- {
- if (flags & DATA_SELECTION)
- put_oom_string (selection, "Text", buffer);
- else if (flags & DATA_BODY)
- {
- put_oom_string (mailItem, "Body", buffer);
- }
- }
- }
- else
- {
- /* Just to be save not to overwrite the selection with
- an empty buffer */
- log_error ("%s:%s: unexpected problem ", SRCNAME, __func__);
- goto failure;
- }
- }
-
-failure:
- if (rc)
- log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
- gpg_strerror (rc), gpg_strsource (rc));
- engine_cancel (filter);
- gpgol_release(wordEditor);
- gpgol_release(application);
- gpgol_release(selection);
- gpgol_release(sender);
- gpgol_release(recipients);
- gpgol_release(mailItem);
- gpgol_release(tmpstream);
- xfree (plaintext);
- xfree (senderAddr);
- if (recipientAddrs)
- {
- for (i=0; recipientAddrs && recipientAddrs[i]; i++)
- xfree (recipientAddrs[i]);
- xfree (recipientAddrs);
- }
- log_debug ("%s:%s: leave", SRCNAME, __func__);
-
- return S_OK;
-}
-
HRESULT
decryptAttachments (LPDISPATCH ctrl)
{
LPDISPATCH context = NULL;
LPDISPATCH attachmentSelection;
int attachmentCount;
HRESULT hr = 0;
int i = 0;
HWND curWindow;
int err;
hr = getContext(ctrl, &context);
attachmentSelection = get_oom_object (context, "AttachmentSelection");
if (!attachmentSelection)
{
/* We can be called from a context menu, in that case we
directly have an AttachmentSelection context. Otherwise
we have an Explorer context with an Attachment Selection property. */
attachmentSelection = context;
}
attachmentCount = get_oom_int (attachmentSelection, "Count");
curWindow = get_oom_context_window (context);
{
char *filenames[attachmentCount + 1];
filenames[attachmentCount] = NULL;
/* Yes the items start at 1! */
for (i = 1; i <= attachmentCount; i++)
{
char buf[16];
char *filename;
wchar_t *wcsOutFilename;
DISPPARAMS saveParams;
VARIANT aVariant[1];
LPDISPATCH attachmentObj;
DISPID saveID;
snprintf (buf, sizeof (buf), "Item(%i)", i);
attachmentObj = get_oom_object (attachmentSelection, buf);
if (!attachmentObj)
{
/* Should be impossible */
filenames[i-1] = NULL;
log_error ("%s:%s: could not find Item %i;",
SRCNAME, __func__, i);
break;
}
filename = get_oom_string (attachmentObj, "FileName");
saveID = lookup_oom_dispid (attachmentObj, "SaveAsFile");
saveParams.rgvarg = aVariant;
saveParams.rgvarg[0].vt = VT_BSTR;
filenames[i-1] = get_save_filename (NULL, filename);
xfree (filename);
if (!filenames [i-1])
continue;
wcsOutFilename = utf8_to_wchar2 (filenames[i-1],
strlen(filenames[i-1]));
saveParams.rgvarg[0].bstrVal = SysAllocString (wcsOutFilename);
saveParams.cArgs = 1;
saveParams.cNamedArgs = 0;
hr = attachmentObj->Invoke (saveID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &saveParams,
NULL, NULL, NULL);
SysFreeString (saveParams.rgvarg[0].bstrVal);
gpgol_release (attachmentObj);
if (FAILED(hr))
{
int j;
log_debug ("%s:%s: Saving to file failed. hr: %x",
SRCNAME, __func__, (unsigned int) hr);
for (j = 0; j < i; j++)
xfree (filenames[j]);
gpgol_release (attachmentSelection);
return hr;
}
}
gpgol_release (attachmentSelection);
err = op_assuan_start_decrypt_files (curWindow, filenames);
for (i = 0; i < attachmentCount; i++)
xfree (filenames[i]);
}
log_debug ("%s:%s: Leaving. Err: %i",
SRCNAME, __func__, err);
return S_OK; /* If we return an error outlook will show that our
callback function failed in an ugly window. */
}
-/* MIME erify mail helper. Returns 0 if it
- was not called with a MIME crypto message or on error. */
-static int
-verify_mime (LPDISPATCH mailitem)
-{
- int ret = 0;
-
- LPMESSAGE message = get_oom_base_message (mailitem);
- if (!message)
- {
- log_error ("%s:%s: Failed to get the base message",
- SRCNAME, __func__);
- return 0;
- }
- ret = -1;
-// ret = message_incoming_handler (message, NULL, true /*force */);
- gpgol_release (message);
-
- return ret;
-}
-
-/* do_reader_action
- decrypts the content of an inspector. Controled by flags
- similary to the do_composer_action.
-*/
-
-HRESULT
-do_reader_action (LPDISPATCH ctrl, int flags)
-{
- LPDISPATCH context = NULL;
- LPDISPATCH selection = NULL;
- LPDISPATCH wordEditor = NULL;
- LPDISPATCH mailItem = NULL;
- LPDISPATCH wordApplication = NULL;
-
- struct sink_s decsinkmem;
- sink_t decsink = &decsinkmem;
- struct sink_s sinkmem;
- sink_t sink = &sinkmem;
-
- LPSTREAM tmpstream = NULL;
- engine_filter_t filter = NULL;
- HWND curWindow;
- char* encData = NULL;
- char* senderAddr = NULL;
- char* subject = NULL;
- int encDataLen = 0;
- int rc = 0;
- unsigned int session_number;
- HRESULT hr;
- STATSTG tmpStat;
-
- protocol_t protocol;
-
- hr = getContext (ctrl, &context);
- if (FAILED(hr))
- return hr;
-
- memset (decsink, 0, sizeof *decsink);
- memset (sink, 0, sizeof *sink);
-
- curWindow = get_oom_context_window (context);
-
- if (!(flags & DATA_BODY))
- {
- wordEditor = get_oom_object (context, "WordEditor");
- wordApplication = get_oom_object (wordEditor, "get_Application");
- selection = get_oom_object (wordApplication, "get_Selection");
- }
- mailItem = get_oom_object (context, "CurrentItem");
-
- if ((!wordEditor || !wordApplication || !selection || !mailItem) &&
- !(flags & DATA_BODY))
- {
- MessageBox (NULL,
- "Internal error in GpgOL.\n"
- "Could not find all objects.",
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- log_error ("%s:%s: Could not find all objects.",
- SRCNAME, __func__);
- goto failure;
- }
-
- if (!mailItem)
- {
- /* This happens when we try to decrypt the body of a mail in the
- explorer context. */
- mailItem = get_oom_object (context, "Selection.Item(1)");
-
- if (!mailItem)
- {
- MessageBox (NULL,
- _("Please select a Mail."),
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- goto failure;
- }
- }
-
- if (flags & DATA_SELECTION)
- {
- encData = get_oom_string (selection, "Text");
-
- if (!encData || (encDataLen = strlen (encData)) <= 1)
- {
- MessageBox (NULL,
- _("Please select the data you wish to decrypt."),
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- goto failure;
- }
- }
- else if (flags & DATA_BODY)
- {
- encData = get_oom_string (mailItem, "Body");
-
- if (!encData || (encDataLen = strlen (encData)) <= 1)
- {
- MessageBox (NULL,
- _("Nothing to decrypt."),
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- goto failure;
- }
- }
-
- fix_linebreaks (encData, &encDataLen);
-
- /* We check if the data we work on was opaque signed. This is
- true for signed stuff created by ribbon-callbacks and not a
- decent MIME implementation. So in that case we don't use
- verify_mime */
- if (!strstr (encData, OPAQUE_SIGNED_MARKER) && verify_mime (mailItem))
- {
- log_debug ("%s:%s: This was a mime message.",
- SRCNAME, __func__);
-
- if (flags & OP_DECRYPT)
- {
- MessageBox (NULL,
- "This message is in MIME format. Due to technical restrictions "
- "it can only be decrypted once per session. To decrypt it again "
- "please restart Outlook and open the message.",
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- }
- goto failure;
- }
-
- subject = get_oom_string (mailItem, "Subject");
- if (get_oom_bool (mailItem, "Sent"))
- {
- char *addrType = get_oom_string (mailItem, "SenderEmailType");
- if (addrType && strcmp("SMTP", addrType) == 0)
- {
- senderAddr = get_oom_string (mailItem, "SenderEmailAddress");
- }
- else
- {
- /* Not SMTP, fall back to try getting the property. */
- LPDISPATCH sender = get_oom_object (mailItem, "Sender");
- senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
- gpgol_release (sender);
- }
- xfree (addrType);
- }
- else
- {
- /* If the message has not been sent we might be composing
- in this case use the current address */
- LPDISPATCH sender = get_oom_object (mailItem, "Session.CurrentUser");
- senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
- gpgol_release (sender);
- }
-
- /* Determine the protocol based on the content */
- protocol = is_cms_data (encData, encDataLen) ? PROTOCOL_SMIME :
- PROTOCOL_OPENPGP;
-
- hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
- (SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
- | STGM_CREATE | STGM_READWRITE),
- NULL, GpgOLStr("GPG"), &tmpstream);
-
- if (FAILED (hr))
- {
- log_error ("%s:%s: can't create temp file: hr=%#lx\n",
- SRCNAME, __func__, hr);
- rc = -1;
- goto failure;
- }
-
- sink->cb_data = tmpstream;
- sink->writefnc = sink_std_write;
-
- session_number = engine_new_session_number ();
- if (engine_create_filter (&filter, write_buffer_for_cb, sink))
- goto failure;
-
- decsink->cb_data = filter;
- decsink->writefnc = sink_encryption_write;
-
- engine_set_session_number (filter, session_number);
- engine_set_session_title (filter, subject ? subject : _("GpgOL"));
-
- if (flags & OP_DECRYPT)
- {
- if ((rc=engine_decrypt_start (filter, curWindow,
- protocol,
- 1, NULL)))
- {
- log_error ("%s:%s: engine decrypt start failed: %s",
- SRCNAME, __func__, gpg_strerror (rc));
- goto failure;
- }
- }
- else if (flags & OP_VERIFY)
- {
- log_debug ("Starting verify");
- if ((rc=engine_verify_start (filter, curWindow,
- NULL, 0, protocol, senderAddr)))
- {
- log_error ("%s:%s: engine verify start failed: %s",
- SRCNAME, __func__, gpg_strerror (rc));
- goto failure;
- }
- }
-
- /* Write the text in the decryption sink. */
- rc = write_buffer (decsink, encData, encDataLen);
-
- /* Flush the decryption sink and wait for the decryption to get
- ready. */
- if ((rc = write_buffer (decsink, NULL, 0)))
- goto failure;
- if ((rc = engine_wait (filter)))
- goto failure;
- filter = NULL; /* Not valid anymore. */
- decsink->cb_data = NULL; /* Not needed anymore. */
-
- if (!sink->enc_counter)
- {
- log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
- goto failure;
- }
-
- /* Check the size of the decrypted data */
- tmpstream->Stat (&tmpStat, 0);
-
- if (tmpStat.cbSize.QuadPart > UINT_MAX)
- {
- log_error ("%s:%s: No one should write so large mails.",
- SRCNAME, __func__);
- goto failure;
- }
-
- /* Copy the decrypted stream to the message editor. */
- {
- LARGE_INTEGER off;
- ULONG nread;
- char buffer[(unsigned int)tmpStat.cbSize.QuadPart + 1];
-
- memset (buffer, 0, sizeof buffer);
-
- off.QuadPart = 0;
- hr = tmpstream->Seek (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;
- }
- hr = tmpstream->Read (buffer, sizeof (buffer) - 1, &nread);
- if (hr)
- {
- log_error ("%s:%s: IStream::Read failed: hr=%#lx",
- SRCNAME, __func__, hr);
- rc = gpg_error (GPG_ERR_EIO);
- goto failure;
- }
- if (strlen (buffer) > 1)
- {
- /* Now replace the crypto data with the decrypted data or show it
- somehow.*/
- int err = 0;
- if (flags & DATA_SELECTION)
- {
- err = put_oom_string (selection, "Text", buffer);
- }
- else if (flags & DATA_BODY)
- {
- err = put_oom_string (mailItem, "Body", buffer);
- }
-
- if (err)
- {
- MessageBox (NULL, buffer,
- flags & OP_DECRYPT ? _("Plain text") :
- _("Signed text"),
- MB_ICONINFORMATION|MB_OK);
- }
- }
- else
- {
- /* Just to be save not to overwrite the selection with
- an empty buffer */
- log_error ("%s:%s: unexpected problem ", SRCNAME, __func__);
- goto failure;
- }
- }
-
- failure:
- if (rc)
- log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
- gpg_strerror (rc), gpg_strsource (rc));
- engine_cancel (filter);
- gpgol_release (mailItem);
- gpgol_release (selection);
- gpgol_release (wordEditor);
- gpgol_release (wordApplication);
- xfree (encData);
- xfree (senderAddr);
- xfree (subject);
- if (tmpstream)
- gpgol_release (tmpstream);
-
- return S_OK;
-}
-
-
/* getIcon
Loads a PNG image from the resurce converts it into a Bitmap
and Wraps it in an PictureDispatcher that is returned as result.
Based on documentation from:
http://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI
*/
HRESULT
getIcon (int id, VARIANT* result)
{
PICTDESC pdesc;
LPDISPATCH pPict;
HRESULT hr;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::Bitmap* pbitmap;
ULONG_PTR gdiplusToken;
HRSRC hResource;
DWORD imageSize;
const void* pResourceData;
HGLOBAL hBuffer;
memset (&pdesc, 0, sizeof pdesc);
pdesc.cbSizeofstruct = sizeof pdesc;
pdesc.picType = PICTYPE_BITMAP;
if (!result)
{
log_error ("getIcon called without result variant.");
return E_POINTER;
}
/* Initialize GDI */
gdiplusStartupInput.DebugEventCallback = NULL;
gdiplusStartupInput.SuppressBackgroundThread = FALSE;
gdiplusStartupInput.SuppressExternalCodecs = FALSE;
gdiplusStartupInput.GdiplusVersion = 1;
GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL);
/* Get the image from the resource file */
hResource = FindResource (glob_hinst, MAKEINTRESOURCE(id), RT_RCDATA);
if (!hResource)
{
log_error ("%s:%s: failed to find image: %i",
SRCNAME, __func__, id);
return E_FAIL;
}
imageSize = SizeofResource (glob_hinst, hResource);
if (!imageSize)
return E_FAIL;
pResourceData = LockResource (LoadResource(glob_hinst, hResource));
if (!pResourceData)
{
log_error ("%s:%s: failed to load image: %i",
SRCNAME, __func__, id);
return E_FAIL;
}
hBuffer = GlobalAlloc (GMEM_MOVEABLE, imageSize);
if (hBuffer)
{
void* pBuffer = GlobalLock (hBuffer);
if (pBuffer)
{
IStream* pStream = NULL;
CopyMemory (pBuffer, pResourceData, imageSize);
if (CreateStreamOnHGlobal (hBuffer, FALSE, &pStream) == S_OK)
{
pbitmap = Gdiplus::Bitmap::FromStream (pStream);
gpgol_release (pStream);
if (!pbitmap || pbitmap->GetHBITMAP (0, &pdesc.bmp.hbitmap))
{
log_error ("%s:%s: failed to get PNG.",
SRCNAME, __func__);
}
}
}
GlobalUnlock (pBuffer);
}
GlobalFree (hBuffer);
Gdiplus::GdiplusShutdown (gdiplusToken);
/* Wrap the image into an OLE object. */
hr = OleCreatePictureIndirect (&pdesc, IID_IPictureDisp,
TRUE, (void **) &pPict);
if (hr != S_OK || !pPict)
{
log_error ("%s:%s: OleCreatePictureIndirect failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return -1;
}
result->pdispVal = pPict;
result->vt = VT_DISPATCH;
return S_OK;
}
-/* Adds an encrypted attachment if the flag OP_SIGN is set
- a detached signature of the encrypted file is also added. */
-static HRESULT
-attachEncryptedFile (LPDISPATCH ctrl, int flags)
-{
- LPDISPATCH context = NULL;
- LPDISPATCH mailItem = NULL;
- LPDISPATCH sender = NULL;
- LPDISPATCH recipients = NULL;
- HRESULT hr;
- char* senderAddr = NULL;
- char** recipientAddrs = NULL;
- char* subject = NULL;
-
- HWND curWindow;
- char *fileToEncrypt = NULL;
- wchar_t *fileToEncryptW = NULL;
- wchar_t *encryptedFile = NULL;
- wchar_t *attachName = NULL;
- HANDLE hFile = NULL;
- HANDLE hEncFile = NULL;
-
- unsigned int session_number;
- struct sink_s encsinkmem;
- sink_t encsink = &encsinkmem;
- struct sink_s sinkmem;
- sink_t sink = &sinkmem;
- engine_filter_t filter = NULL;
- protocol_t protocol;
- int rc = 0;
- int i = 0;
-
- memset (encsink, 0, sizeof *encsink);
- memset (sink, 0, sizeof *sink);
-
- hr = getContext (ctrl, &context);
- if (FAILED(hr))
- return hr;
-
- /* First do the check for recipients as this is likely
- to fail */
- mailItem = get_oom_object (context, "CurrentItem");
- sender = get_oom_object (mailItem, "Session.CurrentUser");
- recipients = get_oom_object (mailItem, "Recipients");
- recipientAddrs = get_oom_recipients (recipients);
-
- if (!recipientAddrs || !(*recipientAddrs))
- {
- MessageBox (NULL,
- _("Please add at least one recipent."),
- _("GpgOL"),
- MB_ICONINFORMATION|MB_OK);
- goto failure;
- }
-
- /* Get a file handle to read from */
- fileToEncrypt = get_open_filename (NULL, _("Select file to encrypt"));
-
- if (!fileToEncrypt)
- {
- log_debug ("No file selected");
- goto failure;
- }
-
- fileToEncryptW = utf8_to_wchar2 (fileToEncrypt, strlen(fileToEncrypt));
- xfree (fileToEncrypt);
-
- hFile = CreateFileW (fileToEncryptW,
- GENERIC_READ,
- FILE_SHARE_READ,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
- if (hFile == INVALID_HANDLE_VALUE)
- {
- /* Should not happen as the Open File dialog
- should have prevented this.
- Maybe this also happens when a file is
- not readable. In that case we might want
- to switch to a localized error naming the file. */
- MessageBox (NULL,
- "Internal error in GpgOL.\n"
- "Could not open File.",
- _("GpgOL"),
- MB_ICONERROR|MB_OK);
- return S_OK;
- }
-
- /* Now do the encryption preperations */
-
- if (!mailItem || !sender || !recipients)
- {
- MessageBox (NULL,
- "Internal error in GpgOL.\n"
- "Could not find all objects.",
- _("GpgOL"),
- MB_ICONERROR|MB_OK);
- log_error ("%s:%s: Could not find all objects.",
- SRCNAME, __func__);
- goto failure;
- }
-
- senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
-
- curWindow = get_oom_context_window (context);
-
- session_number = engine_new_session_number ();
-
- subject = get_oom_string (mailItem, "Subject");
-
- /* Prepare the encryption sink */
- if ((rc = engine_create_filter (&filter, write_buffer_for_cb, sink)))
- {
- goto failure;
- }
-
- encsink->cb_data = filter;
- encsink->writefnc = sink_encryption_write;
-
- engine_set_session_number (filter, session_number);
- engine_set_session_title (filter, subject ? subject :_("GpgOL"));
- if ((rc=engine_encrypt_prepare (filter, curWindow,
- PROTOCOL_UNKNOWN,
- ENGINE_FLAG_BINARY_OUTPUT,
- senderAddr, recipientAddrs, &protocol)))
- {
- log_error ("%s:%s: engine encrypt prepare failed : %s",
- SRCNAME, __func__, gpg_strerror (rc));
- goto failure;
- }
-
- attachName = get_pretty_attachment_name (fileToEncryptW, protocol, 0);
-
- if (!attachName)
- {
- log_error ("%s:%s: Could not get a decent attachment name",
- SRCNAME, __func__);
- goto failure;
- }
-
- encryptedFile = get_tmp_outfile (attachName, &hEncFile);
- sink->cb_data = hEncFile;
- sink->writefnc = sink_file_write;
-
- if ((rc=engine_encrypt_start (filter, 0)))
- {
- log_error ("%s:%s: engine encrypt start failed: %s",
- SRCNAME, __func__, gpg_strerror (rc));
- goto failure;
- }
-
- if ((rc=copyFileToSink (hFile, encsink)))
- goto failure;
-
- /* Lets hope the user did not select a huge file. We are hanging
- here until encryption is completed.. */
- if ((rc = engine_wait (filter)))
- goto failure;
-
- filter = NULL; /* Not valid anymore. */
- encsink->cb_data = NULL; /* Not needed anymore. */
-
- if (!sink->enc_counter)
- {
- log_error ("%s:%s: nothing received from engine", SRCNAME, __func__);
- goto failure;
- }
-
- /* Now we have an encrypted file behind encryptedFile. Let's add it */
- add_oom_attachment (mailItem, encryptedFile, nullptr);
-
- if (flags & OP_SIGN)
- {
- attachSignature (mailItem, subject, hEncFile, protocol, session_number,
- curWindow, encryptedFile, senderAddr);
- }
-
-failure:
- if (filter)
- engine_cancel (filter);
-
- if (hEncFile)
- {
- CloseHandle (hEncFile);
- DeleteFileW (encryptedFile);
- }
- xfree (senderAddr);
- xfree (encryptedFile);
- xfree (fileToEncryptW);
- xfree (attachName);
- xfree (subject);
- gpgol_release (mailItem);
- gpgol_release (sender);
- gpgol_release (recipients);
-
- if (hFile)
- CloseHandle (hFile);
- if (recipientAddrs)
- {
- for (i=0; recipientAddrs && recipientAddrs[i]; i++)
- xfree (recipientAddrs[i]);
- xfree (recipientAddrs);
- }
-
- return S_OK;
-}
-
-HRESULT
-startCertManager (LPDISPATCH ctrl)
-{
- HRESULT hr;
- LPDISPATCH context;
- HWND curWindow;
-
- hr = getContext (ctrl, &context);
- if (FAILED(hr))
- return hr;
-
- curWindow = get_oom_context_window (context);
-
- engine_start_keymanager (curWindow);
- return S_OK;
-}
-
-HRESULT
-decryptBody (LPDISPATCH ctrl)
-{
- return do_reader_action (ctrl, OP_DECRYPT | DATA_BODY);
-}
-
-HRESULT
-decryptSelection (LPDISPATCH ctrl)
-{
- return do_reader_action (ctrl, OP_DECRYPT | DATA_SELECTION);
-}
-
-HRESULT
-encryptBody (LPDISPATCH ctrl)
-{
- return do_composer_action (ctrl, OP_ENCRYPT | DATA_BODY);
-}
-
-HRESULT
-encryptSelection (LPDISPATCH ctrl)
-{
- return do_composer_action (ctrl, OP_ENCRYPT | DATA_SELECTION);
-}
-
-HRESULT
-addEncSignedAttachment (LPDISPATCH ctrl)
-{
- return attachEncryptedFile (ctrl, OP_SIGN);
-}
-
-HRESULT
-addEncAttachment (LPDISPATCH ctrl)
-{
- return attachEncryptedFile (ctrl, 0);
-}
-
-HRESULT signBody (LPDISPATCH ctrl)
-{
- return do_composer_action (ctrl, DATA_BODY | OP_SIGN);
-}
-
-HRESULT verifyBody (LPDISPATCH ctrl)
-{
- return do_reader_action (ctrl, DATA_BODY | OP_VERIFY);
-}
-
HRESULT
mark_mime_action (LPDISPATCH ctrl, int flags, bool is_explorer)
{
HRESULT hr;
HRESULT rc = E_FAIL;
LPDISPATCH context = NULL,
mailitem = NULL;
LPMESSAGE message = NULL;
int oldflags,
newflags;
log_debug ("%s:%s: enter", SRCNAME, __func__);
hr = getContext (ctrl, &context);
if (FAILED(hr))
return hr;
mailitem = get_oom_object (context, is_explorer ? "ActiveInlineResponse" :
"CurrentItem");
if (!mailitem)
{
log_error ("%s:%s: Failed to get mailitem.",
SRCNAME, __func__);
goto done;
}
message = get_oom_base_message (mailitem);
if (!message)
{
log_error ("%s:%s: Failed to get message.",
SRCNAME, __func__);
goto done;
}
oldflags = get_gpgol_draft_info_flags (message);
if (flags == 3 && oldflags != 3)
{
// If only one sub button is active activate
// both now.
newflags = 3;
}
else
{
newflags = oldflags xor flags;
}
if (set_gpgol_draft_info_flags (message, newflags))
{
log_error ("%s:%s: Failed to set draft flags.",
SRCNAME, __func__);
}
rc = S_OK;
/* We need to invalidate the UI to update the toggle
states of the subbuttons and the top button. Yeah,
we invalidate a lot *sigh* */
gpgoladdin_invalidate_ui ();
if (newflags & 1)
{
Mail::locate_all_crypto_recipients ();
}
done:
gpgol_release (context);
gpgol_release (mailitem);
gpgol_release (message);
return rc;
}
/* Get the state of encrypt / sign toggle buttons.
flag values: 1 get the state of the encrypt button.
2 get the state of the sign button.
If is_explorer is set to true we look at the inline response.
*/
HRESULT get_crypt_pressed (LPDISPATCH ctrl, int flags, VARIANT *result,
bool is_explorer)
{
HRESULT hr;
bool value;
LPDISPATCH context = NULL,
mailitem = NULL;
LPMESSAGE message = NULL;
result->vt = VT_BOOL | VT_BYREF;
result->pboolVal = (VARIANT_BOOL*) xmalloc (sizeof (VARIANT_BOOL));
*(result->pboolVal) = VARIANT_FALSE;
/* First the usual defensive check about our parameters */
if (!ctrl || !result)
{
log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
return E_FAIL;
}
hr = getContext (ctrl, &context);
if (hr)
{
log_error ("%s:%s:%i : hresult %lx", SRCNAME, __func__, __LINE__,
hr);
return E_FAIL;
}
mailitem = get_oom_object (context, is_explorer ? "ActiveInlineResponse" :
"CurrentItem");
if (!mailitem)
{
log_error ("%s:%s: Failed to get mailitem.",
SRCNAME, __func__);
goto done;
}
message = get_oom_base_message (mailitem);
if (!message)
{
log_error ("%s:%s: No message found.",
SRCNAME, __func__);
goto done;
}
value = (get_gpgol_draft_info_flags (message) & flags) == flags;
*(result->pboolVal) = value ? VARIANT_TRUE : VARIANT_FALSE;
done:
gpgol_release (context);
gpgol_release (mailitem);
gpgol_release (message);
return S_OK;
}
static Mail *
get_mail_from_control (LPDISPATCH ctrl, bool *none_selected)
{
HRESULT hr;
LPDISPATCH context = NULL,
mailitem = NULL;
*none_selected = false;
if (!ctrl)
{
log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
return NULL;
}
hr = getContext (ctrl, &context);
if (hr)
{
log_error ("%s:%s:%i : hresult %lx", SRCNAME, __func__, __LINE__,
hr);
return NULL;
}
const auto ctx_name = std::string (get_object_name (context));
if (ctx_name.empty())
{
log_error ("%s:%s: Failed to get context name",
SRCNAME, __func__);
gpgol_release (context);
return NULL;
}
if (!strcmp (ctx_name.c_str(), "_Inspector"))
{
mailitem = get_oom_object (context, "CurrentItem");
}
else if (!strcmp (ctx_name.c_str(), "_Explorer"))
{
if (g_ol_version_major >= 16)
{
// Avoid showing wrong crypto state if we don't have a reading
// pane. In that case the parser will finish for a mail which is gone
// and the crypto state will not get updated.
//
//
// Somehow latest Outlook 2016 crashes when accessing the current view
// of the Explorer. This is even reproducible with
// GpgOL disabled and only with Outlook Spy active. If you select
// the explorer of an Outlook.com resource and then access
// the CurrentView and close the CurrentView again in Outlook Spy
// outlook crashes.
LPDISPATCH prevEdit = get_oom_object (context, "PreviewPane.WordEditor");
gpgol_release (prevEdit);
if (!prevEdit)
{
*none_selected = true;
gpgol_release (mailitem);
mailitem = nullptr;
}
}
else
{
// Preview Pane is not available in older outlooks
LPDISPATCH tableView = get_oom_object (context, "CurrentView");
if (!tableView)
{
// Woops, should not happen.
TRACEPOINT;
*none_selected = true;
gpgol_release (mailitem);
mailitem = nullptr;
}
else
{
int hasReadingPane = get_oom_bool (tableView, "ShowReadingPane");
gpgol_release (tableView);
if (!hasReadingPane)
{
*none_selected = true;
gpgol_release (mailitem);
mailitem = nullptr;
}
}
}
if (!*none_selected)
{
/* Accessing the selection item can trigger a load event
so we only do this here if we think that there might be
something visible / selected. To avoid triggering a load
if there is no content shown. */
LPDISPATCH selection = get_oom_object (context, "Selection");
if (!selection)
{
log_error ("%s:%s: Failed to get selection.",
SRCNAME, __func__);
gpgol_release (context);
return NULL;
}
int count = get_oom_int (selection, "Count");
if (count == 1)
{
// If we call this on a selection with more items
// Outlook sends an ItemLoad event for each mail
// in that selection.
mailitem = get_oom_object (selection, "Item(1)");
}
gpgol_release (selection);
if (!mailitem)
{
*none_selected = true;
}
}
}
else if (!strcmp (ctx_name.c_str(), "Selection"))
{
int count = get_oom_int (context, "Count");
if (count == 1)
{
// If we call this on a selection with more items
// Outlook sends an ItemLoad event for each mail
// in that selection.
mailitem = get_oom_object (context, "Item(1)");
}
if (!mailitem)
{
*none_selected = true;
}
}
gpgol_release (context);
if (!mailitem)
{
log_debug ("%s:%s: No mailitem. From %s",
SRCNAME, __func__, ctx_name.c_str());
return NULL;
}
char *uid;
/* Get the uid of this item. */
uid = get_unique_id (mailitem, 0, nullptr);
if (!uid)
{
LPMESSAGE msg = get_oom_base_message (mailitem);
uid = mapi_get_uid (msg);
gpgol_release (msg);
if (!uid)
{
log_debug ("%s:%s: Failed to get uid for %p",
SRCNAME, __func__, mailitem);
gpgol_release (mailitem);
return NULL;
}
}
auto ret = Mail::get_mail_for_uuid (uid);
xfree (uid);
if (!ret)
{
log_error ("%s:%s: Failed to find mail %p in map.",
SRCNAME, __func__, mailitem);
}
gpgol_release (mailitem);
return ret;
}
/* Helper to reduce code duplication.*/
#define MY_MAIL_GETTER \
if (!ctrl) \
{ \
log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__); \
return E_FAIL; \
} \
bool none_selected; \
const auto mail = get_mail_from_control (ctrl, &none_selected); \
(void)none_selected; \
if (!mail) \
{ \
log_oom ("%s:%s:%i Failed to get mail", \
SRCNAME, __func__, __LINE__); \
}
HRESULT get_is_details_enabled (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
if (!result)
{
TRACEPOINT;
return S_OK;
}
result->vt = VT_BOOL | VT_BYREF;
result->pboolVal = (VARIANT_BOOL*) xmalloc (sizeof (VARIANT_BOOL));
*(result->pboolVal) = none_selected ? VARIANT_FALSE : VARIANT_TRUE;
return S_OK;
}
HRESULT get_sig_label (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
result->vt = VT_BSTR;
wchar_t *w_result;
if (!mail)
{
log_debug ("%s:%s: No mail.",
SRCNAME, __func__);
w_result = utf8_to_wchar (_("Insecure"));
result->bstrVal = SysAllocString (w_result);
xfree (w_result);
return S_OK;
}
w_result = utf8_to_wchar (mail->get_crypto_summary ().c_str ());
result->bstrVal = SysAllocString (w_result);
xfree (w_result);
return S_OK;
}
HRESULT get_sig_ttip (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
result->vt = VT_BSTR;
wchar_t *w_result;
if (mail)
{
w_result = utf8_to_wchar (mail->get_crypto_one_line().c_str());
}
else if (!none_selected)
{
w_result = utf8_to_wchar (_("Insecure message"));
}
else
{
w_result = utf8_to_wchar (_("No message selected"));
}
result->bstrVal = SysAllocString (w_result);
xfree (w_result);
return S_OK;
}
HRESULT get_sig_stip (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
result->vt = VT_BSTR;
if (none_selected)
{
result->bstrVal = SysAllocString (L"");
return S_OK;
}
if (!mail || !mail->is_crypto_mail ())
{
wchar_t *w_result;
w_result = utf8_to_wchar (utf8_gettext ("You cannot be sure who sent, "
"modified and read the message in transit.\n\n"
"Click here to learn more."));
result->bstrVal = SysAllocString (w_result);
xfree (w_result);
return S_OK;
}
const auto message = mail->get_crypto_details ();
wchar_t *w_message = utf8_to_wchar (message.c_str());
result->bstrVal = SysAllocString (w_message);
xfree (w_message);
return S_OK;
}
HRESULT launch_cert_details (LPDISPATCH ctrl)
{
MY_MAIL_GETTER
if (!mail || (!mail->is_signed () && !mail->is_encrypted ()))
{
ShellExecuteA(NULL, NULL, "https://emailselfdefense.fsf.org/infographic",
0, 0, SW_SHOWNORMAL);
return S_OK;
}
if (!mail->is_signed () && mail->is_encrypted ())
{
/* Encrypt only, no information but show something. because
we want the button to be active.
Aheinecke: I don't think we should show to which keys the message
is encrypted here. This would confuse users if they see keyids
of unknown keys and the information can't be "true" because the
sender could have sent the same information to other people or
used throw keyids etc.
*/
char * buf;
gpgrt_asprintf (&buf, _("The message was not cryptographically signed.\n"
"There is no additional information available if it "
"was actually sent by '%s' or if someone faked the sender address."), mail->get_sender ().c_str());
MessageBox (NULL, buf, _("GpgOL"),
MB_ICONINFORMATION|MB_OK);
xfree (buf);
return S_OK;
}
if (!mail->get_sig_fpr())
{
std::string buf = _("There was an error verifying the signature.\n"
"Full details:\n");
buf += mail->get_verification_result_dump();
gpgol_message_box (get_active_hwnd(), buf.c_str(), _("GpgOL"), MB_OK);
}
char *uiserver = get_uiserver_name ();
bool showError = false;
if (uiserver)
{
std::string path (uiserver);
xfree (uiserver);
if (path.find("kleopatra.exe") != std::string::npos)
{
size_t dpos;
if ((dpos = path.find(" --daemon")) != std::string::npos)
{
path.erase(dpos, strlen(" --daemon"));
}
auto ctx = Context::createForEngine(SpawnEngine);
if (!ctx)
{
log_error ("%s:%s: No spawn engine.",
SRCNAME, __func__);
}
std::string parentWid = std::to_string ((int) (intptr_t) get_active_hwnd ());
const char *argv[] = {path.c_str(),
"--query",
mail->get_sig_fpr(),
"--parent-windowid",
parentWid.c_str(),
NULL };
log_debug ("%s:%s: Starting %s %s %s",
SRCNAME, __func__, path.c_str(), argv[1], argv[2]);
Data d(Data::null);
ctx->spawnAsync(path.c_str(), argv, d, d,
d, (GpgME::Context::SpawnFlags) (
GpgME::Context::SpawnAllowSetFg |
GpgME::Context::SpawnShowWindow));
}
else
{
showError = true;
}
}
else
{
showError = true;
}
if (showError)
{
MessageBox (NULL,
_("Could not find Kleopatra.\n"
"Please reinstall Gpg4win with the Kleopatra component enabled."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
}
return S_OK;
}
HRESULT get_crypto_icon (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
if (mail)
{
return getIcon (mail->get_crypto_icon_id (), result);
}
return getIcon (IDI_LEVEL_0, result);
}
HRESULT get_is_crypto_mail (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
result->vt = VT_BOOL | VT_BYREF;
result->pboolVal = (VARIANT_BOOL*) xmalloc (sizeof (VARIANT_BOOL));
*(result->pboolVal) = (mail && (mail->is_signed () || mail->is_encrypted ())) ?
VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
}
HRESULT print_decrypted (LPDISPATCH ctrl)
{
MY_MAIL_GETTER
if (!mail)
{
log_error ("%s:%s: Failed to get mail.",
SRCNAME, __func__);
return S_OK;
}
invoke_oom_method (mail->item(), "PrintOut", NULL);
return S_OK;
}
diff --git a/src/ribbon-callbacks.h b/src/ribbon-callbacks.h
index 6fe63df..54369d6 100644
--- a/src/ribbon-callbacks.h
+++ b/src/ribbon-callbacks.h
@@ -1,109 +1,90 @@
/* ribbon-callbacks.h - Callbacks for the ribbon extension interface
* Copyright (C) 2013 Intevation GmbH
* Software engineering by Intevation 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 .
*/
#ifndef RIBBON_CALLBACKS_H
#define RIBBON_CALLBACKS_H
#include "gpgoladdin.h"
/* For the Icon IDS */
#include "dialogs.h"
/* Id's of our callbacks */
#define ID_CMD_DECRYPT 2
-#define ID_CMD_ENCRYPT_SELECTION 3
-#define ID_CMD_DECRYPT_SELECTION 4
-#define ID_CMD_CERT_MANAGER 5
-#define ID_CMD_ENCRYPT_BODY 6
-#define ID_CMD_DECRYPT_BODY 8
-#define ID_CMD_ATT_ENCSIGN_FILE 9
-#define ID_CMD_SIGN_BODY 10
-#define ID_CMD_ATT_ENC_FILE 11
-#define ID_CMD_VERIFY_BODY 12
#define ID_CMD_MIME_SIGN 13
#define ID_CMD_MIME_ENCRYPT 14
#define ID_GET_SIGN_PRESSED 15
#define ID_GET_ENCRYPT_PRESSED 16
#define ID_ON_LOAD 17
#define ID_CMD_OPEN_OPTIONS 18
#define ID_GET_IS_DETAILS_ENABLED 19
#define ID_CMD_MIME_SIGN_EX 21
#define ID_CMD_MIME_ENCRYPT_EX 22
#define ID_GET_SIGN_PRESSED_EX 23
#define ID_GET_ENCRYPT_PRESSED_EX 24
#define ID_GET_SIG_STIP 25
#define ID_GET_SIG_TTIP 26
#define ID_GET_SIG_LABEL 27
#define ID_LAUNCH_CERT_DETAILS 28
#define ID_BTN_SIGSTATE_LARGE 29
#define ID_GET_SIGN_ENCRYPT_PRESSED 30
#define ID_GET_SIGN_ENCRYPT_PRESSED_EX 31
#define ID_CMD_SIGN_ENCRYPT_MIME 32
#define ID_CMD_SIGN_ENCRYPT_MIME_EX 33
#define ID_CMD_PRINT_DECRYPTED 34
#define ID_GET_IS_CRYPTO_MAIL 35
-#define ID_BTN_CERTMANAGER IDI_KEY_MANAGER_64_PNG
#define ID_BTN_DECRYPT IDI_DECRYPT_16_PNG
#define ID_BTN_DECRYPT_LARGE IDI_DECRYPT_48_PNG
#define ID_BTN_ENCRYPT IDI_ENCRYPT_16_PNG
#define ID_BTN_ENCRYPT_LARGE IDI_ENCRYPT_48_PNG
#define ID_BTN_ENCSIGN_LARGE IDI_ENCSIGN_FILE_48_PNG
#define ID_BTN_SIGN_LARGE IDI_SIGN_48_PNG
#define ID_BTN_VERIFY_LARGE IDI_VERIFY_48_PNG
#define OP_ENCRYPT 1 /* Encrypt the data */
#define OP_SIGN 2 /* Sign the data */
HRESULT decryptAttachments (LPDISPATCH ctrl);
-HRESULT encryptSelection (LPDISPATCH ctrl);
-HRESULT decryptSelection (LPDISPATCH ctrl);
-HRESULT decryptBody (LPDISPATCH ctrl);
-HRESULT encryptBody (LPDISPATCH ctrl);
-HRESULT addEncSignedAttachment (LPDISPATCH ctrl);
-HRESULT addEncAttachment (LPDISPATCH ctrl);
HRESULT getIcon (int id, VARIANT* result);
-HRESULT startCertManager (LPDISPATCH ctrl);
-HRESULT signBody (LPDISPATCH ctrl);
-HRESULT verifyBody (LPDISPATCH ctrl);
/* Get the toggle state of a crypt button. Flag value 1: encrypt, 2: sign */
HRESULT get_crypt_pressed (LPDISPATCH ctrl, int flags, VARIANT *result, bool is_explorer);
/* Mark the mail to be mime encrypted on send. Flags as above */
HRESULT mark_mime_action (LPDISPATCH ctrl, int flags, bool is_explorer);
/* Check the if the gpgol button should be enabled */
HRESULT get_is_details_enabled (LPDISPATCH ctrl, VARIANT *result);
/* Get the label for the signature. Returns BSTR */
HRESULT get_sig_label (LPDISPATCH ctrl, VARIANT *result);
/* Get the tooltip for the signature. Returns BSTR */
HRESULT get_sig_ttip (LPDISPATCH ctrl, VARIANT *result);
/* Get the supertip for the signature. Returns BSTR */
HRESULT get_sig_stip (LPDISPATCH ctrl, VARIANT *result);
/* Show a certificate details dialog. Returns nothing. */
HRESULT launch_cert_details (LPDISPATCH ctrl);
/* Callback to get the sigstate icon. */
HRESULT get_crypto_icon (LPDISPATCH ctrl, VARIANT *result);
/* Callback to get our own control reference */
HRESULT ribbon_loaded (LPDISPATCH ctrl);
/* Is the currently selected mail a crypto mail ? */
HRESULT get_is_crypto_mail (LPDISPATCH ctrl, VARIANT *result);
/* Print out the decrypted mail */
HRESULT print_decrypted (LPDISPATCH ctrl);
#endif