diff --git a/src/addressbook.cpp b/src/addressbook.cpp index 777b04f..80c093d 100644 --- a/src/addressbook.cpp +++ b/src/addressbook.cpp @@ -1,475 +1,521 @@ /* addressbook.cpp - Functions for the Addressbook * 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 "addressbook.h" #include "oomhelp.h" #include "keycache.h" #include "mail.h" #include "cpphelp.h" #include "windowmessages.h" #include "recipient.h" #include #include #include #include typedef struct { std::string name; std::string pgp_data; std::string cms_data; + std::string entry_id; HWND hwnd; shared_disp_t contact; } keyadder_args_t; static std::set s_checked_entries; +static std::vector s_opened_contacts_vec; +GPGRT_LOCK_DEFINE (s_opened_contacts_lock); static Addressbook::callback_args_t parse_output (const std::string &output) { Addressbook::callback_args_t ret; std::istringstream ss(output); std::string line; std::string pgp_data; std::string cms_data; bool in_pgp_data = false; bool in_options = false; bool in_cms_data = false; while (std::getline (ss, line)) { rtrim (line); if (in_pgp_data) { if (line == "empty") { pgp_data = ""; } else if (line != "END KEYADDER PGP DATA") { pgp_data += line + std::string("\n"); } else { in_pgp_data = false; } } else if (in_cms_data) { if (line == "empty") { in_cms_data = false; cms_data = ""; } else if (line != "END KEYADDER CMS DATA") { cms_data += line + std::string("\n"); } else { in_cms_data = false; } } else if (in_options) { if (line == "END KEYADDER OPTIONS") { in_options = false; continue; } std::istringstream lss (line); std::string key, value; std::getline (lss, key, '='); std::getline (lss, value, '='); if (key == "secure") { int val = atoi (value.c_str()); if (val > 3 || val < 0) { log_error ("%s:%s: Loading secure value: %s failed", SRCNAME, __func__, value.c_str ()); continue; } ret.crypto_flags = val; } else { log_debug ("%s:%s: Unknown setting: %s", SRCNAME, __func__, key.c_str ()); } continue; } else { if (line == "BEGIN KEYADDER OPTIONS") { in_options = true; } else if (line == "BEGIN KEYADDER CMS DATA") { in_cms_data = true; } else if (line == "BEGIN KEYADDER PGP DATA") { in_pgp_data = true; } else { log_debug ("%s:%s: Unknown line: %s", SRCNAME, __func__, line.c_str ()); } } } ret.pgp_data = xstrdup (pgp_data.c_str ()); ret.cms_data = xstrdup (cms_data.c_str ()); return ret; } static DWORD WINAPI open_keyadder (LPVOID arg) { TSTART; auto adder_args = std::unique_ptr ((keyadder_args_t*) arg); std::vector args; // Collect the arguments char *gpg4win_dir = get_gpg4win_dir (); if (!gpg4win_dir) { TRACEPOINT; TRETURN -1; } const auto keyadder = std::string (gpg4win_dir) + "\\bin\\gpgolkeyadder.exe"; args.push_back (keyadder); args.push_back (std::string ("--hwnd")); args.push_back (std::to_string ((int) (intptr_t) adder_args->hwnd)); args.push_back (std::string ("--username")); args.push_back (adder_args->name); if (opt.enable_smime) { args.push_back (std::string ("--cms")); } auto ctx = GpgME::Context::createForEngine (GpgME::SpawnEngine); if (!ctx) { // can't happen TRACEPOINT; TRETURN -1; } std::string input = adder_args->pgp_data; input += "BEGIN CMS DATA\n"; input += adder_args->cms_data; GpgME::Data mystdin (input.c_str(), input.size(), false); GpgME::Data mystdout, mystderr; char **cargs = vector_to_cArray (args); log_data ("%s:%s: launching keyadder args:", SRCNAME, __func__); for (size_t i = 0; cargs && cargs[i]; i++) { log_data (SIZE_T_FORMAT ": '%s'", i, cargs[i]); } GpgME::Error err = ctx->spawn (cargs[0], const_cast (cargs), mystdin, mystdout, mystderr, (GpgME::Context::SpawnFlags) ( GpgME::Context::SpawnAllowSetFg | GpgME::Context::SpawnShowWindow)); release_cArray (cargs); + if (!adder_args->entry_id.empty ()) + { + log_dbg ("Removing entry from list of opened contacts."); + gpgrt_lock_lock (&s_opened_contacts_lock); + const auto id = adder_args->entry_id; + std::remove_if (s_opened_contacts_vec.begin(), + s_opened_contacts_vec.end (), + [id] (const auto &val) { + return val == id; + }); + gpgrt_lock_unlock (&s_opened_contacts_lock); + } + if (err) { log_error ("%s:%s: Err code: %i asString: %s", SRCNAME, __func__, err.code(), err.asString()); TRETURN 0; } auto output = mystdout.toString (); rtrim(output); if (output.empty()) { log_debug ("%s:%s: keyadder canceled.", SRCNAME, __func__); TRETURN 0; } Addressbook::callback_args_t cb_args = parse_output (output); cb_args.contact = adder_args->contact; do_in_ui_thread (CONFIG_KEY_DONE, (void*) &cb_args); xfree (cb_args.pgp_data); xfree (cb_args.cms_data); TRETURN 0; } void Addressbook::update_key_o (void *callback_args) { TSTART; if (!callback_args) { TRACEPOINT; TRETURN; } callback_args_t *cb_args = static_cast (callback_args); LPDISPATCH contact = cb_args->contact.get(); LPDISPATCH user_props = get_oom_object (contact, "UserProperties"); if (!user_props) { TRACEPOINT; TRETURN; } LPDISPATCH pgp_key = find_or_add_text_prop (user_props, "OpenPGP Key"); if (!pgp_key) { TRACEPOINT; gpgol_release (user_props); TRETURN; } put_oom_string (pgp_key, "Value", cb_args->pgp_data); gpgol_release (pgp_key); log_debug ("%s:%s: PGP key data updated", SRCNAME, __func__); if (opt.enable_smime) { LPDISPATCH cms_data = find_or_add_text_prop (user_props, "GpgOL CMS Cert"); if (!cms_data) { TRACEPOINT; gpgol_release (user_props); TRETURN; } put_oom_string (cms_data, "Value", cb_args->cms_data); gpgol_release (cms_data); log_debug ("%s:%s: CMS key data updated", SRCNAME, __func__); } gpgol_release (user_props); s_checked_entries.clear (); TRETURN; } void Addressbook::edit_key_o (LPDISPATCH contact) { TSTART; if (!contact) { TRACEPOINT; TRETURN; } + /* First check that we have not already opened an editor. + This can happen when starting the editor takes a while + and the user clicks multiple times because nothing + happens. */ + const auto entry_id = get_oom_string_s (contact, "EntryID"); + + if (!entry_id.empty()) + { + gpgrt_lock_lock (&s_opened_contacts_lock); + if (std::find (s_opened_contacts_vec.begin (), + s_opened_contacts_vec.end (), + entry_id) != s_opened_contacts_vec.end ()) + { + log_dbg ("Contact already opened."); + /* TODO: Find the window if it exists and bring it to front. */ + gpgrt_lock_unlock (&s_opened_contacts_lock); + TRETURN; + } + gpgrt_lock_unlock (&s_opened_contacts_lock); + } + else + { + log_dbg ("Empty EntryID."); + } auto user_props = MAKE_SHARED (get_oom_object (contact, "UserProperties")); if (!user_props) { TRACEPOINT; TRETURN; } auto pgp_key = MAKE_SHARED (find_or_add_text_prop (user_props.get (), "OpenPGP Key")); if (!pgp_key) { TRACEPOINT; TRETURN; } char *key_data = get_oom_string (pgp_key.get(), "Value"); if (!key_data) { TRACEPOINT; TRETURN; } char *cms_data = nullptr; if (opt.enable_smime) { auto cms_key = MAKE_SHARED (find_or_add_text_prop (user_props.get (), "GpgOL CMS Cert")); cms_data = get_oom_string (cms_key.get(), "Value"); if (!cms_data) { TRACEPOINT; TRETURN; } } + /* Insert into our vec of opened contacts. */ + gpgrt_lock_lock (&s_opened_contacts_lock); + s_opened_contacts_vec.push_back (entry_id); + gpgrt_lock_unlock (&s_opened_contacts_lock); + char *name = get_oom_string (contact, "Subject"); if (!name) { TRACEPOINT; name = get_oom_string (contact, "Email1Address"); if (!name) { name = xstrdup (/* TRANSLATORS: Placeholder for a contact without a configured name */ _("Unknown contact")); } } keyadder_args_t *args = new keyadder_args_t; args->name = name; args->pgp_data = key_data; args->cms_data = cms_data ? cms_data : ""; args->hwnd = get_active_hwnd (); contact->AddRef (); memdbg_addRef (contact); args->contact = MAKE_SHARED (contact); + args->entry_id = entry_id; CloseHandle (CreateThread (NULL, 0, open_keyadder, (LPVOID) args, 0, NULL)); xfree (name); xfree (key_data); xfree (cms_data); TRETURN; } /* For each new recipient check the address book to look for a potentially configured key for this recipient and import / register it into the keycache. */ void Addressbook::check_o (Mail *mail) { TSTART; if (!mail) { TRACEPOINT; TRETURN; } LPDISPATCH mailitem = mail->item (); if (!mailitem) { TRACEPOINT; TRETURN; } auto recipients_obj = MAKE_SHARED (get_oom_object (mailitem, "Recipients")); if (!recipients_obj) { TRACEPOINT; TRETURN; } bool err = false; const auto recipient_entries = get_oom_recipients_with_addrEntry (recipients_obj.get(), &err); for (const auto pair: recipient_entries) { const auto mbox = pair.first.mbox (); if (s_checked_entries.find (mbox) != s_checked_entries.end ()) { continue; } if (!pair.second) { TRACEPOINT; continue; } auto contact = MAKE_SHARED (get_oom_object (pair.second.get (), "GetContact")); if (!contact) { log_debug ("%s:%s: failed to resolve contact for %s", SRCNAME, __func__, anonstr (mbox.c_str())); continue; } s_checked_entries.insert (mbox); LPDISPATCH user_props = get_oom_object (contact.get (), "UserProperties"); if (!user_props) { TRACEPOINT; continue; } LPDISPATCH pgp_key = find_or_add_text_prop (user_props, "OpenPGP Key"); LPDISPATCH cms_prop = nullptr; if (opt.enable_smime) { cms_prop = find_or_add_text_prop (user_props, "GpgOL CMS Cert"); } gpgol_release (user_props); if (!pgp_key && !cms_prop) { continue; } log_debug ("%s:%s: found configured key for %s", SRCNAME, __func__, anonstr (mbox.c_str())); char *pgp_data = get_oom_string (pgp_key, "Value"); char *cms_data = nullptr; if (cms_prop) { cms_data = get_oom_string (cms_prop, "Value"); } if ((!pgp_data || !strlen (pgp_data)) && (!cms_data || !strlen (cms_data))) { log_debug ("%s:%s: No key data", SRCNAME, __func__); } if (pgp_data && strlen (pgp_data)) { KeyCache::instance ()->importFromAddrBook (mbox, pgp_data, mail, GpgME::OpenPGP); } if (cms_data && strlen (cms_data)) { KeyCache::instance ()->importFromAddrBook (mbox, cms_data, mail, GpgME::CMS); } xfree (pgp_data); xfree (cms_data); gpgol_release (pgp_key); gpgol_release (cms_prop); } TRETURN; }