diff --git a/src/common_indep.h b/src/common_indep.h index 73910f4..fb693ef 100644 --- a/src/common_indep.h +++ b/src/common_indep.h @@ -1,324 +1,326 @@ #ifndef COMMON_INDEP_H #define COMMON_INDEP_H /* common_indep.h - Common, platform indepentent routines used by GpgOL * Copyright (C) 2005, 2007, 2008 g10 Code GmbH * Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik * Software engineering by Intevation GmbH * * This file is part of GpgOL. * * GpgOL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * GpgOL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "xmalloc.h" #include "debug.h" #include "memdbg.h" #ifdef HAVE_W32_SYSTEM /* Not so independenent ;-) need this for logging HANDLE */ # include #endif /* The Registry key used by Gpg4win. */ #ifdef _WIN64 # define GPG4WIN_REGKEY_2 "Software\\Wow6432Node\\GNU\\GnuPG" #else # define GPG4WIN_REGKEY_2 "Software\\GNU\\GnuPG" #endif #ifdef _WIN64 # define GPG4WIN_REGKEY_3 "Software\\Wow6432Node\\Gpg4win" #else # define GPG4WIN_REGKEY_3 "Software\\Gpg4win" #endif /* Identifiers for the protocol. We use different one than those use by gpgme. FIXME: We might want to define an unknown protocol to non-null and define such a value also in gpgme. */ typedef enum { PROTOCOL_UNKNOWN = 0, PROTOCOL_OPENPGP = 1000, PROTOCOL_SMIME = 1001 } protocol_t; /* Possible options for the recipient dialog. */ enum { OPT_FLAG_TEXT = 2, OPT_FLAG_FORCE = 4, OPT_FLAG_CANCEL = 8 }; typedef enum { GPG_FMT_NONE = 0, /* do not encrypt attachments */ GPG_FMT_CLASSIC = 1, /* encrypt attachments without any encoding */ GPG_FMT_PGP_PEF = 2 /* use the PGP partioned encoding format (PEF) */ } gpgol_format_t; /* Type of a message. */ typedef enum { OPENPGP_NONE = 0, OPENPGP_MSG, OPENPGP_SIG, OPENPGP_CLEARSIG, OPENPGP_PUBKEY, /* Note, that this type is only partly supported */ OPENPGP_SECKEY /* Note, that this type is only partly supported */ } openpgp_t; /* The list of message types we support in GpgOL. */ typedef enum { MSGTYPE_UNKNOWN = 0, MSGTYPE_SMIME, /* Original SMIME class. */ MSGTYPE_GPGOL, MSGTYPE_GPGOL_MULTIPART_SIGNED, MSGTYPE_GPGOL_MULTIPART_ENCRYPTED, MSGTYPE_GPGOL_OPAQUE_SIGNED, MSGTYPE_GPGOL_OPAQUE_ENCRYPTED, MSGTYPE_GPGOL_CLEAR_SIGNED, MSGTYPE_GPGOL_PGP_MESSAGE, MSGTYPE_GPGOL_WKS_CONFIRMATION } msgtype_t; typedef enum { ATTACHTYPE_UNKNOWN = 0, ATTACHTYPE_MOSS = 1, /* The original MOSS message (ie. a S/MIME or PGP/MIME message. */ ATTACHTYPE_FROMMOSS = 2, /* Attachment created from MOSS. */ ATTACHTYPE_MOSSTEMPL = 3, /* Attachment has been created in the course of sending a message */ ATTACHTYPE_PGPBODY = 4, /* Attachment contains the original PGP message body of PGP inline encrypted messages. */ ATTACHTYPE_FROMMOSS_DEC = 5 /* A FROMMOSS attachment that has been temporarily decrypted and needs to be encrypted before it is written back into storage. */ } attachtype_t; /* An object to collect information about one MAPI attachment. */ struct mapi_attach_item_s { int end_of_table; /* True if this is the last plus one entry of the table. */ void *private_mapitable; /* Only for use by mapi_release_attach_table. */ int mapipos; /* The position which needs to be passed to MAPI to open the attachment. -1 means that there is no valid attachment. */ int method; /* MAPI attachment method. */ char *filename; /* Malloced filename of this attachment or NULL. */ /* Malloced string with the MIME attrib or NULL. Parameters are stripped off thus a compare against "type/subtype" is sufficient. */ char *content_type; /* If not NULL the parameters of the content_type. */ const char *content_type_parms; /* If not NULL the content_id */ char *content_id; /* The attachment type from Property GpgOL Attach Type. */ attachtype_t attach_type; }; typedef struct mapi_attach_item_s mapi_attach_item_t; /* Passphrase callback structure. */ struct passphrase_cb_s { gpgme_key_t signer; gpgme_ctx_t ctx; char keyid[16+1]; char *user_id; char *pass; int opts; int ttl; /* TTL of the passphrase. */ unsigned int decrypt_cmd:1; /* 1 = show decrypt dialog, otherwise secret key selection. */ unsigned int hide_pwd:1; unsigned int last_was_bad:1; }; /* Global options - initialized to default by main.c. */ #ifdef __cplusplus extern "C" { #if 0 } #endif #endif #ifdef __cplusplus extern #endif struct { int enable_debug; /* Enable extra debug options. Values larger than 1 increases the debug log verbosity. */ int enable_smime; /* Enable S/MIME support. */ int encrypt_default; /* Encrypt by default. */ int sign_default; /* Sign by default. */ int prefer_html; /* Prefer html in html/text alternatives. */ int inline_pgp; /* Only for Addin. Use Inline PGP by default. */ int autoresolve; /* Autresolve keys with --locate-keys. */ int autosecure; /* Autmatically encrypt if locate returns enough validity. */ int reply_crypt; /* Only for Addin. Encrypt / Sign based on cryptostatus. */ int automation; /* General automation */ int autotrust; /* TOFU configured for GpgOL */ int sync_enc; /* Disabed async encryption */ int sync_dec; /* Disabed async decryption */ int prefer_smime; /* S/MIME prefered when autoresolving */ int smime_html_warn_shown; /* Flag to save if unsigned smime warning was shown */ int autoretrieve; /* Use --auto-key-retrieve. */ + int search_smime_servers; /* Search for S/MIME keys on all configured S/MIME keyservers + for each new unknown mail */ /* 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 } #endif #endif // COMMON_INDEP_H diff --git a/src/keycache.cpp b/src/keycache.cpp index f885694..523ab31 100644 --- a/src/keycache.cpp +++ b/src/keycache.cpp @@ -1,1258 +1,1266 @@ /* @file keycache.cpp * @brief Internal keycache * * Copyright (C) 2018 Intevation GmbH * * This file is part of GpgOL. * * GpgOL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * GpgOL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #include "keycache.h" #include "common.h" #include "cpphelp.h" #include "mail.h" #include #include #include #include #include #include #include #include #include GPGRT_LOCK_DEFINE (keycache_lock); GPGRT_LOCK_DEFINE (fpr_map_lock); GPGRT_LOCK_DEFINE (update_lock); GPGRT_LOCK_DEFINE (import_lock); static KeyCache* singleton = nullptr; /** At some point we need to set a limit. There seems to be no limit on how many recipients a mail can have in outlook. We would run out of resources or block. 50 Threads already seems a bit excessive but it should really cover most legit use cases. */ #define MAX_LOCATOR_THREADS 50 static int s_thread_cnt; namespace { class LocateArgs { public: LocateArgs (const std::string& mbox, Mail *mail = nullptr): m_mbox (mbox), m_mail (mail) { TSTART; s_thread_cnt++; Mail::lockDelete (); if (Mail::isValidPtr (m_mail)) { m_mail->incrementLocateCount (); } Mail::unlockDelete (); TRETURN; }; ~LocateArgs() { TSTART; s_thread_cnt--; Mail::lockDelete (); if (Mail::isValidPtr (m_mail)) { m_mail->decrementLocateCount (); } Mail::unlockDelete (); TRETURN; } std::string m_mbox; Mail *m_mail; }; } // namespace typedef std::pair update_arg_t; typedef std::pair, std::string> import_arg_t; static DWORD WINAPI do_update (LPVOID arg) { TSTART; auto args = std::unique_ptr ((update_arg_t*) arg); log_debug ("%s:%s updating: \"%s\" with protocol %s", SRCNAME, __func__, anonstr (args->first.c_str ()), to_cstr (args->second)); auto ctx = std::unique_ptr (GpgME::Context::createForProtocol (args->second)); if (!ctx) { TRACEPOINT; KeyCache::instance ()->onUpdateJobDone (args->first.c_str(), GpgME::Key ()); TRETURN 0; } ctx->setKeyListMode (GpgME::KeyListMode::Local | GpgME::KeyListMode::Signatures | GpgME::KeyListMode::Validate | GpgME::KeyListMode::WithTofu); GpgME::Error err; const auto newKey = ctx->key (args->first.c_str (), err, false); TRACEPOINT; if (newKey.isNull()) { log_debug ("%s:%s Failed to find key for %s", SRCNAME, __func__, anonstr (args->first.c_str ())); } if (err) { log_debug ("%s:%s Failed to find key for %s err: %s", SRCNAME, __func__, anonstr (args->first.c_str()), err.asString ()); } KeyCache::instance ()->onUpdateJobDone (args->first.c_str(), newKey); log_debug ("%s:%s Update job done", SRCNAME, __func__); TRETURN 0; } static DWORD WINAPI do_import (LPVOID arg) { TSTART; auto args = std::unique_ptr ((import_arg_t*) arg); const std::string mbox = args->first->m_mbox; log_debug ("%s:%s importing for: \"%s\" with data \n%s", SRCNAME, __func__, anonstr (mbox.c_str ()), anonstr (args->second.c_str ())); auto ctx = std::unique_ptr (GpgME::Context::createForProtocol (GpgME::OpenPGP)); if (!ctx) { TRACEPOINT; TRETURN 0; } // We want to avoid unneccessary copies. The c_str will be valid // until args goes out of scope. const char *keyStr = args->second.c_str (); GpgME::Data data (keyStr, strlen (keyStr), /* copy */ false); if (data.type () != GpgME::Data::PGPKey) { log_debug ("%s:%s Data for: %s is not a PGP Key", SRCNAME, __func__, anonstr (mbox.c_str ())); TRETURN 0; } data.rewind (); const auto result = ctx->importKeys (data); std::vector fingerprints; for (const auto import: result.imports()) { if (import.error()) { log_debug ("%s:%s Error importing: %s", SRCNAME, __func__, import.error().asString()); continue; } const char *fpr = import.fingerprint (); if (!fpr) { TRACEPOINT; continue; } update_arg_t * update_args = new update_arg_t; update_args->first = std::string (fpr); update_args->second = GpgME::OpenPGP; // We do it blocking to be sure that when all imports // are done they are also part of the keycache. do_update ((LPVOID) update_args); fingerprints.push_back (fpr); log_debug ("%s:%s Imported: %s from addressbook.", SRCNAME, __func__, anonstr (fpr)); } KeyCache::instance ()->onAddrBookImportJobDone (mbox, fingerprints); log_debug ("%s:%s Import job done for: %s", SRCNAME, __func__, anonstr (mbox.c_str ())); TRETURN 0; } class KeyCache::Private { public: Private() { } void setPgpKey(const std::string &mbox, const GpgME::Key &key) { TSTART; gpgol_lock (&keycache_lock); auto it = m_pgp_key_map.find (mbox); if (it == m_pgp_key_map.end ()) { m_pgp_key_map.insert (std::pair (mbox, key)); } else { it->second = key; } insertOrUpdateInFprMap (key); gpgol_unlock (&keycache_lock); TRETURN; } void setSmimeKey(const std::string &mbox, const GpgME::Key &key) { TSTART; gpgol_lock (&keycache_lock); auto it = m_smime_key_map.find (mbox); if (it == m_smime_key_map.end ()) { m_smime_key_map.insert (std::pair (mbox, key)); } else { it->second = key; } insertOrUpdateInFprMap (key); gpgol_unlock (&keycache_lock); TRETURN; } void setPgpKeySecret(const std::string &mbox, const GpgME::Key &key) { TSTART; gpgol_lock (&keycache_lock); auto it = m_pgp_skey_map.find (mbox); if (it == m_pgp_skey_map.end ()) { m_pgp_skey_map.insert (std::pair (mbox, key)); } else { it->second = key; } insertOrUpdateInFprMap (key); gpgol_unlock (&keycache_lock); TRETURN; } void setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key) { TSTART; gpgol_lock (&keycache_lock); auto it = m_smime_skey_map.find (mbox); if (it == m_smime_skey_map.end ()) { m_smime_skey_map.insert (std::pair (mbox, key)); } else { it->second = key; } insertOrUpdateInFprMap (key); gpgol_unlock (&keycache_lock); TRETURN; } std::vector getPGPOverrides (const char *addr) { TSTART; std::vector ret; if (!addr) { TRETURN ret; } auto mbox = GpgME::UserID::addrSpecFromString (addr); gpgol_lock (&keycache_lock); const auto it = m_addr_book_overrides.find (mbox); if (it == m_addr_book_overrides.end ()) { gpgol_unlock (&keycache_lock); TRETURN ret; } for (const auto fpr: it->second) { const auto key = getByFpr (fpr.c_str (), false); if (key.isNull()) { log_debug ("%s:%s: No key for %s in the cache?!", SRCNAME, __func__, anonstr (fpr.c_str())); continue; } ret.push_back (key); } gpgol_unlock (&keycache_lock); TRETURN ret; } GpgME::Key getKey (const char *addr, GpgME::Protocol proto) { TSTART; if (!addr) { TRETURN GpgME::Key(); } auto mbox = GpgME::UserID::addrSpecFromString (addr); if (proto == GpgME::OpenPGP) { gpgol_lock (&keycache_lock); const auto it = m_pgp_key_map.find (mbox); if (it == m_pgp_key_map.end ()) { gpgol_unlock (&keycache_lock); TRETURN GpgME::Key(); } const auto ret = it->second; gpgol_unlock (&keycache_lock); TRETURN ret; } gpgol_lock (&keycache_lock); const auto it = m_smime_key_map.find (mbox); if (it == m_smime_key_map.end ()) { gpgol_unlock (&keycache_lock); TRETURN GpgME::Key(); } const auto ret = it->second; gpgol_unlock (&keycache_lock); TRETURN ret; } GpgME::Key getSKey (const char *addr, GpgME::Protocol proto) { TSTART; if (!addr) { TRETURN GpgME::Key(); } auto mbox = GpgME::UserID::addrSpecFromString (addr); if (proto == GpgME::OpenPGP) { gpgol_lock (&keycache_lock); const auto it = m_pgp_skey_map.find (mbox); if (it == m_pgp_skey_map.end ()) { gpgol_unlock (&keycache_lock); TRETURN GpgME::Key(); } const auto ret = it->second; gpgol_unlock (&keycache_lock); TRETURN ret; } gpgol_lock (&keycache_lock); const auto it = m_smime_skey_map.find (mbox); if (it == m_smime_skey_map.end ()) { gpgol_unlock (&keycache_lock); TRETURN GpgME::Key(); } const auto ret = it->second; gpgol_unlock (&keycache_lock); TRETURN ret; } GpgME::Key getSigningKey (const char *addr, GpgME::Protocol proto) { TSTART; const auto key = getSKey (addr, proto); if (key.isNull()) { log_debug ("%s:%s: secret key for %s is null", SRCNAME, __func__, anonstr (addr)); TRETURN key; } if (!key.canReallySign()) { log_debug ("%s:%s: Discarding key for %s because it can't sign", SRCNAME, __func__, anonstr (addr)); TRETURN GpgME::Key(); } if (!key.hasSecret()) { log_debug ("%s:%s: Discarding key for %s because it has no secret", SRCNAME, __func__, anonstr (addr)); TRETURN GpgME::Key(); } if (in_de_vs_mode () && !key.isDeVs()) { log_debug ("%s:%s: signing key for %s is not deVS", SRCNAME, __func__, anonstr (addr)); TRETURN GpgME::Key(); } TRETURN key; } std::vector getEncryptionKeys (const std::vector &recipients, GpgME::Protocol proto) { TSTART; std::vector ret; if (recipients.empty ()) { TRACEPOINT; TRETURN ret; } for (const auto &recip: recipients) { if (proto == GpgME::OpenPGP) { const auto overrides = getPGPOverrides (recip.c_str ()); if (!overrides.empty()) { ret.insert (ret.end (), overrides.begin (), overrides.end ()); log_debug ("%s:%s: Using overides for %s", SRCNAME, __func__, anonstr (recip.c_str ())); continue; } } const auto key = getKey (recip.c_str (), proto); if (key.isNull()) { log_debug ("%s:%s: No key for %s. no internal encryption", SRCNAME, __func__, anonstr (recip.c_str ())); TRETURN std::vector(); } if (!key.canEncrypt() || key.isRevoked() || key.isExpired() || key.isDisabled() || key.isInvalid()) { log_data ("%s:%s: Invalid key for %s. no internal encryption", SRCNAME, __func__, anonstr (recip.c_str ())); TRETURN std::vector(); } if (in_de_vs_mode () && !key.isDeVs ()) { log_data ("%s:%s: key for %s is not deVS", SRCNAME, __func__, anonstr (recip.c_str ())); TRETURN std::vector(); } bool validEnough = false; /* Here we do the check if the key is valid for this recipient */ const auto addrSpec = GpgME::UserID::addrSpecFromString (recip.c_str ()); for (const auto &uid: key.userIDs ()) { if (addrSpec != uid.addrSpec()) { // Ignore unmatching addr specs continue; } if (uid.validity() >= GpgME::UserID::Marginal || uid.origin() == GpgME::Key::OriginWKD) { validEnough = true; break; } } if (!validEnough) { log_debug ("%s:%s: UID for %s does not have at least marginal trust", SRCNAME, __func__, anonstr (recip.c_str ())); TRETURN std::vector(); } // Accepting key ret.push_back (key); } TRETURN ret; } void insertOrUpdateInFprMap (const GpgME::Key &key) { TSTART; if (key.isNull() || !key.primaryFingerprint()) { TRACEPOINT; TRETURN; } gpgol_lock (&fpr_map_lock); /* First ensure that we have the subkeys mapped to the primary fpr */ const char *primaryFpr = key.primaryFingerprint (); for (const auto &sub: key.subkeys()) { const char *subFpr = sub.fingerprint(); auto it = m_sub_fpr_map.find (subFpr); if (it == m_sub_fpr_map.end ()) { m_sub_fpr_map.insert (std::make_pair( std::string (subFpr), std::string (primaryFpr))); } } auto it = m_fpr_map.find (primaryFpr); log_debug ("%s:%s \"%s\" updated.", SRCNAME, __func__, anonstr (primaryFpr)); if (it == m_fpr_map.end ()) { m_fpr_map.insert (std::make_pair (primaryFpr, key)); gpgol_unlock (&fpr_map_lock); TRETURN; } if (it->second.hasSecret () && !key.hasSecret()) { log_debug ("%s:%s Lost secret info on update. Merging.", SRCNAME, __func__); auto merged = key; merged.mergeWith (it->second); it->second = merged; } else { it->second = key; } gpgol_unlock (&fpr_map_lock); TRETURN; } GpgME::Key getFromMap (const char *fpr) const { TSTART; if (!fpr) { TRACEPOINT; TRETURN GpgME::Key(); } gpgol_lock (&fpr_map_lock); std::string primaryFpr; const auto it = m_sub_fpr_map.find (fpr); if (it != m_sub_fpr_map.end ()) { log_debug ("%s:%s using \"%s\" for \"%s\"", SRCNAME, __func__, anonstr (it->second.c_str()), anonstr (fpr)); primaryFpr = it->second; } else { primaryFpr = fpr; } const auto keyIt = m_fpr_map.find (primaryFpr); if (keyIt != m_fpr_map.end ()) { const auto ret = keyIt->second; gpgol_unlock (&fpr_map_lock); TRETURN ret; } gpgol_unlock (&fpr_map_lock); TRETURN GpgME::Key(); } GpgME::Key getByFpr (const char *fpr, bool block) const { TSTART; if (!fpr) { TRACEPOINT; TRETURN GpgME::Key (); } TRACEPOINT; const auto ret = getFromMap (fpr); if (ret.isNull()) { // If the key was not found we need to check if there is // an update running. if (block) { const std::string sFpr (fpr); int i = 0; gpgol_lock (&update_lock); while (m_update_jobs.find(sFpr) != m_update_jobs.end ()) { i++; if (i % 100 == 0) { log_debug ("%s:%s Waiting on update for \"%s\"", SRCNAME, __func__, anonstr (fpr)); } gpgol_unlock (&update_lock); Sleep (10); gpgol_lock (&update_lock); if (i == 3000) { /* Just to be on the save side */ log_error ("%s:%s Waiting on update for \"%s\" " "failed! Bug!", SRCNAME, __func__, anonstr (fpr)); break; } } gpgol_unlock (&update_lock); TRACEPOINT; const auto ret2 = getFromMap (fpr); if (ret2.isNull ()) { log_debug ("%s:%s Cache miss after blocking check %s.", SRCNAME, __func__, anonstr (fpr)); } else { log_debug ("%s:%s Cache hit after wait for %s.", SRCNAME, __func__, anonstr (fpr)); TRETURN ret2; } } log_debug ("%s:%s Cache miss for %s.", SRCNAME, __func__, anonstr (fpr)); TRETURN GpgME::Key(); } log_debug ("%s:%s Cache hit for %s.", SRCNAME, __func__, anonstr (fpr)); TRETURN ret; } void update (const char *fpr, GpgME::Protocol proto) { TSTART; if (!fpr) { TRETURN; } const std::string sFpr (fpr); gpgol_lock (&update_lock); if (m_update_jobs.find(sFpr) != m_update_jobs.end ()) { log_debug ("%s:%s Update for \"%s\" already in progress.", SRCNAME, __func__, anonstr (fpr)); gpgol_unlock (&update_lock); } m_update_jobs.insert (sFpr); gpgol_unlock (&update_lock); update_arg_t * args = new update_arg_t; args->first = sFpr; args->second = proto; CloseHandle (CreateThread (NULL, 0, do_update, (LPVOID) args, 0, NULL)); TRETURN; } void onUpdateJobDone (const char *fpr, const GpgME::Key &key) { TSTART; if (!fpr) { TRETURN; } TRACEPOINT; insertOrUpdateInFprMap (key); gpgol_lock (&update_lock); const auto it = m_update_jobs.find(fpr); if (it == m_update_jobs.end()) { log_debug ("%s:%s Update for \"%s\" already finished.", SRCNAME, __func__, anonstr (fpr)); gpgol_unlock (&update_lock); TRETURN; } m_update_jobs.erase (it); gpgol_unlock (&update_lock); TRACEPOINT; TRETURN; } void importFromAddrBook (const std::string &mbox, const char *data, Mail *mail) { TSTART; if (!data || mbox.empty() || !mail) { TRACEPOINT; TRETURN; } gpgol_lock (&import_lock); if (m_import_jobs.find (mbox) != m_import_jobs.end ()) { log_debug ("%s:%s import for \"%s\" already in progress.", SRCNAME, __func__, anonstr (mbox.c_str ())); gpgol_unlock (&import_lock); } m_import_jobs.insert (mbox); gpgol_unlock (&import_lock); import_arg_t * args = new import_arg_t; args->first = std::unique_ptr (new LocateArgs (mbox, mail)); args->second = std::string (data); CloseHandle (CreateThread (NULL, 0, do_import, (LPVOID) args, 0, NULL)); TRETURN; } void onAddrBookImportJobDone (const std::string &mbox, const std::vector &result_fprs) { TSTART; gpgol_lock (&keycache_lock); auto it = m_addr_book_overrides.find (mbox); if (it != m_addr_book_overrides.end ()) { it->second = result_fprs; } else { m_addr_book_overrides.insert ( std::make_pair (mbox, result_fprs)); } gpgol_unlock (&keycache_lock); gpgol_lock (&import_lock); const auto job_it = m_import_jobs.find(mbox); if (job_it == m_import_jobs.end()) { log_error ("%s:%s import for \"%s\" already finished.", SRCNAME, __func__, anonstr (mbox.c_str ())); gpgol_unlock (&import_lock); TRETURN; } m_import_jobs.erase (job_it); gpgol_unlock (&import_lock); TRETURN; } std::unordered_map m_pgp_key_map; std::unordered_map m_smime_key_map; std::unordered_map m_pgp_skey_map; std::unordered_map m_smime_skey_map; std::unordered_map m_fpr_map; std::unordered_map m_sub_fpr_map; std::unordered_map > m_addr_book_overrides; std::set m_update_jobs; std::set m_import_jobs; }; KeyCache::KeyCache(): d(new Private) { } KeyCache * KeyCache::instance () { if (!singleton) { singleton = new KeyCache(); } return singleton; } GpgME::Key KeyCache::getSigningKey (const char *addr, GpgME::Protocol proto) const { return d->getSigningKey (addr, proto); } std::vector KeyCache::getEncryptionKeys (const std::vector &recipients, GpgME::Protocol proto) const { return d->getEncryptionKeys (recipients, proto); } static GpgME::Key get_most_valid_key_simple (const std::vector &keys) { GpgME::Key candidate; for (const auto &key: keys) { if (key.isRevoked() || key.isExpired() || key.isDisabled() || key.isInvalid()) { log_debug ("%s:%s: Skipping invalid S/MIME key", SRCNAME, __func__); continue; } if (candidate.isNull() || !candidate.numUserIDs()) { if (key.numUserIDs() && candidate.userID(0).validity() <= key.userID(0).validity()) { candidate = key; } } } return candidate; } static std::vector get_local_smime_keys (const std::string &addr) { TSTART; std::vector keys; auto ctx = std::unique_ptr ( GpgME::Context::createForProtocol (GpgME::CMS)); if (!ctx) { TRACEPOINT; TRETURN keys; } // We need to validate here to fetch CRL's ctx->setKeyListMode (GpgME::KeyListMode::Local | GpgME::KeyListMode::Validate | GpgME::KeyListMode::Signatures); GpgME::Error e = ctx->startKeyListing (addr.c_str()); if (e) { TRACEPOINT; TRETURN keys; } GpgME::Error err; do { keys.push_back(ctx->nextKey(err)); } while (!err); keys.pop_back(); TRETURN keys; } static std::vector get_extern_smime_keys (const std::string &addr, bool import) { TSTART; std::vector keys; auto ctx = std::unique_ptr ( GpgME::Context::createForProtocol (GpgME::CMS)); if (!ctx) { TRACEPOINT; TRETURN keys; } // We need to validate here to fetch CRL's ctx->setKeyListMode (GpgME::KeyListMode::Extern); GpgME::Error e = ctx->startKeyListing (addr.c_str()); if (e) { TRACEPOINT; TRETURN keys; } GpgME::Error err; do { const auto key = ctx->nextKey (err); if (!err && !key.isNull()) { keys.push_back (key); log_debug ("%s:%s: Found extern S/MIME key for %s with fpr: %s", SRCNAME, __func__, anonstr (addr.c_str()), anonstr (key.primaryFingerprint())); } } while (!err); if (import && keys.size ()) { const GpgME::ImportResult res = ctx->importKeys(keys); log_debug ("%s:%s: Import result for %s: err: %s", SRCNAME, __func__, anonstr (addr.c_str()), res.error ().asString ()); } TRETURN keys; } static DWORD WINAPI do_locate (LPVOID arg) { TSTART; if (!arg) { TRETURN 0; } auto args = std::unique_ptr ((LocateArgs *) arg); const auto addr = args->m_mbox; log_debug ("%s:%s searching key for addr: \"%s\"", SRCNAME, __func__, anonstr (addr.c_str())); const auto k = GpgME::Key::locate (addr.c_str()); if (!k.isNull ()) { log_debug ("%s:%s found key for addr: \"%s\":%s", SRCNAME, __func__, anonstr (addr.c_str()), anonstr (k.primaryFingerprint())); KeyCache::instance ()->setPgpKey (addr, k); } + log_debug ("%s:%s pgp locate done", + SRCNAME, __func__); if (opt.enable_smime) { GpgME::Key candidate = get_most_valid_key_simple ( get_local_smime_keys (addr)); if (!candidate.isNull()) { log_debug ("%s:%s found SMIME key for addr: \"%s\":%s", SRCNAME, __func__, anonstr (addr.c_str()), anonstr (candidate.primaryFingerprint())); KeyCache::instance()->setSmimeKey (addr, candidate); TRETURN 0; } + if (!opt.search_smime_servers || (!k.isNull() && !opt.prefer_smime)) + { + log_debug ("%s:%s Found no S/MIME key locally and external " + "search is disabled.", SRCNAME, __func__); + TRETURN 0; + } /* Search for extern keys and import them */ const auto externs = get_extern_smime_keys (addr, true); if (externs.empty()) { TRETURN 0; } /* We found and imported external keys. We need to get them locally now to ensure that they are valid etc. */ candidate = get_most_valid_key_simple ( get_local_smime_keys (addr)); if (!candidate.isNull()) { log_debug ("%s:%s found ext. SMIME key for addr: \"%s\":%s", SRCNAME, __func__, anonstr (addr.c_str()), anonstr (candidate.primaryFingerprint())); KeyCache::instance()->setSmimeKey (addr, candidate); TRETURN 0; } else { log_debug ("%s:%s: Found no valid key in extern S/MIME certs", SRCNAME, __func__); } } TRETURN 0; } static void locate_secret (const char *addr, GpgME::Protocol proto) { TSTART; auto ctx = std::unique_ptr ( GpgME::Context::createForProtocol (proto)); if (!ctx) { TRACEPOINT; TRETURN; } if (!addr) { TRACEPOINT; TRETURN; } const auto mbox = GpgME::UserID::addrSpecFromString (addr); if (mbox.empty()) { log_debug ("%s:%s: Empty mbox for addr %s", SRCNAME, __func__, anonstr (addr)); TRETURN; } // We need to validate here to fetch CRL's ctx->setKeyListMode (GpgME::KeyListMode::Local | GpgME::KeyListMode::Validate); GpgME::Error e = ctx->startKeyListing (mbox.c_str(), true); if (e) { TRACEPOINT; TRETURN; } std::vector keys; GpgME::Error err; do { const auto key = ctx->nextKey(err); if (key.isNull()) { continue; } if (key.isRevoked() || key.isExpired() || key.isDisabled() || key.isInvalid()) { if ((opt.enable_debug & DBG_DATA)) { std::stringstream ss; ss << key; log_data ("%s:%s: Skipping invalid secret key %s", SRCNAME, __func__, ss.str().c_str()); } continue; } if (proto == GpgME::OpenPGP) { log_debug ("%s:%s found pgp skey for addr: \"%s\":%s", SRCNAME, __func__, anonstr (mbox.c_str()), anonstr (key.primaryFingerprint())); KeyCache::instance()->setPgpKeySecret (mbox, key); TRETURN; } if (proto == GpgME::CMS) { log_debug ("%s:%s found cms skey for addr: \"%s\":%s", SRCNAME, __func__, anonstr (mbox.c_str ()), anonstr (key.primaryFingerprint())); KeyCache::instance()->setSmimeKeySecret (mbox, key); TRETURN; } } while (!err); TRETURN; } static DWORD WINAPI do_locate_secret (LPVOID arg) { TSTART; auto args = std::unique_ptr ((LocateArgs *) arg); log_debug ("%s:%s searching secret key for addr: \"%s\"", SRCNAME, __func__, anonstr (args->m_mbox.c_str ())); locate_secret (args->m_mbox.c_str(), GpgME::OpenPGP); if (opt.enable_smime) { locate_secret (args->m_mbox.c_str(), GpgME::CMS); } log_debug ("%s:%s locator sthread thread done", SRCNAME, __func__); TRETURN 0; } void KeyCache::startLocate (const std::vector &addrs, Mail *mail) const { for (const auto &addr: addrs) { startLocate (addr.c_str(), mail); } } void KeyCache::startLocate (const char *addr, Mail *mail) const { TSTART; if (!addr) { TRACEPOINT; TRETURN; } std::string recp = GpgME::UserID::addrSpecFromString (addr); if (recp.empty ()) { TRETURN; } gpgol_lock (&keycache_lock); if (d->m_pgp_key_map.find (recp) == d->m_pgp_key_map.end ()) { // It's enough to look at the PGP Key map. We marked // searched keys there. d->m_pgp_key_map.insert (std::pair (recp, GpgME::Key())); log_debug ("%s:%s Creating a locator thread", SRCNAME, __func__); const auto args = new LocateArgs(recp, mail); HANDLE thread = CreateThread (NULL, 0, do_locate, args, 0, NULL); CloseHandle (thread); } gpgol_unlock (&keycache_lock); TRETURN; } void KeyCache::startLocateSecret (const char *addr, Mail *mail) const { TSTART; if (!addr) { TRACEPOINT; TRETURN; } std::string recp = GpgME::UserID::addrSpecFromString (addr); if (recp.empty ()) { TRETURN; } gpgol_lock (&keycache_lock); if (d->m_pgp_skey_map.find (recp) == d->m_pgp_skey_map.end ()) { // It's enough to look at the PGP Key map. We marked // searched keys there. d->m_pgp_skey_map.insert (std::pair (recp, GpgME::Key())); log_debug ("%s:%s Creating a locator thread", SRCNAME, __func__); const auto args = new LocateArgs(recp, mail); HANDLE thread = CreateThread (NULL, 0, do_locate_secret, (LPVOID) args, 0, NULL); CloseHandle (thread); } gpgol_unlock (&keycache_lock); TRETURN; } void KeyCache::setSmimeKey(const std::string &mbox, const GpgME::Key &key) { d->setSmimeKey(mbox, key); } void KeyCache::setPgpKey(const std::string &mbox, const GpgME::Key &key) { d->setPgpKey(mbox, key); } void KeyCache::setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key) { d->setSmimeKeySecret(mbox, key); } void KeyCache::setPgpKeySecret(const std::string &mbox, const GpgME::Key &key) { d->setPgpKeySecret(mbox, key); } bool KeyCache::isMailResolvable(Mail *mail) { TSTART; /* Get the data from the mail. */ const auto sender = mail->getSender (); auto recps = mail->getCachedRecipients (); if (sender.empty() || recps.empty()) { log_debug ("%s:%s: Mail has no sender or no recipients.", SRCNAME, __func__); TRETURN false; } std::vector encKeys = getEncryptionKeys (recps, GpgME::OpenPGP); if (!encKeys.empty()) { TRETURN true; } if (!opt.enable_smime) { TRETURN false; } /* Check S/MIME instead here we need to include the sender as we can't just generate a key. */ recps.push_back (sender); GpgME::Key sigKey= getSigningKey (sender.c_str(), GpgME::CMS); encKeys = getEncryptionKeys (recps, GpgME::CMS); TRETURN !encKeys.empty() && !sigKey.isNull(); } void KeyCache::update (const char *fpr, GpgME::Protocol proto) { d->update (fpr, proto); } GpgME::Key KeyCache::getByFpr (const char *fpr, bool block) const { return d->getByFpr (fpr, block); } void KeyCache::onUpdateJobDone (const char *fpr, const GpgME::Key &key) { return d->onUpdateJobDone (fpr, key); } void KeyCache::importFromAddrBook (const std::string &mbox, const char *key_data, Mail *mail) const { return d->importFromAddrBook (mbox, key_data, mail); } void KeyCache::onAddrBookImportJobDone (const std::string &mbox, const std::vector &result_fprs) { return d->onAddrBookImportJobDone (mbox, result_fprs); } diff --git a/src/main.c b/src/main.c index 73d8f04..50f5061 100644 --- a/src/main.c +++ b/src/main.c @@ -1,412 +1,413 @@ /* 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; /* For certain operations we need to acquire a log on the logging functions. This lock is controlled by this Mutex. */ HANDLE log_mutex; /* Early initialization of this module. This is done right at startup with only one thread running. Should be called only once. Returns 0 on success. */ static int initialize_main (void) { SECURITY_ATTRIBUTES sa; memset (&sa, 0, sizeof sa); sa.bInheritHandle = FALSE; sa.lpSecurityDescriptor = NULL; sa.nLength = sizeof sa; log_mutex = CreateMutex (&sa, FALSE, NULL); return log_mutex? 0 : -1; } 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); /* Early initializations of our subsystems. */ if (initialize_main ()) return FALSE; } 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\n", (opt.enable_debug & DBG_MEMORY)? " memory":"", (opt.enable_debug & DBG_DATA)? " data":"", (opt.enable_debug & DBG_OOM)? " oom":"" ); 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); 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; } /* Hidden options */ opt.sync_enc = get_conf_bool ("syncEnc", 0); opt.sync_dec = get_conf_bool ("syncDec", 0); } /* Write current options back to the Registry. */ int write_options (void) { struct { const char *name; int mode; int value; char *s_val; } table[] = { {"smimeHtmlWarnShown", 0, opt.smime_html_warn_shown, NULL}, {NULL, 0, 0, NULL} }; char buf[32]; int rc, i; const char *string; for (i=0; table[i].name; i++) { switch (table[i].mode) { case 0: string = table[i].value? "1": "0"; log_debug ("storing option `%s' value=`%s'\n", table[i].name, string); rc = store_extension_value (table[i].name, string); break; case 1: sprintf (buf, "%d", table[i].value); log_debug ("storing option `%s' value=`%s'\n", table[i].name, buf); rc = store_extension_value (table[i].name, buf); break; case 2: string = table[i].s_val? table[i].s_val : ""; log_debug ("storing option `%s' value=`%s'\n", table[i].name, string); rc = store_extension_value (table[i].name, string); break; /* case 3: */ /* buf[0] = '0'; */ /* buf[1] = 0; */ /* switch (opt.default_protocol) */ /* { */ /* case PROTOCOL_UNKNOWN: buf[0] = '0'; /\* auto *\/ break; */ /* case PROTOCOL_OPENPGP: buf[0] = '1'; break; */ /* case PROTOCOL_SMIME: buf[0] = '2'; break; */ /* } */ /* log_debug ("storing option `%s' value=`%s'\n", */ /* table[i].name, buf); */ /* rc = store_extension_value (table[i].name, buf); */ /* break; */ case 4: sprintf (buf, "0x%x", table[i].value); log_debug ("storing option `%s' value=`%s'\n", table[i].name, buf); rc = store_extension_value (table[i].name, buf); break; default: rc = -1; break; } if (rc) log_error ("error storing option `%s': rc = %d\n", table[i].name, rc); } return 0; }