diff --git a/src/common_indep.h b/src/common_indep.h
index b11867a..9d02792 100644
--- a/src/common_indep.h
+++ b/src/common_indep.h
@@ -1,339 +1,340 @@
#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"
#include "debug.h"
#include "memdbg.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 encrypt_default; /* Encrypt by default. */
int sign_default; /* Sign by default. */
int prefer_html; /* Prefer html in html/text alternatives. */
int inline_pgp; /* Only for Addin. Use Inline PGP by default. */
int autoresolve; /* Autresolve keys with --locate-keys. */
int autosecure; /* Autmatically encrypt if locate returns enough validity. */
int reply_crypt; /* Only for Addin. Encrypt / Sign based on cryptostatus. */
int automation; /* General automation */
int autotrust; /* TOFU configured for GpgOL */
int sync_enc; /* Disabed async encryption */
int sync_dec; /* Disabed async decryption */
int prefer_smime; /* S/MIME prefered when autoresolving */
int smime_html_warn_shown; /* Flag to save if unsigned smime warning was shown */
int autoretrieve; /* Use --auto-key-retrieve. */
int search_smime_servers; /* Search for S/MIME keys on all configured S/MIME keyservers
for each new unknown mail */
int auto_unstrusted; /* Automatically encrypt even to untrusted keys. */
int autoimport; /* Automatically import keys from headers or attachments. */
char *draft_key; /* Encrypt drafts with this key (fingerprint) */
+ int alwaysShowApproval; /* Always show the certificate approval dialog. */
/* The forms revision number of the binary. */
int forms_revision;
} 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;
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)
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*/
/* The length of the boundary - the buffer needs to be allocated one
byte larger. */
#define BOUNDARYSIZE 20
char *generate_boundary (char *buffer);
#ifdef __cplusplus
}
#include
#include
struct autocrypt_s
{
bool exists;
std::string pref;
std::string addr;
GpgME::Data data;
};
#endif
#endif // COMMON_INDEP_H
diff --git a/src/cryptcontroller.cpp b/src/cryptcontroller.cpp
index b219a22..105538b 100644
--- a/src/cryptcontroller.cpp
+++ b/src/cryptcontroller.cpp
@@ -1,1292 +1,1292 @@
/* @file cryptcontroller.cpp
* @brief Helper to do crypto on a mail.
*
* Copyright (C) 2018 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 .
*/
#include "config.h"
#include "common.h"
#include "cpphelp.h"
#include "cryptcontroller.h"
#include "mail.h"
#include "mapihelp.h"
#include "mimemaker.h"
#include "wks-helper.h"
#include "overlay.h"
#include "keycache.h"
#include "mymapitags.h"
#include
#include
#include
#include "common.h"
#include
static int
sink_data_write (sink_t sink, const void *data, size_t datalen)
{
GpgME::Data *d = static_cast(sink->cb_data);
d->write (data, datalen);
return 0;
}
static int
create_sign_attach (sink_t sink, protocol_t protocol,
GpgME::Data &signature,
GpgME::Data &signedData,
const char *micalg);
/** We have some C Style cruft in here as this was historically how
GpgOL worked directly in the MAPI data objects. To reduce the regression
risk the new object oriented way for crypto reused as much as possible
from this.
*/
CryptController::CryptController (Mail *mail, bool encrypt, bool sign,
GpgME::Protocol proto):
m_mail (mail),
m_encrypt (encrypt),
m_sign (sign),
m_crypto_success (false),
m_proto (proto)
{
TSTART;
memdbg_ctor ("CryptController");
log_debug ("%s:%s: CryptController ctor for %p encrypt %i sign %i inline %i.",
SRCNAME, __func__, mail, encrypt, sign, mail->getDoPGPInline ());
m_recipient_addrs = mail->getCachedRecipients ();
TRETURN;
}
CryptController::~CryptController()
{
TSTART;
memdbg_dtor ("CryptController");
log_debug ("%s:%s:%p",
SRCNAME, __func__, m_mail);
TRETURN;
}
int
CryptController::collect_data ()
{
TSTART;
/* Get the attachment info and the body. We need to do this before
creating the engine's filter because sending the cancel to
the engine with nothing for the engine to process. Will result
in an error. This is actually a bug in our engine code but
we better avoid triggering this bug because the engine
sometimes hangs. Fixme: Needs a proper fix. */
/* Take the Body from the mail if possible. This is a fix for
GnuPG-Bug-ID: T3614 because the body is not always properly
updated in MAPI when sending. */
char *body = m_mail->takeCachedPlainBody ();
if (body && !*body)
{
xfree (body);
body = nullptr;
}
LPMESSAGE message = m_mail->isCryptoMail() ?
get_oom_base_message (m_mail->item ()) :
get_oom_message (m_mail->item ());
if (!message)
{
log_error ("%s:%s: Failed to get message.",
SRCNAME, __func__);
}
auto att_table = mapi_create_attach_table (message, 0);
int n_att_usable = count_usable_attachments (att_table);
if (!n_att_usable && !body)
{
if (!m_mail->isDraftEncrypt())
{
gpgol_message_box (m_mail->getWindow (),
utf8_gettext ("Can't encrypt / sign an empty message."),
utf8_gettext ("GpgOL"), MB_OK);
}
gpgol_release (message);
mapi_release_attach_table (att_table);
xfree (body);
TRETURN -1;
}
bool do_inline = m_mail->getDoPGPInline ();
if (n_att_usable && do_inline)
{
log_debug ("%s:%s: PGP Inline not supported for attachments."
" Using PGP MIME",
SRCNAME, __func__);
do_inline = false;
m_mail->setDoPGPInline (false);
}
else if (do_inline)
{
/* Inline. Use Body as input.
We need to collect also our mime structure for S/MIME
as we don't know yet if we are S/MIME or OpenPGP */
m_bodyInput.write (body, strlen (body));
log_debug ("%s:%s: Inline. Caching body.",
SRCNAME, __func__);
/* Set the input buffer to start. */
m_bodyInput.seek (0, SEEK_SET);
}
/* Set up the sink object to collect the mime structure */
struct sink_s sinkmem;
sink_t sink = &sinkmem;
memset (sink, 0, sizeof *sink);
sink->cb_data = &m_input;
sink->writefnc = sink_data_write;
/* Collect the mime strucutre */
int err = add_body_and_attachments (sink, message, att_table, m_mail,
body, n_att_usable);
xfree (body);
if (err)
{
log_error ("%s:%s: Collecting body and attachments failed.",
SRCNAME, __func__);
gpgol_release (message);
mapi_release_attach_table (att_table);
TRETURN -1;
}
/* Message is no longer needed */
gpgol_release (message);
mapi_release_attach_table (att_table);
/* Set the input buffer to start. */
m_input.seek (0, SEEK_SET);
TRETURN 0;
}
int
CryptController::lookup_fingerprints (const std::string &sigFpr,
const std::vector recpFprs)
{
TSTART;
auto ctx = std::shared_ptr (GpgME::Context::createForProtocol (m_proto));
if (!ctx)
{
log_error ("%s:%s: failed to create context with protocol '%s'",
SRCNAME, __func__,
m_proto == GpgME::CMS ? "smime" :
m_proto == GpgME::OpenPGP ? "openpgp" :
"unknown");
TRETURN -1;
}
ctx->setKeyListMode (GpgME::Local);
GpgME::Error err;
if (!sigFpr.empty()) {
m_signer_key = ctx->key (sigFpr.c_str (), err, true);
if (err || m_signer_key.isNull ()) {
log_error ("%s:%s: failed to lookup key for '%s' with protocol '%s'",
SRCNAME, __func__, anonstr (sigFpr.c_str ()),
m_proto == GpgME::CMS ? "smime" :
m_proto == GpgME::OpenPGP ? "openpgp" :
"unknown");
TRETURN -1;
}
// reset context
ctx = std::shared_ptr (GpgME::Context::createForProtocol (m_proto));
ctx->setKeyListMode (GpgME::Local);
}
if (!recpFprs.size()) {
TRETURN 0;
}
// Convert recipient fingerprints
char **cRecps = vector_to_cArray (recpFprs);
err = ctx->startKeyListing (const_cast (cRecps));
if (err) {
log_error ("%s:%s: failed to start recipient keylisting",
SRCNAME, __func__);
release_cArray (cRecps);
TRETURN -1;
}
do {
m_recipients.push_back(ctx->nextKey(err));
} while (!err);
m_recipients.pop_back();
release_cArray (cRecps);
TRETURN 0;
}
int
CryptController::parse_output (GpgME::Data &resolverOutput)
{
TSTART;
// Todo: Use Data::toString
std::istringstream ss(resolverOutput.toString());
std::string line;
std::string sigFpr;
std::vector recpFprs;
while (std::getline (ss, line))
{
rtrim (line);
if (line == "cancel")
{
log_debug ("%s:%s: resolver canceled",
SRCNAME, __func__);
TRETURN -2;
}
if (line == "unencrypted")
{
log_debug ("%s:%s: FIXME resolver wants unencrypted",
SRCNAME, __func__);
TRETURN -1;
}
std::istringstream lss (line);
// First is sig or enc
std::string what;
std::string how;
std::string fingerprint;
std::getline (lss, what, ':');
std::getline (lss, how, ':');
std::getline (lss, fingerprint, ':');
if (m_proto == GpgME::UnknownProtocol)
{
m_proto = (how == "smime") ? GpgME::CMS : GpgME::OpenPGP;
}
if (what == "sig")
{
if (!sigFpr.empty ())
{
log_error ("%s:%s: multiple signing keys not supported",
SRCNAME, __func__);
}
sigFpr = fingerprint;
continue;
}
if (what == "enc")
{
recpFprs.push_back (fingerprint);
}
}
if (m_sign && sigFpr.empty())
{
log_error ("%s:%s: Sign requested but no signing fingerprint - sending unsigned",
SRCNAME, __func__);
m_sign = false;
}
if (m_encrypt && !recpFprs.size())
{
log_error ("%s:%s: Encrypt requested but no recipient fingerprints",
SRCNAME, __func__);
gpgol_message_box (m_mail->getWindow (),
utf8_gettext ("No recipients for encryption selected."),
_("GpgOL"), MB_OK);
TRETURN -2;
}
TRETURN lookup_fingerprints (sigFpr, recpFprs);
}
static bool
resolve_through_protocol (const GpgME::Protocol &proto, bool sign,
bool encrypt, const std::string &sender,
const std::vector &recps,
std::vector &r_keys,
GpgME::Key &r_sig)
{
TSTART;
bool sig_ok = true;
bool enc_ok = true;
const auto cache = KeyCache::instance();
if (encrypt)
{
r_keys = cache->getEncryptionKeys(recps, proto);
enc_ok = !r_keys.empty();
}
if (sign && enc_ok)
{
r_sig = cache->getSigningKey (sender.c_str (), proto);
sig_ok = !r_sig.isNull();
}
TRETURN sig_ok && enc_ok;
}
int
CryptController::resolve_keys_cached()
{
TSTART;
// Prepare variables
const auto cached_sender = m_mail->getSender ();
auto recps = m_recipient_addrs;
if (m_encrypt)
{
recps.push_back (cached_sender);
}
bool resolved = false;
if (opt.enable_smime && opt.prefer_smime)
{
resolved = resolve_through_protocol (GpgME::CMS, m_sign, m_encrypt,
cached_sender, recps, m_recipients,
m_signer_key);
if (resolved)
{
log_debug ("%s:%s: Resolved with CMS due to preference.",
SRCNAME, __func__);
m_proto = GpgME::CMS;
}
}
if (!resolved)
{
resolved = resolve_through_protocol (GpgME::OpenPGP, m_sign, m_encrypt,
cached_sender, recps, m_recipients,
m_signer_key);
if (resolved)
{
log_debug ("%s:%s: Resolved with OpenPGP.",
SRCNAME, __func__);
m_proto = GpgME::OpenPGP;
}
}
if (!resolved && (opt.enable_smime && !opt.prefer_smime))
{
resolved = resolve_through_protocol (GpgME::CMS, m_sign, m_encrypt,
cached_sender, recps, m_recipients,
m_signer_key);
if (resolved)
{
log_debug ("%s:%s: Resolved with CMS as fallback.",
SRCNAME, __func__);
m_proto = GpgME::CMS;
}
}
if (!resolved)
{
log_debug ("%s:%s: Failed to resolve through cache",
SRCNAME, __func__);
m_recipients.clear();
m_signer_key = GpgME::Key();
m_proto = GpgME::UnknownProtocol;
TRETURN 1;
}
if (!m_recipients.empty())
{
log_debug ("%s:%s: Encrypting with protocol %s to:",
SRCNAME, __func__, to_cstr (m_proto));
}
for (const auto &key: m_recipients)
{
log_debug ("%s", anonstr (key.primaryFingerprint ()));
}
if (!m_signer_key.isNull())
{
log_debug ("%s:%s: Signing key: %s:%s",
SRCNAME, __func__, anonstr (m_signer_key.primaryFingerprint ()),
to_cstr (m_signer_key.protocol()));
}
TRETURN 0;
}
int
CryptController::resolve_keys ()
{
TSTART;
m_recipients.clear();
if (m_mail->isDraftEncrypt() && opt.draft_key)
{
const auto key = KeyCache::instance()->getByFpr (opt.draft_key);
if (key.isNull())
{
const char *buf = utf8_gettext ("Failed to encrypt draft.\n\n"
"The configured encryption key for drafts "
"could not be found.\n"
"Please check your configuration or "
"turn off draft encryption in the settings.");
gpgol_message_box (get_active_hwnd (),
buf,
_("GpgOL"), MB_OK);
TRETURN -1;
}
log_debug ("%s:%s: resolved draft encryption key protocol is: %s",
SRCNAME, __func__, to_cstr (key.protocol()));
m_proto = key.protocol ();
m_recipients.push_back (key);
TRETURN 0;
}
if (!m_recipient_addrs.size())
{
/* Should not happen. But we add it for better bug reports. */
const char *bugmsg = utf8_gettext ("Operation failed.\n\n"
"This is usually caused by a bug in GpgOL or an error in your setup.\n"
"Please see https://www.gpg4win.org/reporting-bugs.html "
"or ask your Administrator for support.");
char *buf;
gpgrt_asprintf (&buf, "Failed to resolve recipients.\n\n%s\n", bugmsg);
memdbg_alloc (buf);
gpgol_message_box (get_active_hwnd (),
buf,
_("GpgOL"), MB_OK);
xfree(buf);
TRETURN -1;
}
- if (opt.autoresolve && !resolve_keys_cached ())
+ if (opt.autoresolve && !opt.alwaysShowApproval && !resolve_keys_cached ())
{
log_debug ("%s:%s: resolved keys through the cache",
SRCNAME, __func__);
start_crypto_overlay();
TRETURN 0;
}
std::vector args;
// Collect the arguments
char *gpg4win_dir = get_gpg4win_dir ();
if (!gpg4win_dir)
{
TRACEPOINT;
TRETURN -1;
}
const auto resolver = std::string (gpg4win_dir) + "\\bin\\resolver.exe";
args.push_back (resolver);
log_debug ("%s:%s: resolving keys with '%s'",
SRCNAME, __func__, resolver.c_str ());
// We want debug output as OutputDebugString
args.push_back (std::string ("--debug"));
// Yes passing it as int is ok.
auto wnd = m_mail->getWindow ();
if (wnd)
{
// Pass the handle of the active window for raise / overlay.
args.push_back (std::string ("--hwnd"));
args.push_back (std::to_string ((int) (intptr_t) wnd));
}
// Set the overlay caption
args.push_back (std::string ("--overlayText"));
if (m_encrypt)
{
args.push_back (std::string (utf8_gettext ("Resolving recipients...")));
}
else if (m_sign)
{
args.push_back (std::string (utf8_gettext ("Resolving signers...")));
}
if (!opt.enable_smime)
{
args.push_back (std::string ("--protocol"));
args.push_back (std::string ("pgp"));
}
if (m_sign)
{
args.push_back (std::string ("--sign"));
}
const auto cached_sender = m_mail->getSender ();
if (cached_sender.empty())
{
log_error ("%s:%s: resolve keys without sender.",
SRCNAME, __func__);
}
else
{
args.push_back (std::string ("--sender"));
args.push_back (cached_sender);
}
- if (!opt.autoresolve)
+ if (!opt.autoresolve || opt.alwaysShowApproval)
{
args.push_back (std::string ("--alwaysShow"));
}
if (opt.prefer_smime)
{
args.push_back (std::string ("--preferred-protocol"));
args.push_back (std::string ("cms"));
}
args.push_back (std::string ("--lang"));
args.push_back (std::string (gettext_localename ()));
bool has_smime_override = false;
if (m_encrypt)
{
args.push_back (std::string ("--encrypt"));
// Get the recipients that are cached from OOM
for (const auto &addr: m_recipient_addrs)
{
const auto mbox = GpgME::UserID::addrSpecFromString (addr.c_str());
auto overrides =
KeyCache::instance ()->getOverrides (mbox, GpgME::OpenPGP);
const auto cms_overrides =
KeyCache::instance ()->getOverrides (mbox, GpgME::CMS);
overrides.insert(overrides.end(), cms_overrides.begin(),
cms_overrides.end());
if (overrides.size())
{
std::string overrideStr = mbox + ":";
for (const auto &key: overrides)
{
if (key.isNull())
{
TRACEPOINT;
continue;
}
has_smime_override |= key.protocol() == GpgME::CMS;
overrideStr += key.primaryFingerprint();
overrideStr += ",";
}
overrideStr.erase(overrideStr.size() - 1, 1);
args.push_back (std::string ("-o"));
args.push_back (overrideStr);
}
args.push_back (mbox);
}
}
if (!opt.prefer_smime && has_smime_override)
{
/* Prefer S/MIME if there was an S/MIME override */
args.push_back (std::string ("--preferred-protocol"));
args.push_back (std::string ("cms"));
}
// Args are prepared. Spawn the resolver.
auto ctx = GpgME::Context::createForEngine (GpgME::SpawnEngine);
if (!ctx)
{
// can't happen
TRACEPOINT;
TRETURN -1;
}
// Convert our collected vector to c strings
// It's a bit overhead but should be quick for such small
// data.
char **cargs = vector_to_cArray (args);
log_data ("%s:%s: Spawn args:",
SRCNAME, __func__);
for (size_t i = 0; cargs && cargs[i]; i++)
{
log_data (SIZE_T_FORMAT ": '%s'", i, cargs[i]);
}
GpgME::Data mystdin (GpgME::Data::null), mystdout, mystderr;
GpgME::Error err = ctx->spawn (cargs[0], const_cast (cargs),
mystdin, mystdout, mystderr,
(GpgME::Context::SpawnFlags) (
GpgME::Context::SpawnAllowSetFg |
GpgME::Context::SpawnShowWindow));
// Somehow Qt messes up which window to bring back to front.
// So we do it manually.
bring_to_front (wnd);
// We need to create an overlay while encrypting as pinentry can take a while
start_crypto_overlay();
log_data ("Resolver stdout:\n'%s'", mystdout.toString ().c_str ());
log_data ("Resolver stderr:\n'%s'", mystderr.toString ().c_str ());
release_cArray (cargs);
if (err)
{
log_debug ("%s:%s: Resolver spawn finished Err code: %i asString: %s",
SRCNAME, __func__, err.code(), err.asString());
}
int ret = parse_output (mystdout);
if (ret == -1)
{
log_debug ("%s:%s: Failed to parse / resolve keys.",
SRCNAME, __func__);
log_data ("Resolver stdout:\n'%s'", mystdout.toString ().c_str ());
log_data ("Resolver stderr:\n'%s'", mystderr.toString ().c_str ());
TRETURN -1;
}
TRETURN ret;
}
int
CryptController::do_crypto (GpgME::Error &err, std::string &r_diag)
{
TSTART;
log_debug ("%s:%s",
SRCNAME, __func__);
if (m_mail->isDraftEncrypt ())
{
log_debug ("%s:%s Disabling sign because of draft encrypt",
SRCNAME, __func__);
m_sign = false;
}
/* Start a WKS check if necessary. */
WKSHelper::instance()->start_check (m_mail->getSender ());
int ret = resolve_keys ();
if (ret == -1)
{
//error
log_debug ("%s:%s: Failure to resolve keys.",
SRCNAME, __func__);
TRETURN -1;
}
if (ret == -2)
{
// Cancel
TRETURN -2;
}
bool do_inline = m_mail->getDoPGPInline ();
if (m_proto == GpgME::CMS && do_inline)
{
log_debug ("%s:%s: Inline for S/MIME not supported. Switching to mime.",
SRCNAME, __func__);
do_inline = false;
m_mail->setDoPGPInline (false);
m_bodyInput = GpgME::Data(GpgME::Data::null);
}
auto ctx = GpgME::Context::create(m_proto);
if (!ctx)
{
log_error ("%s:%s: Failure to create context.",
SRCNAME, __func__);
gpgol_message_box (m_mail->getWindow (),
"Failure to create context.",
utf8_gettext ("GpgOL"), MB_OK);
TRETURN -1;
}
if (!m_signer_key.isNull())
{
ctx->addSigningKey (m_signer_key);
}
ctx->setTextMode (m_proto == GpgME::OpenPGP);
ctx->setArmor (m_proto == GpgME::OpenPGP);
if (m_encrypt && m_sign && do_inline)
{
// Sign encrypt combined
const auto result_pair = ctx->signAndEncrypt (m_recipients,
do_inline ? m_bodyInput : m_input,
m_output,
GpgME::Context::AlwaysTrust);
const auto err1 = result_pair.first.error();
const auto err2 = result_pair.second.error();
if (err1 || err2)
{
log_error ("%s:%s: Encrypt / Sign error %s %s.",
SRCNAME, __func__, result_pair.first.error().asString(),
result_pair.second.error().asString());
err = err1 ? err1 : err2;
GpgME::Data log;
const auto err3 = ctx->getAuditLog (log,
GpgME::Context::DiagnosticAuditLog);
if (!err3)
{
r_diag = log.toString();
}
TRETURN -1;
}
if (err1.isCanceled() || err2.isCanceled())
{
err = err1.isCanceled() ? err1 : err2;
log_debug ("%s:%s: User cancled",
SRCNAME, __func__);
TRETURN -2;
}
}
else if (m_encrypt && m_sign)
{
// First sign then encrypt
const auto sigResult = ctx->sign (m_input, m_output,
GpgME::Detached);
err = sigResult.error();
if (err)
{
log_error ("%s:%s: Signing error %s.",
SRCNAME, __func__, sigResult.error().asString());
GpgME::Data log;
const auto err3 = ctx->getAuditLog (log,
GpgME::Context::DiagnosticAuditLog);
if (!err3)
{
r_diag = log.toString();
}
TRETURN -1;
}
if (err.isCanceled())
{
log_debug ("%s:%s: User cancled",
SRCNAME, __func__);
TRETURN -2;
}
parse_micalg (sigResult);
// We now have plaintext in m_input
// The detached signature in m_output
// Set up the sink object to construct the multipart/signed
GpgME::Data multipart;
struct sink_s sinkmem;
sink_t sink = &sinkmem;
memset (sink, 0, sizeof *sink);
sink->cb_data = &multipart;
sink->writefnc = sink_data_write;
if (create_sign_attach (sink,
m_proto == GpgME::CMS ?
PROTOCOL_SMIME : PROTOCOL_OPENPGP,
m_output, m_input, m_micalg.c_str ()))
{
TRACEPOINT;
TRETURN -1;
}
// Now we have the multipart throw away the rest.
m_output = GpgME::Data ();
m_input = GpgME::Data ();
multipart.seek (0, SEEK_SET);
const auto encResult = ctx->encrypt (m_recipients, multipart,
m_output,
GpgME::Context::AlwaysTrust);
err = encResult.error();
if (err)
{
log_error ("%s:%s: Encryption error %s.",
SRCNAME, __func__, err.asString());
GpgME::Data log;
const auto err3 = ctx->getAuditLog (log,
GpgME::Context::DiagnosticAuditLog);
if (!err3)
{
r_diag = log.toString();
}
TRETURN -1;
}
if (err.isCanceled())
{
log_debug ("%s:%s: User cancled",
SRCNAME, __func__);
TRETURN -2;
}
// Now we have encrypted output just treat it like encrypted.
}
else if (m_encrypt)
{
const auto result = ctx->encrypt (m_recipients, do_inline ? m_bodyInput : m_input,
m_output,
GpgME::Context::AlwaysTrust);
err = result.error();
if (err)
{
log_error ("%s:%s: Encryption error %s.",
SRCNAME, __func__, err.asString());
GpgME::Data log;
const auto err3 = ctx->getAuditLog (log,
GpgME::Context::DiagnosticAuditLog);
if (!err3)
{
r_diag = log.toString();
}
TRETURN -1;
}
if (err.isCanceled())
{
log_debug ("%s:%s: User cancled",
SRCNAME, __func__);
TRETURN -2;
}
}
else if (m_sign)
{
const auto result = ctx->sign (do_inline ? m_bodyInput : m_input, m_output,
do_inline ? GpgME::Clearsigned :
GpgME::Detached);
err = result.error();
if (err)
{
log_error ("%s:%s: Signing error %s.",
SRCNAME, __func__, err.asString());
GpgME::Data log;
const auto err3 = ctx->getAuditLog (log,
GpgME::Context::DiagnosticAuditLog);
if (!err3)
{
r_diag = log.toString();
}
TRETURN -1;
}
if (err.isCanceled())
{
log_debug ("%s:%s: User cancled",
SRCNAME, __func__);
TRETURN -2;
}
parse_micalg (result);
}
else
{
// ???
log_error ("%s:%s: unreachable code reached.",
SRCNAME, __func__);
}
log_debug ("%s:%s: Crypto done sucessfuly.",
SRCNAME, __func__);
m_crypto_success = true;
TRETURN 0;
}
static int
write_data (sink_t sink, GpgME::Data &data)
{
TSTART;
if (!sink || !sink->writefnc)
{
TRETURN -1;
}
char buf[4096];
size_t nread;
data.seek (0, SEEK_SET);
while ((nread = data.read (buf, 4096)) > 0)
{
sink->writefnc (sink, buf, nread);
}
TRETURN 0;
}
int
create_sign_attach (sink_t sink, protocol_t protocol,
GpgME::Data &signature,
GpgME::Data &signedData,
const char *micalg)
{
TSTART;
char boundary[BOUNDARYSIZE+1];
char top_header[BOUNDARYSIZE+200];
int rc = 0;
/* Write the top header. */
generate_boundary (boundary);
create_top_signing_header (top_header, sizeof top_header,
protocol, 1, boundary,
micalg);
if ((rc = write_string (sink, top_header)))
{
TRACEPOINT;
TRETURN rc;
}
/* Write the boundary so that it is not included in the hashing. */
if ((rc = write_boundary (sink, boundary, 0)))
{
TRACEPOINT;
TRETURN rc;
}
/* Write the signed mime structure */
if ((rc = write_data (sink, signedData)))
{
TRACEPOINT;
TRETURN rc;
}
/* Write the signature attachment */
if ((rc = write_boundary (sink, boundary, 0)))
{
TRACEPOINT;
TRETURN rc;
}
if (protocol == PROTOCOL_OPENPGP)
{
rc = write_string (sink,
"Content-Type: application/pgp-signature;\r\n"
"\tname=\"" OPENPGP_SIG_NAME "\"\r\n"
"Content-Transfer-Encoding: 7Bit\r\n");
}
else
{
rc = write_string (sink,
"Content-Transfer-Encoding: base64\r\n"
"Content-Type: application/pkcs7-signature\r\n"
"Content-Disposition: inline;\r\n"
"\tfilename=\"" SMIME_SIG_NAME "\"\r\n");
/* rc = write_string (sink, */
/* "Content-Type: application/x-pkcs7-signature\r\n" */
/* "\tname=\"smime.p7s\"\r\n" */
/* "Content-Transfer-Encoding: base64\r\n" */
/* "Content-Disposition: attachment;\r\n" */
/* "\tfilename=\"smime.p7s\"\r\n"); */
}
if (rc)
{
TRACEPOINT;
TRETURN rc;
}
if ((rc = write_string (sink, "\r\n")))
{
TRACEPOINT;
TRETURN rc;
}
// Write the signature data
if (protocol == PROTOCOL_SMIME)
{
const std::string sigStr = signature.toString();
if ((rc = write_b64 (sink, (const void *) sigStr.c_str (), sigStr.size())))
{
TRACEPOINT;
TRETURN rc;
}
}
else if ((rc = write_data (sink, signature)))
{
TRACEPOINT;
TRETURN rc;
}
// Add an extra linefeed with should not harm.
if ((rc = write_string (sink, "\r\n")))
{
TRACEPOINT;
TRETURN rc;
}
/* Write the final boundary. */
if ((rc = write_boundary (sink, boundary, 1)))
{
TRACEPOINT;
TRETURN rc;
}
TRETURN rc;
}
static int
create_encrypt_attach (sink_t sink, protocol_t protocol,
GpgME::Data &encryptedData,
int exchange_major_version)
{
TSTART;
char boundary[BOUNDARYSIZE+1];
int rc = create_top_encryption_header (sink, protocol, boundary,
false, exchange_major_version);
// From here on use goto failure pattern.
if (rc)
{
log_error ("%s:%s: Failed to create top header.",
SRCNAME, __func__);
TRETURN rc;
}
if (protocol == PROTOCOL_OPENPGP ||
exchange_major_version >= 15)
{
// With exchange 2016 we have to construct S/MIME
// differently and write the raw data here.
rc = write_data (sink, encryptedData);
}
else
{
const auto encStr = encryptedData.toString();
rc = write_b64 (sink, encStr.c_str(), encStr.size());
}
if (rc)
{
log_error ("%s:%s: Failed to create top header.",
SRCNAME, __func__);
TRETURN rc;
}
/* Write the final boundary (for OpenPGP) and finish the attachment. */
if (*boundary && (rc = write_boundary (sink, boundary, 1)))
{
log_error ("%s:%s: Failed to write boundary.",
SRCNAME, __func__);
}
TRETURN rc;
}
int
CryptController::update_mail_mapi ()
{
TSTART;
log_debug ("%s:%s", SRCNAME, __func__);
LPMESSAGE message = get_oom_base_message (m_mail->item());
if (!message)
{
log_error ("%s:%s: Failed to obtain message.",
SRCNAME, __func__);
TRETURN -1;
}
if (m_mail->getDoPGPInline ())
{
// Nothing to do for inline.
log_debug ("%s:%s: Inline mail. Setting encoding.",
SRCNAME, __func__);
SPropValue prop;
prop.ulPropTag = PR_INTERNET_CPID;
prop.Value.l = 65001;
if (HrSetOneProp (message, &prop))
{
log_error ("%s:%s: Failed to set CPID mapiprop.",
SRCNAME, __func__);
}
TRETURN 0;
}
mapi_attach_item_t *att_table = mapi_create_attach_table (message, 0);
/* When we forward e.g. a crypto mail we have sent the message
has a MOSSTEMPL. We need to remove that. T4321 */
for (ULONG pos=0; att_table && !att_table[pos].end_of_table; pos++)
{
if (att_table[pos].attach_type == ATTACHTYPE_MOSSTEMPL)
{
log_debug ("%s:%s: Found existing moss attachment at "
"pos %i removing it.", SRCNAME, __func__,
att_table[pos].mapipos);
if (message->DeleteAttach (att_table[pos].mapipos, 0,
nullptr, 0) != S_OK)
{
log_error ("%s:%s: Failed to remove attachment.",
SRCNAME, __func__);
}
}
}
// Set up the sink object for our MSOXSMIME attachment.
struct sink_s sinkmem;
sink_t sink = &sinkmem;
memset (sink, 0, sizeof *sink);
sink->cb_data = &m_input;
sink->writefnc = sink_data_write;
// For S/MIME encrypted mails we have to use the application/pkcs7-mime
// content type. Otherwise newer (2016) exchange servers will throw
// an M2MCVT.StorageError.Exeption (See GnuPG-Bug-Id: T3853 )
// This means that the conversion / build of the mime structure also
// happens differently.
int exchange_major_version = get_ex_major_version_for_addr (
m_mail->getSender ().c_str ());
std::string overrideMimeTag;
if (m_proto == GpgME::CMS && m_encrypt && exchange_major_version >= 15)
{
log_debug ("%s:%s: CMS Encrypt with Exchange %i activating alternative.",
SRCNAME, __func__, exchange_major_version);
overrideMimeTag = "application/pkcs7-mime";
}
LPATTACH attach = create_mapi_attachment (message, sink,
overrideMimeTag.empty() ? nullptr :
overrideMimeTag.c_str());
if (!attach)
{
log_error ("%s:%s: Failed to create moss attach.",
SRCNAME, __func__);
gpgol_release (message);
TRETURN -1;
}
protocol_t protocol = m_proto == GpgME::CMS ?
PROTOCOL_SMIME :
PROTOCOL_OPENPGP;
int rc = 0;
/* Do we have override MIME ? */
const auto overrideMime = m_mail->get_override_mime_data ();
if (!overrideMime.empty())
{
rc = write_string (sink, overrideMime.c_str ());
}
else if (m_sign && m_encrypt)
{
rc = create_encrypt_attach (sink, protocol, m_output, exchange_major_version);
}
else if (m_encrypt)
{
rc = create_encrypt_attach (sink, protocol, m_output, exchange_major_version);
}
else if (m_sign)
{
rc = create_sign_attach (sink, protocol, m_output, m_input, m_micalg.c_str ());
}
// Close our attachment
if (!rc)
{
rc = close_mapi_attachment (&attach, sink);
}
// Set message class etc.
if (!rc)
{
rc = finalize_message (message, att_table, protocol, m_encrypt ? 1 : 0,
false, m_mail->isDraftEncrypt (), exchange_major_version);
}
// only on error.
if (rc)
{
cancel_mapi_attachment (&attach, sink);
}
// cleanup
mapi_release_attach_table (att_table);
gpgol_release (attach);
gpgol_release (message);
TRETURN rc;
}
std::string
CryptController::get_inline_data ()
{
TSTART;
std::string ret;
if (!m_mail->getDoPGPInline ())
{
TRETURN ret;
}
m_output.seek (0, SEEK_SET);
char buf[4096];
size_t nread;
while ((nread = m_output.read (buf, 4096)) > 0)
{
ret += std::string (buf, nread);
}
TRETURN ret;
}
void
CryptController::parse_micalg (const GpgME::SigningResult &result)
{
TSTART;
if (result.isNull())
{
TRACEPOINT;
TRETURN;
}
const auto signature = result.createdSignature(0);
if (signature.isNull())
{
TRACEPOINT;
TRETURN;
}
const char *hashAlg = signature.hashAlgorithmAsString ();
if (!hashAlg)
{
TRACEPOINT;
TRETURN;
}
if (m_proto == GpgME::OpenPGP)
{
m_micalg = std::string("pgp-") + hashAlg;
}
else
{
m_micalg = hashAlg;
}
std::transform(m_micalg.begin(), m_micalg.end(), m_micalg.begin(), ::tolower);
log_debug ("%s:%s: micalg is: '%s'.",
SRCNAME, __func__, m_micalg.c_str ());
TRETURN;
}
void
CryptController::start_crypto_overlay ()
{
TSTART;
auto wid = m_mail->getWindow ();
std::string text;
if (m_encrypt)
{
text = utf8_gettext ("Encrypting...");
}
else if (m_sign)
{
text = utf8_gettext ("Signing...");
}
m_overlay = std::unique_ptr (new Overlay (wid, text));
TRETURN;
}
diff --git a/src/main.c b/src/main.c
index c0a31a0..5ce473a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,411 +1,412 @@
/* 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;
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);
memdbg_alloc (tmp);
if (!access(tmp, R_OK))
{
xfree (tmp);
gpgrt_asprintf (&tmp, "%s\\bin", gpg4win_dir);
memdbg_alloc (tmp);
xfree (gpg4win_dir);
return tmp;
}
xfree (tmp);
gpgrt_asprintf (&tmp, "%s\\gpgme-w32spawn.exe", gpg4win_dir);
memdbg_alloc (tmp);
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);
}
else if (reason == DLL_PROCESS_DETACH)
{
gpg_err_deinit (0);
}
return TRUE;
}
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 = xmalloc (strlen (instdir) + strlen (SLDIR) + 1);
if (!dname)
{
xfree (instdir);
return NULL;
}
p = dname;
strcpy (p, instdir);
p += strlen (instdir);
strcpy (p, SLDIR);
xfree (instdir);
return dname;
}
static void
drop_locale_dir (char *locale_dir)
{
xfree (locale_dir);
}
static int
get_conf_bool (const char *name, int defaultVal)
{
char *val = NULL;
int ret;
load_extension_value (name, &val);
ret = val == NULL ? defaultVal : *val != '1' ? 0 : 1;
xfree (val);
return ret;
}
static int
dbg_compat (int oldval)
{
// We broke the debug levels at some point
// This is cmpatibility code with the old
// levels.
#define DBG_MEMORY_OLD (1<<5) // 32
#define DBG_MIME_PARSER_OLD (1<<7) // 128 Unified as DBG_DATA
#define DBG_MIME_DATA_OLD (1<<8) // 256 Unified in read_options
#define DBG_OOM_OLD (1<<9) // 512 Unified as DBG_OOM
#define DBG_OOM_EXTRA_OLD (1<<10)// 1024 Unified in read_options
int new_dbg = oldval;
if ((oldval & DBG_MEMORY_OLD))
{
new_dbg |= DBG_MEMORY;
new_dbg -= DBG_MEMORY_OLD;
}
if ((oldval & DBG_OOM_OLD))
{
new_dbg |= DBG_OOM;
new_dbg -= DBG_OOM_OLD;
}
if ((oldval & DBG_MIME_PARSER_OLD))
{
new_dbg |= DBG_DATA;
new_dbg -= DBG_MIME_PARSER_OLD;
}
if ((oldval & DBG_MIME_DATA_OLD))
{
new_dbg |= DBG_DATA;
new_dbg -= DBG_MIME_DATA_OLD;
}
if ((oldval & DBG_OOM_OLD))
{
new_dbg |= DBG_OOM;
new_dbg -= DBG_OOM_OLD;
}
if ((oldval & DBG_OOM_EXTRA_OLD))
{
new_dbg |= DBG_OOM;
new_dbg -= DBG_OOM_EXTRA_OLD;
}
#undef DBG_MEMORY_OLD
#undef DBG_MIME_PARSER_OLD
#undef DBG_MIME_DATA_OLD
#undef DBG_OOM_OLD
#undef DBG_OOM_EXTRA_OLD
return new_dbg;
}
/* Read option settings from the Registry. */
void
read_options (void)
{
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 |= dbg_compat (strtoul (p, NULL, 0));
}
else if (!strcmp (p, "memory"))
opt.enable_debug |= DBG_MEMORY;
else if (!strcmp (p, "mime-parser"))
opt.enable_debug |= DBG_DATA;
else if (!strcmp (p, "mime-data"))
opt.enable_debug |= DBG_DATA;
else if (!strcmp (p, "oom"))
opt.enable_debug |= DBG_OOM;
else if (!strcmp (p, "oom-extra"))
opt.enable_debug |= DBG_OOM;
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");
}
/* Yes we use free here because memtracing did not track the alloc
as the option for debuging was not read before. */
free (val); val = NULL;
if (opt.enable_debug)
log_debug ("enabled debug flags:%s%s%s%s\n",
(opt.enable_debug & DBG_MEMORY)? " memory":"",
(opt.enable_debug & DBG_DATA)? " data":"",
(opt.enable_debug & DBG_OOM)? " oom":"",
(opt.enable_debug & DBG_TRACE)? " trace":""
);
opt.enable_smime = get_conf_bool ("enableSmime", 0);
opt.encrypt_default = get_conf_bool ("encryptDefault", 0);
opt.sign_default = get_conf_bool ("signDefault", 0);
opt.inline_pgp = get_conf_bool ("inlinePGP", 0);
opt.reply_crypt = get_conf_bool ("replyCrypt", 1);
opt.prefer_smime = get_conf_bool ("preferSmime", 0);
opt.autoresolve = get_conf_bool ("autoresolve", 1);
opt.autoretrieve = get_conf_bool ("autoretrieve", 0);
opt.automation = get_conf_bool ("automation", 1);
opt.autosecure = get_conf_bool ("autosecure", 1);
opt.autotrust = get_conf_bool ("autotrust", 0);
opt.search_smime_servers = get_conf_bool ("searchSmimeServers", 0);
opt.smime_html_warn_shown = get_conf_bool ("smimeHtmlWarnShown", 0);
opt.auto_unstrusted = get_conf_bool ("autoencryptUntrusted", 0);
opt.autoimport = get_conf_bool ("autoimport", 0);
if (!opt.automation)
{
// Disabling automation is a shorthand to disable the
// others, too.
opt.autosecure = 0;
opt.autoresolve = 0;
opt.autotrust = 0;
opt.autoretrieve = 0;
opt.autoimport = 0;
opt.auto_unstrusted = 0;
}
/* Draft encryption handling. */
if (get_conf_bool ("draftEnc", 0))
{
load_extension_value ("draftKey", &val);
if (val)
{
xfree (opt.draft_key);
opt.draft_key = val;
val = NULL;
}
}
else
{
xfree (opt.draft_key);
opt.draft_key = NULL;
}
+ opt.alwaysShowApproval = get_conf_bool ("alwaysShowApproval", 0);
/* Hidden options */
opt.sync_enc = get_conf_bool ("syncEnc", 0);
opt.sync_dec = get_conf_bool ("syncDec", 0);
}
/* Write current options back to the Registry. */
int
write_options (void)
{
struct
{
const char *name;
int mode;
int value;
char *s_val;
} table[] = {
{"smimeHtmlWarnShown", 0, opt.smime_html_warn_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;
}