diff --git a/NEWS b/NEWS
index 03ae566..5c55fd9 100644
--- a/NEWS
+++ b/NEWS
@@ -1,913 +1,917 @@
Noteworthy changes for version 2.5.2 (unreleased)
=================================================
* Fixed re-encryption of drafts after modification
if draft encryption is enabled. (T5812)
+ * Added setting "auto" for draftKey registry
+ value to autoselect a draft encryption key.
+ (T5564)
+
Noteworthy changes for version 2.5.1 (2021-12-01)
=================================================
* Improved ReadAsPlain detection and plaintext
handling. (T5681)
Noteworthy changes for version 2.5.0 (2021-06-11)
=================================================
* Changed encryption code to work even more on
OOM. This avoids temporarily storing unencrypted
data to MAPI when asynchronously encrypting.
(T5022)
* Added support to encrypt / sign Outlook internal
data objects as attachments like mails, calendar
entries and contacts. (T4184)
Noteworthy changes for version 2.4.10 (2021-01-08)
=================================================
* Fixed a logic error in GpgOL's recipient selection
which caused one recipient to be ignored.
Noteworthy changes for version 2.4.9 (2021-01-07)
=================================================
* Fixed selection of "No Key" for a recipient.
(T5223)
* Fix preview of PGP mails when auto-key-retrieve
is enabled. (T5164)
Noteworthy changes for version 2.4.8 (2020-11-20)
=================================================
* Fixed attachment realted isses because of preview.
Noteworthy changes for version 2.4.7 (2020-09-04)
=================================================
* Fixed an issue that unencrypted drafts were sent
to the server even when draft encryption is on.
(T5022)
Noteworthy changes for version 2.4.6 (2020-07-22)
=================================================
* Improved handling of protected headers mails. (T4796)
* Experimental code for combined S/MIME and OpenPGP
operations and protected headers. Options are:
combinedOpsEnabled, encryptSubject, splitBCCMails
* Fixed handling of WKS mails. (T4839)
* Improved Addressbook integration. (T4874)
* Automatically learn keys from smartcards. (T4877)
* Fix signing key selection for group accounts.
(T4940)
* Show a preview when mail verification takes long.
(T4944)
* Allow changing the printer when printing crypto
mails. (T4890)
* Fix reply crypt selection when signing is selcted
by default. (T4949)
* Properly show level 2 validitity for Mails with keys
from WKD.
* Show a warning when both outlooks internal crypto
and GpgOL is active für a mail. (T4953)
* Fix cases where GpgOL would detect permanently decrypted
mails as still encrypted. (T4718)
* Disable automatic when displaying plaintext mails. (T4987)
* Use a generic filename for attachments that with
characters disallowed by Windows. (T4835)
Noteworthy changes for version 2.4.5 (2019-12-20)
=================================================
* Fix a crash when closing Outlook. (T4787)
Noteworthy changes for version 2.4.4 (2019-12-13)
=================================================
* Enable File -> Save As also for mails opened
in their own window.
Noteworthy changes for version 2.4.3 (2019-12-13)
=================================================
* Improved compatibilty with OWA and other Mail
clients working with the same S/MIME mails. (T4525)
* Added user friendly error handling when attachments
cannot be added. (T4731)
* Fix a crash that could happen in rare cases when opening
broken mails.
* Crypto mails are no longer always classified as
HTML. (T4639)
Noteworthy changes for version 2.4.2 (2019-07-14)
=================================================
* Fixed a possible plaintext leak. (T4662 T4661)
* Fixed an issue when changing plain text options
at runtime. (T4611)
Noteworthy changes for version 2.4.1 (2019-05-15)
=================================================
* Fixed printing of encrypted mails.
* File -> Save As does work for encrypted mails
now.
Noteworthy changes for version 2.4.0 (2019-06-06)
=================================================
* S/MIME Mails now use the same icons as Outlook
* Message classes in GpgOL have been changed to
improve compatibility with other clients.
* Draft encryption was added as an experimental feature.
* GpgOL autosecure no longer triggers for users
without an S/MIME certificate.
* Forwarding of crypto and non crypto mails has been
very much improved.
* Mails without headers are now handled better.
* S/MIME Address book integration was added.
Noteworthy changes for version 2.3.3 (2019-03-26)
=================================================
* Fixed external API for sent mails. (T4241)
* Fixed a crash in debug API. (T4262)
* Fixed some cases where S/MIME was not detected
correctly. (T4267, T4403)
* Fixed tooltip for bad signatures. (T4299)
* Fixed forwarding of sent mails. (T4321)
* Improved generated attachment names. (T4258)
* Added more, less secure automation options.
* Added minimalistic protected headers support.
* Added an option to decrypt mails permanently.
* Improved error handling in case encryption failed.
* No longer silently ignores unsupported attachments.
(T4184)
* Added external API to re-encrypt and decrypt. (T4241)
Noteworthy changes for version 2.3.2 (2018-11-12)
=================================================
* Reduced leakage of private information without
DBG_DATA. (T4193)
* Added handling for Junk folders. (T4188)
* Added a fallback for encoding problems. (T4156)
* Fixed system wide default configuration.
* Improved S/MIME handling.
* Populate keycache on startup.
Noteworthy changes for version 2.3.1 (2018-10-16)
=================================================
* Fixed attachement handling for office and pdf attachments.
* Improved signature info display.
* Added address book integration for OpenPGP.
* Added auto import capabilities for S/MIME.
* Added generic prefer S/MIME mode.
* Various bugfixes and regression fixes.
Noteworthy changes for version 2.3.0 (2018-08-31)
=================================================
* Massive stability and performance improvements.
* New configuration dialog.
* New option to automatically encrypt if possible.
* Moving mails is now possible.
* Improvements to attachment handling with long filenames.
* Support for contact Groups has been added.
Noteworthy changes for version 2.2.0 (2018-06-15)
=================================================
* Removed support for Outlook 2003 and 2007.
* Fixed reply handling of PGP/Inline mails. (T3964)
* Fixed a seemingly random crash. (T3946)
* Added dutch and ukrainian translation.
* Fixed encoding for some PGP/Inline mails. (T3986)
Noteworthy changes for version 2.1.1 (2018-04-24)
=================================================
* Fixed a regression in 3.1.0 that could lead to
decryption errors.
* Fixed internal keycache in de-vs mode.
* Fixed a crash during recipient lookup.
* Improved error handling.
* Keys from WKD are automatically acceptable
for auto encryption.
* Added quick print context menu option.
Noteworthy changes for version 2.1.0 (2018-04-12)
=================================================
* Encryption and Signing has been reworked to, again,
work without Kleopatra.
* WKS Setup is supported in a basic way.
* PGP/Inline is now fully supported.
* Many Bugfixes and Parser improvements.
Noteworthy changes for version 2.0.6 (2018-01-12)
=================================================
* PGP/Inline sending is now compatible with Microsoft Exchange
Online. (T3662)
* A bug that caused encrypted mails not to be displayed has been
fixed. (T3537)
* A bug that caused drafted mails not to encrypt the correct
content has been fixed. (T3419)
* The recipient lookup for Exchange addresses has been slightly
improved.
* When Outlooks internal S/MIME handling code was activated
mails might be sent out unencrypted (T3656)
* Fixed signed only PGP Mails with attachments. (T3735)
Noteworthy changes for version 2.0.5 (2017-12-08)
=================================================
* A crash when receiving crypto mails with attachments without
file extension has been fixed. (T3582).
* Fixed a cause for potentially undefined behavior when closing.
Noteworthy changes for version 2.0.4 (2017-12-05)
=================================================
* Some possible "random" crashes in GpgOL have been fixed (T3484)
* Fixed Outlook hang when selecting and deleting many mails (T3433)
* G Suite Sync plugin accounts are now detected. Only
no-mime PGP/Messages (without attachments) and encrypted only
is supported. Reading is fully supported.
* Basic support for No-MIME inline PGP Encryption (T3514)
* Improved error handling for signed, unencrypted mails (T3538)
* Performance improvements / Fix running out of resources (T3523)
* Improved detection of large PGP/MIME messages and MS-TNEF Messages.
(T3419 , T3542)
Noteworthy changes for version 2.0.3 (2017-11-20)
=================================================
* Additional saveguards have been added to prevent
sending out unencrypted bodys when used with
Exchange 2007.
* Fixed a regression from 2.0.2 regarding message
list display in Outlook 2010 and 2013.
Noteworthy changes for version 2.0.2 (2017-11-16)
=================================================
* A potential crash when pasting recpients was fixed.
* A potential random crash in Outlook 2016 has been worked
around.
* Encoding problems when reading HTML mails have been fixed.
* S/MIME Mails are reverted again if S/MIME is disabled.
* S/MIME Mails through Exchange and sent mails are now
handled correctly.
Noteworthy changes for version 2.0.1 (2017-09-12)
=================================================
* Support for some kinds of PGP Multipart / Signed
mails has been fixed if S/MIME is disabled.
Noteworthy changes for version 2.0.0 (2017-09-12)
=================================================
* Decryption / verification is done in a second thread so outlook
stays responsive while decrypting.
* Opening a mail in a reader window no longer causes Outlook to
resync the mail.
* Inline editors (Reply and Forward in the messagelist) are now
supported.
* The HTML preferences from Outlook are now respected when viewing
an encrypted multipart/alternative mail.
* Two crashes that sometimes occured when sending mail have been
fixed.
* The "Do you want to save the changes" Messageboxes from outlook
no longer show up.
* Signature details are now shown in the Mail ribbon when reading
messages.
* Signature and encryption status is now shown in Outlook through
categorisation. No more popups when reading encrypted mails.
* There is now an Option to use inline-pgp when encrypting mails
without attachments.
* When opening a mail in a reader window closing it no longer causes
the mail in the Messagelist not to be displayed anymore.
* Decryption no longer requires an UI-Server (GPA or Kleopatra).
* Various bugfixes.
Noteworthy changes for version 1.4.0 (2016-03-30)
=================================================
* (OL > 2007) An option dialog has been added to enable / disable
S/MIME support and the new "simplified interface"
* (OL > 2007) An option for a "simplified interface" has been Added.
With this option encrypt / sign is now done while sending,
including all attachments and using a standard format. (MIME Support)
In this mode GpgOL automatically decrypts / verifies messages. And the
only interface are the encrypt & sign buttons in the New Mail tab.
This option is currently disabled by default but will eventually become
the standard.
* 64 Bit versions of Outlook are now supported.
* (OL > 2007) Settings dialog added. (accessible over the new
mail ribbon)
* (OL > 2007) S/MIME Support is disabled by default. Enable it
in the settings.
* (OL > 2007) Reduced amount of syncing done by Outlook while
looking at decrypted MIME mails.
* (OL > 2007) If S/MIME is disabled GpgOL reverts the changes
it made while reading an S/MIME mail so that Outlook can
handle them again.
* (OL > 2007) If GpgOL can't prevent syncing changes through
IMAP or Exchange it tries to restore the original mail so
that other clients can also read them.
* (OL > 2007) Improved lookup of Exchange Sender address.
* (OL > 2007) Fixed crash when Exchange Active Sync (Outlook.com)
is used.
Noteworthy changes for version 1.3.0 (2015-11-24)
=================================================
* Outlook 2010 and later now handle recieved MIME mails.
* A class of random crashes in Outlook 2010 and later has been
fixed. Bug#1837
* Attachments of mime mails with non ASCII characters are
now handled correctly.
* Outlook 2016 is now supported.
* Added translations for Chinese and French.
Noteworthy changes for version 1.2.1 (2014-08-13)
=================================================
* Fixed recipient/sender lookup problems when using Exchange or
Active Directory.
Noteworthy changes for version 1.2.0 (2013-08-19)
=================================================
* Basic support for Outlook 2010 and later.
Noteworthy changes for version 1.1.3 (2011-12-27)
=================================================
* Fix data corruption bug for certain attachments. Bug#1352.
* Fix crash on opening attachments with OL2007. Bug #1110.
* Use the GIT commit ids instead of SVN revision numbers for version
checks and to construct the Windows file version.
Noteworthy changes for version 1.1.2 (2010-07-21)
=================================================
* Add Portuguese translation
* Fixed linking problems with latest libgpg-error.
Noteworthy changes for version 1.1.1 (2010-01-13)
=================================================
* Cleaned up some icons.
Noteworthy changes for version 1.1.0 (2010-01-05)
=================================================
* Replaced most ECE code by direct OOM code. This was required to
support better icons; i.e. icons not limited to a 16 color palette.
* New icons.
* Removed protocol selection. The UI-server is now expected to select
the protocol (i.e. the auto selection mode is now the only one).
Noteworthy changes for version 1.0.1 (2009-09-28)
=================================================
* No more event loop peeking to avoid problem with Office programs.
* S/MIME support is now enabled by default.
Noteworthy changes for version 1.0.0 (2009-06-18)
=================================================
* Show a notice about potential problems.
* After about 2 years of development, the 1.0 version is now due.
Noteworthy changes for version 0.10.19 (2009-02-27)
===================================================
* Save the crypto settings in a message draft.
* Unnamed attachments are now shown with a suffix matching its MIME
type.
Noteworthy changes for version 0.10.18 (2009-01-28)
===================================================
* Handle OL created S/MIME messages.
Noteworthy changes for version 0.10.17 (2008-11-14)
===================================================
* Minor cleanups.
* All operations are now somewhat faster.
Noteworthy changes for version 0.10.16 (2008-11-11)
===================================================
* Fixed a regression in the last release with opaque signatures.
* Fixed PGP cleartext signature verification.
* Encryption of attachments is now much faster.
Noteworthy changes for version 0.10.15 (2008-08-06)
===================================================
* New option to present the body of a message as an attachment. This
is useful to make sure that the body will never show up as
plaintext in the message store.
* New menu item to remove all GpgOL created flags and attachments
from all messages in a folder.
* Icons are now installed for messages processed by GpgOL. For now
only for the German version of Outlook.
Noteworthy changes for version 0.10.14 (2008-05-28)
===================================================
* Minor fixes.
Noteworthy changes for version 0.10.13 (2008-05-06)
===================================================
* Properly handle the disposition of text attachments.
Noteworthy changes for version 0.10.12 (2008-04-16)
===================================================
* Added icons.
* Minor usuability changes.
Noteworthy changes for version 0.10.11 (2008-04-04)
===================================================
* Fixed a performance problem with signed+encrypted.
Noteworthy changes for version 0.10.10 (2008-04-02)
===================================================
* Visual cleanups.
* Changes to the I/O dispatcher.
Noteworthy changes for version 0.10.9 (2008-03-19)
==================================================
* Decrypt opaque signed and encrypted S/MIME mails.
* Handle old-style PGP message with attachments. Note that the
signature verification currently may indicate a bad signature.
Noteworthy changes for version 0.10.8 (2008-03-18)
==================================================
* Fixed a segv introduced with 0.10.6.
Noteworthy changes for version 0.10.7 (2008-03-11)
==================================================
* Changed the way sign+encrypt works to help the UI-server.
Noteworthy changes for version 0.10.6 (2008-03-10)
==================================================
* More tweaks to allow processing of opaque encrypted or signed
S/MIME.
* Shows an error message when trying to decrypt/verify messages not
signed or encrypted.
* Soft line breaks in QP encoded messages are now correctly
processed.
* The sender's address is send to the UI server to allow it to select
an appropriate signing key.
* Automatic protocol selection works now also with signing.
* Processing large messages is faster.
Noteworthy changes for version 0.10.5 (2008-02-18)
==================================================
* PGP inline encrypted mails are not anymore deleted after the first
decryption.
Noteworthy changes for version 0.10.4 (2008-02-06)
==================================================
* Sign and encrypt works now.
* Texts with embedded attachments are now concatenated.
* Encrypted message are now viewable in the sent messages folder.
Noteworthy changes for version 0.10.3 (2007-12-10)
==================================================
* Minor fixes.
Noteworthy changes for version 0.10.2 (2007-11-12)
==================================================
* New menu items to select the default protocol.
* Code cleanups.
Noteworthy changes for version 0.10.1 (2007-10-22)
==================================================
* Auto start the server.
* Code cleanups.
* Made all dialogs language neutral.
* The manual has some notes about the Registry usage and new MAPI
properties.
Noteworthy changes for version 0.10.0 (2007-10-11)
==================================================
* Basically a complete rewrite. A lot of things are still missing but
if might be useful to see the direction the development takes.
Noteworthy changes for version 0.9.91 (2006-10-13)
==================================================
* Fixed a crash in the recipients dialog.
Noteworthy changes for version 0.9.90 (2006-08-28)
==================================================
* Fix problem that message would be sent in clear
text if the user cancelled the operation.
* Cosmetic updates for some dialogs.
* Do not show the 'select signer dialog' when only
one secret key is available in the keyring.
* Fixes for the automatic key selection algorithm
used in the recipient key dialog.
Noteworthy changes for version 0.9.10 (2006-04-25)
==================================================
* Fixes for Umlaut problems.
Noteworthy changes for version 0.9.9 (2006-04-24)
=================================================
* Some cosmetic changes.
* Encryption to the default key works again.
Noteworthy changes for version 0.9.8 (2006-03-28)
=================================================
* PGP/MIME signature verification may now work in some cases.
* New option to prefer displaying of the HTML part.
Noteworthy changes for version 0.9.7 (2006-03-21)
=================================================
* Minor changes
Noteworthy changes for version 0.9.6 (2006-01-26)
=================================================
* Cosmetic fixes.
Noteworthy changes for version 0.9.5 (2005-12-07)
=================================================
* Fixed problems related to use on non-admin accounts.
* Print a warning if used with OL prior to OL2003 SP2.
Noteworthy changes for version 0.9.4 (2005-12-06)
=================================================
* Added translation framework. Provided German translation.
* New option to enable automatic decryption in the preview window.
* Removed deprecated options to configure gpg path and homedir.
* Default key from the option dialog works.
* Support for HTML mails.
Noteworthy changes for version 0.9.3 (2005-09-29)
=================================================
* Fixed bugs introduced with the last release.
* PGP/MIME decryption works now correctly with Latin-1 and utf-8.
* No more pop-ups to ask whether to save changes after just decrypting
a message.
* Fixed a couple of bugs possibly leading to crashes.
Noteworthy changes for version 0.9.2 (2005-09-22)
=================================================
* Saving attachments from PGP/MIME encrypted messages works.
Noteworthy changes for version 0.9.1 (2005-09-19)
=================================================
* Bug fixes
Noteworthy changes for version 0.9.0 (2005-09-04)
=================================================
* Major rewrite. Renamed the package to GPGol. Note, that there used
to be intermediate versions unter the name OutlGPG
* The package as been renamed to GPGol and consist of only one DLL
named "gpgol.dll". Installation of gpgme.dll and libgpg-error.dll
is required.
* It may by now only be build using the Mingw32 toolchain.
* GPGol now uses the standard GPGME.
Noteworthy changes for version 0.6.1 (unreleased)
=================================================
* Fix the problem that the user can just reply with
the encrypted text.
* Fixes for a lot of minor problems with NT5 based
systems and for Outlook version 2003.
* Support for handling HTML mails.
This includes the encryption of the contents and
the proper decryption without losing the special
(html) text attributes like colors.
* Support for '%ENV%' strings for the log file.
Noteworthy changes for version 0.5.5 (2005-07-12)
=================================================
* Support to sign all outgoing attachments.
* Support for logging.
* Fixed some memory leaks.
Noteworthy changes for version 0.5.4 (2005-07-03)
=================================================
* Support for securing attachments.
This means the all attachments will be encrypted
if encryption has been selected for the message.
* A new option to allow to save decrypted attachments
to the disk.
* Several bug fixes all over the place.
Noteworthy changes for version 0.5.3 (2005-06-16)
=================================================
* Allow to set a default key which is used automatically
for encryption.
* Handle old V3 keys in the signature verification dialog.
* Issue and error if the encrypt process returned invalid
recipients.
Noteworthy changes for version 0.5.2 (2005-06-05)
=================================================
* Differ between possible decryption failures.
- General errors.
- No secret key available.
* Add a 'encrypt-to' listbox to the decryption dialog
to know the recipients the message was encrypted for.
* Add some checks to report problems with permissions
related to the Registry.
* Fixed a format string problem which was possible for
crashes when the signature has been expired.
Noteworthy changes for version 0.5.1 (2005-05-29)
=================================================
* Issue a warning if the user cancels the sign or
encryption procedure.
* Support to read and write X- headers for messages.
* Fixed a problem which crashes Outlook if the keyManager
exe did not exist but was set in the registry.
Noteworthy changes for version 0.4.0 (2005-05-10)
=================================================
* Verify dialog is automatically shown whenever needed. Plus it
contains a hint-label whenever the signature is special. For
example the signature has expire or it was issued by a key which
is not trustworthy.
* Offer a GPG configuration dialog to set the path to GPG, the home
directory and an optional field to specify a key manager.
* Common dialogs for the following procedures:
- verify a clearsign signature
- decrypt a message (and verify a signature)
- encrypt a message (and sign the plaintext)
- clearsign a message
* Provide a class to encapsulate MAPI messages and high-level functions
for all crypto operations.
diff --git a/src/cryptcontroller.cpp b/src/cryptcontroller.cpp
index 38da2fe..1462aa5 100644
--- a/src/cryptcontroller.cpp
+++ b/src/cryptcontroller.cpp
@@ -1,1567 +1,1589 @@
/* @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 "recipient.h"
#include "recipientmanager.h"
#include "windowmessages.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_recipients = mail->getCachedRecipients ();
m_signer_keys = mail->getSigningKeys ();
m_sender = mail->getSender ();
TRETURN;
}
CryptController::~CryptController()
{
TSTART;
memdbg_dtor ("CryptController");
log_debug ("%s:%s:%p",
SRCNAME, __func__, m_mail);
TRETURN;
}
int
CryptController::collect_data ()
{
TSTART;
int count = m_mail->plainAttachments ().size ();
/* 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;
}
if (!count && !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);
}
xfree (body);
TRETURN -1;
}
bool do_inline = m_mail->getDoPGPInline ();
if (count && 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, m_mail, body);
xfree (body);
if (err)
{
log_error ("%s:%s: Collecting body and attachments failed.",
SRCNAME, __func__);
TRETURN -1;
}
/* Set the input buffer to start. */
m_input.seek (0, SEEK_SET);
TRETURN 0;
}
int
CryptController::lookup_fingerprints (const std::vector &sigFprs,
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 (sigFprs.size())
{
char **cSigners = vector_to_cArray (sigFprs);
err = ctx->startKeyListing (const_cast (cSigners), true);
if (err)
{
log_error ("%s:%s: failed to start signer keylisting",
SRCNAME, __func__);
release_cArray (cSigners);
TRETURN -1;
}
do
{
m_signer_keys.push_back(ctx->nextKey(err));
}
while (!err);
release_cArray (cSigners);
m_signer_keys.pop_back();
if (m_signer_keys.empty())
{
log_error ("%s:%s: failed to lookup key for '%s' with protocol '%s'",
SRCNAME, __func__, anonstr (sigFprs[0].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;
}
std::vector all_fingerprints;
for (const auto &pair: recpFprs)
{
all_fingerprints.push_back (pair.second);
}
for (auto &recp: m_recipients)
{
std::vector fingerprintsToLookup;
/* Find the matching fingerprints for the recpient. */
for (const auto &pair: recpFprs)
{
if (pair.first.empty())
{
log_error ("%s:%s Recipient mbox is empty. Wrong Resolver Version?",
SRCNAME, __func__);
}
/* We also accept the empty string here for backwards compatibility
but we should still log an error to be clear. */
if (pair.first.empty() || pair.first == recp.mbox ())
{
if (pair.second.empty ())
{
log_err ("Would have added an empty string!");
continue;
}
fingerprintsToLookup.push_back (pair.second);
all_fingerprints.erase(std::remove_if(all_fingerprints.begin(),
all_fingerprints.end(),
[&pair](const std::string &x){return x == pair.second;}),
all_fingerprints.end());
}
}
if (!fingerprintsToLookup.size ())
{
log_dbg ("No key selected for '%s'",
anonstr (recp.mbox().c_str ()));
continue;
}
// Convert recipient fingerprints
char **cRecps = vector_to_cArray (fingerprintsToLookup);
err = ctx->startKeyListing (const_cast (cRecps));
if (err)
{
log_error ("%s:%s: failed to start recipient keylisting",
SRCNAME, __func__);
release_cArray (cRecps);
TRETURN -1;
}
std::vector keys;
do {
const auto key = ctx->nextKey (err);
if (key.isNull () || err)
{
continue;
}
keys.push_back (key);
log_dbg ("Adding '%s' as key for '%s",
anonstr (key.primaryFingerprint ()),
anonstr (recp.mbox ().c_str ()));
} while (!err);
release_cArray (cRecps);
recp.setKeys (keys);
// reset context
ctx = std::shared_ptr (GpgME::Context::createForProtocol (m_proto));
ctx->setKeyListMode (GpgME::Local);
}
if (!all_fingerprints.empty())
{
log_error ("%s:%s: BUG: Not all fingerprints could be matched "
"to a recipient.",
SRCNAME, __func__);
for (const auto &fpr: all_fingerprints)
{
log_debug ("%s:%s Failed to find: %s", SRCNAME, __func__,
anonstr (fpr.c_str ()));
}
clear_keys ();
TRETURN -1;
}
resolving_done ();
TRETURN 0;
}
int
CryptController::parse_output (GpgME::Data &resolverOutput)
{
TSTART;
std::istringstream ss(resolverOutput.toString());
std::string line;
std::vector sigFprs;
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::string mbox;
std::getline (lss, what, ':');
std::getline (lss, how, ':');
std::getline (lss, fingerprint, ':');
std::getline (lss, mbox, ':');
if (m_proto == GpgME::UnknownProtocol)
{
/* TODO: Allow mixed */
m_proto = (how == "smime") ? GpgME::CMS : GpgME::OpenPGP;
}
if (what == "sig")
{
sigFprs.push_back (fingerprint);
continue;
}
if (what == "enc")
{
recpFprs.push_back(std::make_pair(mbox, fingerprint));
}
}
if (m_sign && sigFprs.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 (sigFprs, recpFprs);
}
/* Combines all recipient keys for this operation
into a list and copys the resolved recipients
back. */
void
CryptController::resolving_done ()
{
TSTART;
m_enc_keys.clear ();
for (const auto &recp: m_recipients)
{
const auto &keys = recp.keys ();
m_enc_keys.insert (m_enc_keys.end (), keys.begin (), keys.end ());
}
/* Copy the resolved recipients back to the mail */
m_mail->setRecipients (m_recipients);
m_mail->setSigningKeys (m_signer_keys);
TRETURN;
}
bool
CryptController::resolve_through_protocol (GpgME::Protocol proto)
{
TSTART;
const auto cache = KeyCache::instance ();
if (m_encrypt)
{
for (auto &recp: m_recipients)
{
recp.setKeys(cache->getEncryptionKeys(recp.mbox (), proto));
}
}
if (m_sign)
{
if (m_sender.empty())
{
log_error ("%s:%s: Asked to sign but sender is empty.",
SRCNAME, __func__);
return false;
}
const auto key = cache->getSigningKey (m_sender.c_str (), proto);
if (m_signer_keys.empty ())
{
m_signer_keys.push_back (key);
}
for (const auto &k: m_signer_keys)
{
if (k.protocol () != proto)
{
m_signer_keys.push_back (key);
break;
}
}
}
TRETURN is_resolved ();
}
/* Check if we can be resolved by a single protocol
and return it. */
GpgME::Protocol
CryptController::get_resolved_protocol () const
{
TSTART;
GpgME::Protocol ret = GpgME::UnknownProtocol;
bool hasOpenPGPSignKey = false;
bool hasSMIMESignKey = false;
if (m_sign)
{
for (const auto &sig_key: m_signer_keys)
{
hasOpenPGPSignKey |= (!sig_key.isNull() &&
sig_key.protocol () == GpgME::OpenPGP);
hasSMIMESignKey |= (!sig_key.isNull() &&
sig_key.protocol () == GpgME::CMS);
}
}
if (m_encrypt)
{
for (const auto &recp: m_recipients)
{
if (!recp.keys ().size () || recp.keys ()[0].isNull ())
{
/* No keys or the first key in the list is null. */
TRETURN GpgME::UnknownProtocol;
}
for (const auto &key: recp.keys())
{
if (key.protocol () == GpgME::OpenPGP &&
(!m_sign || hasOpenPGPSignKey) &&
(ret == GpgME::UnknownProtocol || ret == GpgME::OpenPGP))
{
ret = GpgME::OpenPGP;
continue;
}
if (key.protocol () == GpgME::CMS &&
(!m_sign || hasSMIMESignKey) &&
(ret == GpgME::UnknownProtocol || ret == GpgME::CMS))
{
ret = GpgME::CMS;
continue;
}
// Unresolvable
TRETURN GpgME::UnknownProtocol;
}
}
}
TRETURN ret;
}
/* Check that the crypt operation is resolved. This
supports combined S/MIME and OpenPGP operations. */
bool
CryptController::is_resolved () const
{
/* Check that we have signing keys if necessary and at
least one encryption key for each recipient. */
bool hasOpenPGPSignKey = false;
bool hasSMIMESignKey = false;
if (m_sign)
{
for (const auto &sig_key: m_signer_keys)
{
hasOpenPGPSignKey |= (!sig_key.isNull() &&
sig_key.protocol () == GpgME::OpenPGP);
hasSMIMESignKey |= (!sig_key.isNull() &&
sig_key.protocol () == GpgME::CMS);
}
}
log_dbg ("Has OpenPGP Sig Key: %i SMIME: %i",
hasOpenPGPSignKey, hasSMIMESignKey);
if (m_encrypt)
{
Recipient::dump (m_recipients);
for (const auto &recp: m_recipients)
{
if (!recp.keys ().size () || recp.keys ()[0].isNull ())
{
/* No keys or the first key in the list is null. */
return false;
}
/* If we don't sign we need no more checks. */
if (!m_sign)
{
continue;
}
for (const auto &key: recp.keys())
{
if (key.protocol () == GpgME::OpenPGP && !hasOpenPGPSignKey)
{
return false;
}
if (key.protocol () == GpgME::CMS && !hasSMIMESignKey)
{
return false;
}
}
}
}
return true;
}
int
CryptController::resolve_keys_cached()
{
TSTART;
// Prepare variables
const auto recps = m_mail->getCachedRecipientAddresses ();
bool resolved = false;
if (opt.enable_smime && opt.prefer_smime)
{
resolved = resolve_through_protocol (GpgME::CMS);
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);
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);
if (resolved)
{
log_debug ("%s:%s: Resolved with CMS as fallback.",
SRCNAME, __func__);
m_proto = GpgME::CMS;
}
}
for (const auto &recp: m_recipients)
{
log_debug ("Enc Key for: '%s' Type: %i", anonstr (recp.mbox ().c_str ()),
recp.type ());
for (const auto &key: recp.keys ())
{
log_debug ("%s:%s", to_cstr (key.protocol ()),
anonstr (key.primaryFingerprint ()));
}
}
for (const auto &sig_key: m_signer_keys)
{
log_debug ("%s:%s: Signing key: %s:%s",
SRCNAME, __func__,
to_cstr (sig_key.protocol()),
anonstr (sig_key.primaryFingerprint ()));
}
if (!resolved)
{
log_debug ("%s:%s: Failed to resolve through cache",
SRCNAME, __func__);
m_enc_keys.clear ();
m_signer_keys.clear ();
m_proto = GpgME::UnknownProtocol;
TRETURN 1;
}
if (m_encrypt)
{
log_debug ("%s:%s: Encrypting with protocol %s to:",
SRCNAME, __func__, to_cstr (m_proto));
}
TRETURN 0;
}
void
CryptController::clear_keys ()
{
for (auto &recp: m_recipients)
{
recp.setKeys (std::vector ());
}
m_signer_keys.clear ();
}
int
CryptController::resolve_keys ()
{
TSTART;
m_proto = get_resolved_protocol ();
if (m_proto != GpgME::UnknownProtocol)
{
log_debug ("%s:%s: Already resolved by %s Not resolving again.",
SRCNAME, __func__, to_cstr (m_proto));
start_crypto_overlay();
resolving_done ();
TRETURN 0;
}
m_enc_keys.clear();
if (m_mail->isDraftEncrypt() && opt.draft_key)
{
- const auto key = KeyCache::instance()->getByFpr (opt.draft_key);
+ GpgME::Key key;
+ if (opt.draft_key && !strcmp (opt.draft_key, "auto"))
+ {
+ log_dbg ("Autoselecting draft key first ultimate key.");
+ for (const auto &k: KeyCache::instance()->getUltimateKeys ())
+ {
+ if (k.hasSecret () && k.canEncrypt ())
+ {
+ xfree (opt.draft_key);
+ gpgrt_asprintf (&opt.draft_key, "%s", k.primaryFingerprint());
+ log_dbg ("Autoselecting %s as draft encryption key.",
+ opt.draft_key);
+ write_options ();
+ key = k;
+ break;
+ }
+ }
+ }
+
+ if (key.isNull ())
+ {
+ 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_enc_keys.push_back (key);
TRETURN 0;
}
if (!m_recipients.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 && !opt.alwaysShowApproval && !resolve_keys_cached ())
{
log_debug ("%s:%s: resolved keys through the cache",
SRCNAME, __func__);
start_crypto_overlay();
resolving_done ();
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 || 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 &recp: m_recipients)
{
const auto mbox = recp.mbox ();
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 = 0;
if (m_mail->copyParent ())
{
/* Bypass resolving if we are working on a split mail */
m_recipients = m_mail->getCachedRecipients ();
m_signer_keys = m_mail->getSigningKeys ();
if (m_recipients.size () && m_recipients[0].keys ().size ())
{
m_proto = m_recipients[0].keys ()[0].protocol ();
}
else if (m_signer_keys.size ())
{
m_proto = m_signer_keys[0].protocol ();
}
m_enc_keys.clear ();
for (const auto &recp: m_recipients)
{
const auto &keys = recp.keys ();
m_enc_keys.insert (m_enc_keys.end (), keys.begin (), keys.end ());
}
if ((opt.enable_debug & DBG_DATA))
{
log_data ("Encrypting to: ");
Recipient::dump (m_recipients);
}
}
else
{
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;
}
if (!m_mail->copyParent ())
{
RecipientManager mngr (m_recipients, m_signer_keys);
if (mngr.getRequiredMails () > 1)
{
log_dbg ("More then one mail required for this recipient selection.");
/* If we need to send multiple emails we jump back from
here into the main event loop. Copy the mail object
and send it out mutiple times. */
do_in_ui_thread_async (SEND_MULTIPLE_MAILS, m_mail);
/* Cancel the crypto of this mail this continues
in Mail::splitAndSend_o */
wm_unregister_pending_op (m_mail);
TRETURN -3;
}
}
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_keys.empty ())
{
for (const auto &key: m_signer_keys)
{
if (key.protocol () == m_proto)
{
ctx->addSigningKey (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_enc_keys,
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_enc_keys, 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_enc_keys, 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 92119bf..5ce3ef1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,419 +1,420 @@
/* 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.smime_insecure_reply_fw_allowed = get_conf_bool ("smimeInsecureReplyAllowed", 0);
opt.auto_unstrusted = get_conf_bool ("autoencryptUntrusted", 0);
opt.autoimport = get_conf_bool ("autoimport", 0);
opt.splitBCCMails = get_conf_bool ("splitBCCMails", 0);
opt.combinedOpsEnabled = get_conf_bool ("combinedOpsEnabled", 0);
opt.encryptSubject = get_conf_bool ("encryptSubject", 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 = 1; //get_conf_bool ("syncEnc", 0);
/* Due to an issue where async encryption would leave
unencrypted mails in the recently deleted folder on the
server we block it. */
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},
+ {"draftKey", 2, 0, opt.draft_key},
{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;
}