diff --git a/src/common_indep.h b/src/common_indep.h index aad03a8..0ddc2c7 100644 --- a/src/common_indep.h +++ b/src/common_indep.h @@ -1,343 +1,345 @@ #ifndef COMMON_INDEP_H #define COMMON_INDEP_H /* common_indep.h - Common, platform indepentent routines used by GpgOL * Copyright (C) 2005, 2007, 2008 g10 Code GmbH * Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik * Software engineering by Intevation GmbH * * This file is part of GpgOL. * * GpgOL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * GpgOL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "xmalloc.h" #include "debug.h" #include "memdbg.h" #ifdef HAVE_W32_SYSTEM /* Not so independenent ;-) need this for logging HANDLE */ # include #endif /* The Registry key used by Gpg4win. */ #define GPG4WIN_REGKEY_2 "Software\\GNU\\GnuPG" #define GPG4WIN_REGKEY_3 "Software\\Gpg4win" /* Identifiers for the protocol. We use different one than those use by gpgme. FIXME: We might want to define an unknown protocol to non-null and define such a value also in gpgme. */ typedef enum { PROTOCOL_UNKNOWN = 0, PROTOCOL_OPENPGP = 1000, PROTOCOL_SMIME = 1001 } protocol_t; /* Possible options for the recipient dialog. */ enum { OPT_FLAG_TEXT = 2, OPT_FLAG_FORCE = 4, OPT_FLAG_CANCEL = 8 }; typedef enum { GPG_FMT_NONE = 0, /* do not encrypt attachments */ GPG_FMT_CLASSIC = 1, /* encrypt attachments without any encoding */ GPG_FMT_PGP_PEF = 2 /* use the PGP partioned encoding format (PEF) */ } gpgol_format_t; /* Type of a message. */ typedef enum { OPENPGP_NONE = 0, OPENPGP_MSG, OPENPGP_SIG, OPENPGP_CLEARSIG, OPENPGP_PUBKEY, /* Note, that this type is only partly supported */ OPENPGP_SECKEY /* Note, that this type is only partly supported */ } openpgp_t; /* The list of message types we support in GpgOL. */ typedef enum { MSGTYPE_UNKNOWN = 0, MSGTYPE_SMIME, /* Original SMIME class. */ MSGTYPE_GPGOL, MSGTYPE_GPGOL_MULTIPART_SIGNED, MSGTYPE_GPGOL_MULTIPART_ENCRYPTED, MSGTYPE_GPGOL_OPAQUE_SIGNED, MSGTYPE_GPGOL_OPAQUE_ENCRYPTED, MSGTYPE_GPGOL_CLEAR_SIGNED, MSGTYPE_GPGOL_PGP_MESSAGE, MSGTYPE_GPGOL_WKS_CONFIRMATION } msgtype_t; typedef enum { ATTACHTYPE_UNKNOWN = 0, ATTACHTYPE_MOSS = 1, /* The original MOSS message (ie. a S/MIME or PGP/MIME message. */ ATTACHTYPE_FROMMOSS = 2, /* Attachment created from MOSS. */ ATTACHTYPE_MOSSTEMPL = 3, /* Attachment has been created in the course of sending a message */ ATTACHTYPE_PGPBODY = 4, /* Attachment contains the original PGP message body of PGP inline encrypted messages. */ ATTACHTYPE_FROMMOSS_DEC = 5 /* A FROMMOSS attachment that has been temporarily decrypted and needs to be encrypted before it is written back into storage. */ } attachtype_t; /* An object to collect information about one MAPI attachment. */ struct mapi_attach_item_s { int end_of_table; /* True if this is the last plus one entry of the table. */ void *private_mapitable; /* Only for use by mapi_release_attach_table. */ int mapipos; /* The position which needs to be passed to MAPI to open the attachment. -1 means that there is no valid attachment. */ int method; /* MAPI attachment method. */ char *filename; /* Malloced filename of this attachment or NULL. */ /* Malloced string with the MIME attrib or NULL. Parameters are stripped off thus a compare against "type/subtype" is sufficient. */ char *content_type; /* If not NULL the parameters of the content_type. */ const char *content_type_parms; /* If not NULL the content_id */ char *content_id; /* The attachment type from Property GpgOL Attach Type. */ attachtype_t attach_type; }; typedef struct mapi_attach_item_s mapi_attach_item_t; /* Passphrase callback structure. */ struct passphrase_cb_s { gpgme_key_t signer; gpgme_ctx_t ctx; char keyid[16+1]; char *user_id; char *pass; int opts; int ttl; /* TTL of the passphrase. */ unsigned int decrypt_cmd:1; /* 1 = show decrypt dialog, otherwise secret key selection. */ unsigned int hide_pwd:1; unsigned int last_was_bad:1; }; /* Global options - initialized to default by main.c. */ #ifdef __cplusplus extern "C" { #if 0 } #endif #endif #ifdef __cplusplus extern #endif struct { int enable_debug; /* Enable extra debug options. Values larger than 1 increases the debug log verbosity. */ int enable_smime; /* Enable S/MIME support. */ int encrypt_default; /* Encrypt by default. */ int sign_default; /* Sign by default. */ int prefer_html; /* Prefer html in html/text alternatives. */ int inline_pgp; /* Only for Addin. Use Inline PGP by default. */ int autoresolve; /* Autresolve keys with --locate-keys. */ int autosecure; /* Autmatically encrypt if locate returns enough validity. */ int reply_crypt; /* Only for Addin. Encrypt / Sign based on cryptostatus. */ int automation; /* General automation */ int autotrust; /* TOFU configured for GpgOL */ int sync_enc; /* Disabed async encryption */ int sync_dec; /* Disabed async decryption */ int prefer_smime; /* S/MIME prefered when autoresolving */ int smime_html_warn_shown; /* Flag to save if unsigned smime warning was shown */ + int smime_insecure_reply_fw_allowed ; /* Flag to save if unsigned smime reply forwards + should be allowed */ int autoretrieve; /* Use --auto-key-retrieve. */ int search_smime_servers; /* Search for S/MIME keys on all configured S/MIME keyservers for each new unknown mail */ int auto_unstrusted; /* Automatically encrypt even to untrusted keys. */ int autoimport; /* Automatically import keys from headers or attachments. */ char *draft_key; /* Encrypt drafts with this key (fingerprint) */ int alwaysShowApproval; /* Always show the certificate approval dialog. */ int combinedOpsEnabled; /* Enable S/MIME and OpenPGP combined operations. */ int splitBCCMails; /* Split BCC recipients in their own mails. */ int encryptSubject; /* Encrypt the subject with protected headers. */ /* The forms revision number of the binary. */ int forms_revision; } opt; /* The state object used by b64_decode. */ struct b64_state_s { int idx; unsigned char val; int stop_seen; int invalid_encoding; }; typedef struct b64_state_s b64_state_t; size_t qp_decode (char *buffer, size_t length, int *r_slbrk); char *qp_encode (const char *input, size_t length, size_t* outlen); void b64_init (b64_state_t *state); size_t b64_decode (b64_state_t *state, char *buffer, size_t length); char * b64_encode (const char *input, size_t length); char *latin1_to_utf8 (const char *string); char *mem2str (char *dest, const void *src, size_t n); char *trim_spaces (char *string); char *trim_trailing_spaces (char *string); /* To avoid that a compiler optimizes certain memset calls away, these macros may be used instead. */ #define wipememory2(_ptr,_set,_len) do { \ volatile char *_vptr=(volatile char *)(_ptr); \ size_t _vlen=(_len); \ while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ } while(0) #define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) #define wipestring(_ptr) do { \ volatile char *_vptr=(volatile char *)(_ptr); \ while(*_vptr) { *_vptr=0; _vptr++; } \ } while(0) void set_default_key (const char *name); /*-- Convenience macros. -- */ #define DIM(v) (sizeof(v)/sizeof((v)[0])) #define DIMof(type,member) DIM(((type *)0)->member) /*-- Macros to replace ctype ones to avoid locale problems. --*/ #define spacep(p) (*(p) == ' ' || *(p) == '\t') #define digitp(p) (*(p) >= '0' && *(p) <= '9') #define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f')) /* Note this isn't identical to a C locale isspace() without \f and \v, but works for the purposes used here. */ #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t') /* The atoi macros assume that the buffer has only valid digits. */ #define atoi_1(p) (*(p) - '0' ) #define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1)) #define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2)) #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) #define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2)) #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A')) #define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a')) /***** Inline functions. ****/ /* Return true if LINE consists only of white space (up to and including the LF). */ static inline int trailing_ws_p (const char *line) { for ( ; *line && *line != '\n'; line++) if (*line != ' ' && *line != '\t' && *line != '\r') return 0; return 1; } /* An strcmp variant with the compare ending at the end of B. */ static inline int tagcmp (const char *a, const char *b) { return strncmp (a, b, strlen (b)); } #ifdef HAVE_W32_SYSTEM extern HANDLE log_mutex; #endif /***** Missing functions. ****/ #ifndef HAVE_STPCPY static inline char * _gpgol_stpcpy (char *a, const char *b) { while (*b) *a++ = *b++; *a = 0; return a; } #define stpcpy(a,b) _gpgol_stpcpy ((a), (b)) #endif /*!HAVE_STPCPY*/ /* The length of the boundary - the buffer needs to be allocated one byte larger. */ #define BOUNDARYSIZE 20 char *generate_boundary (char *buffer); #ifdef __cplusplus } #include #include struct autocrypt_s { bool exists; std::string pref; std::string addr; GpgME::Data data; }; /* A general way to transfer some header infos around without having to parse them multiple times. */ struct header_info_s { std::string boundary; autocrypt_s acInfo; }; #endif #endif // COMMON_INDEP_H diff --git a/src/mailitem-events.cpp b/src/mailitem-events.cpp index c7f4a1b..670dcaa 100644 --- a/src/mailitem-events.cpp +++ b/src/mailitem-events.cpp @@ -1,1066 +1,1066 @@ /* mailitem-events.h - Event handling for mails. * Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik * Software engineering by Intevation GmbH * * This file is part of GpgOL. * * GpgOL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * GpgOL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #include "config.h" #include "common.h" #include "eventsink.h" #include "eventsinks.h" #include "mymapi.h" #include "mymapitags.h" #include "oomhelp.h" #include "ocidl.h" #include "windowmessages.h" #include "mail.h" #include "mapihelp.h" #include "gpgoladdin.h" #include "wks-helper.h" #undef _ #define _(a) utf8_gettext (a) const wchar_t *prop_blacklist[] = { L"Body", L"HTMLBody", L"To", /* Somehow this is done when a mail is opened */ L"CC", /* Ditto */ L"BCC", /* Ditto */ L"Categories", L"UnRead", L"OutlookVersion", L"OutlookInternalVersion", L"ReceivedTime", L"InternetCodepage", L"ConversationIndex", L"Subject", L"SentOnBehalfOfName", L"MessageClass", L"BodyFormat", NULL }; typedef enum { AfterWrite = 0xFC8D, AttachmentAdd = 0xF00B, AttachmentRead = 0xF00C, AttachmentRemove = 0xFBAE, BeforeAttachmentAdd = 0xFBB0, BeforeAttachmentPreview = 0xFBAF, BeforeAttachmentRead = 0xFBAB, BeforeAttachmentSave = 0xF00D, BeforeAttachmentWriteToTempFile = 0xFBB2, BeforeAutoSave = 0xFC02, BeforeCheckNames = 0xF00A, BeforeDelete = 0xFA75, BeforeRead = 0xFC8C, Close = 0xF004, CustomAction = 0xF006, CustomPropertyChange = 0xF008, Forward = 0xF468, Open = 0xF003, PropertyChange = 0xF009, Read = 0xF001, ReadComplete = 0xFC8F, Reply = 0xF466, ReplyAll = 0xF467, Send = 0xF005, Unload = 0xFBAD, Write = 0xF002 } MailEvent; /* Mail Item Events */ BEGIN_EVENT_SINK(MailItemEvents, IDispatch) /* We are still in the class declaration */ private: Mail * m_mail; /* The mail object related to this mailitem */ }; MailItemEvents::MailItemEvents() : m_object(NULL), m_pCP(NULL), m_cookie(0), m_ref(1), m_mail(NULL) { } MailItemEvents::~MailItemEvents() { if (m_pCP) m_pCP->Unadvise(m_cookie); if (m_object) gpgol_release (m_object); } static bool propchangeWarnShown = false; static bool attachRemoveWarnShown = false; static bool addinsLogged = false; static DWORD WINAPI do_delayed_locate (LPVOID arg) { Sleep(100); do_in_ui_thread (RECIPIENT_ADDED, arg); return 0; } /* The main Invoke function. The return value of this function does not appear to have any effect on outlook although I have read in an example somewhere that you should return S_OK so that outlook continues to handle the event I have not yet seen any effect by returning error values here and no MSDN documentation about the return values. */ EVENT_SINK_INVOKE(MailItemEvents) { USE_INVOKE_ARGS TSTART; if (!m_mail) { m_mail = Mail::getMailForItem (m_object); if (!m_mail) { log_error ("%s:%s: mail event without mail object known. Bug.", SRCNAME, __func__); TRETURN S_OK; } } bool is_reply = false; switch(dispid) { case BeforeAutoSave: { log_oom ("%s:%s: BeforeAutoSave : %p", SRCNAME, __func__, m_mail); if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF)) { /* This happens in the weird case */ log_debug ("%s:%s: Uncancellable BeforeAutoSave.", SRCNAME, __func__); TBREAK; } if (m_mail->isCryptoMail() && !m_mail->decryptedSuccessfully ()) { *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; log_debug ("%s:%s: Autosave for not successfuly decrypted mail." "Cancel it.", SRCNAME, __func__); TBREAK; } if (opt.draft_key && (m_mail->needs_crypto_m () & 1) && !m_mail->isDraftEncrypt()) { log_debug ("%s:%s: Draft encryption for autosave starting now.", SRCNAME, __func__); m_mail->setIsDraftEncrypt (true); m_mail->prepareCrypto_o (); } TRETURN S_OK; } case Open: { log_oom ("%s:%s: Open : %p", SRCNAME, __func__, m_mail); int draft_flags = 0; if (!opt.encrypt_default && !opt.sign_default) { TRETURN S_OK; } LPMESSAGE message = get_oom_base_message (m_object); if (!message) { log_error ("%s:%s: Failed to get message.", SRCNAME, __func__); TBREAK; } int old_flags = get_gpgol_draft_info_flags (message); if (old_flags) { log_dbg ("Open of already flagged mail. " "Not overwriting with settings."); gpgol_release (message); TBREAK; } if (opt.encrypt_default) { draft_flags = 1; } if (opt.sign_default) { draft_flags += 2; } set_gpgol_draft_info_flags (message, draft_flags); gpgol_release (message); TBREAK; } case BeforeRead: { log_oom ("%s:%s: BeforeRead : %p", SRCNAME, __func__, m_mail); if (GpgolAddin::get_instance ()->isShutdown()) { log_debug ("%s:%s: Ignoring read after shutdown.", SRCNAME, __func__); TBREAK; } if (m_mail->preProcessMessage_m ()) { log_error ("%s:%s: Pre process message failed.", SRCNAME, __func__); } if (m_mail->isDecryptAgain ()) { log_debug ("%s:%s: Decrypting after 500ms", SRCNAME, __func__); do_in_ui_thread_async (DECRYPT, m_mail, 500); } TBREAK; } case Read: { log_oom ("%s:%s: Read : %p", SRCNAME, __func__, m_mail); if (!addinsLogged) { // We do it here as this nearly always comes and we want to remove // as much as possible from the startup time. log_addins (); addinsLogged = true; } if (!m_mail->isCryptoMail ()) { log_debug ("%s:%s: Non crypto mail %p opened. Updating sigstatus.", SRCNAME, __func__, m_mail); /* Ensure that no wrong sigstatus is shown */ CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) 300, 0, NULL)); TBREAK; } if (m_mail->setUUID_o ()) { log_debug ("%s:%s: Failed to set uuid.", SRCNAME, __func__); delete m_mail; /* deletes this, too */ TRETURN S_OK; } if (m_mail->decryptVerify_o ()) { log_error ("%s:%s: Decrypt message failed.", SRCNAME, __func__); } if (!opt.enable_smime && m_mail->isSMIME_m ()) { /* We want to save the mail when it's an smime mail and smime is disabled to revert it. */ log_debug ("%s:%s: S/MIME mail but S/MIME is disabled." " Need save.", SRCNAME, __func__); m_mail->setNeedsSave (true); } TBREAK; } case PropertyChange: { if (!parms || parms->cArgs != 1 || parms->rgvarg[0].vt != VT_BSTR || !parms->rgvarg[0].bstrVal) { log_error ("%s:%s: Unexpected params.", SRCNAME, __func__); TBREAK; } const wchar_t *prop_name = parms->rgvarg[0].bstrVal; if (!m_mail->isCryptoMail ()) { if (m_mail->hasOverrideMimeData()) { /* This is a mail created by us. Ignore propchanges. */ TBREAK; } if (!wcscmp (prop_name, L"To") /* || !wcscmp (prop_name, L"BCC") || !wcscmp (prop_name, L"CC") Testing shows that Outlook always sends these three in a row */) { if (opt.autosecure || (m_mail->needs_crypto_m () & 1)) { /* XXX Racy race. This is a fix for crashes that happend if a resolved recipient is copied an pasted. If we then access the recipients object in the Property Change event we crash. Thus we do the delay dance. */ HANDLE thread = CreateThread (NULL, 0, do_delayed_locate, (LPVOID) m_mail, 0, NULL); CloseHandle(thread); } } TBREAK; } for (const wchar_t **cur = prop_blacklist; *cur; cur++) { if (!wcscmp (prop_name, *cur)) { log_oom ("%s:%s: Mail %p propchange: %ls no warning.", SRCNAME, __func__, m_mail, prop_name); TRETURN S_OK; } } log_oom ("%s:%s: Mail %p propchange: %ls.", SRCNAME, __func__, m_mail, prop_name); if (!wcscmp (prop_name, L"SendUsingAccount")) { bool sent = get_oom_bool (m_object, "Sent"); if (sent) { log_debug ("%s:%s: Ignoring SendUsingAccount change for sent %p ", SRCNAME, __func__, m_object); TRETURN S_OK; } log_debug ("%s:%s: Message %p looks like send again.", SRCNAME, __func__, m_object); m_mail->setIsSendAgain (true); TRETURN S_OK; } if (is_draft_mail (m_object)) { log_oom ("%s:%s: Change allowed for draft", SRCNAME, __func__); TRETURN S_OK; } /* We have tried several scenarios to handle propery changes. Only save the property in MAPI and call MAPI SaveChanges worked and did not leak plaintext but this caused outlook still to break the attachments of PGP/MIME Mails into two attachments and add them as winmail.dat so other clients are broken. Alternatively reverting the mail, saving the property and then decrypt again also worked a bit but there were some weird side effects and breakages. But this has the usual problem of a revert that the mail is created by outlook and e.g. multipart/signed signatures from most MUA's are broken. Some things to try out might be the close approach and then another open or a selection change. But for now we just warn. As a workardound a user should make property changes when the mail was not read by us. */ if (propchangeWarnShown) { TRETURN S_OK; } wchar_t *title = utf8_to_wchar (_("Sorry, that's not possible, yet")); char *fmt; gpgrt_asprintf (&fmt, _("GpgOL has prevented the change to the \"%s\" property.\n" "Property changes are not yet handled for crypto messages.\n\n" "To workaround this limitation please change the property when the " "message is not open in any window and not selected in the " "messagelist.\n\nFor example by right clicking but not selecting the message.\n"), wchar_to_utf8(prop_name)); memdbg_alloc (fmt); wchar_t *msg = utf8_to_wchar (fmt); xfree (fmt); MessageBoxW (get_active_hwnd(), msg, title, MB_ICONINFORMATION | MB_OK); xfree (msg); xfree (title); propchangeWarnShown = true; TRETURN S_OK; } case CustomPropertyChange: { log_oom ("%s:%s: CustomPropertyChange : %p", SRCNAME, __func__, m_mail); /* TODO */ TBREAK; } case Send: { /* This is the only event where we can cancel the send of a mailitem. But it is too early for us to encrypt as the MAPI structures are not yet filled. Crypto based on the Outlook Object Model data did not work as the messages were only sent out empty. See 2b376a48 for a try of this. This is why we store send_seen and invoke a save which may result in an error but only after triggering all the behavior we need -> filling mapi structures and invoking the AfterWrite handler where we encrypt. If this encryption is successful and we pass the send as then the encrypted data is sent. */ log_oom ("%s:%s: Send : %p", SRCNAME, __func__, m_mail); if (!m_mail->needs_crypto_m () && m_mail->cryptState () == Mail::NoCryptMail) { if (m_mail->isCryptoMail ()) { log_debug ("%s:%s: Want to send crypto mail without crypto. " "Decrypting.", SRCNAME, __func__); m_mail->decryptPermanently_o (); TBREAK; } log_debug ("%s:%s: No crypto neccessary. Passing send for %p obj %p", SRCNAME, __func__, m_mail, m_object); TBREAK; } if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF)) { log_debug ("%s:%s: Uncancellable send event.", SRCNAME, __func__); TBREAK; } if (m_mail->cryptState () == Mail::NoCryptMail && m_mail->needs_crypto_m ()) { log_debug ("%s:%s: Send event for crypto mail %p saving and starting.", SRCNAME, __func__, m_mail); if (m_mail->prepareCrypto_o ()) { log_dbg ("Prepare crypto requested send abort."); *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; /* Reset the crypter state */ m_mail->setCryptState (Mail::NoCryptMail); TBREAK; } m_mail->setIsDraftEncrypt (false); // Save the Mail invoke_oom_method (m_object, "Save", NULL); if (!m_mail->isAsyncCryptDisabled ()) { // The afterwrite in the save should have triggered // the encryption. We cancel send for our asyncness. // Cancel send *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; TBREAK; } else { if (m_mail->cryptState () == Mail::NoCryptMail) { // Crypto failed or was canceled log_debug ("%s:%s: Message %p mail %p cancelling send - " "Crypto failed or canceled.", SRCNAME, __func__, m_object, m_mail); *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; /* Reset the crypter state */ m_mail->setCryptState (Mail::NoCryptMail); TBREAK; } // For inline response we can't trigger send programatically // so we do the encryption in sync. if (m_mail->cryptState () == Mail::NeedsUpdateInOOM) { m_mail->updateCryptOOM_o (); } if (m_mail->cryptState () == Mail::NeedsSecondAfterWrite) { m_mail->setCryptState (Mail::WantsSendMIME); } if (m_mail->getDoPGPInline () && m_mail->cryptState () != Mail::WantsSendInline) { log_debug ("%s:%s: Message %p mail %p cancelling send - " "Invalid state.", SRCNAME, __func__, m_object, m_mail); gpgol_bug (m_mail->getWindow (), ERR_INLINE_BODY_INV_STATE); *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; TBREAK; } } } if (m_mail->cryptState () == Mail::WantsSendInline) { if (!m_mail->hasCryptedOrEmptyBody_o ()) { log_debug ("%s:%s: Message %p mail %p cancelling send - " "not encrypted or not empty body detected.", SRCNAME, __func__, m_object, m_mail); gpgol_bug (m_mail->getWindow (), ERR_WANTS_SEND_INLINE_BODY); m_mail->setCryptState (Mail::NoCryptMail); *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; TBREAK; } log_debug ("%s:%s: Passing send event for no-mime message %p.", SRCNAME, __func__, m_object); WKSHelper::instance()->allow_notify (1000); TBREAK; } if (m_mail->cryptState () == Mail::WantsSendMIME) { if (!m_mail->hasCryptedOrEmptyBody_o ()) { /* The safety checks here trigger too often. Somehow for some users the body is not empty after the encryption but when it is sent it is still sent with the crypto content because the encrypted MIME Structure is used because it is correct in MAPI land. For safety reasons enabling the checks might be better but until we figure out why for some users the body replacement does not work we have to disable them. Otherwise GpgOL is unusuable for such users. GnuPG-Bug-Id: T3875 */ #define DISABLE_SAFTEY_CHECKS #ifndef DISABLE_SAFTEY_CHECKS gpgol_bug (m_mail->getWindow (), ERR_WANTS_SEND_MIME_BODY); log_debug ("%s:%s: Message %p mail %p cancelling send mime - " "not encrypted or not empty body detected.", SRCNAME, __func__, m_object, m_mail); m_mail->setCryptState (Mail::NoCryptMail); *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; TBREAK; #else log_debug ("%s:%s: Message %p mail %p - " "not encrypted or not empty body detected - MIME.", SRCNAME, __func__, m_object, m_mail); #endif } /* Now we adress T3656 if Outlooks internal S/MIME is somehow * mixed in (even if it is enabled and then disabled) it might * cause strange behavior in that it sends the plain message * and not the encrypted message. Tests have shown that we can * bypass that by calling submit message on our base * message. * * We do this conditionally as our other way of using OOM * to send is proven to work and we don't want to mess * with it. */ // Get the Message class. HRESULT hr; LPSPropValue propval = NULL; // It's important we use the _not_ base message here. LPMESSAGE message = get_oom_message (m_object); hr = HrGetOneProp ((LPMAPIPROP)message, PR_MESSAGE_CLASS_A, &propval); gpgol_release (message); if (FAILED (hr)) { log_error ("%s:%s: HrGetOneProp() failed: hr=%#lx\n", SRCNAME, __func__, hr); TBREAK; } if (propval->Value.lpszA && !strstr (propval->Value.lpszA, "GpgOL") && strcmp (propval->Value.lpszA, "IPM.Note.SMIME.MultipartSigned") && strcmp (propval->Value.lpszA, "IPM.Note.SMIME")) { // Does not have a message class by us. log_debug ("%s:%s: Message %p - No GpgOL Message class " "after crypto. cls is: '%s'", SRCNAME, __func__, m_object, propval->Value.lpszA); log_debug ("%s:%s: Message %p - Activating T3656 Workaround", SRCNAME, __func__, m_object); message = get_oom_base_message (m_object); if (message) { // It's important we use the _base_ message here. mapi_save_changes (message, FORCE_SAVE); message->SubmitMessage(0); gpgol_release (message); // Close the composer and trigger unloads CloseHandle(CreateThread (NULL, 0, close_mail, (LPVOID) m_mail, 0, NULL)); } else { gpgol_bug (nullptr, ERR_GET_BASE_MSG_FAILED); } // Cancel send *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; } MAPIFreeBuffer (propval); if (*(parms->rgvarg[0].pboolVal) == VARIANT_TRUE) { TBREAK; } log_debug ("%s:%s: Passing send event for mime-encrypted message %p.", SRCNAME, __func__, m_object); WKSHelper::instance()->allow_notify (1000); TBREAK; } else { log_debug ("%s:%s: Message %p cancelling send - " "crypto or second save failed.", SRCNAME, __func__, m_object); *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; } TRETURN S_OK; } case ReadComplete: { log_oom ("%s:%s: ReadComplete: %p", SRCNAME, __func__, m_mail); TRETURN S_OK; } case Write: { log_oom ("%s:%s: Write : %p", SRCNAME, __func__, m_mail); /* This is a bit strange. We sometimes get multiple write events without a read in between. When we access the message in the second event it fails and if we cancel the event outlook crashes. So we have keep the m_needs_wipe state variable to keep track of that. */ if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF)) { /* This happens in the weird case */ log_debug ("%s:%s: Uncancellable write event.", SRCNAME, __func__); TBREAK; } if (m_mail->passWrite()) { log_debug ("%s:%s: Passing write because passNextWrite was set for %p", SRCNAME, __func__, m_mail); m_mail->setPassWrite (false); TBREAK; } if (m_mail->cryptState() == Mail::NeedsFirstAfterWrite) { log_debug ("%s:%s: Crypto mail needs first after write. " "Auto-Passing but removing our categories.", SRCNAME, __func__); m_mail->removeCategories_o (); if (g_mail_copy_triggerer) { log_dbg ("Needs first after write for copied mail."); g_mail_copy_triggerer->splitCopyMailCallback (m_mail); } TBREAK; } if (m_mail->isCryptoMail () && !m_mail->needsSave ()) { if (opt.draft_key && (m_mail->needs_crypto_m () & 1) && is_draft_mail (m_object) && m_mail->decryptedSuccessfully ()) { if (m_mail->cryptState () == Mail::NeedsFirstAfterWrite || m_mail->cryptState () == Mail::NeedsSecondAfterWrite) { log_debug ("%s:%s: re-encryption in progress. Passing.", SRCNAME, __func__); TBREAK; } /* This is the case for a modified draft */ log_debug ("%s:%s: Draft re-encryption starting now.", SRCNAME, __func__); m_mail->setIsDraftEncrypt (true); m_mail->prepareCrypto_o (); /* Passing write to trigger encrypt in after write */ TBREAK; } if (m_mail->cryptState() == Mail::WantsSendMIME) { log_debug ("%s:%s: Mail wants send mime. Passing.", SRCNAME, __func__); TBREAK; } Mail *last_mail = Mail::getLastMail (); if (Mail::isValidPtr (last_mail)) { /* We want to identify here if there was a mail created that should receive the contents of this mail. For this we check for a write in the same loop as a mail creation. Now when switching from one mail to another this is also what happens. The new mail is loaded and the old mail is written. To distinguish the two we check that the new mail does not have an entryID, a Subject and No Size. Maybe just size or entryID would be enough but better save then sorry. Security consideration: Worst case we pass the write here but an unload follows before we get the scheduled revert. This would leak plaintext. But does not happen in our tests. Similarly if we crash or Outlook is closed before we see this revert. But as we immediately revert after the write this should also not happen. */ const std::string lastSubject = last_mail->getSubject_o (); char *lastEntryID = get_oom_string (last_mail->item (), "EntryID"); int lastSize = get_oom_int (last_mail->item (), "Size"); std::string lastEntryStr; if (lastEntryID) { lastEntryStr = lastEntryID; xfree (lastEntryID); } if (!lastSize && !lastEntryStr.size () && !lastSubject.size ()) { log_debug ("%s:%s: Write in the same loop as empty load." " Pass but schedule revert.", SRCNAME, __func__); /* This might be a forward. So don't invalidate yet. */ // Mail::clearLastMail (); do_in_ui_thread_async (REVERT_MAIL, m_mail); TRETURN S_OK; } } /* We cancel the write event to stop outlook from excessively syncing our changes. if smime support is disabled and we still have an smime mail we also don't want to cancel the write event to enable reverting this mails. */ *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; log_debug ("%s:%s: Canceling write event.", SRCNAME, __func__); TRETURN S_OK; } if (m_mail->isCryptoMail () && m_mail->needsSave () && m_mail->revert_o ()) { /* An error cleaning the mail should not happen normally. But just in case there is an error we cancel the write here. */ log_debug ("%s:%s: Failed to remove plaintext. Canceling.", SRCNAME, __func__); *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; TRETURN S_OK; } if (!m_mail->isCryptoMail () && m_mail->is_forwarded_crypto_mail () && !m_mail->needs_crypto_m () && m_mail->cryptState () == Mail::NoCryptMail) { /* We are sure now that while this is a forward of an encrypted * mail that the forward should not be signed or encrypted. So * it's not constructed by us. We need to remove our attachments * though so that they are not included in the forward. */ log_debug ("%s:%s: Writing unencrypted forward of crypt mail. " "Removing attachments. mail: %p item: %p", SRCNAME, __func__, m_mail, m_object); if (m_mail->removeOurAttachments_o ()) { // Worst case we forward some encrypted data here not // a security problem, so let it pass. log_error ("%s:%s: Failed to remove our attachments.", SRCNAME, __func__); } /* Remove marker because we did this now. */ m_mail->setIsForwardedCryptoMail (false); } if (m_mail->isDraftEncrypt () && m_mail->cryptState () != Mail::NeedsFirstAfterWrite && m_mail->cryptState () != Mail::NeedsSecondAfterWrite) { log_debug ("%s:%s: Canceling write because draft encrypt is in" " progress.", SRCNAME, __func__); *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; TRETURN S_OK; } if (opt.draft_key && (m_mail->needs_crypto_m () & 1) && !m_mail->isDraftEncrypt() && m_mail->cryptState() == Mail::NoCryptMail) { log_debug ("%s:%s: Draft encryption starting now.", SRCNAME, __func__); m_mail->setIsDraftEncrypt (true); m_mail->prepareCrypto_o (); } log_debug ("%s:%s: Passing write event. %i %i", SRCNAME, __func__, m_mail->isDraftEncrypt(), m_mail->cryptState()); m_mail->setNeedsSave (false); TBREAK; } case AfterWrite: { log_oom ("%s:%s: AfterWrite : %p", SRCNAME, __func__, m_mail); if (m_mail->cryptState () == Mail::NeedsFirstAfterWrite) { /* Seen the first after write. Advance the state */ m_mail->setCryptState (Mail::NeedsActualCrypt); if (m_mail->encryptSignStart_o ()) { log_debug ("%s:%s: Encrypt sign start failed.", SRCNAME, __func__); m_mail->setCryptState (Mail::NoCryptMail); if (!m_mail->isAsyncCryptDisabled()) { m_mail->releaseCurrentItem(); } } TRETURN S_OK; } if (m_mail->cryptState () == Mail::NeedsSecondAfterWrite) { m_mail->setCryptState (Mail::NeedsUpdateInMAPI); m_mail->updateCryptMAPI_m (); log_debug ("%s:%s: Second after write done.", SRCNAME, __func__); TRETURN S_OK; } TBREAK; } case Close: { log_oom ("%s:%s: Close : %p", SRCNAME, __func__, m_mail); if (m_mail->isCryptoMail ()) { if (is_draft_mail (m_object)) { /* In that case we want to ask the question to avoid data loss */ log_oom ("%s:%s: Passing close because of draft status: %p", SRCNAME, __func__, m_mail); m_mail->setDecryptAgain (true); TBREAK; } /* Close. This happens when an Opened mail is closed. To prevent the question of wether or not to save the changes (Which would save the decrypted data without an event to prevent it) we cancel the close and then either close it with discard changes or revert / save it. Contrary to documentation we can invoke close from close. */ if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF)) { /* This happens in the weird case */ log_debug ("%s:%s: Uncancellable close event.", SRCNAME, __func__); TBREAK; } if (m_mail->getCloseTriggered ()) { /* Our close with discard changes, pass through */ m_mail->setCloseTriggered (false); TRETURN S_OK; } *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE; log_oom ("%s:%s: Canceling close event.", SRCNAME, __func__); if (m_mail->close ()) { log_debug ("%s:%s: Close request failed.", SRCNAME, __func__); } } TRETURN S_OK; } case Unload: { log_oom ("%s:%s: Unload : %p", SRCNAME, __func__, m_mail); log_debug ("%s:%s: Removing Mail for message: %p.", SRCNAME, __func__, m_object); delete m_mail; log_oom ("%s:%s: deletion done", SRCNAME, __func__); memdbg_dump (); TRETURN S_OK; } case ReplyAll: case Reply: is_reply = true; /* fall through */ case Forward: { log_oom ("%s:%s: %s : %p", SRCNAME, __func__, is_reply ? "reply" : "forward", m_mail); int draft_flags = 0; if (opt.encrypt_default) { draft_flags = 1; } if (opt.sign_default) { draft_flags += 2; } bool is_crypto_mail = m_mail->isCryptoMail (); if (opt.reply_crypt && is_crypto_mail) { int crypto_flags = m_mail->getCryptoFlags (); if (crypto_flags) { if (opt.sign_default) { /* When default signing is on we also sign unsigned encrypted * mails. */ crypto_flags |= 2; /* reply / forward flags override the setting flags. */ } draft_flags = crypto_flags; } } /* If it is a crypto mail and the settings should not be taken * from the crypto mail and always encrypt / sign is on. Or * If it is not a crypto mail and we have automaticalls sign_encrypt. */ if (draft_flags) { /* Check if we can use the dispval */ if (parms->cArgs == 2 && parms->rgvarg[1].vt == (VT_DISPATCH) && parms->rgvarg[0].vt == (VT_BOOL | VT_BYREF)) { LPMESSAGE msg = get_oom_base_message (parms->rgvarg[1].pdispVal); if (msg) { set_gpgol_draft_info_flags (msg, draft_flags); gpgol_release (msg); } else { log_error ("%s:%s: Failed to get base message.", SRCNAME, __func__); } } else { log_error ("%s:%s: Unexpected parameters.", SRCNAME, __func__); } } if (!is_crypto_mail) { /* Replys to non crypto mails do not interest us anymore. */ TBREAK; } Mail *last_mail = Mail::getLastMail (); if (Mail::isValidPtr (last_mail)) { /* We want to identify here if there was a mail created that should receive the contents of this mail. For this we check for a forward in the same loop as a mail creation. We need to do it this complicated and can't just use get_mail_for_item because the mailitem pointer we get here is a different one then the one with which the mail was loaded. */ char *lastEntryID = get_oom_string (last_mail->item (), "EntryID"); int lastSize = get_oom_int (last_mail->item (), "Size"); std::string lastEntryStr; if (lastEntryID) { lastEntryStr = lastEntryID; xfree (lastEntryID); } if (!lastSize && !lastEntryStr.size ()) { if (!is_reply) { log_debug ("%s:%s: Forward in the same loop as empty " "load Marking %p (item %p) as forwarded.", SRCNAME, __func__, last_mail, last_mail->item ()); last_mail->setIsForwardedCryptoMail (true); } else { log_debug ("%s:%s: Reply in the same loop as empty " "load treating %p (item %p) as reply.", SRCNAME, __func__, last_mail, last_mail->item ()); } - if (m_mail->isBlockHTML ()) + if (m_mail->isBlockHTML () && !opt.smime_insecure_reply_fw_allowed) { std::string buf; /** TRANSLATORS: Part of a warning dialog that disallows reply and forward with contents */ buf = is_reply ? _("You are replying to an unsigned S/MIME " "email.") : _("You are forwarding an unsigned S/MIME " "email."); buf +="\n\n"; buf += _("In this version of S/MIME an attacker could " "use the missing signature to have you " "decrypt contents from a different, otherwise " "completely unrelated email and place it in the " "quote so they can get hold of it.\n" "This is why we only allow quoting to be done manually."); buf += "\n\n"; buf += _("Please copy the relevant contents and insert " "them into the new email."); gpgol_message_box (get_active_hwnd (), buf.c_str(), _("GpgOL"), MB_OK); do_in_ui_thread_async (CLEAR_REPLY_FORWARD, last_mail, 1000); } } // We can now invalidate the last mail Mail::clearLastMail (); } TBREAK; } case AttachmentRemove: { log_oom ("%s:%s: AttachmentRemove: %p", SRCNAME, __func__, m_mail); if (!m_mail->isCryptoMail () || attachRemoveWarnShown || m_mail->attachmentRemoveWarningDisabled ()) { TRETURN S_OK; } gpgol_message_box (get_active_hwnd (), _("Attachments are part of the crypto message.\nThey " "can't be permanently removed and will be shown again the next " "time this message is opened."), _("Sorry, that's not possible, yet"), MB_OK); attachRemoveWarnShown = true; TRETURN S_OK; } default: log_oom ("%s:%s: Message:%p Unhandled Event: %lx \n", SRCNAME, __func__, m_object, dispid); } TRETURN S_OK; } END_EVENT_SINK(MailItemEvents, IID_MailItemEvents) diff --git a/src/main.c b/src/main.c index d779c0f..92119bf 100644 --- a/src/main.c +++ b/src/main.c @@ -1,418 +1,419 @@ /* main.c - DLL entry point * Copyright (C) 2005, 2007, 2008 g10 Code GmbH * * This file is part of GpgOL. * * GpgOL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * GpgOL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "mymapi.h" #include "mymapitags.h" #include "common.h" #include "mymapi.h" /* Local function prototypes. */ static char *get_locale_dir (void); static void drop_locale_dir (char *locale_dir); /* The major version of Outlook we are attached to */ int g_ol_version_major; void i18n_init (void) { char *locale_dir; #ifdef ENABLE_NLS # ifdef HAVE_LC_MESSAGES setlocale (LC_TIME, ""); setlocale (LC_MESSAGES, ""); # else setlocale (LC_ALL, "" ); # endif #endif locale_dir = get_locale_dir (); if (locale_dir) { bindtextdomain (PACKAGE_GT, locale_dir); drop_locale_dir (locale_dir); } textdomain (PACKAGE_GT); } static char * get_gpgme_w32_inst_dir (void) { char *gpg4win_dir = get_gpg4win_dir (); char *tmp; gpgrt_asprintf (&tmp, "%s\\bin\\gpgme-w32spawn.exe", gpg4win_dir); memdbg_alloc (tmp); if (!access(tmp, R_OK)) { xfree (tmp); gpgrt_asprintf (&tmp, "%s\\bin", gpg4win_dir); memdbg_alloc (tmp); xfree (gpg4win_dir); return tmp; } xfree (tmp); gpgrt_asprintf (&tmp, "%s\\gpgme-w32spawn.exe", gpg4win_dir); memdbg_alloc (tmp); if (!access(tmp, R_OK)) { xfree (tmp); return gpg4win_dir; } OutputDebugString("Failed to find gpgme-w32spawn.exe!"); return NULL; } /* Entry point called by DLL loader. */ int WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved) { (void)reserved; if (reason == DLL_PROCESS_ATTACH) { set_global_hinstance (hinst); gpg_err_init (); /* Set the installation directory for GpgME so that it can find tools like gpgme-w32-spawn correctly. */ char *instdir = get_gpgme_w32_inst_dir(); gpgme_set_global_flag ("w32-inst-dir", instdir); xfree (instdir); /* The next call initializes subsystems of gpgme and should be done as early as possible. The actual return value (the version string) is not used here. It may be called at any time later for this. */ gpgme_check_version (NULL); } else if (reason == DLL_PROCESS_DETACH) { gpg_err_deinit (0); } return TRUE; } static char * get_locale_dir (void) { char *instdir; char *p; char *dname; instdir = get_gpg4win_dir(); if (!instdir) return NULL; /* Build the key: "/share/locale". */ #define SLDIR "\\share\\locale" dname = xmalloc (strlen (instdir) + strlen (SLDIR) + 1); if (!dname) { xfree (instdir); return NULL; } p = dname; strcpy (p, instdir); p += strlen (instdir); strcpy (p, SLDIR); xfree (instdir); return dname; } static void drop_locale_dir (char *locale_dir) { xfree (locale_dir); } static int get_conf_bool (const char *name, int defaultVal) { char *val = NULL; int ret; load_extension_value (name, &val); ret = val == NULL ? defaultVal : *val != '1' ? 0 : 1; xfree (val); return ret; } static int dbg_compat (int oldval) { // We broke the debug levels at some point // This is cmpatibility code with the old // levels. #define DBG_MEMORY_OLD (1<<5) // 32 #define DBG_MIME_PARSER_OLD (1<<7) // 128 Unified as DBG_DATA #define DBG_MIME_DATA_OLD (1<<8) // 256 Unified in read_options #define DBG_OOM_OLD (1<<9) // 512 Unified as DBG_OOM #define DBG_OOM_EXTRA_OLD (1<<10)// 1024 Unified in read_options int new_dbg = oldval; if ((oldval & DBG_MEMORY_OLD)) { new_dbg |= DBG_MEMORY; new_dbg -= DBG_MEMORY_OLD; } if ((oldval & DBG_OOM_OLD)) { new_dbg |= DBG_OOM; new_dbg -= DBG_OOM_OLD; } if ((oldval & DBG_MIME_PARSER_OLD)) { new_dbg |= DBG_DATA; new_dbg -= DBG_MIME_PARSER_OLD; } if ((oldval & DBG_MIME_DATA_OLD)) { new_dbg |= DBG_DATA; new_dbg -= DBG_MIME_DATA_OLD; } if ((oldval & DBG_OOM_OLD)) { new_dbg |= DBG_OOM; new_dbg -= DBG_OOM_OLD; } if ((oldval & DBG_OOM_EXTRA_OLD)) { new_dbg |= DBG_OOM; new_dbg -= DBG_OOM_EXTRA_OLD; } #undef DBG_MEMORY_OLD #undef DBG_MIME_PARSER_OLD #undef DBG_MIME_DATA_OLD #undef DBG_OOM_OLD #undef DBG_OOM_EXTRA_OLD return new_dbg; } /* Read option settings from the Registry. */ void read_options (void) { char *val = NULL; /* Set the log file first so that output from this function is logged too. */ load_extension_value ("logFile", &val); set_log_file (val); xfree (val); val = NULL; /* Parse the debug flags. */ load_extension_value ("enableDebug", &val); opt.enable_debug = 0; if (val) { char *p, *pend; trim_spaces (val); for (p = val; p; p = pend) { pend = strpbrk (p, ", \t\n\r\f"); if (pend) { *pend++ = 0; pend += strspn (pend, ", \t\n\r\f"); } if (isascii (*p) && isdigit (*p)) { opt.enable_debug |= dbg_compat (strtoul (p, NULL, 0)); } else if (!strcmp (p, "memory")) opt.enable_debug |= DBG_MEMORY; else if (!strcmp (p, "mime-parser")) opt.enable_debug |= DBG_DATA; else if (!strcmp (p, "mime-data")) opt.enable_debug |= DBG_DATA; else if (!strcmp (p, "oom")) opt.enable_debug |= DBG_OOM; else if (!strcmp (p, "oom-extra")) opt.enable_debug |= DBG_OOM; else log_debug ("invalid debug flag `%s' ignored", p); } } else { /* To help the user enable debugging make sure that the registry key exists. Note that the other registry keys are stored after using the configuration dialog. */ store_extension_value ("enableDebug", "0"); } /* Yes we use free here because memtracing did not track the alloc as the option for debuging was not read before. */ free (val); val = NULL; if (opt.enable_debug) log_debug ("enabled debug flags:%s%s%s%s\n", (opt.enable_debug & DBG_MEMORY)? " memory":"", (opt.enable_debug & DBG_DATA)? " data":"", (opt.enable_debug & DBG_OOM)? " oom":"", (opt.enable_debug & DBG_TRACE)? " trace":"" ); opt.enable_smime = get_conf_bool ("enableSmime", 0); opt.encrypt_default = get_conf_bool ("encryptDefault", 0); opt.sign_default = get_conf_bool ("signDefault", 0); opt.inline_pgp = get_conf_bool ("inlinePGP", 0); opt.reply_crypt = get_conf_bool ("replyCrypt", 1); opt.prefer_smime = get_conf_bool ("preferSmime", 0); opt.autoresolve = get_conf_bool ("autoresolve", 1); opt.autoretrieve = get_conf_bool ("autoretrieve", 0); opt.automation = get_conf_bool ("automation", 1); opt.autosecure = get_conf_bool ("autosecure", 1); opt.autotrust = get_conf_bool ("autotrust", 0); opt.search_smime_servers = get_conf_bool ("searchSmimeServers", 0); opt.smime_html_warn_shown = get_conf_bool ("smimeHtmlWarnShown", 0); + opt.smime_insecure_reply_fw_allowed = get_conf_bool ("smimeInsecureReplyAllowed", 0); opt.auto_unstrusted = get_conf_bool ("autoencryptUntrusted", 0); opt.autoimport = get_conf_bool ("autoimport", 0); opt.splitBCCMails = get_conf_bool ("splitBCCMails", 0); opt.combinedOpsEnabled = get_conf_bool ("combinedOpsEnabled", 0); opt.encryptSubject = get_conf_bool ("encryptSubject", 0); if (!opt.automation) { // Disabling automation is a shorthand to disable the // others, too. opt.autosecure = 0; opt.autoresolve = 0; opt.autotrust = 0; opt.autoretrieve = 0; opt.autoimport = 0; opt.auto_unstrusted = 0; } /* Draft encryption handling. */ if (get_conf_bool ("draftEnc", 0)) { load_extension_value ("draftKey", &val); if (val) { xfree (opt.draft_key); opt.draft_key = val; val = NULL; } } else { xfree (opt.draft_key); opt.draft_key = NULL; } opt.alwaysShowApproval = get_conf_bool ("alwaysShowApproval", 0); /* Hidden options */ opt.sync_enc = 1; //get_conf_bool ("syncEnc", 0); /* Due to an issue where async encryption would leave unencrypted mails in the recently deleted folder on the server we block it. */ opt.sync_dec = get_conf_bool ("syncDec", 0); } /* Write current options back to the Registry. */ int write_options (void) { struct { const char *name; int mode; int value; char *s_val; } table[] = { {"smimeHtmlWarnShown", 0, opt.smime_html_warn_shown, NULL}, {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; }