Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623130
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
120 KB
Subscribers
None
View Options
diff --git a/NEWS b/NEWS
index 4ea7a01..3ee4dd5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,1015 +1,1018 @@
-Noteworthy changes for version 2.5.11 (unreleased)
+Noteworthy changes for version 2.5.11 (2023-11-29)
==================================================
+* Moved initialization code from DLL Load point to
+ COM initialization. (T6856)
+
Noteworthy changes for version 2.5.10 (2023-11-29)
==================================================
* Internal attachments are now called
GpgOL_MIME_strucutre.mime to make it easier to
link them to Kleopatra. (T6656)
* Improved compatibility with other mail clients
or mails that might have been modified in transfer
to still be able to decrypt them. (T6686)
* It is now possible that after a warning users
can encrypt to S/MIME certificates which are not
trusted due to CRL errors or an untrusted root
CA. This is not VS-NfD compliant. (T6701)
* The error handling was improved if a preference
for S/MIME is set, signing selected but no signing
certificate can be found.
See: https://gnupg.com/vsd/registry-settings.html
How to add a custom message for that case. (T6683)
* Draft encryption with S/MIME certificates now
skips CRL checks and is much faster and reliable
now. (T6827)
Noteworthy changes for version 2.5.9 (2023-08-02)
=================================================
* Additional handling for multiline and language
encoded attachment names according to RFC2231.
(T6604)
* Fixed an issue with S/MIME opaque signed mails
where the contents of invalid signed mails would
not be shown. (T6624)
Noteworthy changes for version 2.5.8 (2023-07-07)
=================================================
* Fix crash for attachments without filename.
(T6546)
Noteworthy changes for version 2.5.7 (2023-05-16)
=================================================
* Fix for potential plaintext leaks. (dd3ff839)
* Ensure category and flag changes are saved
before decrypting a mail. (T4127)
* Fix truncation of short mails with a single
"protected-header" text/plain part. (T6357)
Noteworthy changes for version 2.5.6 (2022-12-19)
=================================================
* Fix theoretical integer overwflow in TLV parser.
Noteworthy changes for version 2.5.5 (2022-10-13)
=================================================
* Fixed IMAP access to encrypted mails. (T6203)
Noteworthy changes for version 2.5.4 (2022-09-06)
=================================================
* Fixed some encoding problems.
* Support sending drafts with modified sender.
* If an exclamation mark is added to a config
value in HKEY_LOCAL_MACHINE it is now forced
and cannot be changed by the user. (T5827)
* Fixed an issue that could cause a rare
hang when looking at unencrypted mails.
(RT #8917)
* Delete temporary files on error to avoid
problems with existing tmp files. (T5926)
Noteworthy changes for version 2.5.3 (2022-04-20)
=================================================
* Fixed a double free error which could lead
to random crashes. This double free was not
exploitable as a security issue.
Noteworthy changes for version 2.5.2 (2022-02-09)
=================================================
* 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/common.cpp b/src/common.cpp
index 2f6d2d6..1382276 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -1,1203 +1,1226 @@
/* common.c - Common routines used by GpgOL
* Copyright (C) 2005, 2007, 2008 g10 Code GmbH
* 2015, 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
* 2020 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#define OEMRESOURCE /* Required for OBM_CHECKBOXES. */
#include <windows.h>
#include <shlobj.h>
#ifndef CSIDL_APPDATA
#define CSIDL_APPDATA 0x001a
#endif
#ifndef CSIDL_LOCAL_APPDATA
#define CSIDL_LOCAL_APPDATA 0x001c
#endif
#ifndef CSIDL_FLAG_CREATE
#define CSIDL_FLAG_CREATE 0x8000
#endif
#include <time.h>
#include <fcntl.h>
#include <ctype.h>
#include "common.h"
#include "dialogs.h"
#include "cpphelp.h"
#include <string>
#include <fstream>
#include <gpgme++/context.h>
#include <gpgme++/error.h>
#include <gpgme++/configuration.h>
#define COPYBUFSIZE (8 * 1024)
HINSTANCE glob_hinst = NULL;
-void
-set_global_hinstance (HINSTANCE hinst)
-{
- glob_hinst = hinst;
-}
-
void
bring_to_front (HWND wid)
{
if (wid)
{
if (!SetForegroundWindow (wid))
{
log_debug ("%s:%s: SetForegroundWindow failed", SRCNAME, __func__);
/* Yet another fallback which will not work on some
* versions and is not recommended by msdn */
if (!ShowWindow (wid, SW_SHOWNORMAL))
{
log_debug ("%s:%s: ShowWindow failed.", SRCNAME, __func__);
}
}
}
log_debug ("%s:%s: done", SRCNAME, __func__);
}
void
fatal_error (const char *format, ...)
{
va_list arg_ptr;
char buf[512];
va_start (arg_ptr, format);
vsnprintf (buf, sizeof buf -1, format, arg_ptr);
buf[sizeof buf - 1] = 0;
va_end (arg_ptr);
MessageBox (NULL, buf, "Fatal Error", MB_OK);
abort ();
}
/* Helper for read_w32_registry_string(). */
static HKEY
get_root_key(const char *root)
{
HKEY root_key;
if( !root )
root_key = HKEY_CURRENT_USER;
else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
root_key = HKEY_CLASSES_ROOT;
else if( !strcmp( root, "HKEY_CURRENT_USER" ) )
root_key = HKEY_CURRENT_USER;
else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
root_key = HKEY_LOCAL_MACHINE;
else if( !strcmp( root, "HKEY_USERS" ) )
root_key = HKEY_USERS;
else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
root_key = HKEY_PERFORMANCE_DATA;
else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
root_key = HKEY_CURRENT_CONFIG;
else
return NULL;
return root_key;
}
#if defined(_WIN64)
#define CROSS_ACCESS KEY_WOW64_32KEY
#else
#define CROSS_ACCESS KEY_WOW64_64KEY
#endif
/* Read a registry value from HKLM or HKCU of type dword and
return 1 if the value is larger then 0, 0 if it is zero and
-1 if it is not set or not found. */
int
read_reg_bool (HKEY root, const char *path, const char *value)
{
TSTART;
HKEY h;
HKEY tmp = root ? root : HKEY_CURRENT_USER;
int err = RegOpenKeyEx (tmp, path , 0, KEY_READ, &h);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: not found %s",
SRCNAME, __func__, path);
if (!root)
{
TRETURN read_reg_bool (HKEY_LOCAL_MACHINE, path, value);
}
TRETURN -1;
}
DWORD type;
err = RegQueryValueEx (h, value, NULL, &type, NULL, NULL);
if (err != ERROR_SUCCESS || type != REG_DWORD)
{
log_debug ("%s:%s: No type or key for %s",
SRCNAME, __func__, value);
if (!root)
{
TRETURN read_reg_bool (HKEY_LOCAL_MACHINE, path, value);
}
TRETURN -1;
}
DWORD data;
DWORD size = sizeof (DWORD);
err = RegQueryValueEx (h, value, NULL, NULL, (LPBYTE)&data,
&size);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: Failed to find value of %s",
SRCNAME, __func__, value);
if (!root)
{
TRETURN read_reg_bool (HKEY_LOCAL_MACHINE, path, value);
}
TRETURN -1;
}
TRETURN !!data;
}
std::string
_readRegStr (HKEY root_key, const char *dir,
const char *name, bool alternate)
{
#ifndef _WIN32
(void) root_key; (void)alternate; (void)dir; (void)name;
return std::string();
#else
HKEY key_handle;
DWORD n1, nbytes, type;
std::string ret;
DWORD flags = KEY_READ;
if (alternate) {
flags |= CROSS_ACCESS;
}
if (RegOpenKeyExA(root_key, dir, 0, flags, &key_handle)) {
return ret;
}
nbytes = 1;
if (RegQueryValueExA(key_handle, name, 0, nullptr, nullptr, &nbytes)) {
RegCloseKey (key_handle);
return ret;
}
n1 = nbytes+1;
char result[n1];
if (RegQueryValueExA(key_handle, name, 0, &type, (LPBYTE)result, &n1)) {
RegCloseKey(key_handle);
return ret;
}
RegCloseKey(key_handle);
result[nbytes] = 0; /* make sure it is really a string */
ret = result;
if (type == REG_EXPAND_SZ && strchr (result, '%')) {
n1 += 1000;
char tmp[n1 +1];
nbytes = ExpandEnvironmentStringsA(ret.c_str(), tmp, n1);
if (nbytes && nbytes > n1) {
n1 = nbytes;
char tmp2[n1 +1];
nbytes = ExpandEnvironmentStringsA(result, tmp2, n1);
if (nbytes && nbytes > n1) {
/* oops - truncated, better don't expand at all */
return ret;
}
tmp2[nbytes] = 0;
ret = tmp2;
} else if (nbytes) { /* okay, reduce the length */
tmp[nbytes] = 0;
ret = tmp;
}
}
return ret;
#endif
}
std::string
readRegStr (const char *root, const char *dir, const char *name)
{
#ifndef _WIN32
(void)root; (void)dir; (void)name;
return std::string();
#else
HKEY root_key;
std::string ret;
if (!(root_key = get_root_key(root))) {
return ret;
}
if (root == nullptr)
{
/* Nullptr so we first look into HKLM if we have
an override */
ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, false);
if (ret.empty()) {
// Try alternate as fallback
ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, true);
}
if (ret.size() && ret[ret.size() - 1] == '!')
{
// Using override reg value
log_dbg ("Using override for %s", name);
ret.pop_back();
return ret;
}
}
ret = _readRegStr (root_key, dir, name, false);
if (ret.empty()) {
// Try alternate as fallback
ret = _readRegStr (root_key, dir, name, true);
}
if (ret.empty()) {
// Try local machine as fallback.
ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, false);
if (ret.empty()) {
// Try alternative registry view as fallback
ret = _readRegStr (HKEY_LOCAL_MACHINE, dir, name, true);
}
}
return ret;
#endif
}
/* Return a string from the Win32 Registry or NULL in case of error.
Caller must release the return value. A NULL for root is an alias
for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn. NOTE: The value
is allocated with a plain xmalloc () - use xfree () and not the usual
xfree(). */
char *
read_w32_registry_string (const char *root, const char *dir, const char *name)
{
const auto ret = readRegStr (root, dir, name);
if (ret.empty())
{
return nullptr;
}
return xstrdup (ret.c_str ());
}
/* Return the data dir used for forms etc. Returns NULL on error. */
char *
get_data_dir (void)
{
char *instdir;
char *p;
char *dname;
instdir = get_gpg4win_dir();
if (!instdir)
return NULL;
/* Build the key: "<instdir>/share/gpgol". */
#define SDDIR "\\share\\gpgol"
dname = (char*) xmalloc (strlen (instdir) + strlen (SDDIR) + 1);
if (!dname)
{
xfree (instdir);
return NULL;
}
p = dname;
strcpy (p, instdir);
p += strlen (instdir);
strcpy (p, SDDIR);
xfree (instdir);
#undef SDDIR
return dname;
}
/* Percent-escape the string STR by replacing colons with '%3a'. If
EXTRA is not NULL all characters in it are also escaped. */
char *
percent_escape (const char *str, const char *extra)
{
int i, j;
char *ptr;
if (!str)
return NULL;
for (i=j=0; str[i]; i++)
if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i])))
j++;
ptr = (char *) xmalloc (i + 2 * j + 1);
i = 0;
while (*str)
{
/* FIXME: Work around a bug in Kleo. */
if (*str == ':')
{
ptr[i++] = '%';
ptr[i++] = '3';
ptr[i++] = 'a';
}
else
{
if (*str == '%')
{
ptr[i++] = '%';
ptr[i++] = '2';
ptr[i++] = '5';
}
else if (extra && strchr (extra, *str))
{
ptr[i++] = '%';
ptr[i++] = tohex_lower ((*str >> 4) & 15);
ptr[i++] = tohex_lower (*str & 15);
}
else
ptr[i++] = *str;
}
str++;
}
ptr[i] = '\0';
return ptr;
}
/* Fix linebreaks.
This replaces all consecutive \r or \n characters
by a single \n.
There can be extremly weird combinations of linebreaks
like \r\r\n\r\r\n at the end of each line when
getting the body of a mail message.
*/
void
fix_linebreaks (char *str, int *len)
{
char *src;
char *dst;
src = str;
dst = str;
while (*src)
{
if (*src == '\r' || *src == '\n')
{
do
src++;
while (*src == '\r' || *src == '\n');
*(dst++) = '\n';
}
else
{
*(dst++) = *(src++);
}
}
*dst = '\0';
*len = dst - str;
}
/* Get a pretty name for the file at path path. File extension
will be set to work for the protocol as provided in protocol and
depends on the signature setting. Set signature to 0 if the
extension should not be a signature extension.
Returns NULL on success.
Caller must free result. */
wchar_t *
get_pretty_attachment_name (wchar_t *path, protocol_t protocol,
int signature)
{
wchar_t* pretty;
wchar_t* buf;
if (!path || !wcslen (path))
{
log_error("%s:%s: No path given", SRCNAME, __func__);
return NULL;
}
pretty = (wchar_t*) xmalloc ((MAX_PATH + 1) * sizeof (wchar_t));
memset (pretty, 0, (MAX_PATH + 1) * sizeof (wchar_t));
buf = wcsrchr (path, '\\') + 1;
if (!buf || !*buf)
{
log_error("%s:%s: No filename found in path", SRCNAME, __func__);
xfree (pretty);
return NULL;
}
wcscpy (pretty, buf);
buf = pretty + wcslen(pretty);
if (signature)
{
if (protocol == PROTOCOL_SMIME)
{
*(buf++) = '.';
*(buf++) = 'p';
*(buf++) = '7';
*(buf++) = 's';
}
else
{
*(buf++) = '.';
*(buf++) = 's';
*(buf++) = 'i';
*(buf++) = 'g';
}
}
else
{
if (protocol == PROTOCOL_SMIME)
{
*(buf++) = '.';
*(buf++) = 'p';
*(buf++) = '7';
*(buf++) = 'm';
}
else
{
*(buf++) = '.';
*(buf++) = 'g';
*(buf++) = 'p';
*(buf++) = 'g';
}
}
return pretty;
}
BOOL
DeleteFileUtf8 (const char *utf8Name)
{
SetLastError (0);
if (!utf8Name)
{
STRANGEPOINT;
return false;
}
wchar_t *wname = utf8_to_wchar (utf8Name);
BOOL ret = DeleteFileW (wname);
xfree (wname);
return ret;
}
HANDLE
CreateFileUtf8 (const char *utf8Name)
{
SetLastError (0);
if (!utf8Name)
{
return INVALID_HANDLE_VALUE;
}
wchar_t *wname = utf8_to_wchar (utf8Name);
if (!wname)
{
TRACEPOINT;
return INVALID_HANDLE_VALUE;
}
auto ret = CreateFileW (wname,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_TEMPORARY,
NULL);
xfree (wname);
return ret;
}
int
readFullFile (HANDLE hFile, GpgME::Data &data)
{
char buf[COPYBUFSIZE];
DWORD bRead = 0;
BOOL ret;
while ((ret = ReadFile (hFile, buf, COPYBUFSIZE, &bRead, nullptr)))
{
if (!bRead)
{
// EOF
break;
}
data.write (buf, bRead);
}
if (!ret && bRead)
{
log_err ("Failed to read from file");
TRETURN -1;
}
TRETURN 0;
}
static std::string
getTmpPathUtf8 ()
{
static std::string ret;
if (!ret.empty())
{
return ret;
}
wchar_t tmpPath[MAX_PATH + 2];
if (!GetTempPathW (MAX_PATH, tmpPath))
{
log_error ("%s:%s: Could not get tmp path.",
SRCNAME, __func__);
return ret;
}
char *utf8Name = wchar_to_utf8 (tmpPath);
if (!utf8Name)
{
TRACEPOINT;
return ret;
}
ret = utf8Name;
xfree (utf8Name);
return ret;
}
/* Open a file in a temporary directory, take name as a
suggestion and put the open Handle in outHandle.
Returns the actually used file name in case there
were other files with that name. */
wchar_t*
get_tmp_outfile (const wchar_t *name, HANDLE *outHandle)
{
TSTART
auto utf8Name = wchar_to_utf8_string (name);
const auto tmpPath = getTmpPathUtf8 ();
if (utf8Name.empty() || tmpPath.empty())
{
TRACEPOINT;
TRETURN nullptr;
}
auto outName = tmpPath + utf8Name;
log_data("%s:%s: Attachment candidate is %s",
SRCNAME, __func__, outName.c_str ());
int tries = 1;
bool deletedFile = false;
while ((*outHandle = CreateFileUtf8 (outName.c_str ())) == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError ();
log_debug_w32 (err, "%s:%s: Failed to open candidate '%s'",
SRCNAME, __func__, anonstr (outName.c_str()));
if (!deletedFile && err == ERROR_FILE_EXISTS)
{
/* Try to delete the file. If someone is reading it we should
not be able to delete it. */
if (DeleteFileUtf8 (outName.c_str ()))
{
log_dbg ("Deleted existing tmp file '%s'", anonstr (outName.c_str ()));
deletedFile = true;
continue;
}
}
/* Just to make sure we do not loop endlessly if we get strange return
* values. */
deletedFile = false;
char *outNameC = xstrdup ((tmpPath + utf8Name).c_str ());
const auto lastBackslash = strrchr (outNameC, '\\');
if (!lastBackslash)
{
/* This is an error because tmp name by definition contains one */
log_error ("%s:%s: No backslash in origname '%s'",
SRCNAME, __func__, outNameC);
xfree (outNameC);
TRETURN NULL;
}
auto fileExt = strchr (lastBackslash, '.');
if (fileExt)
{
*fileExt = '\0';
++fileExt;
}
// OutNameC is now without an extension and if
// there is a file ext it now points to the extension.
outName = outNameC + std::string("_") + std::to_string(tries++);
if (fileExt)
{
outName += std::string(".") + fileExt;
}
xfree (outNameC);
if (tries == 50)
{
/* Mmh fishy, maybe the name cannot be created on the file
system. Let's switch to a generic name. */
log_dbg ("Can't find an attachment name. "
"Switching over to a generic attachment name.");
outName = tmpPath + "attachment";
utf8Name = "attachment";
if (fileExt)
{
outName += ".";
outName += fileExt;
utf8Name += ".";
utf8Name += fileExt;
}
}
if (tries > 100)
{
char *buf;
gpgrt_asprintf (&buf,"Failed to obtain temporary filename '%s'"
" please check that the folder '%s' exists and the file is"
" writable. Look into configuration dialog for debug options.",
wchar_to_utf8_string (name).c_str (), tmpPath.c_str ());
memdbg_alloc (buf);
/* You have to know when to give up,.. */
gpgol_message_box (NULL, buf, _("GpgOL Error"), MB_OK);
xfree (buf);
log_error ("%s:%s: Could not get a name out of 100 tries",
SRCNAME, __func__);
TRETURN NULL;
}
}
TRETURN utf8_to_wchar (outName.c_str ());
}
char *
get_tmp_outfile_utf8 (const char *name, HANDLE *outHandle)
{
wchar_t *wname = utf8_to_wchar (name);
wchar_t *wret = get_tmp_outfile (wname, outHandle);
xfree (wname);
char *ret = wchar_to_utf8 (wret);
xfree (wret);
return ret;
}
/** Get the Gpg4win Install directory.
*
* Looks first for the Gpg4win 3.x registry key. Then for the Gpg4win
* 2.x registry key. And checks that the directory can be read.
*
* @returns NULL if no dir could be found. Otherwise a malloced string.
*/
char *
get_gpg4win_dir()
{
const char *g4win_keys[] = {GPG4WIN_REGKEY_3,
GPG4WIN_REGKEY_2,
NULL};
const char **key;
for (key = g4win_keys; *key; key++)
{
char *tmp = read_w32_registry_string (NULL, *key, "Install Directory");
if (!tmp)
{
continue;
}
if (!access(tmp, R_OK))
{
return tmp;
}
else
{
log_debug ("Failed to access: %s\n", tmp);
xfree (tmp);
}
}
return NULL;
}
static void
epoch_to_file_time (unsigned long time, LPFILETIME pft)
{
LONGLONG ll;
ll = Int32x32To64(time, 10000000) + 116444736000000000;
pft->dwLowDateTime = (DWORD)ll;
pft->dwHighDateTime = ll >> 32;
}
char *
format_date_from_gpgme (unsigned long time)
{
wchar_t buf[256];
FILETIME ft;
SYSTEMTIME st;
epoch_to_file_time (time, &ft);
FileTimeToSystemTime(&ft, &st);
int ret = GetDateFormatEx (NULL,
DATE_SHORTDATE,
&st,
NULL,
buf,
256,
NULL);
if (ret == 0)
{
return NULL;
}
return wchar_to_utf8 (buf);
}
/* Return the name of the default UI server. This name is used to
auto start an UI server if an initial connect failed. */
char *
get_uiserver_name (void)
{
char *name = NULL;
char *dir, *uiserver, *p;
int extra_arglen = 9;
const char * server_names[] = {"kleopatra.exe",
"bin\\kleopatra.exe",
"gpa.exe",
"bin\\gpa.exe",
NULL};
const char **tmp = NULL;
dir = get_gpg4win_dir ();
if (!dir)
{
log_error ("Failed to find gpg4win dir");
return NULL;
}
uiserver = read_w32_registry_string (NULL, GPG4WIN_REGKEY_3,
"UI Server");
if (!uiserver)
{
uiserver = read_w32_registry_string (NULL, GPG4WIN_REGKEY_2,
"UI Server");
}
if (uiserver)
{
name = (char*) xmalloc (strlen (dir) + strlen (uiserver) + extra_arglen + 2);
strcpy (stpcpy (stpcpy (name, dir), "\\"), uiserver);
for (p = name; *p; p++)
if (*p == '/')
*p = '\\';
xfree (uiserver);
}
if (name && !access (name, F_OK))
{
/* Set through registry and is accessible */
xfree(dir);
return name;
}
/* Fallbacks */
for (tmp = server_names; *tmp; tmp++)
{
if (name)
{
xfree (name);
}
name = (char *) xmalloc (strlen (dir) + strlen (*tmp) + extra_arglen + 2);
strcpy (stpcpy (stpcpy (name, dir), "\\"), *tmp);
for (p = name; *p; p++)
if (*p == '/')
*p = '\\';
if (!access (name, F_OK))
{
/* Found a viable candidate */
if (strstr (name, "kleopatra.exe"))
{
strcat (name, " --daemon");
}
xfree (dir);
return name;
}
}
xfree (dir);
log_error ("Failed to find a viable UIServer");
return NULL;
}
int
has_high_integrity(HANDLE hToken)
{
PTOKEN_MANDATORY_LABEL integrity_label = NULL;
DWORD integrity_level = 0,
size = 0;
if (hToken == NULL || hToken == INVALID_HANDLE_VALUE)
{
log_debug ("Invalid parameters.");
return 0;
}
/* Get the required size */
if (!GetTokenInformation (hToken, TokenIntegrityLevel,
NULL, 0, &size))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
log_debug ("Failed to get required size.\n");
return 0;
}
}
integrity_label = (PTOKEN_MANDATORY_LABEL) LocalAlloc(0, size);
if (integrity_label == NULL)
{
log_debug ("Failed to allocate label. \n");
return 0;
}
if (!GetTokenInformation (hToken, TokenIntegrityLevel,
integrity_label, size, &size))
{
log_debug ("Failed to get integrity level.\n");
LocalFree(integrity_label);
return 0;
}
/* Get the last integrity level */
integrity_level = *GetSidSubAuthority(integrity_label->Label.Sid,
(DWORD)(UCHAR)(*GetSidSubAuthorityCount(
integrity_label->Label.Sid) - 1));
LocalFree (integrity_label);
return integrity_level >= SECURITY_MANDATORY_HIGH_RID;
}
int
is_elevated()
{
int ret = 0;
HANDLE hToken = NULL;
if (OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
DWORD elevation;
DWORD cbSize = sizeof (DWORD);
if (GetTokenInformation (hToken, TokenElevation, &elevation,
sizeof (TokenElevation), &cbSize))
{
ret = elevation;
}
}
/* Elevation will be true and ElevationType TokenElevationTypeFull even
if the token is a user token created by SAFER so we additionally
check the integrity level of the token which will only be high in
the real elevated process and medium otherwise. */
ret = ret && has_high_integrity (hToken);
if (hToken)
CloseHandle (hToken);
return ret;
}
int
gpgol_message_box (HWND parent, const char *utf8_text,
const char *utf8_caption, UINT type)
{
wchar_t *w_text = utf8_to_wchar (utf8_text);
wchar_t *w_caption = utf8_to_wchar (utf8_caption);
int ret = 0;
MSGBOXPARAMSW mbp;
mbp.cbSize = sizeof (MSGBOXPARAMS);
mbp.hwndOwner = parent;
mbp.hInstance = glob_hinst;
mbp.lpszText = w_text;
mbp.lpszCaption = w_caption;
mbp.dwStyle = type | MB_USERICON;
mbp.dwLanguageId = MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT);
mbp.lpfnMsgBoxCallback = NULL;
mbp.dwContextHelpId = 0;
mbp.lpszIcon = (LPCWSTR) MAKEINTRESOURCE (IDI_GPGOL_LOCK_ICON);
ret = MessageBoxIndirectW (&mbp);
xfree (w_text);
xfree (w_caption);
return ret;
}
void
gpgol_bug (HWND parent, int code)
{
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 *with_code;
gpgrt_asprintf (&with_code, "%s\nCode: %i", bugmsg, code);
memdbg_alloc (with_code);
gpgol_message_box (parent,
with_code,
_("GpgOL Error"), MB_OK);
xfree (with_code);
return;
}
static int
store_config_value (HKEY hk, const char *path, const char *key, const char *val)
{
HKEY h;
int type;
int ec;
if (hk == NULL)
{
hk = HKEY_CURRENT_USER;
}
ec = RegCreateKeyEx (hk, path, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS, NULL, &h, NULL);
if (ec != ERROR_SUCCESS)
{
log_debug_w32 (ec, "creating/opening registry key `%s' failed", path);
return -1;
}
type = strchr (val, '%')? REG_EXPAND_SZ : REG_SZ;
ec = RegSetValueEx (h, key, 0, type, (const BYTE*)val, strlen (val));
if (ec != ERROR_SUCCESS)
{
log_debug_w32 (ec, "saving registry key `%s'->`%s' failed", path, key);
RegCloseKey(h);
return -1;
}
RegCloseKey(h);
return 0;
}
/* Store a key in the registry with the key given by @key and the
value @value. */
int
store_extension_value (const char *key, const char *val)
{
return store_config_value (HKEY_CURRENT_USER, GPGOL_REGPATH, key, val);
}
/* Load a key from the registry with the key given by @key. The value is
returned in @val and needs to freed by the caller. */
int
load_extension_value (const char *key, char **val)
{
if (!val)
{
STRANGEPOINT;
return -1;
}
*val = read_w32_registry_string (nullptr, GPGOL_REGPATH, key);
log_debug ("%s:%s: LoadReg '%s' val '%s'",
SRCNAME, __func__, key ? key : "null",
*val ? *val : "null");
return 0;
}
int
store_extension_subkey_value (const char *subkey,
const char *key,
const char *val)
{
int ret;
char *path;
gpgrt_asprintf (&path, "%s\\%s", GPGOL_REGPATH, subkey);
memdbg_alloc (path);
ret = store_config_value (HKEY_CURRENT_USER, path, key, val);
xfree (path);
return ret;
}
bool
in_de_vs_mode()
{
/* We cache the values only once. A change requires restart.
This is because checking this is very expensive as gpgconf
spawns each process to query the settings. */
static bool checked;
static bool vs_mode;
if (checked)
{
return vs_mode;
}
checked = true;
GpgME::Error err;
const auto components = GpgME::Configuration::Component::load (err);
log_debug ("%s:%s: Checking for de-vs mode.",
SRCNAME, __func__);
if (err)
{
log_error ("%s:%s: Failed to get gpgconf components: %s",
SRCNAME, __func__, err.asString ());
vs_mode = false;
return vs_mode;
}
for (const auto &component: components)
{
if (component.name () && !strcmp (component.name (), "gpg"))
{
for (const auto &option: component.options ())
{
if (option.name () && !strcmp (option.name (), "compliance") &&
option.currentValue ().stringValue () &&
#ifdef HAVE_W32_SYSTEM
!stricmp (option.currentValue ().stringValue (), "de-vs"))
#else
!strcasecmp (option.currentValue ().stringValue (), "de-vs"))
#endif
{
log_debug ("%s:%s: Detected de-vs mode",
SRCNAME, __func__);
vs_mode = true;
return vs_mode;
}
}
vs_mode = false;
return vs_mode;
}
}
vs_mode = false;
return false;
}
const char *
de_vs_name (bool isCompliant)
{
static std::string compName;
static std::string uncompName;
if (!compName.empty() && isCompliant)
{
return compName.c_str ();
}
if (!uncompName.empty() && !isCompliant)
{
return uncompName.c_str ();
}
TSTART;
/* Only look once */
compName = utf8_gettext ("VS-NfD compliant");
uncompName = utf8_gettext ("not VS-NfD compliant");
/* Find the libkleopatrarc */
char *instdir = get_gpg4win_dir();
if (!instdir)
{
STRANGEPOINT;
TRETURN isCompliant ? compName.c_str () : uncompName.c_str ();
}
std::string filename = instdir;
filename += "\\share\\libkleopatrarc";
std::ifstream file(filename.c_str ());
if (!file.is_open ())
{
log_err ("Failed to open '%s'", filename.c_str ());
TRETURN isCompliant ? compName.c_str () : uncompName.c_str ();
}
std::string line;
bool in_de_vs_filter = false;
bool in_not_de_vs_filter = false;
const char *lname = gettext_localename ();
/* lname is never null */
std::string localemain = gpgol_split (lname, '_')[0];
std::string idealname = std::string ("Name[") +
localemain + std::string("]");
while (std::getline(file, line))
{
if (starts_with (line, "["))
{
in_de_vs_filter = false;
in_not_de_vs_filter = false;
continue;
}
if (starts_with (line, "id=de-vs-filter"))
{
in_de_vs_filter = true;
continue;
}
if (starts_with (line, "id=not-de-vs-filter"))
{
in_not_de_vs_filter = true;
continue;
}
if ((in_de_vs_filter || in_not_de_vs_filter) && starts_with (line, "Name"))
{
const auto split = gpgol_split (line, '=');
if (split.size() != 2)
{
log_err ("Invalid libkleopatrarc line: %s", line.c_str());
continue;
}
if (split[0] == "Name")
{
if (in_de_vs_filter)
{
compName = split[1];
}
else
{
uncompName = split[1];
}
}
if (split[0] == idealname)
{
log_dbg ("Found localized de-vs name %s", split[1].c_str ());
if (in_de_vs_filter)
{
compName = split[1];
}
else
{
uncompName = split[1];
}
}
}
}
file.close();
TRETURN isCompliant ? compName.c_str () : uncompName.c_str ();
}
std::string
compliance_string (bool forVerify, bool forDecrypt, bool isCompliant)
{
const char *comp_name = de_vs_name (isCompliant);
if (forDecrypt && forVerify)
{
return asprintf_s (utf8_gettext ("The message is %s."), comp_name);
}
if (forVerify)
{
/* TRANSLATORS %s is compliance name like VS-NfD */
return asprintf_s (utf8_gettext ("The signature is %s."), comp_name);
}
if (forDecrypt)
{
/* TRANSLATORS %s is compliance name like VS-NfD */
return asprintf_s (utf8_gettext ("The encryption is %s."),
comp_name);
}
/* Should not happen */
log_err ("Invalid call to compliance string.");
STRANGEPOINT;
return std::string();
}
+
+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;
+}
diff --git a/src/common.h b/src/common.h
index 839f675..dfeab05 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,165 +1,164 @@
/* common.h - Common declarations for GpgOL
* Copyright (C) 2004 Timo Schulz
* Copyright (C) 2005, 2006, 2007, 2008 g10 Code GmbH
* Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GPGOL_COMMON_H
#define GPGOL_COMMON_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gpgme.h>
#include "common_indep.h"
#include <windows.h>
/* i18n stuff */
#include "w32-gettext.h"
#define _(a) w32_gettext (a)
#define N_(a) gettext_noop (a)
/* Registry path to store plugin settings */
#define GPGOL_REGPATH "Software\\GNU\\GpgOL"
#ifdef __cplusplus
#include <string.h>
std::string readRegStr (const char *root, const char *dir, const char *name);
namespace GpgME
{
class Data;
} // namespace GpgME
/* Read a file into a GpgME::Data object returns 0 on success */
int readFullFile (HANDLE hFile, GpgME::Data &data);
extern "C" {
#if 0
}
#endif
#endif
extern HINSTANCE glob_hinst;
extern UINT this_dll;
-/*-- common.c --*/
-void set_global_hinstance (HINSTANCE hinst);
-
+/*-- common.cpp --*/
char *get_data_dir (void);
char *get_gpg4win_dir (void);
+char *get_gpgme_w32_inst_dir (void);
int store_extension_value (const char *key, const char *val);
int store_extension_subkey_value (const char *subkey, const char *key,
const char *val);
int load_extension_value (const char *key, char **val);
/* Get a temporary filename with and its name */
wchar_t *get_tmp_outfile (const wchar_t *name, HANDLE *outHandle);
char *get_tmp_outfile_utf8 (const char *name, HANDLE *outHandle);
wchar_t *get_pretty_attachment_name (wchar_t *path, protocol_t protocol,
int signature);
/*-- verify-dialog.c --*/
int verify_dialog_box (gpgme_protocol_t protocol,
gpgme_verify_result_t res,
const char *filename);
/*-- inspectors.cpp --*/
int initialize_inspectors (void);
#if __GNUC__ >= 4
# define GPGOL_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a)))
#else
# define GPGOL_GCC_A_SENTINEL(a)
#endif
/*-- common.c --*/
void fatal_error (const char *format, ...);
char *read_w32_registry_string (const char *root, const char *dir,
const char *name);
char *percent_escape (const char *str, const char *extra);
void fix_linebreaks (char *str, int *len);
/* Format a date from gpgme (seconds since epoch)
with windows system locale. */
char *format_date_from_gpgme (unsigned long time);
/* Get the name of the uiserver */
char *get_uiserver_name (void);
int is_elevated (void);
/*-- main.c --*/
void read_options (void);
int write_options (void);
extern int g_ol_version_major;
void bring_to_front (HWND wid);
int gpgol_message_box (HWND parent, const char *utf8_text,
const char *utf8_caption, UINT type);
/* Show a bug message with the code. */
void gpgol_bug (HWND parent, int code);
void i18n_init (void);
#define ERR_CRYPT_RESOLVER_FAILED 1
#define ERR_WANTS_SEND_MIME_BODY 2
#define ERR_WANTS_SEND_INLINE_BODY 3
#define ERR_INLINE_BODY_TO_BODY 4
#define ERR_INLINE_BODY_INV_STATE 5
#define ERR_SEND_FALLBACK_FAILED 6
#define ERR_GET_BASE_MSG_FAILED 7
#define ERR_SPLIT_UNEXPECTED 8
#define ERR_SPLIT_RECIPIENTS 9
#ifdef __cplusplus
}
/* Check if we are in de_vs mode. */
bool in_de_vs_mode ();
/* Get the name of the de_vs compliance mode as configured
in libkleopatrarc */
const char *de_vs_name (bool isCompliant = false);
/* Get a localized string of the compliance of an operation
mode is the usual 1 encrypt, 2 sign. isCompliant if the value
of the operation was compliant or not. */
std::string
compliance_string (bool forVerify, bool forDecrypt, bool isCompliant);
HANDLE CreateFileUtf8 (const char *utf8Name);
/* Read a registry value from HKLM or HKCU of type dword and
return 1 if the value is larger then 0, 0 if it is zero and
-1 if it is not set or not found. */
int
read_reg_bool (HKEY root, const char *path, const char *value);
#endif
#endif /*GPGOL_COMMON_H*/
diff --git a/src/gpgoladdin.cpp b/src/gpgoladdin.cpp
index f24fe54..f2a40c9 100644
--- a/src/gpgoladdin.cpp
+++ b/src/gpgoladdin.cpp
@@ -1,1336 +1,1353 @@
/* gpgoladdin.cpp - Connect GpgOL to Outlook as an addin
* Copyright (C) 2013 Intevation GmbH
* 2015 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "common.h"
#include "gpgoladdin.h"
#include "mymapi.h"
#include "mymapitags.h"
#include "mapihelp.h"
#include "oomhelp.h"
#include "olflange.h"
#include "gpgol-ids.h"
#include "ribbon-callbacks.h"
#include "eventsinks.h"
#include "eventsink.h"
#include "windowmessages.h"
#include "mail.h"
#include "addin-options.h"
#include "cpphelp.h"
#include "dispcache.h"
#include "categorymanager.h"
#include "keycache.h"
#include <gpg-error.h>
#include <list>
#define ICON_SIZE_LARGE 32
#define ICON_SIZE_NORMAL 16
/* We use UTF-8 internally. */
#undef _
#define _(a) utf8_gettext (a)
ULONG addinLocks = 0;
bool can_unload = false;
static GpgolAddin * addin_instance = NULL;
/* This is the main entry point for the addin
Outlook uses this function to query for an Object implementing
the IClassFactory interface.
*/
STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppvObj)
{
if (!ppvObj)
return E_POINTER;
*ppvObj = NULL;
if (rclsid != CLSID_GPGOL)
return CLASS_E_CLASSNOTAVAILABLE;
/* Let the factory give the requested interface. */
GpgolAddinFactory* factory = new GpgolAddinFactory();
if (!factory)
return E_OUTOFMEMORY;
HRESULT hr = factory->QueryInterface (riid, ppvObj);
if(FAILED(hr))
{
*ppvObj = NULL;
delete factory;
}
return hr;
}
STDAPI DllCanUnloadNow()
{
/* This is called regularly to check if memory can be freed
by unloading the dll. The following unload will not call
any addin methods like disconnect etc. It will just
unload the Library. Any callbacks will become invalid.
So we _only_ say it's ok to unload if we were disconnected.
For the epic story behind the next line see GnuPG-Bug-Id 1837 */
TRACEPOINT;
return can_unload ? S_OK : S_FALSE;
}
/* Class factory */
STDMETHODIMP GpgolAddinFactory::QueryInterface (REFIID riid, LPVOID* ppvObj)
{
HRESULT hr = S_OK;
*ppvObj = NULL;
if ((IID_IUnknown == riid) || (IID_IClassFactory == riid))
*ppvObj = static_cast<IClassFactory*>(this);
else
{
hr = E_NOINTERFACE;
LPOLESTR sRiid = NULL;
StringFromIID (riid, &sRiid);
/* Should not happen */
log_debug ("GpgolAddinFactory queried for unknown interface: %S \n", sRiid);
}
if (*ppvObj)
((LPUNKNOWN)*ppvObj)->AddRef();
return hr;
}
/* This actually creates the instance of our COM object */
STDMETHODIMP GpgolAddinFactory::CreateInstance (LPUNKNOWN punk, REFIID riid,
LPVOID* ppvObj)
{
(void)punk;
*ppvObj = NULL;
GpgolAddin* obj = GpgolAddin::get_instance();
if (NULL == obj)
return E_OUTOFMEMORY;
HRESULT hr = obj->QueryInterface (riid, ppvObj);
if (FAILED(hr))
{
LPOLESTR sRiid = NULL;
StringFromIID (riid, &sRiid);
fprintf(stderr, "failed to create instance for: %S", sRiid);
}
return hr;
}
GpgolAddinFactory::~GpgolAddinFactory()
{
log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__);
}
/* GpgolAddin definition */
/* Constructor of GpgolAddin
Initializes members and creates the interface objects for the new
context. Does the DLL initialization if it has not been done
before.
The ref count is set by the factory after creation.
*/
GpgolAddin::GpgolAddin (void) : m_lRef(0),
m_application(nullptr),
m_addin(nullptr),
m_applicationEventSink(nullptr),
m_explorersEventSink(nullptr),
m_disabled(false),
m_shutdown(false),
m_hook(nullptr),
m_dispcache(new DispCache)
{
+ /* Required first to start logging */
read_options ();
+ TRACEPOINT;
+ /* Start initialization */
+ 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);
/* RibbonExtender is it's own object to avoid the pitfalls of
multiple inheritance
*/
m_ribbonExtender = new GpgolRibbonExtender();
+ TRACEPOINT;
}
GpgolAddin::~GpgolAddin (void)
{
if (m_disabled)
{
return;
}
addin_instance = NULL;
log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__);
}
STDMETHODIMP
GpgolAddin::QueryInterface (REFIID riid, LPVOID* ppvObj)
{
HRESULT hr = S_OK;
*ppvObj = NULL;
if (m_disabled)
return E_NOINTERFACE;
if ((riid == IID_IUnknown) || (riid == IID_IDTExtensibility2) ||
(riid == IID_IDispatch))
{
*ppvObj = (LPUNKNOWN) this;
}
else if (riid == IID_IRibbonExtensibility)
{
return m_ribbonExtender->QueryInterface (riid, ppvObj);
}
else
{
hr = E_NOINTERFACE;
#if 0
LPOLESTR sRiid = NULL;
StringFromIID(riid, &sRiid);
log_debug ("%s:%s: queried for unimplmented interface: %S",
SRCNAME, __func__, sRiid);
#endif
}
if (*ppvObj)
((LPUNKNOWN)*ppvObj)->AddRef();
return hr;
}
static void
addGpgOLToReg (const std::string &path)
{
HKEY h;
int err = RegOpenKeyEx (HKEY_CURRENT_USER, path.c_str(), 0,
KEY_ALL_ACCESS, &h);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: no DoNotDisableAddinList entry '%s' creating it",
SRCNAME, __func__, path.c_str ());
err = RegCreateKeyEx (HKEY_CURRENT_USER, path.c_str (), 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&h, NULL);
}
if (err != ERROR_SUCCESS)
{
log_error ("%s:%s: failed to create key.",
SRCNAME, __func__);
return;
}
DWORD type;
err = RegQueryValueEx (h, GPGOL_PROGID, NULL, &type, NULL, NULL);
if (err == ERROR_SUCCESS)
{
log_debug ("%s:%s: Found gpgol reg key. Leaving it unchanged.",
SRCNAME, __func__);
RegCloseKey (h);
return;
}
// No key exists. Create one.
DWORD dwTemp = 1;
err = RegSetValueEx (h, GPGOL_PROGID, 0, REG_DWORD, (BYTE*)&dwTemp, 4);
RegCloseKey (h);
if (err != ERROR_SUCCESS)
{
log_error ("%s:%s: failed to set registry value.",
SRCNAME, __func__);
}
else
{
log_debug ("%s:%s: added gpgol to %s",
SRCNAME, __func__, path.c_str ());
}
}
/* This is a bit evil as we basically disable outlooks resiliency
for us. But users are still able to manually disable the addon
or change the donotdisable setting to zero and we won't change
it.
It has been much requested by users that we do this automatically.
*/
static void
setupDoNotDisable ()
{
std::string path = "Software\\Microsoft\\Office\\";
path += std::to_string (g_ol_version_major);
path += ".0\\Outlook\\Resiliency\\DoNotDisableAddinList";
addGpgOLToReg (path);
path = "Software\\Microsoft\\Office\\";
path += std::to_string (g_ol_version_major);
path += ".0\\Outlook\\Resiliency\\AddinList";
addGpgOLToReg (path);
}
STDMETHODIMP
GpgolAddin::OnConnection (LPDISPATCH Application, ext_ConnectMode ConnectMode,
LPDISPATCH AddInInst, SAFEARRAY ** custom)
{
(void)custom;
char* version;
log_debug ("%s:%s: this is GpgOL %s\n",
SRCNAME, __func__, PACKAGE_VERSION);
m_shutdown = false;
can_unload = false;
m_application = Application;
m_application->AddRef();
memdbg_addRef (m_application);
m_addin = AddInInst;
version = get_oom_string (Application, "Version");
log_debug ("%s:%s: using GPGME %s\n",
SRCNAME, __func__, gpgme_check_version (NULL));
log_debug ("%s:%s: in Outlook %s\n",
SRCNAME, __func__, version);
g_ol_version_major = atoi (version);
if (!version || !strlen (version) ||
(strncmp (version, "14", 2) &&
strncmp (version, "15", 2) &&
strncmp (version, "16", 2)))
{
m_disabled = true;
log_debug ("%s:%s: Disabled addin for unsupported version.",
SRCNAME, __func__);
xfree (version);
return S_OK;
}
xfree (version);
setupDoNotDisable ();
if (ConnectMode != ext_cm_Startup)
{
OnStartupComplete (custom);
}
return S_OK;
}
STDMETHODIMP
GpgolAddin::OnDisconnection (ext_DisconnectMode RemoveMode,
SAFEARRAY** custom)
{
(void)custom;
(void)RemoveMode;
log_debug ("%s:%s: cleaning up GpgolAddin object;",
SRCNAME, __func__);
/* Doing the wipe in the dtor is too late. Outlook
does not allow us any OOM calls then and only returns
"Unexpected error" in that case. Weird. */
shutdown ();
can_unload = true;
return S_OK;
}
STDMETHODIMP
GpgolAddin::OnAddInsUpdate (SAFEARRAY** custom)
{
(void)custom;
return S_OK;
}
void
check_html_preferred()
{
/* Check if HTML Mail should be enabled. */
std::string path = "Software\\Microsoft\\Office\\" +
std::to_string (g_ol_version_major) +
".0\\Outlook\\Options\\Mail";
int val = read_reg_bool (nullptr, path.c_str (), "ReadAsPlain");
if (val == -1)
{
path = "Software\\policies\\Microsoft\\Office\\" +
std::to_string (g_ol_version_major) +
".0\\Outlook\\Options\\Mail";
val = read_reg_bool (nullptr, path.c_str (), "ReadAsPlain");
}
if (val == -1)
{
opt.prefer_html = 1;
}
else
{
opt.prefer_html = !val;
}
}
static LPDISPATCH
install_explorer_sinks (LPDISPATCH application)
{
LPDISPATCH explorers = get_oom_object (application, "Explorers");
if (!explorers)
{
log_error ("%s:%s: No explorers object",
SRCNAME, __func__);
return nullptr;
}
int count = get_oom_int (explorers, "Count");
for (int i = 1; i <= count; i++)
{
std::string item = "Item(";
item += std::to_string (i) + ")";
LPDISPATCH explorer = get_oom_object (explorers, item.c_str());
if (!explorer)
{
log_error ("%s:%s: failed to get explorer %i",
SRCNAME, __func__, i);
continue;
}
/* Explorers delete themself in the close event of the explorer. */
LPDISPATCH sink = install_ExplorerEvents_sink (explorer);
if (!sink)
{
log_error ("%s:%s: failed to create eventsink for explorer %i",
SRCNAME, __func__, i);
}
else
{
log_oom ("%s:%s: created sink %p for explorer %i",
SRCNAME, __func__, sink, i);
GpgolAddin::get_instance ()->registerExplorerSink (sink);
}
gpgol_release (explorer);
}
/* Now install the event sink to handle new explorers */
LPDISPATCH ret = install_ExplorersEvents_sink (explorers);
gpgol_release (explorers);
return ret;
}
static DWORD WINAPI
init_gpgme_config (LPVOID)
{
/* This is a check we need to do anyway. GpgME++ caches
the configuration once it is accessed for the first time
so this call also initializes GpgME++ */
bool de_vs_mode = in_de_vs_mode ();
log_debug ("%s:%s: init_gpgme_config de_vs_mode %i",
SRCNAME, __func__, de_vs_mode);
return 0;
}
STDMETHODIMP
GpgolAddin::OnStartupComplete (SAFEARRAY** custom)
{
(void)custom;
TRACEPOINT;
i18n_init ();
if (!create_responder_window())
{
log_error ("%s:%s: Failed to create the responder window;",
SRCNAME, __func__);
}
if (!m_application)
{
/* Should not happen as OnConnection should be called before */
log_error ("%s:%s: no application set;",
SRCNAME, __func__);
return E_NOINTERFACE;
}
if (!(m_hook = create_message_hook ()))
{
log_error ("%s:%s: Failed to create messagehook. ",
SRCNAME, __func__);
}
/* Clean GpgOL prefixed categories.
They might be left over from a crash or something unexpected
error. We want to avoid pollution with the signed by categories.
*/
CategoryManager::removeAllGpgOLCategories ();
install_forms ();
m_applicationEventSink = install_ApplicationEvents_sink (m_application);
m_explorersEventSink = install_explorer_sinks (m_application);
check_html_preferred ();
CloseHandle (CreateThread (NULL, 0, init_gpgme_config, nullptr, 0,
NULL));
KeyCache::instance ()->populate ();
return S_OK;
}
STDMETHODIMP
GpgolAddin::OnBeginShutdown (SAFEARRAY * * custom)
{
(void)custom;
TRACEPOINT;
return S_OK;
}
STDMETHODIMP
GpgolAddin::GetTypeInfoCount (UINT *r_count)
{
*r_count = 0;
TRACEPOINT; /* Should not happen */
return S_OK;
}
STDMETHODIMP
GpgolAddin::GetTypeInfo (UINT iTypeInfo, LCID lcid,
LPTYPEINFO *r_typeinfo)
{
(void)iTypeInfo;
(void)lcid;
(void)r_typeinfo;
TRACEPOINT; /* Should not happen */
return S_OK;
}
STDMETHODIMP
GpgolAddin::GetIDsOfNames (REFIID riid, LPOLESTR *rgszNames,
UINT cNames, LCID lcid,
DISPID *rgDispId)
{
(void)riid;
(void)rgszNames;
(void)cNames;
(void)lcid;
(void)rgDispId;
TRACEPOINT; /* Should not happen */
return E_NOINTERFACE;
}
STDMETHODIMP
GpgolAddin::Invoke (DISPID dispid, REFIID riid, LCID lcid,
WORD flags, DISPPARAMS *parms, VARIANT *result,
EXCEPINFO *exepinfo, UINT *argerr)
{
USE_INVOKE_ARGS
TRACEPOINT; /* Should not happen */
return DISP_E_MEMBERNOTFOUND;
}
/* Definition of GpgolRibbonExtender */
GpgolRibbonExtender::GpgolRibbonExtender (void) : m_lRef(0)
{
}
GpgolRibbonExtender::~GpgolRibbonExtender (void)
{
log_debug ("%s:%s: cleaning up GpgolRibbonExtender object;",
SRCNAME, __func__);
memdbg_dump ();
}
STDMETHODIMP
GpgolRibbonExtender::QueryInterface(REFIID riid, LPVOID* ppvObj)
{
HRESULT hr = S_OK;
*ppvObj = NULL;
if ((riid == IID_IUnknown) || (riid == IID_IRibbonExtensibility) ||
(riid == IID_IDispatch))
{
*ppvObj = (LPUNKNOWN) this;
}
else
{
LPOLESTR sRiid = NULL;
StringFromIID (riid, &sRiid);
log_debug ("%s:%s: queried for unknown interface: %S",
SRCNAME, __func__, sRiid);
}
if (*ppvObj)
((LPUNKNOWN)*ppvObj)->AddRef();
return hr;
}
STDMETHODIMP
GpgolRibbonExtender::GetTypeInfoCount (UINT *r_count)
{
*r_count = 0;
TRACEPOINT; /* Should not happen */
return S_OK;
}
STDMETHODIMP
GpgolRibbonExtender::GetTypeInfo (UINT iTypeInfo, LCID lcid,
LPTYPEINFO *r_typeinfo)
{
(void)iTypeInfo;
(void)lcid;
(void)r_typeinfo;
TRACEPOINT; /* Should not happen */
return S_OK;
}
/* Good documentation of what this function is supposed to do can
be found at: http://msdn.microsoft.com/en-us/library/cc237568.aspx
There is also a very good blog explaining how Ribbon Extensibility
is supposed to work.
http://blogs.msdn.com/b/andreww/archive/2007/03/09/
why-is-it-so-hard-to-shim-iribbonextensibility.aspx
*/
#define ID_MAPPER(name,id) \
if (!wcscmp (rgszNames[i], name)) \
{ \
found = true; \
rgDispId[i] = id; \
break; \
} \
STDMETHODIMP
GpgolRibbonExtender::GetIDsOfNames (REFIID riid, LPOLESTR *rgszNames,
UINT cNames, LCID lcid,
DISPID *rgDispId)
{
(void)riid;
(void)lcid;
bool found = false;
if (!rgszNames || !cNames || !rgDispId)
{
return E_POINTER;
}
for (unsigned int i = 0; i < cNames; i++)
{
log_debug ("%s:%s: GetIDsOfNames for: %S",
SRCNAME, __func__, rgszNames[i]);
/* How this is supposed to work with cNames > 1 is unknown,
but we can just say that we won't support callbacks with
different parameters and just match the name (the first element)
and we give it one of our own dispIds's that are later handled in
the invoke part */
ID_MAPPER (L"btnDecrypt", ID_BTN_DECRYPT)
ID_MAPPER (L"btnDecryptLarge", ID_BTN_DECRYPT_LARGE)
ID_MAPPER (L"btnEncrypt", ID_BTN_ENCRYPT)
ID_MAPPER (L"btnEncryptLarge", ID_BTN_ENCRYPT_LARGE)
ID_MAPPER (L"btnEncryptSmall", IDI_ENCRYPT_20_PNG)
ID_MAPPER (L"btnSignSmall", IDI_SIGN_20_PNG)
ID_MAPPER (L"btnSignEncryptLarge", IDI_SIGN_ENCRYPT_40_PNG)
ID_MAPPER (L"btnEncryptFileLarge", ID_BTN_ENCSIGN_LARGE)
ID_MAPPER (L"btnSignLarge", ID_BTN_SIGN_LARGE)
ID_MAPPER (L"btnVerifyLarge", ID_BTN_VERIFY_LARGE)
ID_MAPPER (L"btnSigstateLarge", ID_BTN_SIGSTATE_LARGE)
/* MIME support: */
ID_MAPPER (L"encryptMime", ID_CMD_MIME_ENCRYPT)
ID_MAPPER (L"encryptMimeEx", ID_CMD_MIME_ENCRYPT_EX)
ID_MAPPER (L"signMime", ID_CMD_MIME_SIGN)
ID_MAPPER (L"signMimeEx", ID_CMD_MIME_SIGN_EX)
ID_MAPPER (L"encryptSignMime", ID_CMD_SIGN_ENCRYPT_MIME)
ID_MAPPER (L"encryptSignMimeEx", ID_CMD_SIGN_ENCRYPT_MIME_EX)
ID_MAPPER (L"getEncryptPressed", ID_GET_ENCRYPT_PRESSED)
ID_MAPPER (L"getEncryptPressedEx", ID_GET_ENCRYPT_PRESSED_EX)
ID_MAPPER (L"getSignPressed", ID_GET_SIGN_PRESSED)
ID_MAPPER (L"getSignPressedEx", ID_GET_SIGN_PRESSED_EX)
ID_MAPPER (L"getSignEncryptPressed", ID_GET_SIGN_ENCRYPT_PRESSED)
ID_MAPPER (L"getSignEncryptPressedEx", ID_GET_SIGN_ENCRYPT_PRESSED_EX)
ID_MAPPER (L"ribbonLoaded", ID_ON_LOAD)
ID_MAPPER (L"openOptions", ID_CMD_OPEN_OPTIONS)
ID_MAPPER (L"getSigLabel", ID_GET_SIG_LABEL)
ID_MAPPER (L"getSigSTip", ID_GET_SIG_STIP)
ID_MAPPER (L"getSigTip", ID_GET_SIG_TTIP)
ID_MAPPER (L"launchDetails", ID_LAUNCH_CERT_DETAILS)
ID_MAPPER (L"getIsDetailsEnabled", ID_GET_IS_DETAILS_ENABLED)
ID_MAPPER (L"getIsAddrBookEnabled", ID_GET_IS_ADDR_BOOK_ENABLED)
ID_MAPPER (L"getIsCrypto", ID_GET_IS_CRYPTO_MAIL)
ID_MAPPER (L"openContactKey", ID_CMD_OPEN_CONTACT_KEY)
ID_MAPPER (L"overrideFileClose", ID_CMD_FILE_CLOSE)
ID_MAPPER (L"overrideFileSaveAs", ID_CMD_FILE_SAVE_AS)
ID_MAPPER (L"overrideFileSaveAsWindowed", ID_CMD_FILE_SAVE_AS_IN_WINDOW)
ID_MAPPER (L"decryptPermanently", ID_CMD_DECRYPT_PERMANENTLY)
}
if (cNames > 1)
{
log_debug ("More then one name provided. Should not happen");
}
return found ? S_OK : E_NOINTERFACE;
}
STDMETHODIMP
GpgolRibbonExtender::Invoke (DISPID dispid, REFIID riid, LCID lcid,
WORD flags, DISPPARAMS *parms, VARIANT *result,
EXCEPINFO *exepinfo, UINT *argerr)
{
USE_INVOKE_ARGS
log_oom ("%s:%s: enter with dispid: %x",
SRCNAME, __func__, (int)dispid);
/*
log_oom ("%s:%s: Parms: %s",
SRCNAME, __func__, format_dispparams (parms).c_str ());
*/
if (!(flags & DISPATCH_METHOD))
{
log_debug ("%s:%s: not called in method mode. Bailing out.",
SRCNAME, __func__);
return DISP_E_MEMBERNOTFOUND;
}
switch (dispid)
{
case ID_CMD_SIGN_ENCRYPT_MIME:
return mark_mime_action (parms->rgvarg[1].pdispVal,
OP_SIGN|OP_ENCRYPT, false);
case ID_CMD_SIGN_ENCRYPT_MIME_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal,
OP_SIGN|OP_ENCRYPT, true);
case ID_CMD_MIME_ENCRYPT:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_ENCRYPT,
false);
case ID_CMD_MIME_SIGN:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_SIGN,
false);
case ID_GET_ENCRYPT_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_ENCRYPT,
result, false);
case ID_GET_SIGN_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN,
result, false);
case ID_GET_SIGN_ENCRYPT_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal,
OP_SIGN | OP_ENCRYPT,
result, false);
case ID_CMD_MIME_SIGN_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_SIGN, true);
case ID_CMD_MIME_ENCRYPT_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_ENCRYPT, true);
case ID_GET_ENCRYPT_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_ENCRYPT,
result, true);
case ID_GET_SIGN_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN,
result, true);
case ID_GET_SIGN_ENCRYPT_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN | OP_ENCRYPT,
result, true);
case ID_GET_SIG_STIP:
return get_sig_stip (parms->rgvarg[0].pdispVal, result);
case ID_GET_SIG_TTIP:
return get_sig_ttip (parms->rgvarg[0].pdispVal, result);
case ID_GET_SIG_LABEL:
return get_sig_label (parms->rgvarg[0].pdispVal, result);
case ID_LAUNCH_CERT_DETAILS:
return launch_cert_details (parms->rgvarg[0].pdispVal);
case ID_GET_IS_DETAILS_ENABLED:
return get_is_details_enabled (parms->rgvarg[0].pdispVal, result);
case ID_GET_IS_ADDR_BOOK_ENABLED:
return get_is_addr_book_enabled (parms->rgvarg[0].pdispVal, result);
case ID_ON_LOAD:
{
GpgolAddin::get_instance ()->addRibbon (parms->rgvarg[0].pdispVal);
return S_OK;
}
case ID_CMD_OPEN_OPTIONS:
{
options_dialog_box (NULL);
return S_OK;
}
case ID_CMD_DECRYPT_PERMANENTLY:
return decrypt_permanently (parms->rgvarg[0].pdispVal);
case ID_GET_IS_CRYPTO_MAIL:
return get_is_crypto_mail (parms->rgvarg[0].pdispVal, result);
case ID_CMD_OPEN_CONTACT_KEY:
return open_contact_key (parms->rgvarg[0].pdispVal);
case ID_CMD_FILE_CLOSE :
return override_file_close ();
case ID_CMD_FILE_SAVE_AS:
return override_file_save_as (parms);
case ID_CMD_FILE_SAVE_AS_IN_WINDOW:
return override_file_save_as_in_window (parms);
case ID_BTN_ENCRYPT:
case ID_BTN_DECRYPT:
case ID_BTN_DECRYPT_LARGE:
case ID_BTN_ENCRYPT_LARGE:
case ID_BTN_ENCSIGN_LARGE:
case ID_BTN_SIGN_LARGE:
case ID_BTN_VERIFY_LARGE:
case IDI_SIGN_ENCRYPT_40_PNG:
case IDI_ENCRYPT_20_PNG:
case IDI_SIGN_20_PNG:
return getIcon (dispid, result);
case ID_BTN_SIGSTATE_LARGE:
return get_crypto_icon (parms->rgvarg[0].pdispVal, result);
}
log_debug ("%s:%s: leave", SRCNAME, __func__);
return DISP_E_MEMBERNOTFOUND;
}
/* Returns the XML markup for the various RibbonID's
The custom ui syntax is documented at:
http://msdn.microsoft.com/en-us/library/dd926139%28v=office.12%29.aspx
The outlook specific elements are documented at:
http://msdn.microsoft.com/en-us/library/office/ee692172%28v=office.14%29.aspx
*/
static STDMETHODIMP
GetCustomUI_MIME (BSTR RibbonID, BSTR * RibbonXml)
{
char * buffer = NULL;
/* const char *certManagerTTip =
_("Start the Certificate Management Software");
const char *certManagerSTip =
_("Open GPA or Kleopatra to manage your certificates. "
"You can use this you to generate your "
"own certificates. ");*/
const char *encryptTTip =
_("Encrypt the message");
const char *encryptSTip =
_("Encrypts the message and all attachments before sending");
const char *signTTip =
_("Sign the message");
const char *signSTip =
_("Sign the message and all attachments before sending");
const char *secureTTip =
_("Sign and encrypt the message");
const char *secureSTip =
_("Encrypting and cryptographically signing a message means that the "
"recipients can be sure that no one modified the message and only the "
"recipients can read it");
const char *optsSTip =
_("Open the settings dialog for GpgOL");
log_debug ("%s:%s: GetCustomUI_MIME for id: %ls", SRCNAME, __func__, RibbonID);
if (!RibbonXml || !RibbonID)
return E_POINTER;
if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Compose"))
{
gpgrt_asprintf (&buffer,
"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
" onLoad=\"ribbonLoaded\">"
" <ribbon>"
" <tabs>"
/*
" <tab idMso=\"TabOptions\">"
" <group idMso=\"GroupRightsManagement\">"
" <toggleButton idMso=\"EncryptMessage\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" getPressed=\"getSignEncryptPressedEx\""
" getImage=\"btnSignEncryptLarge\""
" onAction=\"encryptSignMimeEx\"""/>"
" <toggleButton id=\"DigitallySignMessage\""
" getImage=\"btnSignSmall\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" onAction=\"signMimeEx\""
" getPressed=\"getSignPressedEx\"/>"
" </group>"
" </tab>"
*/
" <tab idMso=\"TabNewMailMessage\">"
" <group id=\"general\""
" label=\"%s\">"
" <splitButton id=\"splitty\" size=\"large\">"
" <toggleButton id=\"mimeSignEncrypt\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" getPressed=\"getSignEncryptPressed\""
" getImage=\"btnSignEncryptLarge\""
" onAction=\"encryptSignMime\"/>"
" <menu id=\"encMenu\" showLabel=\"true\">"
" <toggleButton id=\"mimeSign\""
" getImage=\"btnSignSmall\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" onAction=\"signMime\""
" getPressed=\"getSignPressed\"/>"
" <toggleButton id=\"mimeEncrypt\""
" getImage=\"btnEncryptSmall\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" onAction=\"encryptMime\""
" getPressed=\"getEncryptPressed\"/>"
" </menu></splitButton>"
" <dialogBoxLauncher>"
" <button id=\"optsBtn\""
" onAction=\"openOptions\""
" screentip=\"%s\"/>"
" </dialogBoxLauncher>"
" </group>"
" </tab>"
" </tabs>"
" </ribbon>"
"</customUI>",
/*
_("Sign"), signTTip, signSTip,
_("Encrypt"), encryptTTip, encryptSTip,
*/
_("GpgOL"),
_("Secure"), secureTTip, secureSTip,
_("Sign"), signTTip, signSTip,
_("Encrypt"), encryptTTip, encryptSTip,
optsSTip
);
}
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Read"))
{
gpgrt_asprintf (&buffer,
"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
" onLoad=\"ribbonLoaded\">"
" <commands>"
" <command idMso=\"FileSaveAs\""
" onAction=\"overrideFileSaveAsWindowed\"/>"
" </commands>"
" <ribbon>"
" <tabs>"
" <tab idMso=\"TabReadMessage\">"
" <group id=\"general\""
" label=\"%s\">"
" <button id=\"idSigned\""
" getImage=\"btnSigstateLarge\""
" size=\"large\""
" getLabel=\"getSigLabel\""
" getScreentip=\"getSigTip\""
" getSupertip=\"getSigSTip\""
" onAction=\"launchDetails\"/>"
/* " getEnabled=\"getIsCrypto\"/>" */
" <dialogBoxLauncher>"
" <button id=\"optsBtn\""
" onAction=\"openOptions\""
" screentip=\"%s\"/>"
" </dialogBoxLauncher>"
" </group>"
" </tab>"
" </tabs>"
" </ribbon>"
"</customUI>",
_("GpgOL"),
optsSTip
);
}
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer") && g_ol_version_major > 14)
{
gpgrt_asprintf (&buffer,
"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
" onLoad=\"ribbonLoaded\">"
" <commands>"
" <command idMso=\"FileSaveAs\""
" onAction=\"overrideFileSaveAs\"/>"
" <command idMso=\"FileCloseAndLogOff\""
" onAction=\"overrideFileClose\"/>"
" </commands>"
" <ribbon>"
" <tabs>"
" <tab idMso=\"TabMail\">"
" <group id=\"general_read\""
" label=\"%s\">"
" <button id=\"idSigned\""
" getImage=\"btnSigstateLarge\""
" size=\"large\""
" getLabel=\"getSigLabel\""
" getScreentip=\"getSigTip\""
" getSupertip=\"getSigSTip\""
" onAction=\"launchDetails\""
" getEnabled=\"getIsDetailsEnabled\"/>"
" <dialogBoxLauncher>"
" <button id=\"optsBtn_read\""
" onAction=\"openOptions\""
" screentip=\"%s\"/>"
" </dialogBoxLauncher>"
" </group>"
" </tab>"
" <tab idMso=\"TabContacts\">"
" <group id=\"contacts_gpgol_ex\""
" label=\"%s\">"
" <button id=\"idContactAddkeyEx\""
" getImage=\"btnSignEncryptLarge\""
" size=\"large\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" getEnabled=\"getIsAddrBookEnabled\""
" onAction=\"openContactKey\"/>"
" <dialogBoxLauncher>"
" <button id=\"optsBtn_contact_ex\""
" onAction=\"openOptions\""
" screentip=\"%s\""
" supertip=\"%s\"/>"
" </dialogBoxLauncher>"
" </group>"
" </tab>"
" </tabs>"
" <contextualTabs>"
" <tabSet idMso=\"TabComposeTools\">"
" <tab idMso=\"TabMessage\">"
" <group id=\"general\""
" label=\"%s\">"
" <splitButton id=\"splitty\" size=\"large\">"
" <toggleButton id=\"mimeSignEncrypt\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" getPressed=\"getSignEncryptPressedEx\""
" getImage=\"btnSignEncryptLarge\""
" onAction=\"encryptSignMimeEx\"""/>"
" <menu id=\"encMenu\" showLabel=\"true\">"
" <toggleButton id=\"mimeSign\""
" getImage=\"btnSignSmall\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" onAction=\"signMimeEx\""
" getPressed=\"getSignPressedEx\"/>"
" <toggleButton id=\"mimeEncrypt\""
" getImage=\"btnEncryptSmall\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" onAction=\"encryptMimeEx\""
" getPressed=\"getEncryptPressedEx\"/>"
" </menu></splitButton>"
" <dialogBoxLauncher>"
" <button id=\"optsBtn\""
" onAction=\"openOptions\""
" screentip=\"%s\"/>"
" </dialogBoxLauncher>"
" </group>"
" </tab>"
" </tabSet>"
" </contextualTabs>"
" </ribbon>"
" <contextMenus>"
" <contextMenu idMso=\"ContextMenuMailItem\">"
" <button id=\"decryptPermanentlyBtn\""
" label=\"%s\""
" onAction=\"decryptPermanently\""
" getImage=\"btnEncryptSmall\""
" getVisible=\"getIsCrypto\""
//" insertAfterMso=\"FilePrintQuick\""
" />"
" </contextMenu>"
" </contextMenus>"
"</customUI>",
_("GpgOL"),
optsSTip,
_("GpgOL"),
_("Settings"),
_(/* TRANSLATORS: Tooltip caption */
"Configure GpgOL keys and settings for this contact."),
_(/* TRANSLATORS: Tooltip content */
"Configure contact specific keys and settings to override "
"the default behavior for this contact."),
_("GpgOL Settings"),
optsSTip,
_("GpgOL"),
_("Secure"), secureTTip, secureSTip,
_("Sign"), signTTip, signSTip,
_("Encrypt"), encryptTTip, encryptSTip,
optsSTip,
_("Permanently &decrypt")
);
}
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer"))
{
// No TabComposeTools in Outlook 2010
gpgrt_asprintf (&buffer,
"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
" onLoad=\"ribbonLoaded\">"
" <ribbon>"
" <tabs>"
" <tab idMso=\"TabMail\">"
" <group id=\"general_read\""
" label=\"%s\">"
" <button id=\"idSigned\""
" getImage=\"btnSigstateLarge\""
" size=\"large\""
" getLabel=\"getSigLabel\""
" getScreentip=\"getSigTip\""
" getSupertip=\"getSigSTip\""
" onAction=\"launchDetails\""
" getEnabled=\"getIsDetailsEnabled\"/>"
" <dialogBoxLauncher>"
" <button id=\"optsBtn_read\""
" onAction=\"openOptions\""
" screentip=\"%s\"/>"
" </dialogBoxLauncher>"
" </group>"
" </tab>"
" </tabs>"
" </ribbon>"
"</customUI>",
_("GpgOL"),
optsSTip
);
}
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Contact"))
{
gpgrt_asprintf (&buffer,
"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
" onLoad=\"ribbonLoaded\">"
" <ribbon>"
" <tabs>"
" <tab idMso=\"TabContact\">"
" <group id=\"gpgol_contact\""
" label=\"%s\">"
" <button id=\"idContactAddkey\""
" getImage=\"btnSignEncryptLarge\""
" size=\"large\""
" label=\"%s\""
" screentip=\"%s\""
" supertip=\"%s\""
" onAction=\"openContactKey\"/>"
" <dialogBoxLauncher>"
" <button id=\"optsBtn_contact\""
" onAction=\"openOptions\""
" screentip=\"%s\""
" supertip=\"%s\"/>"
" </dialogBoxLauncher>"
" </group>"
" </tab>"
" </tabs>"
" </ribbon>"
"</customUI>",
_("GpgOL"),
_("Settings"),
_(/* TRANSLATORS: Tooltip caption */
"Configure GpgOL keys and settings for this contact."),
_(/* TRANSLATORS: Tooltip content */
"Configure contact specific keys and settings to override "
"the default behavior for this contact."),
_("GpgOL Settings"),
optsSTip
);
}
//_(/* TRANSLATORS: Tooltip caption */
// "Configure the OpenPGP key for this contact."),
// _(/* TRANSLATORS: Tooltip content */
// "The configured key or keys will be used for this contact even "
// "if they are not certified."),
if (buffer)
{
memdbg_alloc (buffer);
wchar_t *wbuf = utf8_to_wchar (buffer);
xfree (buffer);
*RibbonXml = SysAllocString (wbuf);
xfree (wbuf);
}
else
*RibbonXml = NULL;
return S_OK;
}
STDMETHODIMP
GpgolRibbonExtender::GetCustomUI (BSTR RibbonID, BSTR * RibbonXml)
{
return GetCustomUI_MIME (RibbonID, RibbonXml);
}
/* RibbonUi elements are created on demand but they are reused
in different inspectors. So far and from all documentation
I could find RibbonUi elments are never
deleted. When they are created the onLoad callback is called
to register them.
The callbacks registered in the XML description are only
executed on Load. So to have different information depending
on the available mails we have to invalidate the UI ourself.
This means that the callbacks will be reevaluated and the UI
Updated. Sadly we don't know which ribbon_ui needs updates
so we have to invalidate everything.
*/
void
gpgoladdin_invalidate_ui ()
{
GpgolAddin::get_instance ()->invalidateRibbons();
}
GpgolAddin *
GpgolAddin::get_instance ()
{
if (!addin_instance)
{
addin_instance = new GpgolAddin ();
}
if (addin_instance->isShutdown ())
{
log_warn ("%s:%s: Get instance after shutdown",
SRCNAME, __func__);
}
return addin_instance;
}
void
GpgolAddin::invalidateRibbons()
{
/* This can only be done in the main thread so no
need for locking or copying here. */
for (const auto it: m_ribbon_uis)
{
log_debug ("%s:%s: Invalidating ribbon: %p",
SRCNAME, __func__, it);
invoke_oom_method (it, "Invalidate", NULL);
}
log_debug ("%s:%s: Invalidation done.",
SRCNAME, __func__);
}
void
GpgolAddin::addRibbon (LPDISPATCH disp)
{
m_ribbon_uis.push_back (disp);
}
void
GpgolAddin::shutdown ()
{
TSTART;
if (m_shutdown)
{
log_debug ("%s:%s: Already shutdown",
SRCNAME, __func__);
TRETURN;
}
m_shutdown = true;
/* Disabling message hook */
UnhookWindowsHookEx (m_hook);
log_debug ("%s:%s: Releasing Application Event Sink;",
SRCNAME, __func__);
detach_ApplicationEvents_sink (m_applicationEventSink);
gpgol_release (m_applicationEventSink);
log_debug ("%s:%s: Releasing Explorers Event Sink;",
SRCNAME, __func__);
detach_ExplorersEvents_sink (m_explorersEventSink);
gpgol_release (m_explorersEventSink);
log_debug ("%s:%s: Releasing Explorer Event Sinks;",
SRCNAME, __func__);
for (auto sink: m_explorerEventSinks)
{
detach_ExplorerEvents_sink (sink);
gpgol_release (sink);
}
write_options ();
if (Mail::closeAllMails_o ())
{
MessageBox (NULL,
"Failed to remove plaintext from at least one message.\n\n"
"Until GpgOL is activated again it is possible that the "
"plaintext of messages decrypted in this Session is saved "
"or transfered back to your mailserver.",
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
}
gpgol_release (m_application);
m_application = nullptr;
TRETURN;
}
void
GpgolAddin::registerExplorerSink (LPDISPATCH sink)
{
m_explorerEventSinks.push_back (sink);
}
void
GpgolAddin::unregisterExplorerSink (LPDISPATCH sink)
{
for (int i = 0; i < m_explorerEventSinks.size(); ++i)
{
if (m_explorerEventSinks[i] == sink)
{
m_explorerEventSinks.erase(m_explorerEventSinks.begin() + i);
return;
}
}
log_error ("%s:%s: Unregister %p which was not registered.",
SRCNAME, __func__, sink);
}
std::shared_ptr <CategoryManager>
GpgolAddin::get_category_mngr ()
{
if (!m_category_mngr)
{
m_category_mngr = std::shared_ptr<CategoryManager> (
new CategoryManager ());
}
return m_category_mngr;
}
diff --git a/src/main.c b/src/main.c
index 5572793..9bf625c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,425 +1,384 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <windows.h>
#include <wincrypt.h>
#include <ctype.h>
#include <winnls.h>
#include <unistd.h>
#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);
+ /* Do not do anything in here so Outlook does not blame us
+ for a slow start. (See Screenshot in T6856 ) */
+ glob_hinst = hinst;
}
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: "<instdir>/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);
opt.noSaveBeforeDecrypt = get_conf_bool ("noSaveBeforeDecrypt", 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);
load_extension_value ("smimeNoCertSigErr", &val);
opt.smimeNoCertSigErr = val;
val = NULL;
}
/* Write current options back to the Registry. */
int
write_options (void)
{
struct
{
const char *name;
int mode;
int value;
char *s_val;
} table[] = {
{"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;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:40 PM (14 h, 52 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e3/db/4f1de8dbd05e56751809f8ae2ee9
Attached To
rO GpgOL
Event Timeline
Log In to Comment