From 1979fd80424d16b2e489f9b57d01d9c7811d25a2 Mon Sep 17 00:00:00 2001 From: dartraiden Date: Mon, 2 Jan 2023 21:10:29 +0300 Subject: Update copyrights --- plugins/New_GPG/src/globals.h | 84 +- plugins/New_GPG/src/gpg_wrapper.cpp | 336 ++-- plugins/New_GPG/src/gpg_wrapper.h | 132 +- plugins/New_GPG/src/icons.cpp | 110 +- plugins/New_GPG/src/init.cpp | 482 +++--- plugins/New_GPG/src/jabber_account.h | 104 +- plugins/New_GPG/src/log.cpp | 98 +- plugins/New_GPG/src/log.h | 64 +- plugins/New_GPG/src/main.cpp | 1308 ++++++++-------- plugins/New_GPG/src/messages.cpp | 1576 +++++++++---------- plugins/New_GPG/src/metacontacts.cpp | 58 +- plugins/New_GPG/src/metacontacts.h | 42 +- plugins/New_GPG/src/options.cpp | 2310 +++++++++++++-------------- plugins/New_GPG/src/options.h | 42 +- plugins/New_GPG/src/srmm.cpp | 156 +- plugins/New_GPG/src/stdafx.cxx | 36 +- plugins/New_GPG/src/stdafx.h | 196 +-- plugins/New_GPG/src/ui.cpp | 1804 ++++++++++----------- plugins/New_GPG/src/ui.h | 136 +- plugins/New_GPG/src/utilities.cpp | 2856 +++++++++++++++++----------------- plugins/New_GPG/src/utilities.h | 108 +- plugins/New_GPG/src/version.h | 2 +- 22 files changed, 6020 insertions(+), 6020 deletions(-) (limited to 'plugins/New_GPG') diff --git a/plugins/New_GPG/src/globals.h b/plugins/New_GPG/src/globals.h index 02b1607b6d..ac64da8ecd 100644 --- a/plugins/New_GPG/src/globals.h +++ b/plugins/New_GPG/src/globals.h @@ -1,42 +1,42 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef GLOBALS_H -#define GLOBALS_H - -struct contact_data -{ - list msgs_to_send;// msgs_to_pass; - string key_in_prescense; -}; - -struct globals_s -{ - bool bDecryptFiles = false; - CMStringW wszInopentag, wszInclosetag, wszOutopentag, wszOutclosetag, wszPassword; - wchar_t key_id_global[17] = { 0 }; - list Accounts; - HFONT bold_font = nullptr; - logtofile debuglog; - bool gpg_valid = false, gpg_keyexist = false; - std::map hcontact_data; - bool _terminate; -}; - -extern globals_s globals; - - -#endif +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef GLOBALS_H +#define GLOBALS_H + +struct contact_data +{ + list msgs_to_send;// msgs_to_pass; + string key_in_prescense; +}; + +struct globals_s +{ + bool bDecryptFiles = false; + CMStringW wszInopentag, wszInclosetag, wszOutopentag, wszOutclosetag, wszPassword; + wchar_t key_id_global[17] = { 0 }; + list Accounts; + HFONT bold_font = nullptr; + logtofile debuglog; + bool gpg_valid = false, gpg_keyexist = false; + std::map hcontact_data; + bool _terminate; +}; + +extern globals_s globals; + + +#endif diff --git a/plugins/New_GPG/src/gpg_wrapper.cpp b/plugins/New_GPG/src/gpg_wrapper.cpp index 6b719e1e5e..8d12134c7c 100644 --- a/plugins/New_GPG/src/gpg_wrapper.cpp +++ b/plugins/New_GPG/src/gpg_wrapper.cpp @@ -1,168 +1,168 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -namespace bp = boost::process; - -gpg_execution_params::gpg_execution_params() -{ } - -gpg_execution_params::~gpg_execution_params() -{ } - -void pxEexcute_thread(gpg_execution_params *params) -{ - if (!globals.gpg_valid) - return; - - CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath")); - if (_waccess(bin_path, 0)) { - if (globals.debuglog) - globals.debuglog << "GPG executable not found"; - params->result = pxNotFound; - return; - } - - bp::environment env = boost::this_process::environment(); - env.set("LANGUAGE", "en@quot"); - env.set("LC_ALL", "English"); - env.set("LANG", "C"); - - std::vector argv; - CMStringW home_dir(g_plugin.getMStringW("szHomePath")); - if (!home_dir.IsEmpty()) { // this check are required for first run gpg binary validation - argv.push_back(L"--homedir"); - argv.push_back(home_dir.c_str()); - } - - argv.push_back(L"--display-charset"); - argv.push_back(L"utf-8"); - argv.push_back(L"-z9"); - argv.insert(argv.end(), params->aargv.begin(), params->aargv.end()); - - if (globals.debuglog) { - std::wstring args; - for (unsigned int i = 0; i < argv.size(); ++i) { - args += argv[i]; - args += L" "; - } - args.erase(args.size() - 1, 1); - - globals.debuglog << "gpg in: " << toUTF8(args); - } - - params->out.Empty(); - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - - bp::child *c; - std::future pout, perr; - boost::asio::io_context ios; - if (params->bNoOutput) - c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), ios); - else - c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), bp::std_out > pout, bp::std_err > perr, ios); - - params->child = c; - - ios.run(); - c->wait(); - - if (!params->bNoOutput) { - std::string s = pout.get(); - if (!s.empty()) - params->out.Append(s.c_str()); - - s = perr.get(); - if (!s.empty()) - params->out.Append(s.c_str()); - - params->out.Replace("\r\n", "\n"); - params->out.Replace("\r\r", ""); - - if (globals.debuglog) - globals.debuglog << "gpg out: " << params->out.c_str(); - } - - params->child = nullptr; - params->code = c->exit_code(); - delete c; - - if (params->code) { - if (globals.debuglog) - globals.debuglog << ": warning: wrong gpg exit status, gpg output: " << params->out.c_str(); - params->result = pxSuccessExitCodeInvalid; - } - else params->result = pxSuccess; -} - -bool gpg_launcher(gpg_execution_params ¶ms, boost::posix_time::time_duration t) -{ - bool ret = true; - HANDLE hThread = mir_forkThread(pxEexcute_thread, ¶ms); - if (WaitForSingleObject(hThread, t.total_milliseconds()) == WAIT_TIMEOUT) { - ret = false; - if (params.child) - params.child->terminate(); - if (globals.debuglog) - globals.debuglog << "GPG execution timed out, aborted"; - } - return ret; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void pxEexcute_passwd_change_thread(gpg_execution_params_pass *params) -{ - if (!globals.gpg_valid) { - params->result = pxNotConfigured; - return; - } - - CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath")); - if (_waccess(bin_path, 0)) { - if (globals.debuglog) - globals.debuglog << "GPG executable not found"; - params->result = pxNotFound; - return; - } - - bp::environment env = boost::this_process::environment(); - env.set("LANGUAGE", "en@quot"); - env.set("LC_ALL", "English"); - - std::vector argv; - - argv.push_back(bin_path.c_str()); - argv.push_back(L"--homedir"); - argv.push_back(g_plugin.getMStringW("szHomePath").c_str()); - argv.push_back(L"--display-charset"); - argv.push_back(L"utf-8"); - argv.push_back(L"-z9"); - argv.insert(argv.end(), params->aargv.begin(), params->aargv.end()); - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - - bp::child c(bin_path.c_str(), argv, env, boost::process::windows::hide); - params->child = &c; - - c.wait(); - - params->child = nullptr; -} +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +namespace bp = boost::process; + +gpg_execution_params::gpg_execution_params() +{ } + +gpg_execution_params::~gpg_execution_params() +{ } + +void pxEexcute_thread(gpg_execution_params *params) +{ + if (!globals.gpg_valid) + return; + + CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath")); + if (_waccess(bin_path, 0)) { + if (globals.debuglog) + globals.debuglog << "GPG executable not found"; + params->result = pxNotFound; + return; + } + + bp::environment env = boost::this_process::environment(); + env.set("LANGUAGE", "en@quot"); + env.set("LC_ALL", "English"); + env.set("LANG", "C"); + + std::vector argv; + CMStringW home_dir(g_plugin.getMStringW("szHomePath")); + if (!home_dir.IsEmpty()) { // this check are required for first run gpg binary validation + argv.push_back(L"--homedir"); + argv.push_back(home_dir.c_str()); + } + + argv.push_back(L"--display-charset"); + argv.push_back(L"utf-8"); + argv.push_back(L"-z9"); + argv.insert(argv.end(), params->aargv.begin(), params->aargv.end()); + + if (globals.debuglog) { + std::wstring args; + for (unsigned int i = 0; i < argv.size(); ++i) { + args += argv[i]; + args += L" "; + } + args.erase(args.size() - 1, 1); + + globals.debuglog << "gpg in: " << toUTF8(args); + } + + params->out.Empty(); + + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + + bp::child *c; + std::future pout, perr; + boost::asio::io_context ios; + if (params->bNoOutput) + c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), ios); + else + c = new bp::child(bin_path.c_str(), argv, bp::windows::hide, bp::std_in.close(), bp::std_out > pout, bp::std_err > perr, ios); + + params->child = c; + + ios.run(); + c->wait(); + + if (!params->bNoOutput) { + std::string s = pout.get(); + if (!s.empty()) + params->out.Append(s.c_str()); + + s = perr.get(); + if (!s.empty()) + params->out.Append(s.c_str()); + + params->out.Replace("\r\n", "\n"); + params->out.Replace("\r\r", ""); + + if (globals.debuglog) + globals.debuglog << "gpg out: " << params->out.c_str(); + } + + params->child = nullptr; + params->code = c->exit_code(); + delete c; + + if (params->code) { + if (globals.debuglog) + globals.debuglog << ": warning: wrong gpg exit status, gpg output: " << params->out.c_str(); + params->result = pxSuccessExitCodeInvalid; + } + else params->result = pxSuccess; +} + +bool gpg_launcher(gpg_execution_params ¶ms, boost::posix_time::time_duration t) +{ + bool ret = true; + HANDLE hThread = mir_forkThread(pxEexcute_thread, ¶ms); + if (WaitForSingleObject(hThread, t.total_milliseconds()) == WAIT_TIMEOUT) { + ret = false; + if (params.child) + params.child->terminate(); + if (globals.debuglog) + globals.debuglog << "GPG execution timed out, aborted"; + } + return ret; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void pxEexcute_passwd_change_thread(gpg_execution_params_pass *params) +{ + if (!globals.gpg_valid) { + params->result = pxNotConfigured; + return; + } + + CMStringW bin_path(g_plugin.getMStringW("szGpgBinPath")); + if (_waccess(bin_path, 0)) { + if (globals.debuglog) + globals.debuglog << "GPG executable not found"; + params->result = pxNotFound; + return; + } + + bp::environment env = boost::this_process::environment(); + env.set("LANGUAGE", "en@quot"); + env.set("LC_ALL", "English"); + + std::vector argv; + + argv.push_back(bin_path.c_str()); + argv.push_back(L"--homedir"); + argv.push_back(g_plugin.getMStringW("szHomePath").c_str()); + argv.push_back(L"--display-charset"); + argv.push_back(L"utf-8"); + argv.push_back(L"-z9"); + argv.insert(argv.end(), params->aargv.begin(), params->aargv.end()); + + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + + bp::child c(bin_path.c_str(), argv, env, boost::process::windows::hide); + params->child = &c; + + c.wait(); + + params->child = nullptr; +} diff --git a/plugins/New_GPG/src/gpg_wrapper.h b/plugins/New_GPG/src/gpg_wrapper.h index fbf5f1a62a..fc4ec1db8a 100644 --- a/plugins/New_GPG/src/gpg_wrapper.h +++ b/plugins/New_GPG/src/gpg_wrapper.h @@ -1,67 +1,67 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -#ifndef GPG_WRAPPER_H -#define GPG_WRAPPER_H -enum pxResult -{ - pxSuccess, - pxSuccessExitCodeInvalid, - pxCreatePipeFailed, - pxDuplicateHandleFailed, - pxCloseHandleFailed, - pxCreateProcessFailed, - pxThreadWaitFailed, - pxReadFileFailed, - pxBufferOverflow, - pxNotFound, - pxNotConfigured -}; - -struct gpg_execution_params -{ - gpg_execution_params(); - ~gpg_execution_params(); - - std::vector aargv; - CMStringA out; - uint32_t code = 0; - int bNoOutput = false; - pxResult result = pxSuccess; - boost::process::child *child = nullptr; - - __forceinline void addParam(const std::wstring ¶m) - { aargv.push_back(param); - } -}; - -struct gpg_execution_params_pass : public gpg_execution_params -{ - string &old_pass, &new_pass; - - gpg_execution_params_pass(std::string &o, std::string &n): - old_pass(o), - new_pass(n) - { - } -}; - - -bool gpg_launcher(gpg_execution_params ¶ms, boost::posix_time::time_duration t = boost::posix_time::seconds(10)); -void __cdecl pxEexcute_passwd_change_thread(gpg_execution_params_pass *param); - +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +#ifndef GPG_WRAPPER_H +#define GPG_WRAPPER_H +enum pxResult +{ + pxSuccess, + pxSuccessExitCodeInvalid, + pxCreatePipeFailed, + pxDuplicateHandleFailed, + pxCloseHandleFailed, + pxCreateProcessFailed, + pxThreadWaitFailed, + pxReadFileFailed, + pxBufferOverflow, + pxNotFound, + pxNotConfigured +}; + +struct gpg_execution_params +{ + gpg_execution_params(); + ~gpg_execution_params(); + + std::vector aargv; + CMStringA out; + uint32_t code = 0; + int bNoOutput = false; + pxResult result = pxSuccess; + boost::process::child *child = nullptr; + + __forceinline void addParam(const std::wstring ¶m) + { aargv.push_back(param); + } +}; + +struct gpg_execution_params_pass : public gpg_execution_params +{ + string &old_pass, &new_pass; + + gpg_execution_params_pass(std::string &o, std::string &n): + old_pass(o), + new_pass(n) + { + } +}; + + +bool gpg_launcher(gpg_execution_params ¶ms, boost::posix_time::time_duration t = boost::posix_time::seconds(10)); +void __cdecl pxEexcute_passwd_change_thread(gpg_execution_params_pass *param); + #endif \ No newline at end of file diff --git a/plugins/New_GPG/src/icons.cpp b/plugins/New_GPG/src/icons.cpp index a3c7614803..9935766781 100644 --- a/plugins/New_GPG/src/icons.cpp +++ b/plugins/New_GPG/src/icons.cpp @@ -1,55 +1,55 @@ -// Copyright © 2010-22 SecureIM developers (baloo and others), sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -static IconItem iconList[] = -{ - { "Secured", "secured", IDI_SECURED }, - { "Unsecured", "unsecured", IDI_UNSECURED } -}; - -void InitIconLib() -{ - g_plugin.registerIcon(MODULENAME, iconList); -} - -HANDLE IconLibHookIconsChanged(MIRANDAHOOK hook) -{ - return HookEvent(ME_SKIN_ICONSCHANGED, hook); -} - -void setSrmmIcon(MCONTACT h) -{ - MCONTACT hContact = db_mc_isMeta(h) ? metaGetMostOnline(h) : h; - bool enabled = isContactSecured(hContact); - MCONTACT hMC = db_mc_tryMeta(hContact); - - int flags = enabled ? 0 : MBF_HIDDEN; - Srmm_SetIconFlags(hContact, MODULENAME, 1, flags); - if (hMC != hContact) - Srmm_SetIconFlags(hMC, MODULENAME, 1, flags); - - flags = enabled ? MBF_HIDDEN : 0; - Srmm_SetIconFlags(hContact, MODULENAME, 2, flags); - if (hMC != hContact) - Srmm_SetIconFlags(hMC, MODULENAME, 2, flags); - - const char *szIconId = (enabled) ? "secured" : "unsecured"; - ExtraIcon_SetIconByName(g_plugin.hCLIcon, hContact, szIconId); - if (hMC != hContact) - ExtraIcon_SetIconByName(g_plugin.hCLIcon, hMC, szIconId); -} +// Copyright © 2010-23 SecureIM developers (baloo and others), sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +static IconItem iconList[] = +{ + { "Secured", "secured", IDI_SECURED }, + { "Unsecured", "unsecured", IDI_UNSECURED } +}; + +void InitIconLib() +{ + g_plugin.registerIcon(MODULENAME, iconList); +} + +HANDLE IconLibHookIconsChanged(MIRANDAHOOK hook) +{ + return HookEvent(ME_SKIN_ICONSCHANGED, hook); +} + +void setSrmmIcon(MCONTACT h) +{ + MCONTACT hContact = db_mc_isMeta(h) ? metaGetMostOnline(h) : h; + bool enabled = isContactSecured(hContact); + MCONTACT hMC = db_mc_tryMeta(hContact); + + int flags = enabled ? 0 : MBF_HIDDEN; + Srmm_SetIconFlags(hContact, MODULENAME, 1, flags); + if (hMC != hContact) + Srmm_SetIconFlags(hMC, MODULENAME, 1, flags); + + flags = enabled ? MBF_HIDDEN : 0; + Srmm_SetIconFlags(hContact, MODULENAME, 2, flags); + if (hMC != hContact) + Srmm_SetIconFlags(hMC, MODULENAME, 2, flags); + + const char *szIconId = (enabled) ? "secured" : "unsecured"; + ExtraIcon_SetIconByName(g_plugin.hCLIcon, hContact, szIconId); + if (hMC != hContact) + ExtraIcon_SetIconByName(g_plugin.hCLIcon, hMC, szIconId); +} diff --git a/plugins/New_GPG/src/init.cpp b/plugins/New_GPG/src/init.cpp index cbcf08c80b..4c4406a7ef 100644 --- a/plugins/New_GPG/src/init.cpp +++ b/plugins/New_GPG/src/init.cpp @@ -1,241 +1,241 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -int GpgOptInit(WPARAM, LPARAM); -int OnPreBuildContactMenu(WPARAM, LPARAM); - -INT_PTR RecvMsgSvc(WPARAM, LPARAM); -INT_PTR SendMsgSvc(WPARAM, LPARAM); -INT_PTR onSendFile(WPARAM, LPARAM); - -int HookSendMsg(WPARAM, LPARAM); -int GetJabberInterface(WPARAM, LPARAM); -int onProtoAck(WPARAM, LPARAM); -int onWindowEvent(WPARAM, LPARAM); -int onIconPressed(WPARAM, LPARAM); -int onExtraIconPressed(WPARAM, LPARAM, LPARAM); - -void InitCheck(); -void FirstRun(); -void RemoveHandlers(); - -// global variables -CMPlugin g_plugin; - -///////////////////////////////////////////////////////////////////////////////////////// - -PLUGININFOEX pluginInfoEx = { - sizeof(PLUGININFOEX), - __PLUGIN_NAME, - PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), - __DESCRIPTION, - __AUTHOR, - __COPYRIGHT, - __AUTHORWEB, - UNICODE_AWARE, - // {4227c050-8d97-48d2-91ec-6a952b3dab94} - { 0x4227c050, 0x8d97, 0x48d2, { 0x91, 0xec, 0x6a, 0x95, 0x2b, 0x3d, 0xab, 0x94 } } -}; - -CMPlugin::CMPlugin() : - PLUGIN(MODULENAME, pluginInfoEx), - bDebugLog(MODULENAME, "bDebugLog", false), - bJabberAPI(MODULENAME, "bJabberAPI", true), - bStripTags(MODULENAME, "bStripTags", false), - bAppendTags(MODULENAME, "bAppendTags", false), - bSameAction(MODULENAME, "bSameAction", false), - bAutoExchange(MODULENAME, "bAutoExchange", false), - bFileTransfers(MODULENAME, "bFileTransfers", false), - bPresenceSigning(MODULENAME, "bPresenceSigning", false), - bSendErrorMessages(MODULENAME, "bSendErrorMessages", false) -{ -} - -///////////////////////////////////////////////////////////////////////////////////////// - -INT_PTR LoadKey(WPARAM, LPARAM); -INT_PTR SendKey(WPARAM, LPARAM); -INT_PTR ExportGpGKeys(WPARAM, LPARAM); -INT_PTR ImportGpGKeys(WPARAM, LPARAM); -INT_PTR ToggleEncryption(WPARAM, LPARAM); - -void InitIconLib(); - -void init_vars() -{ - globals.wszInopentag = g_plugin.getMStringW("szInOpenTag", L""); - globals.wszInclosetag = g_plugin.getMStringW("szInCloseTag", L""); - globals.wszOutopentag = g_plugin.getMStringW("szOutOpenTag", L""); - globals.wszOutclosetag = g_plugin.getMStringW("szOutCloseTag", L""); - globals.wszPassword = g_plugin.getMStringW("szKeyPassword"); - globals.bold_font = CreateFont(14, 0, 0, 0, 600, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, L"Arial"); - - globals.debuglog.init(); -} - -static int OnModulesLoaded(WPARAM, LPARAM) -{ - FirstRun(); - if (!g_plugin.getByte("FirstRun", 1)) - InitCheck(); - - StatusIconData sid = {}; - sid.szModule = MODULENAME; - sid.flags = MBF_HIDDEN; - sid.dwId = 0x00000001; - sid.hIcon = IcoLib_GetIcon("secured"); - sid.szTooltip.a = LPGEN("GPG Turn off encryption"); - Srmm_AddIcon(&sid, &g_plugin); - - sid.dwId = 0x00000002; - sid.hIcon = IcoLib_GetIcon("unsecured"); - sid.szTooltip.a = LPGEN("GPG Turn on encryption"); - Srmm_AddIcon(&sid, &g_plugin); - - if (g_plugin.bJabberAPI) { - GetJabberInterface(0, 0); - HookEvent(ME_PROTO_ACCLISTCHANGED, GetJabberInterface); - } - - HookEvent(ME_MSG_WINDOWEVENT, onWindowEvent); - HookEvent(ME_MSG_ICONPRESSED, onIconPressed); - - Proto_RegisterModule(PROTOTYPE_ENCRYPTION, MODULENAME); - - CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, RecvMsgSvc); - CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE, SendMsgSvc); - CreateProtoServiceFunction(MODULENAME, PSS_FILE, onSendFile); - clean_temp_dir(); - return 0; -} - -static int OnShutdown(WPARAM, LPARAM) -{ - RemoveHandlers(); - return 0; -} - -static INT_PTR EventGetIcon(WPARAM flags, LPARAM) -{ - HICON hIcon = g_plugin.getIcon(IDI_SECURED); - return (INT_PTR)((flags & LR_SHARED) ? hIcon : CopyIcon(hIcon)); -} - -static INT_PTR GetEventText(WPARAM pEvent, LPARAM datatype) -{ - DBEVENTINFO *dbei = (DBEVENTINFO *)pEvent; - ptrW wszText(mir_utf8decodeW((char *)dbei->pBlob)); - return (datatype != DBVT_WCHAR) ? (INT_PTR)mir_u2a(wszText) : (INT_PTR)wszText.detach(); -} - -int CMPlugin::Load() -{ - DBEVENTTYPEDESCR dbEventType = {}; - dbEventType.module = MODULENAME; - dbEventType.descr = "GPG service event"; - dbEventType.iconService = MODULENAME "/GetEventIcon"; - dbEventType.textService = MODULENAME "/GetEventText"; - DbEvent_RegisterType(&dbEventType); - - CreateServiceFunction(dbEventType.iconService, &EventGetIcon); - CreateServiceFunction(dbEventType.textService, &GetEventText); - - HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildContactMenu); - HookEvent(ME_DB_EVENT_FILTER_ADD, HookSendMsg); - HookEvent(ME_OPT_INITIALISE, GpgOptInit); - HookEvent(ME_PROTO_ACK, onProtoAck); - HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); - HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown); - - InitIconLib(); - init_vars(); - - //////////////////////////////////////////////////////////////////////////////////////// - // Comtact menu items - - CMenuItem mi(&g_plugin); - mi.hIcolibItem = g_plugin.getIconHandle(IDI_SECURED); - - SET_UID(mi, 0xbd22e3f8, 0xc19c, 0x45a8, 0xb7, 0x37, 0x6b, 0x3b, 0x27, 0xf0, 0x8c, 0xbb); - mi.position = -0x7FFFFFFF; - mi.name.a = LPGEN("Load public GPG key"); - mi.pszService = "/LoadPubKey"; - Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, LoadKey); - - SET_UID(mi, 0xc8008193, 0x56a9, 0x414a, 0x82, 0x98, 0x78, 0xe8, 0xa8, 0x84, 0x20, 0x67); - mi.position = -0x7FFFFFFe; - mi.name.a = LPGEN("Toggle GPG encryption"); - mi.pszService = "/ToggleEncryption"; - g_plugin.hToggleEncryption = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, ToggleEncryption); - - SET_UID(mi, 0x42bb535f, 0xd58e, 0x4edb, 0xbf, 0x2c, 0xfa, 0x9a, 0xbf, 0x1e, 0xb8, 0x69); - mi.position = -0x7FFFFFFd; - mi.name.a = LPGEN("Send public key"); - mi.pszService = "/SendKey"; - g_plugin.hSendKey = Menu_AddContactMenuItem(&mi); - CreateServiceFunction(mi.pszService, SendKey); - - //////////////////////////////////////////////////////////////////////////////////////// - // Main menu items - - SET_UID(mi, 0x0bac023bb, 0xd2e, 0x46e0, 0x93, 0x13, 0x7c, 0xf9, 0xf6, 0xb5, 0x02, 0xd1); - mi.position = -0x7FFFFFFe; - mi.name.a = "GPG"; - mi.root = Menu_AddMainMenuItem(&mi); - mi.flags = CMIF_UNMOVABLE; - - SET_UID(mi, 0x33a204b2, 0xe3c0, 0x413b, 0xbf, 0xd8, 0x8b, 0x2e, 0x3d, 0xa0, 0xef, 0xa4); - mi.position = -0x7FFFFFFe; - mi.name.a = LPGEN("Export GPG Public keys"); - mi.pszService = "/ExportGPGKeys"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, ExportGpGKeys); - - SET_UID(mi, 0x627fcfc1, 0x4e60, 0x4428, 0xaf, 0x96, 0x11, 0x42, 0x24, 0xeb, 0x07, 0xea); - mi.position = -0x7FFFFFFF; - mi.name.a = LPGEN("Import GPG Public keys"); - mi.pszService = "/ImportGPGKeys"; - Menu_AddMainMenuItem(&mi); - CreateServiceFunction(mi.pszService, ImportGpGKeys); - - //////////////////////////////////////////////////////////////////////////////////////// - // Extra icon - - hCLIcon = ExtraIcon_RegisterIcolib(MODULENAME, Translate("GPG encryption status"), "secured", &onExtraIconPressed); - for (auto &cc : Contacts()) - if (isContactHaveKey(cc)) - setSrmmIcon(cc); - - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -extern list transfers; - -int CMPlugin::Unload() -{ - for (auto p : transfers) - if (!p.empty()) - boost::filesystem::remove(p); - - clean_temp_dir(); - return 0; -} +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +int GpgOptInit(WPARAM, LPARAM); +int OnPreBuildContactMenu(WPARAM, LPARAM); + +INT_PTR RecvMsgSvc(WPARAM, LPARAM); +INT_PTR SendMsgSvc(WPARAM, LPARAM); +INT_PTR onSendFile(WPARAM, LPARAM); + +int HookSendMsg(WPARAM, LPARAM); +int GetJabberInterface(WPARAM, LPARAM); +int onProtoAck(WPARAM, LPARAM); +int onWindowEvent(WPARAM, LPARAM); +int onIconPressed(WPARAM, LPARAM); +int onExtraIconPressed(WPARAM, LPARAM, LPARAM); + +void InitCheck(); +void FirstRun(); +void RemoveHandlers(); + +// global variables +CMPlugin g_plugin; + +///////////////////////////////////////////////////////////////////////////////////////// + +PLUGININFOEX pluginInfoEx = { + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESCRIPTION, + __AUTHOR, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + // {4227c050-8d97-48d2-91ec-6a952b3dab94} + { 0x4227c050, 0x8d97, 0x48d2, { 0x91, 0xec, 0x6a, 0x95, 0x2b, 0x3d, 0xab, 0x94 } } +}; + +CMPlugin::CMPlugin() : + PLUGIN(MODULENAME, pluginInfoEx), + bDebugLog(MODULENAME, "bDebugLog", false), + bJabberAPI(MODULENAME, "bJabberAPI", true), + bStripTags(MODULENAME, "bStripTags", false), + bAppendTags(MODULENAME, "bAppendTags", false), + bSameAction(MODULENAME, "bSameAction", false), + bAutoExchange(MODULENAME, "bAutoExchange", false), + bFileTransfers(MODULENAME, "bFileTransfers", false), + bPresenceSigning(MODULENAME, "bPresenceSigning", false), + bSendErrorMessages(MODULENAME, "bSendErrorMessages", false) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR LoadKey(WPARAM, LPARAM); +INT_PTR SendKey(WPARAM, LPARAM); +INT_PTR ExportGpGKeys(WPARAM, LPARAM); +INT_PTR ImportGpGKeys(WPARAM, LPARAM); +INT_PTR ToggleEncryption(WPARAM, LPARAM); + +void InitIconLib(); + +void init_vars() +{ + globals.wszInopentag = g_plugin.getMStringW("szInOpenTag", L""); + globals.wszInclosetag = g_plugin.getMStringW("szInCloseTag", L""); + globals.wszOutopentag = g_plugin.getMStringW("szOutOpenTag", L""); + globals.wszOutclosetag = g_plugin.getMStringW("szOutCloseTag", L""); + globals.wszPassword = g_plugin.getMStringW("szKeyPassword"); + globals.bold_font = CreateFont(14, 0, 0, 0, 600, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, L"Arial"); + + globals.debuglog.init(); +} + +static int OnModulesLoaded(WPARAM, LPARAM) +{ + FirstRun(); + if (!g_plugin.getByte("FirstRun", 1)) + InitCheck(); + + StatusIconData sid = {}; + sid.szModule = MODULENAME; + sid.flags = MBF_HIDDEN; + sid.dwId = 0x00000001; + sid.hIcon = IcoLib_GetIcon("secured"); + sid.szTooltip.a = LPGEN("GPG Turn off encryption"); + Srmm_AddIcon(&sid, &g_plugin); + + sid.dwId = 0x00000002; + sid.hIcon = IcoLib_GetIcon("unsecured"); + sid.szTooltip.a = LPGEN("GPG Turn on encryption"); + Srmm_AddIcon(&sid, &g_plugin); + + if (g_plugin.bJabberAPI) { + GetJabberInterface(0, 0); + HookEvent(ME_PROTO_ACCLISTCHANGED, GetJabberInterface); + } + + HookEvent(ME_MSG_WINDOWEVENT, onWindowEvent); + HookEvent(ME_MSG_ICONPRESSED, onIconPressed); + + Proto_RegisterModule(PROTOTYPE_ENCRYPTION, MODULENAME); + + CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, RecvMsgSvc); + CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE, SendMsgSvc); + CreateProtoServiceFunction(MODULENAME, PSS_FILE, onSendFile); + clean_temp_dir(); + return 0; +} + +static int OnShutdown(WPARAM, LPARAM) +{ + RemoveHandlers(); + return 0; +} + +static INT_PTR EventGetIcon(WPARAM flags, LPARAM) +{ + HICON hIcon = g_plugin.getIcon(IDI_SECURED); + return (INT_PTR)((flags & LR_SHARED) ? hIcon : CopyIcon(hIcon)); +} + +static INT_PTR GetEventText(WPARAM pEvent, LPARAM datatype) +{ + DBEVENTINFO *dbei = (DBEVENTINFO *)pEvent; + ptrW wszText(mir_utf8decodeW((char *)dbei->pBlob)); + return (datatype != DBVT_WCHAR) ? (INT_PTR)mir_u2a(wszText) : (INT_PTR)wszText.detach(); +} + +int CMPlugin::Load() +{ + DBEVENTTYPEDESCR dbEventType = {}; + dbEventType.module = MODULENAME; + dbEventType.descr = "GPG service event"; + dbEventType.iconService = MODULENAME "/GetEventIcon"; + dbEventType.textService = MODULENAME "/GetEventText"; + DbEvent_RegisterType(&dbEventType); + + CreateServiceFunction(dbEventType.iconService, &EventGetIcon); + CreateServiceFunction(dbEventType.textService, &GetEventText); + + HookEvent(ME_CLIST_PREBUILDCONTACTMENU, OnPreBuildContactMenu); + HookEvent(ME_DB_EVENT_FILTER_ADD, HookSendMsg); + HookEvent(ME_OPT_INITIALISE, GpgOptInit); + HookEvent(ME_PROTO_ACK, onProtoAck); + HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); + HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown); + + InitIconLib(); + init_vars(); + + //////////////////////////////////////////////////////////////////////////////////////// + // Comtact menu items + + CMenuItem mi(&g_plugin); + mi.hIcolibItem = g_plugin.getIconHandle(IDI_SECURED); + + SET_UID(mi, 0xbd22e3f8, 0xc19c, 0x45a8, 0xb7, 0x37, 0x6b, 0x3b, 0x27, 0xf0, 0x8c, 0xbb); + mi.position = -0x7FFFFFFF; + mi.name.a = LPGEN("Load public GPG key"); + mi.pszService = "/LoadPubKey"; + Menu_AddContactMenuItem(&mi); + CreateServiceFunction(mi.pszService, LoadKey); + + SET_UID(mi, 0xc8008193, 0x56a9, 0x414a, 0x82, 0x98, 0x78, 0xe8, 0xa8, 0x84, 0x20, 0x67); + mi.position = -0x7FFFFFFe; + mi.name.a = LPGEN("Toggle GPG encryption"); + mi.pszService = "/ToggleEncryption"; + g_plugin.hToggleEncryption = Menu_AddContactMenuItem(&mi); + CreateServiceFunction(mi.pszService, ToggleEncryption); + + SET_UID(mi, 0x42bb535f, 0xd58e, 0x4edb, 0xbf, 0x2c, 0xfa, 0x9a, 0xbf, 0x1e, 0xb8, 0x69); + mi.position = -0x7FFFFFFd; + mi.name.a = LPGEN("Send public key"); + mi.pszService = "/SendKey"; + g_plugin.hSendKey = Menu_AddContactMenuItem(&mi); + CreateServiceFunction(mi.pszService, SendKey); + + //////////////////////////////////////////////////////////////////////////////////////// + // Main menu items + + SET_UID(mi, 0x0bac023bb, 0xd2e, 0x46e0, 0x93, 0x13, 0x7c, 0xf9, 0xf6, 0xb5, 0x02, 0xd1); + mi.position = -0x7FFFFFFe; + mi.name.a = "GPG"; + mi.root = Menu_AddMainMenuItem(&mi); + mi.flags = CMIF_UNMOVABLE; + + SET_UID(mi, 0x33a204b2, 0xe3c0, 0x413b, 0xbf, 0xd8, 0x8b, 0x2e, 0x3d, 0xa0, 0xef, 0xa4); + mi.position = -0x7FFFFFFe; + mi.name.a = LPGEN("Export GPG Public keys"); + mi.pszService = "/ExportGPGKeys"; + Menu_AddMainMenuItem(&mi); + CreateServiceFunction(mi.pszService, ExportGpGKeys); + + SET_UID(mi, 0x627fcfc1, 0x4e60, 0x4428, 0xaf, 0x96, 0x11, 0x42, 0x24, 0xeb, 0x07, 0xea); + mi.position = -0x7FFFFFFF; + mi.name.a = LPGEN("Import GPG Public keys"); + mi.pszService = "/ImportGPGKeys"; + Menu_AddMainMenuItem(&mi); + CreateServiceFunction(mi.pszService, ImportGpGKeys); + + //////////////////////////////////////////////////////////////////////////////////////// + // Extra icon + + hCLIcon = ExtraIcon_RegisterIcolib(MODULENAME, Translate("GPG encryption status"), "secured", &onExtraIconPressed); + for (auto &cc : Contacts()) + if (isContactHaveKey(cc)) + setSrmmIcon(cc); + + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +extern list transfers; + +int CMPlugin::Unload() +{ + for (auto p : transfers) + if (!p.empty()) + boost::filesystem::remove(p); + + clean_temp_dir(); + return 0; +} diff --git a/plugins/New_GPG/src/jabber_account.h b/plugins/New_GPG/src/jabber_account.h index 5026a410f9..b011f70426 100644 --- a/plugins/New_GPG/src/jabber_account.h +++ b/plugins/New_GPG/src/jabber_account.h @@ -1,52 +1,52 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef JABBER_ACCOUNT_H -#define JABBER_ACCOUNT_H - -class JabberAccount -{ - wchar_t *AccountName = nullptr; - int AccountNumber = -1; - IJabberInterface *JabberInterface = nullptr; - HJHANDLER hSendHandler = INVALID_HANDLE_VALUE, hPresenceHandler = INVALID_HANDLE_VALUE, hMessageHandler = INVALID_HANDLE_VALUE; - -public: - __forceinline JabberAccount() - { - } - - __forceinline ~JabberAccount() - { - mir_free(AccountName); - } - - __forceinline void setAccountName(wchar_t *Name) { AccountName = Name; } - __forceinline void setAccountNumber(int Number) { AccountNumber = Number; } - __forceinline void setJabberInterface(IJabberInterface *JIf) { JabberInterface = JIf; } - __forceinline void setSendHandler(HJHANDLER hHandler) { hSendHandler = hHandler; } - __forceinline void setPresenceHandler(HJHANDLER hHandler) { hPresenceHandler = hHandler; } - __forceinline void setMessageHandler(HJHANDLER hHandler) { hMessageHandler = hHandler; } - - __forceinline wchar_t* getAccountName() const { return AccountName; } - __forceinline int getAccountNumber() const { return AccountNumber; } - __forceinline IJabberInterface* getJabberInterface() const { return JabberInterface; } - __forceinline HJHANDLER getSendHandler() const { return hSendHandler; } - __forceinline HJHANDLER getPresenceHandler() const { return hPresenceHandler; } - __forceinline HJHANDLER getMessageHandler() const { return hMessageHandler; } -}; - -#endif +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef JABBER_ACCOUNT_H +#define JABBER_ACCOUNT_H + +class JabberAccount +{ + wchar_t *AccountName = nullptr; + int AccountNumber = -1; + IJabberInterface *JabberInterface = nullptr; + HJHANDLER hSendHandler = INVALID_HANDLE_VALUE, hPresenceHandler = INVALID_HANDLE_VALUE, hMessageHandler = INVALID_HANDLE_VALUE; + +public: + __forceinline JabberAccount() + { + } + + __forceinline ~JabberAccount() + { + mir_free(AccountName); + } + + __forceinline void setAccountName(wchar_t *Name) { AccountName = Name; } + __forceinline void setAccountNumber(int Number) { AccountNumber = Number; } + __forceinline void setJabberInterface(IJabberInterface *JIf) { JabberInterface = JIf; } + __forceinline void setSendHandler(HJHANDLER hHandler) { hSendHandler = hHandler; } + __forceinline void setPresenceHandler(HJHANDLER hHandler) { hPresenceHandler = hHandler; } + __forceinline void setMessageHandler(HJHANDLER hHandler) { hMessageHandler = hHandler; } + + __forceinline wchar_t* getAccountName() const { return AccountName; } + __forceinline int getAccountNumber() const { return AccountNumber; } + __forceinline IJabberInterface* getJabberInterface() const { return JabberInterface; } + __forceinline HJHANDLER getSendHandler() const { return hSendHandler; } + __forceinline HJHANDLER getPresenceHandler() const { return hPresenceHandler; } + __forceinline HJHANDLER getMessageHandler() const { return hMessageHandler; } +}; + +#endif diff --git a/plugins/New_GPG/src/log.cpp b/plugins/New_GPG/src/log.cpp index 7fa5caf3a0..930ad56167 100644 --- a/plugins/New_GPG/src/log.cpp +++ b/plugins/New_GPG/src/log.cpp @@ -1,49 +1,49 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -static string time_str() -{ - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - return (string)boost::posix_time::to_simple_string(now) + ": "; -} - -logtofile &logtofile::operator<<(const char *buf) -{ - if (bEnabled) - mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf); - return *this; -} - -logtofile& logtofile::operator<<(const string &buf) -{ - if (bEnabled) - mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf.c_str()); - return *this; -} - -void logtofile::init() -{ - if (g_plugin.bDebugLog) - hLogger = mir_createLog("NewGPG", L"NewGPG log file", g_plugin.getMStringW("szLogFilePath", L"C:\\GPGdebug.log"), 0); - else { - mir_closeLog(hLogger); - hLogger = nullptr; - } - - bEnabled = g_plugin.bDebugLog; -} +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +static string time_str() +{ + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + return (string)boost::posix_time::to_simple_string(now) + ": "; +} + +logtofile &logtofile::operator<<(const char *buf) +{ + if (bEnabled) + mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf); + return *this; +} + +logtofile& logtofile::operator<<(const string &buf) +{ + if (bEnabled) + mir_writeLogA(hLogger, "%s: %s\n", time_str().c_str(), buf.c_str()); + return *this; +} + +void logtofile::init() +{ + if (g_plugin.bDebugLog) + hLogger = mir_createLog("NewGPG", L"NewGPG log file", g_plugin.getMStringW("szLogFilePath", L"C:\\GPGdebug.log"), 0); + else { + mir_closeLog(hLogger); + hLogger = nullptr; + } + + bEnabled = g_plugin.bDebugLog; +} diff --git a/plugins/New_GPG/src/log.h b/plugins/New_GPG/src/log.h index b478f780aa..91328edb21 100644 --- a/plugins/New_GPG/src/log.h +++ b/plugins/New_GPG/src/log.h @@ -1,32 +1,32 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef LOG_H -#define LOG_H - -class logtofile -{ - HANDLE hLogger; - bool bEnabled = false; - -public: - logtofile& operator<<(const char *buf); - logtofile& operator<<(const std::string &buf); - void init(); - - __forceinline operator bool() const { return bEnabled; } -}; - -#endif +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef LOG_H +#define LOG_H + +class logtofile +{ + HANDLE hLogger; + bool bEnabled = false; + +public: + logtofile& operator<<(const char *buf); + logtofile& operator<<(const std::string &buf); + void init(); + + __forceinline operator bool() const { return bEnabled; } +}; + +#endif diff --git a/plugins/New_GPG/src/main.cpp b/plugins/New_GPG/src/main.cpp index bcaafe6ac0..6f2ed6c799 100644 --- a/plugins/New_GPG/src/main.cpp +++ b/plugins/New_GPG/src/main.cpp @@ -1,654 +1,654 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -#pragma comment(lib, "shlwapi.lib") - -///////////////////////////////////////////////////////////////////////////////////////// -// GPG binaries options - -class CDlgGpgBinOpts : public CDlgBase -{ - CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR, btn_OK, btn_GENERATE_RANDOM; - CCtrlEdit edit_BIN_PATH, edit_HOME_DIR; - CCtrlCheck chk_AUTO_EXCHANGE; - -public: - CDlgGpgBinOpts() : - CDlgBase(g_plugin, IDD_BIN_PATH), - btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), - btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR), - btn_OK(this, ID_OK), - btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM), - edit_BIN_PATH(this, IDC_BIN_PATH), - edit_HOME_DIR(this, IDC_HOME_DIR), - chk_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE) - { - btn_SET_BIN_PATH.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_BIN_PATH); - btn_SET_HOME_DIR.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_HOME_DIR); - btn_OK.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_OK); - btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_GENERATE_RANDOM); - } - - bool OnInitDialog() override - { - CMStringW path; - bool gpg_exists = false, lang_exists = false; - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - SetCurrentDirectoryW(mir_path); - - CMStringW gpg_path(mir_path); gpg_path.Append(L"\\GnuPG\\gpg.exe"); - CMStringW gpg_lang_path(mir_path); gpg_lang_path.Append(L"\\GnuPG\\gnupg.nls\\en@quot.mo"); - - if (boost::filesystem::exists(gpg_path.c_str())) { - gpg_exists = true; - path = L"GnuPG\\gpg.exe"; - } - else path = gpg_path; - - if (boost::filesystem::exists(gpg_lang_path.c_str())) - lang_exists = true; - if (gpg_exists && !lang_exists) - MessageBox(nullptr, TranslateT("GPG binary found in Miranda folder, but English locale does not exist.\nIt's highly recommended that you place \\gnupg.nls\\en@quot.mo in GnuPG folder under Miranda root.\nWithout this file you may experience many problems with GPG output on non-English systems\nand plugin may completely not work.\nYou have been warned."), TranslateT("Warning"), MB_OK); - - DWORD len = MAX_PATH; - bool bad_version = false; - { - ptrW tmp; - if (!gpg_exists) { - tmp = g_plugin.getWStringA("szGpgBinPath", (SHGetValueW(HKEY_CURRENT_USER, L"Software\\GNU\\GnuPG", L"gpgProgram", 0, (void *)path.c_str(), &len) == ERROR_SUCCESS) ? path.c_str() : L""); - if (tmp[0]) - if (!boost::filesystem::exists((wchar_t *)tmp)) - MessageBoxW(nullptr, TranslateT("Wrong GPG binary location found in system.\nPlease choose another location"), TranslateT("Warning"), MB_OK); - } - else tmp = mir_wstrdup(path.c_str()); - - edit_BIN_PATH.SetText(tmp); - if (gpg_exists/* && lang_exists*/) { - g_plugin.setWString("szGpgBinPath", tmp); - - gpg_execution_params params; - params.addParam(L"--version"); - bool _gpg_valid = globals.gpg_valid; - globals.gpg_valid = true; - gpg_launcher(params); - globals.gpg_valid = _gpg_valid; //TODO: check this - g_plugin.delSetting("szGpgBinPath"); - int p1 = params.out.Find("(GnuPG) "); - if (p1 != -1) { - p1 += mir_strlen("(GnuPG) "); - if (params.out[p1] != '1') - bad_version = true; - } - else { - bad_version = false; - MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Error"), MB_OK); - } - if (bad_version) - MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); - } - } - - CMStringW tmp(g_plugin.getMStringW("szHomePath")); - if (tmp.IsEmpty()) { - mir_wstrcat(mir_path, L"\\gpg"); - if (_waccess(mir_path, 0) != -1) { - tmp = mir_path; - MessageBoxW(nullptr, TranslateT("\"GPG\" directory found in Miranda root.\nAssuming it's GPG home directory.\nGPG home directory set."), TranslateT("Info"), MB_OK); - } - else { - wstring path_ = _wgetenv(L"APPDATA"); - path_ += L"\\GnuPG"; - tmp = path_.c_str(); - } - } - edit_HOME_DIR.SetText(!gpg_exists ? tmp : L"gpg"); - - // TODO: additional check for write access - if (gpg_exists && lang_exists && !bad_version) - MessageBox(nullptr, TranslateT("Your GPG version is supported. The language file was found.\nGPG plugin should work fine.\nPress OK to continue."), TranslateT("Info"), MB_OK); - chk_AUTO_EXCHANGE.Enable(); - return true; - } - - void OnDestroy() override - { - void InitCheck(); - InitCheck(); - } - - void onClick_SET_BIN_PATH(CCtrlButton *) - { - GetFilePath(L"Choose gpg.exe", "szGpgBinPath", L"*.exe", L"EXE Executables"); - CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); - edit_BIN_PATH.SetText(tmp); - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - if (tmp.Find(mir_path, 0) == 0) { - CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); - edit_BIN_PATH.SetText(path); - } - } - - void onClick_SET_HOME_DIR(CCtrlButton *) - { - GetFolderPath(L"Set home directory"); - CMStringW tmp(g_plugin.getMStringW("szHomePath")); - edit_HOME_DIR.SetText(tmp); - - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - PathToAbsoluteW(L"\\", mir_path); - if (tmp.Find(mir_path, 0) == 0) { - CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); - edit_HOME_DIR.SetText(path); - } - } - - void onClick_OK(CCtrlButton *) - { - if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) { - gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText()); - globals.gpg_valid = true; - g_plugin.setByte("FirstRun", 0); - this->Hide(); - this->Close(); - ShowFirstRunDialog(); - } - } - - void onClick_GENERATE_RANDOM(CCtrlButton *) - { - if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) { - gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText()); - globals.gpg_valid = true; - if (gpg_use_new_random_key(nullptr)) { - g_plugin.bAutoExchange = chk_AUTO_EXCHANGE.GetState(); - globals.gpg_valid = true; - g_plugin.setByte("FirstRun", 0); - this->Close(); - } - } - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -static int EnumProc(const char *szSetting, void *param) -{ - auto *list = (OBJLIST *)param; - if (strchr(szSetting, '(') && strchr(szSetting, ')')) - list->insert(new CMStringA(szSetting)); - return 0; -} - -void FirstRun() -{ - if (g_plugin.getByte("CompatLevel") != 1) { - OBJLIST settings(1); - db_enum_settings(0, EnumProc, MODULENAME, &settings); - - for (auto &it : settings) { - CMStringA newName(*it); - int p1 = newName.Find('('); - newName.Delete(0, p1+1); - int p2 = newName.Find(')'); - if (p2 == -1) - continue; - newName.Delete(p2, 1); - - CMStringW val = g_plugin.getMStringW(it->c_str()); - g_plugin.delSetting(it->c_str()); - g_plugin.setWString(newName, val); - } - - g_plugin.setByte("CompatLevel", 1); - } - - if (g_plugin.getByte("FirstRun", 1)) - (new CDlgGpgBinOpts())->Show(); -} - -void InitCheck() -{ - // parse gpg output - { - ptrW current_home(g_plugin.getWStringA("szHomePath", L"")); - g_plugin.setWString("szHomePath", L""); //we do not need home for gpg binary validation - globals.gpg_valid = isGPGValid(); - g_plugin.setWString("szHomePath", current_home); //return current home dir back - } - { - bool home_dir_access = false, temp_access = false; - std::wstring test_path(ptrW(g_plugin.getWStringA("szHomePath", L""))); - test_path += L"/"; - test_path += toUTF16(get_random(13)); - wfstream test_file; - test_file.open(test_path, std::ios::trunc | std::ios::out); - if (test_file.is_open() && test_file.good()) { - test_file << L"access_test\n"; - if (test_file.good()) - home_dir_access = true; - test_file.close(); - boost::filesystem::remove(test_path); - } - - test_path = _wgetenv(L"TEMP"); - test_path += L"/"; - test_path += toUTF16(get_random(13)); - test_file.open(test_path, std::ios::trunc | std::ios::out); - if (test_file.is_open() && test_file.good()) { - test_file << L"access_test\n"; - if (test_file.good()) - temp_access = true; - test_file.close(); - boost::filesystem::remove(test_path); - } - if (!home_dir_access || !temp_access || !globals.gpg_valid) { - CMStringW buf; - buf.Append(globals.gpg_valid ? TranslateT("GPG binary is set and valid (this is good).\n") : TranslateT("GPG binary unset or invalid (plugin will not work).\n")); - buf.Append(home_dir_access ? TranslateT("Home dir write access granted (this is good).\n") : TranslateT("Home dir has no write access (plugin most probably will not work).\n")); - buf.Append(temp_access ? TranslateT("Temp dir write access granted (this is good).\n") : TranslateT("Temp dir has no write access (plugin should work, but may have some problems, file transfers will not work).")); - if (!globals.gpg_valid) - buf.Append(TranslateT("\nGPG will be disabled until you solve these problems")); - MessageBox(nullptr, buf, TranslateT("GPG plugin problems"), MB_OK); - } - if (!globals.gpg_valid) - return; - globals.gpg_keyexist = isGPGKeyExist(); - - wstring::size_type p = 0, p2 = 0; - - gpg_execution_params params; - params.addParam(L"--list-secret-keys"); - params.addParam(L"--batch"); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - _wmkdir(g_plugin.getMStringW("szHomePath") + L"\\tmp"); - string out(params.out); - - CMStringW wszQuestion; - for (auto &pa : Accounts()) { - if (StriStr(pa->szModuleName, "metacontacts")) - continue; - if (StriStr(pa->szModuleName, "weather")) - continue; - - std::string acc = pa->szModuleName; - acc += "_KeyID"; - CMStringA keyid = g_plugin.getMStringA(acc.c_str()); - if (!keyid.IsEmpty()) { - wszQuestion = TranslateT("Your secret key with ID: "); - keyid = g_plugin.getMStringA("KeyID"); - if ((p = out.find(keyid)) == string::npos) { - wszQuestion += keyid; - wszQuestion += TranslateT(" for account "); - wszQuestion += pa->tszAccountName; - wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?"); - if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - p2 = p; - p = out.find("[", p); - p2 = out.find("\n", p2); - if ((p != std::string::npos) && (p < p2)) { - p = out.find("expires:", p); - p += mir_strlen("expires:"); - p++; - p2 = out.find("]", p); - wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); - bool expired = false; - { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - wchar_t buf[5]; - wcsncpy_s(buf, expire_date, _TRUNCATE); - int year = _wtoi(buf); - if (year < now.date().year()) - expired = true; - else if (year == now.date().year()) { - wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); - int month = _wtoi(buf); - if (month < now.date().month()) - expired = true; - else if (month == now.date().month()) { - wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); - unsigned day = _wtoi(buf); - if (day <= now.date().day_number()) - expired = true; - } - } - } - if (expired) { - wszQuestion += keyid; - wszQuestion += TranslateT(" for account "); - wszQuestion += pa->tszAccountName; - wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?"); - if (MessageBoxW(nullptr, wszQuestion.c_str(), TranslateT("Own secret key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - mir_free(expire_date); - } - } - } - - wszQuestion = TranslateT("Your secret key with ID: "); - CMStringA keyid(g_plugin.getMStringA("KeyID")); - CMStringA key(g_plugin.getMStringA("GPGPubKey")); - if (!g_plugin.getByte("FirstRun", 1) && (keyid.IsEmpty() || key.IsEmpty())) { - wszQuestion = TranslateT("You didn't set a private key.\nWould you like to set it now?"); - if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own private key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - if ((p = out.find(keyid)) == string::npos) { - wszQuestion += keyid; - wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?"); - if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - p2 = p; - p = out.find("[", p); - p2 = out.find("\n", p2); - if ((p != std::string::npos) && (p < p2)) { - p = out.find("expires:", p); - p += mir_strlen("expires:"); - p++; - p2 = out.find("]", p); - wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); - bool expired = false; - { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - wchar_t buf[5]; - wcsncpy_s(buf, expire_date, _TRUNCATE); - int year = _wtoi(buf); - if (year < now.date().year()) - expired = true; - else if (year == now.date().year()) { - wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); - int month = _wtoi(buf); - if (month < now.date().month()) - expired = true; - else if (month == now.date().month()) { - wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); - unsigned day = _wtoi(buf); - if (day <= now.date().day_number()) - expired = true; - } - } - } - if (expired) { - wszQuestion += keyid; - wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?"); - if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) - ShowFirstRunDialog(); - } - mir_free(expire_date); - } - // TODO: check for expired key - } - { - CMStringW path(g_plugin.getMStringW("szHomePath")); - uint32_t dwFileAttr = GetFileAttributes(path); - if (dwFileAttr != INVALID_FILE_ATTRIBUTES) { - dwFileAttr &= ~FILE_ATTRIBUTE_READONLY; - SetFileAttributes(path, dwFileAttr); - } - } -} - -void ImportKey(MCONTACT hContact, std::wstring new_key) -{ - bool for_all_sub = false; - if (db_mc_isMeta(hContact)) { - if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) - for_all_sub = true; - - if (for_all_sub) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setWString(hcnt, "GPGPubKey", new_key.c_str()); - } - } - else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", new_key.c_str()); - } - else g_plugin.setWString(hContact, "GPGPubKey", new_key.c_str()); - - // gpg execute block - CMStringW tmp2 = g_plugin.getMStringW("szHomePath"); - tmp2 += L"\\temporary_exported.asc"; - boost::filesystem::remove(tmp2.c_str()); - - CMStringW ptmp; - if (db_mc_isMeta(hContact)) - ptmp = g_plugin.getMStringW(metaGetMostOnline(hContact), "GPGPubKey"); - else - ptmp = g_plugin.getMStringW(hContact, "GPGPubKey"); - - wfstream f(tmp2, std::ios::out); - f << ptmp.c_str(); - f.close(); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(tmp2.c_str()); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - string output(params.out); - if (db_mc_isMeta(hContact)) { - if (for_all_sub) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) { - char *tmp = nullptr; - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - g_plugin.setString(hcnt, "KeyID", output.substr(s, s2 - s).c_str()); - s = output.find(RUS_QUOTE, s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else s += sizeof(RUS_QUOTE) - 1; - - bool uncommon = false; - if ((s2 = output.find("(", s)) == string::npos) { - if ((s2 = output.find("<", s)) == string::npos) { - s2 = output.find(RUS_ANGLE, s); - uncommon = true; - } - } - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hcnt, "KeyMainName", tmp); - mir_free(tmp); - } - - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (s != string::npos && s2 != string::npos) { - if (output[s] == ')') { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hcnt, "KeyComment", tmp); - mir_free(tmp); - s += 3; - s2 = output.find(">", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hcnt, "KeyMainEmail", tmp); - mir_free(tmp); - } - } - else { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - mir_free(tmp); - } - } - g_plugin.delSetting(hcnt, "bAlwatsTrust"); - } - } - } - else { - char *tmp = nullptr; - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - g_plugin.setString(metaGetMostOnline(hContact), "KeyID", output.substr(s, s2 - s).c_str()); - s = output.find(RUS_QUOTE, s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else s += sizeof(RUS_QUOTE) - 1; - - bool uncommon = false; - if ((s2 = output.find("(", s)) == string::npos) { - if ((s2 = output.find("<", s)) == string::npos) { - s2 = output.find(RUS_ANGLE, s); - uncommon = true; - } - } - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", tmp); - mir_free(tmp); - } - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (s != string::npos && s2 != string::npos) { - if (output[s] == ')') { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", tmp); - mir_free(tmp); - s += 3; - s2 = output.find(">", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", tmp); - mir_free(tmp); - } - } - else { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str()); - mir_free(tmp); - } - } - g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust"); - } - } - else { - char *tmp = nullptr; - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - g_plugin.setString(hContact, "KeyID", output.substr(s, s2 - s).c_str()); - s = output.find(RUS_QUOTE, s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else s += sizeof(RUS_QUOTE) - 1; - - bool uncommon = false; - if ((s2 = output.find("(", s)) == string::npos) { - if ((s2 = output.find("<", s)) == string::npos) { - s2 = output.find(RUS_ANGLE, s); - uncommon = true; - } - } - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hContact, "KeyMainName", tmp); - mir_free(tmp); - } - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (s != string::npos && s2 != string::npos) { - if (output[s] == ')') { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hContact, "KeyComment", tmp); - mir_free(tmp); - s += 3; - s2 = output.find(">", s); - if (s != string::npos && s2 != string::npos) { - tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); - mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hContact, "KeyMainEmail", tmp); - mir_free(tmp); - } - } - else { - tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); - mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - mir_free(tmp); - } - } - g_plugin.delSetting(hContact, "bAlwatsTrust"); - } - - MessageBox(nullptr, toUTF16(output).c_str(), L"", MB_OK); - boost::filesystem::remove(tmp2.c_str()); -} +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +#pragma comment(lib, "shlwapi.lib") + +///////////////////////////////////////////////////////////////////////////////////////// +// GPG binaries options + +class CDlgGpgBinOpts : public CDlgBase +{ + CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR, btn_OK, btn_GENERATE_RANDOM; + CCtrlEdit edit_BIN_PATH, edit_HOME_DIR; + CCtrlCheck chk_AUTO_EXCHANGE; + +public: + CDlgGpgBinOpts() : + CDlgBase(g_plugin, IDD_BIN_PATH), + btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), + btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR), + btn_OK(this, ID_OK), + btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM), + edit_BIN_PATH(this, IDC_BIN_PATH), + edit_HOME_DIR(this, IDC_HOME_DIR), + chk_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE) + { + btn_SET_BIN_PATH.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_BIN_PATH); + btn_SET_HOME_DIR.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_SET_HOME_DIR); + btn_OK.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_OK); + btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgGpgBinOpts::onClick_GENERATE_RANDOM); + } + + bool OnInitDialog() override + { + CMStringW path; + bool gpg_exists = false, lang_exists = false; + + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + SetCurrentDirectoryW(mir_path); + + CMStringW gpg_path(mir_path); gpg_path.Append(L"\\GnuPG\\gpg.exe"); + CMStringW gpg_lang_path(mir_path); gpg_lang_path.Append(L"\\GnuPG\\gnupg.nls\\en@quot.mo"); + + if (boost::filesystem::exists(gpg_path.c_str())) { + gpg_exists = true; + path = L"GnuPG\\gpg.exe"; + } + else path = gpg_path; + + if (boost::filesystem::exists(gpg_lang_path.c_str())) + lang_exists = true; + if (gpg_exists && !lang_exists) + MessageBox(nullptr, TranslateT("GPG binary found in Miranda folder, but English locale does not exist.\nIt's highly recommended that you place \\gnupg.nls\\en@quot.mo in GnuPG folder under Miranda root.\nWithout this file you may experience many problems with GPG output on non-English systems\nand plugin may completely not work.\nYou have been warned."), TranslateT("Warning"), MB_OK); + + DWORD len = MAX_PATH; + bool bad_version = false; + { + ptrW tmp; + if (!gpg_exists) { + tmp = g_plugin.getWStringA("szGpgBinPath", (SHGetValueW(HKEY_CURRENT_USER, L"Software\\GNU\\GnuPG", L"gpgProgram", 0, (void *)path.c_str(), &len) == ERROR_SUCCESS) ? path.c_str() : L""); + if (tmp[0]) + if (!boost::filesystem::exists((wchar_t *)tmp)) + MessageBoxW(nullptr, TranslateT("Wrong GPG binary location found in system.\nPlease choose another location"), TranslateT("Warning"), MB_OK); + } + else tmp = mir_wstrdup(path.c_str()); + + edit_BIN_PATH.SetText(tmp); + if (gpg_exists/* && lang_exists*/) { + g_plugin.setWString("szGpgBinPath", tmp); + + gpg_execution_params params; + params.addParam(L"--version"); + bool _gpg_valid = globals.gpg_valid; + globals.gpg_valid = true; + gpg_launcher(params); + globals.gpg_valid = _gpg_valid; //TODO: check this + g_plugin.delSetting("szGpgBinPath"); + int p1 = params.out.Find("(GnuPG) "); + if (p1 != -1) { + p1 += mir_strlen("(GnuPG) "); + if (params.out[p1] != '1') + bad_version = true; + } + else { + bad_version = false; + MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Error"), MB_OK); + } + if (bad_version) + MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); + } + } + + CMStringW tmp(g_plugin.getMStringW("szHomePath")); + if (tmp.IsEmpty()) { + mir_wstrcat(mir_path, L"\\gpg"); + if (_waccess(mir_path, 0) != -1) { + tmp = mir_path; + MessageBoxW(nullptr, TranslateT("\"GPG\" directory found in Miranda root.\nAssuming it's GPG home directory.\nGPG home directory set."), TranslateT("Info"), MB_OK); + } + else { + wstring path_ = _wgetenv(L"APPDATA"); + path_ += L"\\GnuPG"; + tmp = path_.c_str(); + } + } + edit_HOME_DIR.SetText(!gpg_exists ? tmp : L"gpg"); + + // TODO: additional check for write access + if (gpg_exists && lang_exists && !bad_version) + MessageBox(nullptr, TranslateT("Your GPG version is supported. The language file was found.\nGPG plugin should work fine.\nPress OK to continue."), TranslateT("Info"), MB_OK); + chk_AUTO_EXCHANGE.Enable(); + return true; + } + + void OnDestroy() override + { + void InitCheck(); + InitCheck(); + } + + void onClick_SET_BIN_PATH(CCtrlButton *) + { + GetFilePath(L"Choose gpg.exe", "szGpgBinPath", L"*.exe", L"EXE Executables"); + CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); + edit_BIN_PATH.SetText(tmp); + + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + if (tmp.Find(mir_path, 0) == 0) { + CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); + edit_BIN_PATH.SetText(path); + } + } + + void onClick_SET_HOME_DIR(CCtrlButton *) + { + GetFolderPath(L"Set home directory"); + CMStringW tmp(g_plugin.getMStringW("szHomePath")); + edit_HOME_DIR.SetText(tmp); + + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + PathToAbsoluteW(L"\\", mir_path); + if (tmp.Find(mir_path, 0) == 0) { + CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); + edit_HOME_DIR.SetText(path); + } + } + + void onClick_OK(CCtrlButton *) + { + if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) { + gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText()); + globals.gpg_valid = true; + g_plugin.setByte("FirstRun", 0); + this->Hide(); + this->Close(); + ShowFirstRunDialog(); + } + } + + void onClick_GENERATE_RANDOM(CCtrlButton *) + { + if (gpg_validate_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText())) { + gpg_save_paths(edit_BIN_PATH.GetText(), edit_HOME_DIR.GetText()); + globals.gpg_valid = true; + if (gpg_use_new_random_key(nullptr)) { + g_plugin.bAutoExchange = chk_AUTO_EXCHANGE.GetState(); + globals.gpg_valid = true; + g_plugin.setByte("FirstRun", 0); + this->Close(); + } + } + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +static int EnumProc(const char *szSetting, void *param) +{ + auto *list = (OBJLIST *)param; + if (strchr(szSetting, '(') && strchr(szSetting, ')')) + list->insert(new CMStringA(szSetting)); + return 0; +} + +void FirstRun() +{ + if (g_plugin.getByte("CompatLevel") != 1) { + OBJLIST settings(1); + db_enum_settings(0, EnumProc, MODULENAME, &settings); + + for (auto &it : settings) { + CMStringA newName(*it); + int p1 = newName.Find('('); + newName.Delete(0, p1+1); + int p2 = newName.Find(')'); + if (p2 == -1) + continue; + newName.Delete(p2, 1); + + CMStringW val = g_plugin.getMStringW(it->c_str()); + g_plugin.delSetting(it->c_str()); + g_plugin.setWString(newName, val); + } + + g_plugin.setByte("CompatLevel", 1); + } + + if (g_plugin.getByte("FirstRun", 1)) + (new CDlgGpgBinOpts())->Show(); +} + +void InitCheck() +{ + // parse gpg output + { + ptrW current_home(g_plugin.getWStringA("szHomePath", L"")); + g_plugin.setWString("szHomePath", L""); //we do not need home for gpg binary validation + globals.gpg_valid = isGPGValid(); + g_plugin.setWString("szHomePath", current_home); //return current home dir back + } + { + bool home_dir_access = false, temp_access = false; + std::wstring test_path(ptrW(g_plugin.getWStringA("szHomePath", L""))); + test_path += L"/"; + test_path += toUTF16(get_random(13)); + wfstream test_file; + test_file.open(test_path, std::ios::trunc | std::ios::out); + if (test_file.is_open() && test_file.good()) { + test_file << L"access_test\n"; + if (test_file.good()) + home_dir_access = true; + test_file.close(); + boost::filesystem::remove(test_path); + } + + test_path = _wgetenv(L"TEMP"); + test_path += L"/"; + test_path += toUTF16(get_random(13)); + test_file.open(test_path, std::ios::trunc | std::ios::out); + if (test_file.is_open() && test_file.good()) { + test_file << L"access_test\n"; + if (test_file.good()) + temp_access = true; + test_file.close(); + boost::filesystem::remove(test_path); + } + if (!home_dir_access || !temp_access || !globals.gpg_valid) { + CMStringW buf; + buf.Append(globals.gpg_valid ? TranslateT("GPG binary is set and valid (this is good).\n") : TranslateT("GPG binary unset or invalid (plugin will not work).\n")); + buf.Append(home_dir_access ? TranslateT("Home dir write access granted (this is good).\n") : TranslateT("Home dir has no write access (plugin most probably will not work).\n")); + buf.Append(temp_access ? TranslateT("Temp dir write access granted (this is good).\n") : TranslateT("Temp dir has no write access (plugin should work, but may have some problems, file transfers will not work).")); + if (!globals.gpg_valid) + buf.Append(TranslateT("\nGPG will be disabled until you solve these problems")); + MessageBox(nullptr, buf, TranslateT("GPG plugin problems"), MB_OK); + } + if (!globals.gpg_valid) + return; + globals.gpg_keyexist = isGPGKeyExist(); + + wstring::size_type p = 0, p2 = 0; + + gpg_execution_params params; + params.addParam(L"--list-secret-keys"); + params.addParam(L"--batch"); + if (!gpg_launcher(params)) + return; + if (params.result == pxNotFound) + return; + + _wmkdir(g_plugin.getMStringW("szHomePath") + L"\\tmp"); + string out(params.out); + + CMStringW wszQuestion; + for (auto &pa : Accounts()) { + if (StriStr(pa->szModuleName, "metacontacts")) + continue; + if (StriStr(pa->szModuleName, "weather")) + continue; + + std::string acc = pa->szModuleName; + acc += "_KeyID"; + CMStringA keyid = g_plugin.getMStringA(acc.c_str()); + if (!keyid.IsEmpty()) { + wszQuestion = TranslateT("Your secret key with ID: "); + keyid = g_plugin.getMStringA("KeyID"); + if ((p = out.find(keyid)) == string::npos) { + wszQuestion += keyid; + wszQuestion += TranslateT(" for account "); + wszQuestion += pa->tszAccountName; + wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?"); + if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) + ShowFirstRunDialog(); + } + p2 = p; + p = out.find("[", p); + p2 = out.find("\n", p2); + if ((p != std::string::npos) && (p < p2)) { + p = out.find("expires:", p); + p += mir_strlen("expires:"); + p++; + p2 = out.find("]", p); + wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); + bool expired = false; + { + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + wchar_t buf[5]; + wcsncpy_s(buf, expire_date, _TRUNCATE); + int year = _wtoi(buf); + if (year < now.date().year()) + expired = true; + else if (year == now.date().year()) { + wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); + int month = _wtoi(buf); + if (month < now.date().month()) + expired = true; + else if (month == now.date().month()) { + wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); + unsigned day = _wtoi(buf); + if (day <= now.date().day_number()) + expired = true; + } + } + } + if (expired) { + wszQuestion += keyid; + wszQuestion += TranslateT(" for account "); + wszQuestion += pa->tszAccountName; + wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?"); + if (MessageBoxW(nullptr, wszQuestion.c_str(), TranslateT("Own secret key warning"), MB_YESNO) == IDYES) + ShowFirstRunDialog(); + } + mir_free(expire_date); + } + } + } + + wszQuestion = TranslateT("Your secret key with ID: "); + CMStringA keyid(g_plugin.getMStringA("KeyID")); + CMStringA key(g_plugin.getMStringA("GPGPubKey")); + if (!g_plugin.getByte("FirstRun", 1) && (keyid.IsEmpty() || key.IsEmpty())) { + wszQuestion = TranslateT("You didn't set a private key.\nWould you like to set it now?"); + if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own private key warning"), MB_YESNO) == IDYES) + ShowFirstRunDialog(); + } + if ((p = out.find(keyid)) == string::npos) { + wszQuestion += keyid; + wszQuestion += TranslateT(" deleted from GPG secret keyring.\nDo you want to set another key?"); + if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) + ShowFirstRunDialog(); + } + p2 = p; + p = out.find("[", p); + p2 = out.find("\n", p2); + if ((p != std::string::npos) && (p < p2)) { + p = out.find("expires:", p); + p += mir_strlen("expires:"); + p++; + p2 = out.find("]", p); + wchar_t *expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); + bool expired = false; + { + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + wchar_t buf[5]; + wcsncpy_s(buf, expire_date, _TRUNCATE); + int year = _wtoi(buf); + if (year < now.date().year()) + expired = true; + else if (year == now.date().year()) { + wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); + int month = _wtoi(buf); + if (month < now.date().month()) + expired = true; + else if (month == now.date().month()) { + wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); + unsigned day = _wtoi(buf); + if (day <= now.date().day_number()) + expired = true; + } + } + } + if (expired) { + wszQuestion += keyid; + wszQuestion += TranslateT(" expired and will not work.\nDo you want to set another key?"); + if (MessageBoxW(nullptr, wszQuestion, TranslateT("Own secret key warning"), MB_YESNO) == IDYES) + ShowFirstRunDialog(); + } + mir_free(expire_date); + } + // TODO: check for expired key + } + { + CMStringW path(g_plugin.getMStringW("szHomePath")); + uint32_t dwFileAttr = GetFileAttributes(path); + if (dwFileAttr != INVALID_FILE_ATTRIBUTES) { + dwFileAttr &= ~FILE_ATTRIBUTE_READONLY; + SetFileAttributes(path, dwFileAttr); + } + } +} + +void ImportKey(MCONTACT hContact, std::wstring new_key) +{ + bool for_all_sub = false; + if (db_mc_isMeta(hContact)) { + if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) + for_all_sub = true; + + if (for_all_sub) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.setWString(hcnt, "GPGPubKey", new_key.c_str()); + } + } + else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", new_key.c_str()); + } + else g_plugin.setWString(hContact, "GPGPubKey", new_key.c_str()); + + // gpg execute block + CMStringW tmp2 = g_plugin.getMStringW("szHomePath"); + tmp2 += L"\\temporary_exported.asc"; + boost::filesystem::remove(tmp2.c_str()); + + CMStringW ptmp; + if (db_mc_isMeta(hContact)) + ptmp = g_plugin.getMStringW(metaGetMostOnline(hContact), "GPGPubKey"); + else + ptmp = g_plugin.getMStringW(hContact, "GPGPubKey"); + + wfstream f(tmp2, std::ios::out); + f << ptmp.c_str(); + f.close(); + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--import"); + params.addParam(tmp2.c_str()); + if (!gpg_launcher(params)) + return; + if (params.result == pxNotFound) + return; + + string output(params.out); + if (db_mc_isMeta(hContact)) { + if (for_all_sub) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) { + char *tmp = nullptr; + string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); + string::size_type s2 = output.find(":", s); + g_plugin.setString(hcnt, "KeyID", output.substr(s, s2 - s).c_str()); + s = output.find(RUS_QUOTE, s2); + if (s == string::npos) { + s = output.find("\"", s2); + s += 1; + } + else s += sizeof(RUS_QUOTE) - 1; + + bool uncommon = false; + if ((s2 = output.find("(", s)) == string::npos) { + if ((s2 = output.find("<", s)) == string::npos) { + s2 = output.find(RUS_ANGLE, s); + uncommon = true; + } + } + else if (s2 > output.find("<", s)) + s2 = output.find("<", s); + if (s != string::npos && s2 != string::npos) { + tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); + mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(hcnt, "KeyMainName", tmp); + mir_free(tmp); + } + + if ((s = output.find(")", s2)) == string::npos) + s = output.find(">", s2); + else if (s > output.find(">", s2)) + s = output.find(">", s2); + s2++; + if (s != string::npos && s2 != string::npos) { + if (output[s] == ')') { + tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); + mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(hcnt, "KeyComment", tmp); + mir_free(tmp); + s += 3; + s2 = output.find(">", s); + if (s != string::npos && s2 != string::npos) { + tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); + mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(hcnt, "KeyMainEmail", tmp); + mir_free(tmp); + } + } + else { + tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); + mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str()); + mir_free(tmp); + } + } + g_plugin.delSetting(hcnt, "bAlwatsTrust"); + } + } + } + else { + char *tmp = nullptr; + string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); + string::size_type s2 = output.find(":", s); + g_plugin.setString(metaGetMostOnline(hContact), "KeyID", output.substr(s, s2 - s).c_str()); + s = output.find(RUS_QUOTE, s2); + if (s == string::npos) { + s = output.find("\"", s2); + s += 1; + } + else s += sizeof(RUS_QUOTE) - 1; + + bool uncommon = false; + if ((s2 = output.find("(", s)) == string::npos) { + if ((s2 = output.find("<", s)) == string::npos) { + s2 = output.find(RUS_ANGLE, s); + uncommon = true; + } + } + else if (s2 > output.find("<", s)) + s2 = output.find("<", s); + if (s != string::npos && s2 != string::npos) { + tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); + mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", tmp); + mir_free(tmp); + } + if ((s = output.find(")", s2)) == string::npos) + s = output.find(">", s2); + else if (s > output.find(">", s2)) + s = output.find(">", s2); + s2++; + if (s != string::npos && s2 != string::npos) { + if (output[s] == ')') { + tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); + mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", tmp); + mir_free(tmp); + s += 3; + s2 = output.find(">", s); + if (s != string::npos && s2 != string::npos) { + tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); + mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", tmp); + mir_free(tmp); + } + } + else { + tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); + mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str()); + mir_free(tmp); + } + } + g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust"); + } + } + else { + char *tmp = nullptr; + string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); + string::size_type s2 = output.find(":", s); + g_plugin.setString(hContact, "KeyID", output.substr(s, s2 - s).c_str()); + s = output.find(RUS_QUOTE, s2); + if (s == string::npos) { + s = output.find("\"", s2); + s += 1; + } + else s += sizeof(RUS_QUOTE) - 1; + + bool uncommon = false; + if ((s2 = output.find("(", s)) == string::npos) { + if ((s2 = output.find("<", s)) == string::npos) { + s2 = output.find(RUS_ANGLE, s); + uncommon = true; + } + } + else if (s2 > output.find("<", s)) + s2 = output.find("<", s); + if (s != string::npos && s2 != string::npos) { + tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); + mir_strcpy(tmp, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(hContact, "KeyMainName", tmp); + mir_free(tmp); + } + if ((s = output.find(")", s2)) == string::npos) + s = output.find(">", s2); + else if (s > output.find(">", s2)) + s = output.find(">", s2); + s2++; + if (s != string::npos && s2 != string::npos) { + if (output[s] == ')') { + tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); + mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(hContact, "KeyComment", tmp); + mir_free(tmp); + s += 3; + s2 = output.find(">", s); + if (s != string::npos && s2 != string::npos) { + tmp = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s).length() + 1)); + mir_strcpy(tmp, output.substr(s, s2 - s).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(hContact, "KeyMainEmail", tmp); + mir_free(tmp); + } + } + else { + tmp = (char*)mir_alloc(sizeof(char)* (output.substr(s2, s - s2).length() + 1)); + mir_strcpy(tmp, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); + mir_free(tmp); + } + } + g_plugin.delSetting(hContact, "bAlwatsTrust"); + } + + MessageBox(nullptr, toUTF16(output).c_str(), L"", MB_OK); + boost::filesystem::remove(tmp2.c_str()); +} diff --git a/plugins/New_GPG/src/messages.cpp b/plugins/New_GPG/src/messages.cpp index f79d09361c..c71281696d 100644 --- a/plugins/New_GPG/src/messages.cpp +++ b/plugins/New_GPG/src/messages.cpp @@ -1,788 +1,788 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -std::list sent_msgs; - -struct RecvParams -{ - RecvParams(MCONTACT _p1, std::wstring _p2, const char *_p3, uint32_t _p4) : - hContact(_p1), - str(_p2), - msg(_p3), - timestamp(_p4) - {} - - MCONTACT hContact; - std::wstring str; - std::string msg; - uint32_t timestamp; -}; - -static void RecvMsgSvc_func(RecvParams *param) -{ - MCONTACT hContact = param->hContact; - std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact))); - - // check for gpg related data - wstring::size_type s1 = param->str.find(L"-----BEGIN PGP MESSAGE-----"); - wstring::size_type s2 = param->str.find(L"-----END PGP MESSAGE-----"); - if (s2 != wstring::npos && s1 != wstring::npos) { //this is generic encrypted data block - if (!isContactSecured(hContact)) { - if (globals.debuglog) - globals.debuglog << "info: received encrypted message from: " + szScreenName + " with turned off encryption"; - if (MessageBox(nullptr, TranslateT("We received encrypted message from contact with encryption turned off.\nDo you want to turn on encryption for this contact?"), TranslateT("Warning"), MB_YESNO) == IDYES) { - if (!isContactHaveKey(hContact)) - ShowLoadPublicKeyDialog(hContact, true); - else { - g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1); - setSrmmIcon(hContact); - } - - if (isContactHaveKey(hContact)) { - g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1); - setSrmmIcon(hContact); - } - } - else if (MessageBox(nullptr, TranslateT("Do you want to try to decrypt encrypted message?"), TranslateT("Warning"), MB_YESNO) == IDNO) { - HistoryLog(hContact, param->msg.c_str(), param->timestamp); - delete param; - return; - } - } - else if (globals.debuglog) - globals.debuglog << "info: received encrypted message from: " + szScreenName; - boost::algorithm::erase_all(param->str, "\r"); - s2 += mir_wstrlen(L"-----END PGP MESSAGE-----"); - - ptrW ptszHomePath(g_plugin.getWStringA("szHomePath", L"")); - wstring encfile = toUTF16(get_random(10)); - wstring decfile = toUTF16(get_random(10)); - { - wstring path = wstring(ptszHomePath) + L"\\tmp\\" + encfile; - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path, e); - } - - { - const int timeout = 5000, step = 100; - int count = 0; - - fstream f(path.c_str(), std::ios::out); - while (!f.is_open()) { - ::Sleep(step); - count += step; - if (count >= timeout) { - g_plugin.setByte(hContact, "GPGEncryption", 0); - setSrmmIcon(hContact); - globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock"; - delete param; - return; - } - f.open(path.c_str(), std::ios::out); - } - char *tmp = mir_u2a(param->str.substr(s1, s2 - s1).c_str()); - f << tmp; - mir_free(tmp); - f.close(); - } - - gpg_execution_params params; - params.addParam(L"--batch"); - { - CMStringA inkeyid = g_plugin.getMStringA(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID"); - CMStringW pass; - if (!inkeyid.IsEmpty()) { - string dbsetting = "szKey_"; - dbsetting += inkeyid; - dbsetting += "_Password"; - pass = g_plugin.getMStringW(dbsetting.c_str()); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to decrypt message from " + szScreenName + " with password"; - } - else { - pass = g_plugin.getMStringW("szKeyPassword"); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + szScreenName + " with password"; - } - if (!pass.IsEmpty()) { - params.addParam(L"--passphrase"); - params.addParam(pass.c_str()); - } - else if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName + " with password"; - params.addParam(L"--passphrase"); - params.addParam(globals.wszPassword.c_str()); - } - else if (globals.debuglog) - globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + szScreenName + " without password"; - } - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + decfile, e); - } - - params.addParam(L"--output"); - params.addParam(std::wstring(ptszHomePath) + L"\\tmp\\" + decfile); - params.addParam(L"-d"); - params.addParam(L"-a"); - params.addParam(path); - - bool bRes = gpg_launcher(params); - if (!bRes || params.result == pxNotFound) { - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path, e); - } - - SendErrorMessage(hContact); - HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); - delete param; - return; - } - - // TODO: check gpg output for errors - globals._terminate = false; - - string out(params.out); - while (out.find("public key decryption failed: bad passphrase") != string::npos) { - if (globals.debuglog) - globals.debuglog << "info: failed to decrypt message from " + szScreenName + " password needed, trying to get one"; - if (globals._terminate) { - SendErrorMessage(hContact); - break; - } - { - // save inkey id - string::size_type s = out.find(" encrypted with "); - s = out.find(" ID ", s); - s += mir_strlen(" ID "); - g_plugin.setString(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID", out.substr(s, out.find(",", s) - s).c_str()); - } - - CDlgKeyPasswordMsgBox(hContact).DoModal(); - - gpg_execution_params params2; - params2.aargv = params.aargv; - if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName; - - params2.addParam(L"--passphrase"); - params2.addParam(globals.wszPassword.c_str()); - } - - bRes = gpg_launcher(params2); - if (!bRes || params2.result == pxNotFound) { - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path, e); - } - - HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); - SendErrorMessage(hContact); - delete param; - return; - } - } - - out.clear(); - bRes = gpg_launcher(params); - if (!bRes || params.result == pxNotFound) { - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path, e); - } - - HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); - SendErrorMessage(hContact); - delete param; - return; - } - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + encfile, e); - } - - if (!boost::filesystem::exists(wstring(ptszHomePath) + L"\\tmp\\" + decfile)) { - if (globals.debuglog) - globals.debuglog << "info: Failed to decrypt GPG encrypted message."; - - string str1 = param->msg; - str1.insert(0, "\n"); - str1.insert(0, TranslateU("Received unencrypted message:")); - - HistoryLog(hContact, str1.c_str(), param->timestamp); - SendErrorMessage(hContact); - delete param; - return; - } - - std::string str; - - wstring tszDecPath = wstring(ptszHomePath) + L"\\tmp\\" + decfile; - - fstream f(tszDecPath.c_str(), std::ios::in | std::ios::ate | std::ios::binary); - if (f.is_open()) { - size_t size = f.tellg(); - char *tmp = new char[size + 1]; - f.seekg(0, std::ios::beg); - f.read(tmp, size); - tmp[size] = '\0'; - - str.append(tmp); - delete[] tmp; - f.close(); - if (!globals.debuglog) { - boost::system::error_code ec; - boost::filesystem::remove(tszDecPath, ec); - if (ec) { - //TODO: handle error - } - } - } - - if (str.empty()) { - if (globals.debuglog) - globals.debuglog << "info: Failed to decrypt GPG encrypted message."; - - string szMsg = param->msg; - szMsg.insert(0, TranslateU("Failed to decrypt GPG encrypted message.\nMessage body for manual decryption:\n")); - - HistoryLog(hContact, param->msg.c_str(), param->timestamp); - SendErrorMessage(hContact); - delete param; - return; - } - - fix_line_term(str); - if (g_plugin.bAppendTags) { - str.insert(0, toUTF8(globals.wszInopentag.c_str())); - str.append(toUTF8(globals.wszInclosetag.c_str())); - } - - HistoryLog(hContact, str.c_str(), param->timestamp); - delete param; - return; - } - } - - if (g_plugin.getByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption")) - HistoryLog(hContact, param->msg.c_str(), param->timestamp, DBEF_READ); - else - HistoryLog(hContact, param->msg.c_str(), param->timestamp); - - delete param; -} - -INT_PTR RecvMsgSvc(WPARAM w, LPARAM l) -{ - CCSDATA *ccs = (CCSDATA*)l; - if (!ccs) - return Proto_ChainRecv(w, ccs); - - PROTORECVEVENT *pre = (PROTORECVEVENT*)(ccs->lParam); - if (!pre) - return Proto_ChainRecv(w, ccs); - - char *msg = pre->szMessage; - if (!msg) - return Proto_ChainRecv(w, ccs); - - if (db_mc_isMeta(ccs->hContact)) { - if (!strstr(msg, "-----BEGIN PGP MESSAGE-----")) - return Proto_ChainRecv(w, ccs); - else { - if (globals.debuglog) - globals.debuglog << "info: blocked pgp message to metacontact:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - return 0; - } - } - - wstring str = toUTF16(msg); - size_t s1, s2; - if (g_plugin.bAutoExchange && (str.find(L"-----PGP KEY RESPONSE-----") != wstring::npos)) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange): parsing key response:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----"); - s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); - if (s1 != wstring::npos && s2 != wstring::npos) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange): found pubkey block:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); - g_plugin.setWString(ccs->hContact, "GPGPubKey", str.substr(s1, s2 - s1).c_str()); - { - // gpg execute block - CMStringW tmp2(g_plugin.getMStringW("szHomePath")); - tmp2 += L"\\"; - tmp2 += get_random(5).c_str(); - tmp2 += L".asc"; - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(tmp2.c_str(), e); - } - wfstream f(tmp2, std::ios::out); - { - const int timeout = 5000, step = 100; - int count = 0; - while (!f.is_open()) { - ::Sleep(step); - count += step; - if (count >= timeout) { - g_plugin.setByte(ccs->hContact, "GPGEncryption", 0); - setSrmmIcon(ccs->hContact); - globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock"; - return 1; - } - f.open(tmp2, std::ios::out); - } - } - f << g_plugin.getMStringW(ccs->hContact, "GPGPubKey").c_str(); - f.close(); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(tmp2.c_str()); - if (!gpg_launcher(params)) - return 1; - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(tmp2.c_str(), e); - } - if (params.result == pxNotFound) - return 1; - - string output(params.out); - s1 = output.find("gpg: key ") + mir_strlen("gpg: key "); - s2 = output.find(":", s1); - g_plugin.setString(ccs->hContact, "KeyID", output.substr(s1, s2 - s1).c_str()); - s2 += 2; - s1 = output.find(RUS_QUOTE, s2); - if (s1 == string::npos) { - s1 = output.find("\"", s2); - s1 += 1; - } - else s1 += sizeof(RUS_QUOTE) - 1; - - if ((s2 = output.find("(", s1)) == string::npos) - s2 = output.find("<", s1); - else if (s2 > output.find("<", s1)) - s2 = output.find("<", s1); - - char *tmp = (char*)mir_alloc(output.substr(s1, s2 - s1 - 1).length() + 1); - mir_strcpy(tmp, output.substr(s1, s2 - s1 - 1).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(ccs->hContact, "KeyMainName", tmp); - mir_free(tmp); - if ((s1 = output.find(")", s2)) == string::npos) - s1 = output.find(">", s2); - else if (s1 > output.find(">", s2)) - s1 = output.find(">", s2); - s2++; - if (output[s1] == ')') { - tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1); - mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(ccs->hContact, "KeyComment", tmp); - mir_free(tmp); - s1 += 3; - s2 = output.find(">", s1); - tmp = (char*)mir_alloc(output.substr(s1, s2 - s1).length() + 1); - mir_strcpy(tmp, output.substr(s1, s2 - s1).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(ccs->hContact, "KeyMainEmail", tmp); - mir_free(tmp); - } - else { - tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1); - mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str()); - mir_utf8decode(tmp, nullptr); - g_plugin.setString(ccs->hContact, "KeyMainEmail", output.substr(s2, s1 - s2).c_str()); - mir_free(tmp); - } - g_plugin.setByte(ccs->hContact, "GPGEncryption", 1); - g_plugin.setByte(ccs->hContact, "bAlwatsTrust", 1); - setSrmmIcon(ccs->hContact); - if (db_mc_isSub(ccs->hContact)) - setSrmmIcon(db_mc_getMeta(ccs->hContact)); - - HistoryLog(ccs->hContact, "PGP Encryption turned on by key autoexchange feature"); - } - return 1; - } - } - if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) == wstring::npos) || ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) == wstring::npos)) { - s2 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----"); - s1 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); - } - if ((s2 != wstring::npos) && (s1 != wstring::npos)) { //this is public key - if (globals.debuglog) - globals.debuglog << "info: received key from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - s1 = 0; - while ((s1 = str.find(L"\r", s1)) != wstring::npos) - str.erase(s1, 1); - if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) - s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); - else if (((s2 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) - s2 += mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----"); - CDlgNewKey *d = new CDlgNewKey(ccs->hContact, str.substr(s1, s2 - s1)); - d->Show(); - HistoryLog(ccs->hContact, msg); - return 0; - } - - if (g_plugin.bAutoExchange && strstr(msg, "-----PGP KEY REQUEST-----") && globals.gpg_valid && globals.gpg_keyexist) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange): received key request from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); - - CMStringA tmp(g_plugin.getMStringA("GPGPubKey")); - if (!tmp.IsEmpty()) { - int enc_state = g_plugin.getByte(ccs->hContact, "GPGEncryption"); - if (enc_state) - g_plugin.setByte(ccs->hContact, "GPGEncryption", 0); - - string str1 = "-----PGP KEY RESPONSE-----"; - str1.append(tmp); - ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)str1.c_str()); - if (enc_state) - g_plugin.setByte(ccs->hContact, "GPGEncryption", 1); - } - return 0; - } - else if (!isContactHaveKey(ccs->hContact) && g_plugin.bAutoExchange && globals.gpg_valid && globals.gpg_keyexist) { - char *proto = Proto_GetBaseAccountName(ccs->hContact); - ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", "")); - if (jid[0]) { - for (auto p : globals.Accounts) { - ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); - if (caps) { - string str1; - for (int i = 0;; i++) { - str1.push_back(caps[i]); - if (caps[i] == '\0') - if (caps[i + 1] == '\0') - break; - } - - if (str1.find("GPG_Key_Auto_Exchange:0") != string::npos) { - ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); - return 0; - } - } - } - } - } - - if (!strstr(msg, "-----BEGIN PGP MESSAGE-----")) - return Proto_ChainRecv(w, ccs); - - mir_forkThread(RecvMsgSvc_func, new RecvParams(ccs->hContact, str, msg, pre->timestamp)); - return 0; -} - -void SendMsgSvc_func(MCONTACT hContact, char *msg, uint32_t flags) -{ - string str = msg; - if (g_plugin.bStripTags && g_plugin.bAppendTags) { - if (globals.debuglog) - globals.debuglog << "info: stripping tags in outgoing message, name: " + toUTF8(Clist_GetContactDisplayName(hContact)); - strip_tags(str); - } - -LBL_Relaunch: - wstring file = toUTF16(get_random(10)); - gpg_execution_params params; - { - CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID")); - if (tmp.IsEmpty()) { - HistoryLog(hContact, "Failed to encrypt message with GPG (not found key for encryption in db", DBEF_SENT); - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - return; - } - - if (g_plugin.getByte(hContact, "bAlwaysTrust", 0)) { - params.addParam(L"--trust-model"); - params.addParam(L"always"); - } - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"-eatr"); - params.addParam(_A2T(tmp).get()); - } - - CMStringW path(g_plugin.getMStringW("szHomePath")); - path += L"\\tmp\\"; - path += file.c_str(); - params.addParam(path.c_str()); - - const int timeout = 5000, step = 100; - int count = 0; - { - fstream f(path.c_str(), std::ios::out); - while (!f.is_open()) { - ::Sleep(step); - count += step; - if (count >= timeout) { - g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption - setSrmmIcon(hContact); - globals.debuglog << "info: failed to create temporary file for encryption, disabling encryption to avoid deadlock"; - break; - } - f.open(path.c_str(), std::ios::out); - } - if (count < timeout) { - f.write(str.c_str(), str.size()); - f.close(); - } - } - - if (!gpg_launcher(params)) { - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - return; - } - - if (params.result == pxNotFound) { - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - return; - } - - if (params.out.Find("There is no assurance this key belongs to the named user") != -1) { - if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO)) - return; - - g_plugin.setByte(hContact, "bAlwaysTrust", 1); - params.aargv.clear(); - goto LBL_Relaunch; - } - - if (params.out.Find("usage: ") != -1) { - MessageBox(nullptr, TranslateT("Something is wrong, GPG does not understand us, aborting encryption."), TranslateT("Warning"), MB_OK); - //mir_free(msg); - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path.c_str(), e); - } - return; - } - - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path.c_str(), e); - } - - path += L".asc"; - fstream f(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); - count = 0; - while (!f.is_open()) { - ::Sleep(step); - f.open(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); - count += step; - if (count >= timeout) { - g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption - setSrmmIcon(hContact); - globals.debuglog << "info: gpg failed to encrypt message, disabling encryption to avoid deadlock"; - break; - } - } - - str.clear(); - if (f.is_open()) { - size_t size = f.tellg(); - char *tmp = new char[size + 1]; - f.seekg(0, std::ios::beg); - f.read(tmp, size); - tmp[size] = '\0'; - str.append(tmp); - delete[] tmp; - f.close(); - if (!globals.debuglog) { - boost::system::error_code e; - boost::filesystem::remove(path.c_str(), e); - } - } - - if (str.empty()) { - HistoryLog(hContact, "Failed to encrypt message with GPG", DBEF_SENT); - if (globals.debuglog) - globals.debuglog << "info: Failed to encrypt message with GPG"; - ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); - return; - } - - string str_event = msg; - if (g_plugin.bAppendTags) { - str_event.insert(0, toUTF8(globals.wszOutopentag.c_str())); - str_event.append(toUTF8(globals.wszOutclosetag.c_str())); - } - - if (globals.debuglog) - globals.debuglog << "adding event to contact: " + toUTF8(Clist_GetContactDisplayName(hContact)) + " on send message."; - - fix_line_term(str); - sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)str.c_str())); -} - -INT_PTR SendMsgSvc(WPARAM w, LPARAM l) -{ - CCSDATA *ccs = (CCSDATA*)l; - if (!ccs) - return Proto_ChainSend(w, ccs); - - if (!ccs->lParam) - return Proto_ChainSend(w, ccs); - - std::string szScreenName(toUTF8(Clist_GetContactDisplayName(ccs->hContact))); - char *msg = (char*)ccs->lParam; - if (!msg) { - if (globals.debuglog) - globals.debuglog << "info: failed to get message data, name: " + szScreenName; - return Proto_ChainSend(w, ccs); - } - - if (strstr(msg, "-----BEGIN PGP MESSAGE-----")) { - if (globals.debuglog) - globals.debuglog << "info: encrypted message, let it go, name: " + szScreenName; - return Proto_ChainSend(w, ccs); - } - - if (globals.debuglog) - globals.debuglog << "info: contact have key, name: " + szScreenName; - - if (globals.debuglog && db_mc_isMeta(ccs->hContact)) - globals.debuglog << "info: protocol is metacontacts, name: " + szScreenName; - - if (!isContactSecured(ccs->hContact) || db_mc_isMeta(ccs->hContact)) { - if (globals.debuglog) - globals.debuglog << "info: contact not secured, name: " + szScreenName; - return Proto_ChainSend(w, ccs); - } - - ProtoBroadcastAsync(Proto_GetBaseAccountName(ccs->hContact), ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)777); - return 777; -} - -int HookSendMsg(WPARAM w, LPARAM l) -{ - if (!l) - return 0; - - DBEVENTINFO *dbei = (DBEVENTINFO*)l; - if (dbei->eventType != EVENTTYPE_MESSAGE || (dbei->flags & DBEF_READ)) - return 0; - - MCONTACT hContact = (MCONTACT)w; - std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact))); - - if (dbei->flags & DBEF_SENT) { - if (isContactSecured(hContact) && strstr((char*)dbei->pBlob, "-----BEGIN PGP MESSAGE-----")) //our service data, can be double added by metacontacts e.w.c. - { - if (globals.debuglog) - globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName; - return 1; - } - if (g_plugin.bAutoExchange && (strstr((char*)dbei->pBlob, "-----PGP KEY RESPONSE-----") || strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----"))) ///do not show service data in history - { - if (globals.debuglog) - globals.debuglog << "info(send handler): block pgp key request/response event, name: " + szScreenName; - return 1; - } - } - - if (db_mc_isMeta(hContact)) - return 0; - - if (!isContactHaveKey(hContact)) { - if (globals.debuglog) - globals.debuglog << "info: contact have not key, name: " + szScreenName; - - if (g_plugin.bAutoExchange && !strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----") && !strstr((char*)dbei->pBlob, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && globals.gpg_valid) { - if (globals.debuglog) - globals.debuglog << "info: checking for autoexchange possibility, name: " + szScreenName; - - LPSTR proto = Proto_GetBaseAccountName(hContact); - ptrA jid(db_get_utfa(hContact, proto, "jid", "")); - if (jid[0]) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange): protocol looks like jabber, name: " + szScreenName; - for (auto p : globals.Accounts) { - ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); - if (caps) { - string str; - for (int i = 0;; i++) { - str.push_back(caps[i]); - if (caps[i] == '\0') - if (caps[i + 1] == '\0') - break; - } - - if (str.find("GPG_Key_Auto_Exchange:0") != string::npos) { - if (globals.debuglog) - globals.debuglog << "info(autoexchange, jabber): autoexchange capability found, sending key request, name: " + szScreenName; - ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); - globals.hcontact_data[hContact].msgs_to_send.push_back((char*)dbei->pBlob); - mir_forkthread(send_encrypted_msgs_thread, (void*)hContact); - return 0; - } - } - } - } - } - else return 0; - } - - if (isContactSecured(hContact) && (dbei->flags & DBEF_SENT)) //aggressive outgoing events filtering - { - SendMsgSvc_func(hContact, (char*)dbei->pBlob, 0); - //TODO: handle errors somehow ... - if (g_plugin.bAppendTags) { - string str_event = (char*)dbei->pBlob; - //mir_free(dbei->pBlob); - str_event.insert(0, toUTF8(globals.wszOutopentag.c_str())); - str_event.append(toUTF8(globals.wszOutclosetag.c_str())); - dbei->pBlob = (uint8_t*)mir_strdup(str_event.c_str()); - dbei->cbBlob = (uint32_t)str_event.length() + 1; - } - - return 0; - } - - if (!isContactSecured(hContact)) { - if (globals.debuglog) - globals.debuglog << "event message: \"" + string((char*)dbei->pBlob) + "\" passed event filter, contact " + szScreenName + " is unsecured"; - return 0; - } - - if (!(dbei->flags & DBEF_SENT) && db_mc_isMeta((MCONTACT)w)) { - char tmp[29]; - strncpy(tmp, (char*)dbei->pBlob, 27); - tmp[28] = '\0'; - if (strstr(tmp, "-----BEGIN PGP MESSAGE-----")) { - if (globals.debuglog) - globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName; - return 1; - } - } - return 0; -} +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +std::list sent_msgs; + +struct RecvParams +{ + RecvParams(MCONTACT _p1, std::wstring _p2, const char *_p3, uint32_t _p4) : + hContact(_p1), + str(_p2), + msg(_p3), + timestamp(_p4) + {} + + MCONTACT hContact; + std::wstring str; + std::string msg; + uint32_t timestamp; +}; + +static void RecvMsgSvc_func(RecvParams *param) +{ + MCONTACT hContact = param->hContact; + std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact))); + + // check for gpg related data + wstring::size_type s1 = param->str.find(L"-----BEGIN PGP MESSAGE-----"); + wstring::size_type s2 = param->str.find(L"-----END PGP MESSAGE-----"); + if (s2 != wstring::npos && s1 != wstring::npos) { //this is generic encrypted data block + if (!isContactSecured(hContact)) { + if (globals.debuglog) + globals.debuglog << "info: received encrypted message from: " + szScreenName + " with turned off encryption"; + if (MessageBox(nullptr, TranslateT("We received encrypted message from contact with encryption turned off.\nDo you want to turn on encryption for this contact?"), TranslateT("Warning"), MB_YESNO) == IDYES) { + if (!isContactHaveKey(hContact)) + ShowLoadPublicKeyDialog(hContact, true); + else { + g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1); + setSrmmIcon(hContact); + } + + if (isContactHaveKey(hContact)) { + g_plugin.setByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption", 1); + setSrmmIcon(hContact); + } + } + else if (MessageBox(nullptr, TranslateT("Do you want to try to decrypt encrypted message?"), TranslateT("Warning"), MB_YESNO) == IDNO) { + HistoryLog(hContact, param->msg.c_str(), param->timestamp); + delete param; + return; + } + } + else if (globals.debuglog) + globals.debuglog << "info: received encrypted message from: " + szScreenName; + boost::algorithm::erase_all(param->str, "\r"); + s2 += mir_wstrlen(L"-----END PGP MESSAGE-----"); + + ptrW ptszHomePath(g_plugin.getWStringA("szHomePath", L"")); + wstring encfile = toUTF16(get_random(10)); + wstring decfile = toUTF16(get_random(10)); + { + wstring path = wstring(ptszHomePath) + L"\\tmp\\" + encfile; + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(path, e); + } + + { + const int timeout = 5000, step = 100; + int count = 0; + + fstream f(path.c_str(), std::ios::out); + while (!f.is_open()) { + ::Sleep(step); + count += step; + if (count >= timeout) { + g_plugin.setByte(hContact, "GPGEncryption", 0); + setSrmmIcon(hContact); + globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock"; + delete param; + return; + } + f.open(path.c_str(), std::ios::out); + } + char *tmp = mir_u2a(param->str.substr(s1, s2 - s1).c_str()); + f << tmp; + mir_free(tmp); + f.close(); + } + + gpg_execution_params params; + params.addParam(L"--batch"); + { + CMStringA inkeyid = g_plugin.getMStringA(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID"); + CMStringW pass; + if (!inkeyid.IsEmpty()) { + string dbsetting = "szKey_"; + dbsetting += inkeyid; + dbsetting += "_Password"; + pass = g_plugin.getMStringW(dbsetting.c_str()); + if (!pass.IsEmpty() && globals.debuglog) + globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to decrypt message from " + szScreenName + " with password"; + } + else { + pass = g_plugin.getMStringW("szKeyPassword"); + if (!pass.IsEmpty() && globals.debuglog) + globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + szScreenName + " with password"; + } + if (!pass.IsEmpty()) { + params.addParam(L"--passphrase"); + params.addParam(pass.c_str()); + } + else if (!globals.wszPassword.IsEmpty()) { + if (globals.debuglog) + globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName + " with password"; + params.addParam(L"--passphrase"); + params.addParam(globals.wszPassword.c_str()); + } + else if (globals.debuglog) + globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + szScreenName + " without password"; + } + + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + decfile, e); + } + + params.addParam(L"--output"); + params.addParam(std::wstring(ptszHomePath) + L"\\tmp\\" + decfile); + params.addParam(L"-d"); + params.addParam(L"-a"); + params.addParam(path); + + bool bRes = gpg_launcher(params); + if (!bRes || params.result == pxNotFound) { + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(path, e); + } + + SendErrorMessage(hContact); + HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); + delete param; + return; + } + + // TODO: check gpg output for errors + globals._terminate = false; + + string out(params.out); + while (out.find("public key decryption failed: bad passphrase") != string::npos) { + if (globals.debuglog) + globals.debuglog << "info: failed to decrypt message from " + szScreenName + " password needed, trying to get one"; + if (globals._terminate) { + SendErrorMessage(hContact); + break; + } + { + // save inkey id + string::size_type s = out.find(" encrypted with "); + s = out.find(" ID ", s); + s += mir_strlen(" ID "); + g_plugin.setString(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "InKeyID", out.substr(s, out.find(",", s) - s).c_str()); + } + + CDlgKeyPasswordMsgBox(hContact).DoModal(); + + gpg_execution_params params2; + params2.aargv = params.aargv; + if (!globals.wszPassword.IsEmpty()) { + if (globals.debuglog) + globals.debuglog << "info: found password in memory, trying to decrypt message from " + szScreenName; + + params2.addParam(L"--passphrase"); + params2.addParam(globals.wszPassword.c_str()); + } + + bRes = gpg_launcher(params2); + if (!bRes || params2.result == pxNotFound) { + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(path, e); + } + + HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); + SendErrorMessage(hContact); + delete param; + return; + } + } + + out.clear(); + bRes = gpg_launcher(params); + if (!bRes || params.result == pxNotFound) { + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(path, e); + } + + HistoryLog(hContact, TranslateU("GPG cannot decrypt incoming message"), param->timestamp); + SendErrorMessage(hContact); + delete param; + return; + } + + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + encfile, e); + } + + if (!boost::filesystem::exists(wstring(ptszHomePath) + L"\\tmp\\" + decfile)) { + if (globals.debuglog) + globals.debuglog << "info: Failed to decrypt GPG encrypted message."; + + string str1 = param->msg; + str1.insert(0, "\n"); + str1.insert(0, TranslateU("Received unencrypted message:")); + + HistoryLog(hContact, str1.c_str(), param->timestamp); + SendErrorMessage(hContact); + delete param; + return; + } + + std::string str; + + wstring tszDecPath = wstring(ptszHomePath) + L"\\tmp\\" + decfile; + + fstream f(tszDecPath.c_str(), std::ios::in | std::ios::ate | std::ios::binary); + if (f.is_open()) { + size_t size = f.tellg(); + char *tmp = new char[size + 1]; + f.seekg(0, std::ios::beg); + f.read(tmp, size); + tmp[size] = '\0'; + + str.append(tmp); + delete[] tmp; + f.close(); + if (!globals.debuglog) { + boost::system::error_code ec; + boost::filesystem::remove(tszDecPath, ec); + if (ec) { + //TODO: handle error + } + } + } + + if (str.empty()) { + if (globals.debuglog) + globals.debuglog << "info: Failed to decrypt GPG encrypted message."; + + string szMsg = param->msg; + szMsg.insert(0, TranslateU("Failed to decrypt GPG encrypted message.\nMessage body for manual decryption:\n")); + + HistoryLog(hContact, param->msg.c_str(), param->timestamp); + SendErrorMessage(hContact); + delete param; + return; + } + + fix_line_term(str); + if (g_plugin.bAppendTags) { + str.insert(0, toUTF8(globals.wszInopentag.c_str())); + str.append(toUTF8(globals.wszInclosetag.c_str())); + } + + HistoryLog(hContact, str.c_str(), param->timestamp); + delete param; + return; + } + } + + if (g_plugin.getByte(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, "GPGEncryption")) + HistoryLog(hContact, param->msg.c_str(), param->timestamp, DBEF_READ); + else + HistoryLog(hContact, param->msg.c_str(), param->timestamp); + + delete param; +} + +INT_PTR RecvMsgSvc(WPARAM w, LPARAM l) +{ + CCSDATA *ccs = (CCSDATA*)l; + if (!ccs) + return Proto_ChainRecv(w, ccs); + + PROTORECVEVENT *pre = (PROTORECVEVENT*)(ccs->lParam); + if (!pre) + return Proto_ChainRecv(w, ccs); + + char *msg = pre->szMessage; + if (!msg) + return Proto_ChainRecv(w, ccs); + + if (db_mc_isMeta(ccs->hContact)) { + if (!strstr(msg, "-----BEGIN PGP MESSAGE-----")) + return Proto_ChainRecv(w, ccs); + else { + if (globals.debuglog) + globals.debuglog << "info: blocked pgp message to metacontact:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); + return 0; + } + } + + wstring str = toUTF16(msg); + size_t s1, s2; + if (g_plugin.bAutoExchange && (str.find(L"-----PGP KEY RESPONSE-----") != wstring::npos)) { + if (globals.debuglog) + globals.debuglog << "info(autoexchange): parsing key response:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); + s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----"); + s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); + if (s1 != wstring::npos && s2 != wstring::npos) { + if (globals.debuglog) + globals.debuglog << "info(autoexchange): found pubkey block:" + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); + s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); + g_plugin.setWString(ccs->hContact, "GPGPubKey", str.substr(s1, s2 - s1).c_str()); + { + // gpg execute block + CMStringW tmp2(g_plugin.getMStringW("szHomePath")); + tmp2 += L"\\"; + tmp2 += get_random(5).c_str(); + tmp2 += L".asc"; + + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(tmp2.c_str(), e); + } + wfstream f(tmp2, std::ios::out); + { + const int timeout = 5000, step = 100; + int count = 0; + while (!f.is_open()) { + ::Sleep(step); + count += step; + if (count >= timeout) { + g_plugin.setByte(ccs->hContact, "GPGEncryption", 0); + setSrmmIcon(ccs->hContact); + globals.debuglog << "info: failed to create temporary file for decryption, disabling gpg for contact to avoid deadlock"; + return 1; + } + f.open(tmp2, std::ios::out); + } + } + f << g_plugin.getMStringW(ccs->hContact, "GPGPubKey").c_str(); + f.close(); + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--import"); + params.addParam(tmp2.c_str()); + if (!gpg_launcher(params)) + return 1; + + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(tmp2.c_str(), e); + } + if (params.result == pxNotFound) + return 1; + + string output(params.out); + s1 = output.find("gpg: key ") + mir_strlen("gpg: key "); + s2 = output.find(":", s1); + g_plugin.setString(ccs->hContact, "KeyID", output.substr(s1, s2 - s1).c_str()); + s2 += 2; + s1 = output.find(RUS_QUOTE, s2); + if (s1 == string::npos) { + s1 = output.find("\"", s2); + s1 += 1; + } + else s1 += sizeof(RUS_QUOTE) - 1; + + if ((s2 = output.find("(", s1)) == string::npos) + s2 = output.find("<", s1); + else if (s2 > output.find("<", s1)) + s2 = output.find("<", s1); + + char *tmp = (char*)mir_alloc(output.substr(s1, s2 - s1 - 1).length() + 1); + mir_strcpy(tmp, output.substr(s1, s2 - s1 - 1).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(ccs->hContact, "KeyMainName", tmp); + mir_free(tmp); + if ((s1 = output.find(")", s2)) == string::npos) + s1 = output.find(">", s2); + else if (s1 > output.find(">", s2)) + s1 = output.find(">", s2); + s2++; + if (output[s1] == ')') { + tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1); + mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(ccs->hContact, "KeyComment", tmp); + mir_free(tmp); + s1 += 3; + s2 = output.find(">", s1); + tmp = (char*)mir_alloc(output.substr(s1, s2 - s1).length() + 1); + mir_strcpy(tmp, output.substr(s1, s2 - s1).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(ccs->hContact, "KeyMainEmail", tmp); + mir_free(tmp); + } + else { + tmp = (char*)mir_alloc(output.substr(s2, s1 - s2).length() + 1); + mir_strcpy(tmp, output.substr(s2, s1 - s2).c_str()); + mir_utf8decode(tmp, nullptr); + g_plugin.setString(ccs->hContact, "KeyMainEmail", output.substr(s2, s1 - s2).c_str()); + mir_free(tmp); + } + g_plugin.setByte(ccs->hContact, "GPGEncryption", 1); + g_plugin.setByte(ccs->hContact, "bAlwatsTrust", 1); + setSrmmIcon(ccs->hContact); + if (db_mc_isSub(ccs->hContact)) + setSrmmIcon(db_mc_getMeta(ccs->hContact)); + + HistoryLog(ccs->hContact, "PGP Encryption turned on by key autoexchange feature"); + } + return 1; + } + } + if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) == wstring::npos) || ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) == wstring::npos)) { + s2 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----"); + s1 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); + } + if ((s2 != wstring::npos) && (s1 != wstring::npos)) { //this is public key + if (globals.debuglog) + globals.debuglog << "info: received key from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); + s1 = 0; + while ((s1 = str.find(L"\r", s1)) != wstring::npos) + str.erase(s1, 1); + if (((s2 = str.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) + s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); + else if (((s2 = str.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((s1 = str.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) + s2 += mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----"); + CDlgNewKey *d = new CDlgNewKey(ccs->hContact, str.substr(s1, s2 - s1)); + d->Show(); + HistoryLog(ccs->hContact, msg); + return 0; + } + + if (g_plugin.bAutoExchange && strstr(msg, "-----PGP KEY REQUEST-----") && globals.gpg_valid && globals.gpg_keyexist) { + if (globals.debuglog) + globals.debuglog << "info(autoexchange): received key request from: " + toUTF8(Clist_GetContactDisplayName(ccs->hContact)); + + CMStringA tmp(g_plugin.getMStringA("GPGPubKey")); + if (!tmp.IsEmpty()) { + int enc_state = g_plugin.getByte(ccs->hContact, "GPGEncryption"); + if (enc_state) + g_plugin.setByte(ccs->hContact, "GPGEncryption", 0); + + string str1 = "-----PGP KEY RESPONSE-----"; + str1.append(tmp); + ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)str1.c_str()); + if (enc_state) + g_plugin.setByte(ccs->hContact, "GPGEncryption", 1); + } + return 0; + } + else if (!isContactHaveKey(ccs->hContact) && g_plugin.bAutoExchange && globals.gpg_valid && globals.gpg_keyexist) { + char *proto = Proto_GetBaseAccountName(ccs->hContact); + ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", "")); + if (jid[0]) { + for (auto p : globals.Accounts) { + ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); + if (caps) { + string str1; + for (int i = 0;; i++) { + str1.push_back(caps[i]); + if (caps[i] == '\0') + if (caps[i + 1] == '\0') + break; + } + + if (str1.find("GPG_Key_Auto_Exchange:0") != string::npos) { + ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); + return 0; + } + } + } + } + } + + if (!strstr(msg, "-----BEGIN PGP MESSAGE-----")) + return Proto_ChainRecv(w, ccs); + + mir_forkThread(RecvMsgSvc_func, new RecvParams(ccs->hContact, str, msg, pre->timestamp)); + return 0; +} + +void SendMsgSvc_func(MCONTACT hContact, char *msg, uint32_t flags) +{ + string str = msg; + if (g_plugin.bStripTags && g_plugin.bAppendTags) { + if (globals.debuglog) + globals.debuglog << "info: stripping tags in outgoing message, name: " + toUTF8(Clist_GetContactDisplayName(hContact)); + strip_tags(str); + } + +LBL_Relaunch: + wstring file = toUTF16(get_random(10)); + gpg_execution_params params; + { + CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID")); + if (tmp.IsEmpty()) { + HistoryLog(hContact, "Failed to encrypt message with GPG (not found key for encryption in db", DBEF_SENT); + ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); + return; + } + + if (g_plugin.getByte(hContact, "bAlwaysTrust", 0)) { + params.addParam(L"--trust-model"); + params.addParam(L"always"); + } + params.addParam(L"--batch"); + params.addParam(L"--yes"); + params.addParam(L"-eatr"); + params.addParam(_A2T(tmp).get()); + } + + CMStringW path(g_plugin.getMStringW("szHomePath")); + path += L"\\tmp\\"; + path += file.c_str(); + params.addParam(path.c_str()); + + const int timeout = 5000, step = 100; + int count = 0; + { + fstream f(path.c_str(), std::ios::out); + while (!f.is_open()) { + ::Sleep(step); + count += step; + if (count >= timeout) { + g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption + setSrmmIcon(hContact); + globals.debuglog << "info: failed to create temporary file for encryption, disabling encryption to avoid deadlock"; + break; + } + f.open(path.c_str(), std::ios::out); + } + if (count < timeout) { + f.write(str.c_str(), str.size()); + f.close(); + } + } + + if (!gpg_launcher(params)) { + ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); + return; + } + + if (params.result == pxNotFound) { + ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); + return; + } + + if (params.out.Find("There is no assurance this key belongs to the named user") != -1) { + if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO)) + return; + + g_plugin.setByte(hContact, "bAlwaysTrust", 1); + params.aargv.clear(); + goto LBL_Relaunch; + } + + if (params.out.Find("usage: ") != -1) { + MessageBox(nullptr, TranslateT("Something is wrong, GPG does not understand us, aborting encryption."), TranslateT("Warning"), MB_OK); + //mir_free(msg); + ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(path.c_str(), e); + } + return; + } + + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(path.c_str(), e); + } + + path += L".asc"; + fstream f(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); + count = 0; + while (!f.is_open()) { + ::Sleep(step); + f.open(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); + count += step; + if (count >= timeout) { + g_plugin.setByte(hContact, "GPGEncryption", 0); //disable encryption + setSrmmIcon(hContact); + globals.debuglog << "info: gpg failed to encrypt message, disabling encryption to avoid deadlock"; + break; + } + } + + str.clear(); + if (f.is_open()) { + size_t size = f.tellg(); + char *tmp = new char[size + 1]; + f.seekg(0, std::ios::beg); + f.read(tmp, size); + tmp[size] = '\0'; + str.append(tmp); + delete[] tmp; + f.close(); + if (!globals.debuglog) { + boost::system::error_code e; + boost::filesystem::remove(path.c_str(), e); + } + } + + if (str.empty()) { + HistoryLog(hContact, "Failed to encrypt message with GPG", DBEF_SENT); + if (globals.debuglog) + globals.debuglog << "info: Failed to encrypt message with GPG"; + ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); + return; + } + + string str_event = msg; + if (g_plugin.bAppendTags) { + str_event.insert(0, toUTF8(globals.wszOutopentag.c_str())); + str_event.append(toUTF8(globals.wszOutclosetag.c_str())); + } + + if (globals.debuglog) + globals.debuglog << "adding event to contact: " + toUTF8(Clist_GetContactDisplayName(hContact)) + " on send message."; + + fix_line_term(str); + sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)str.c_str())); +} + +INT_PTR SendMsgSvc(WPARAM w, LPARAM l) +{ + CCSDATA *ccs = (CCSDATA*)l; + if (!ccs) + return Proto_ChainSend(w, ccs); + + if (!ccs->lParam) + return Proto_ChainSend(w, ccs); + + std::string szScreenName(toUTF8(Clist_GetContactDisplayName(ccs->hContact))); + char *msg = (char*)ccs->lParam; + if (!msg) { + if (globals.debuglog) + globals.debuglog << "info: failed to get message data, name: " + szScreenName; + return Proto_ChainSend(w, ccs); + } + + if (strstr(msg, "-----BEGIN PGP MESSAGE-----")) { + if (globals.debuglog) + globals.debuglog << "info: encrypted message, let it go, name: " + szScreenName; + return Proto_ChainSend(w, ccs); + } + + if (globals.debuglog) + globals.debuglog << "info: contact have key, name: " + szScreenName; + + if (globals.debuglog && db_mc_isMeta(ccs->hContact)) + globals.debuglog << "info: protocol is metacontacts, name: " + szScreenName; + + if (!isContactSecured(ccs->hContact) || db_mc_isMeta(ccs->hContact)) { + if (globals.debuglog) + globals.debuglog << "info: contact not secured, name: " + szScreenName; + return Proto_ChainSend(w, ccs); + } + + ProtoBroadcastAsync(Proto_GetBaseAccountName(ccs->hContact), ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)777); + return 777; +} + +int HookSendMsg(WPARAM w, LPARAM l) +{ + if (!l) + return 0; + + DBEVENTINFO *dbei = (DBEVENTINFO*)l; + if (dbei->eventType != EVENTTYPE_MESSAGE || (dbei->flags & DBEF_READ)) + return 0; + + MCONTACT hContact = (MCONTACT)w; + std::string szScreenName(toUTF8(Clist_GetContactDisplayName(hContact))); + + if (dbei->flags & DBEF_SENT) { + if (isContactSecured(hContact) && strstr((char*)dbei->pBlob, "-----BEGIN PGP MESSAGE-----")) //our service data, can be double added by metacontacts e.w.c. + { + if (globals.debuglog) + globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName; + return 1; + } + if (g_plugin.bAutoExchange && (strstr((char*)dbei->pBlob, "-----PGP KEY RESPONSE-----") || strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----"))) ///do not show service data in history + { + if (globals.debuglog) + globals.debuglog << "info(send handler): block pgp key request/response event, name: " + szScreenName; + return 1; + } + } + + if (db_mc_isMeta(hContact)) + return 0; + + if (!isContactHaveKey(hContact)) { + if (globals.debuglog) + globals.debuglog << "info: contact have not key, name: " + szScreenName; + + if (g_plugin.bAutoExchange && !strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----") && !strstr((char*)dbei->pBlob, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && globals.gpg_valid) { + if (globals.debuglog) + globals.debuglog << "info: checking for autoexchange possibility, name: " + szScreenName; + + LPSTR proto = Proto_GetBaseAccountName(hContact); + ptrA jid(db_get_utfa(hContact, proto, "jid", "")); + if (jid[0]) { + if (globals.debuglog) + globals.debuglog << "info(autoexchange): protocol looks like jabber, name: " + szScreenName; + for (auto p : globals.Accounts) { + ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); + if (caps) { + string str; + for (int i = 0;; i++) { + str.push_back(caps[i]); + if (caps[i] == '\0') + if (caps[i + 1] == '\0') + break; + } + + if (str.find("GPG_Key_Auto_Exchange:0") != string::npos) { + if (globals.debuglog) + globals.debuglog << "info(autoexchange, jabber): autoexchange capability found, sending key request, name: " + szScreenName; + ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); + globals.hcontact_data[hContact].msgs_to_send.push_back((char*)dbei->pBlob); + mir_forkthread(send_encrypted_msgs_thread, (void*)hContact); + return 0; + } + } + } + } + } + else return 0; + } + + if (isContactSecured(hContact) && (dbei->flags & DBEF_SENT)) //aggressive outgoing events filtering + { + SendMsgSvc_func(hContact, (char*)dbei->pBlob, 0); + //TODO: handle errors somehow ... + if (g_plugin.bAppendTags) { + string str_event = (char*)dbei->pBlob; + //mir_free(dbei->pBlob); + str_event.insert(0, toUTF8(globals.wszOutopentag.c_str())); + str_event.append(toUTF8(globals.wszOutclosetag.c_str())); + dbei->pBlob = (uint8_t*)mir_strdup(str_event.c_str()); + dbei->cbBlob = (uint32_t)str_event.length() + 1; + } + + return 0; + } + + if (!isContactSecured(hContact)) { + if (globals.debuglog) + globals.debuglog << "event message: \"" + string((char*)dbei->pBlob) + "\" passed event filter, contact " + szScreenName + " is unsecured"; + return 0; + } + + if (!(dbei->flags & DBEF_SENT) && db_mc_isMeta((MCONTACT)w)) { + char tmp[29]; + strncpy(tmp, (char*)dbei->pBlob, 27); + tmp[28] = '\0'; + if (strstr(tmp, "-----BEGIN PGP MESSAGE-----")) { + if (globals.debuglog) + globals.debuglog << "info(send handler): block pgp message event, name: " + szScreenName; + return 1; + } + } + return 0; +} diff --git a/plugins/New_GPG/src/metacontacts.cpp b/plugins/New_GPG/src/metacontacts.cpp index 276a8fa5d3..524fceba1a 100644 --- a/plugins/New_GPG/src/metacontacts.cpp +++ b/plugins/New_GPG/src/metacontacts.cpp @@ -1,29 +1,29 @@ -// Copyright © 2010-22 SecureIM developers (baloo and others), sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -bool metaIsDefaultSubContact(MCONTACT hContact) -{ - return db_mc_getDefault(db_mc_getMeta(hContact)) == hContact; -} - -MCONTACT metaGetMostOnline(MCONTACT hContact) -{ - if (db_mc_isMeta(hContact)) - return db_mc_getMostOnline(hContact); - return NULL; -} +// Copyright © 2010-23 SecureIM developers (baloo and others), sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +bool metaIsDefaultSubContact(MCONTACT hContact) +{ + return db_mc_getDefault(db_mc_getMeta(hContact)) == hContact; +} + +MCONTACT metaGetMostOnline(MCONTACT hContact) +{ + if (db_mc_isMeta(hContact)) + return db_mc_getMostOnline(hContact); + return NULL; +} diff --git a/plugins/New_GPG/src/metacontacts.h b/plugins/New_GPG/src/metacontacts.h index 307a9c3131..a0d9579bab 100644 --- a/plugins/New_GPG/src/metacontacts.h +++ b/plugins/New_GPG/src/metacontacts.h @@ -1,21 +1,21 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#pragma once - -bool metaIsDefaultSubContact(MCONTACT hContact) ; -MCONTACT metaGetMostOnline(MCONTACT hContact); - +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#pragma once + +bool metaIsDefaultSubContact(MCONTACT hContact) ; +MCONTACT metaGetMostOnline(MCONTACT hContact); + diff --git a/plugins/New_GPG/src/options.cpp b/plugins/New_GPG/src/options.cpp index 2462694f43..cc214ac95a 100644 --- a/plugins/New_GPG/src/options.cpp +++ b/plugins/New_GPG/src/options.cpp @@ -1,1155 +1,1155 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -globals_s globals; - -HWND hwndCurKey_p = nullptr; - -///////////////////////////////////////////////////////////////////////////////////////// -// Load existing key dialog - -class CDlgLoadExistingKey : public CDlgBase -{ - wchar_t id[16]; - CCtrlListView list_EXISTING_KEY_LIST; - -public: - CDlgLoadExistingKey() : - CDlgBase(g_plugin, IDD_LOAD_EXISTING_KEY), - list_EXISTING_KEY_LIST(this, IDC_EXISTING_KEY_LIST) - { - id[0] = 0; - - list_EXISTING_KEY_LIST.OnClick = Callback(this, &CDlgLoadExistingKey::onChange_EXISTING_KEY_LIST); - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); - - list_EXISTING_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50); - list_EXISTING_KEY_LIST.AddColumn(1, TranslateT("Email"), 30); - list_EXISTING_KEY_LIST.AddColumn(2, TranslateT("Name"), 250); - list_EXISTING_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30); - list_EXISTING_KEY_LIST.AddColumn(4, TranslateT("Expiration date"), 30); - list_EXISTING_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30); - list_EXISTING_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); - - // parse gpg output - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--list-keys"); - if (!gpg_launcher(params)) - return false; - if (params.result == pxNotFound) - return false; - - int i = 1; - string out(params.out); - string::size_type p = 0, p2 = 0, stop = 0; - while (p != string::npos) { - if ((p = out.find("pub ", p)) == string::npos) - break; - p += 5; - if (p < stop) - break; - stop = p; - p2 = out.find("/", p) - 1; - - int row = list_EXISTING_KEY_LIST.AddItem(L"", 0); - list_EXISTING_KEY_LIST.SetItemText(row, 5, toUTF16(out.substr(p, p2 - p)).c_str()); - - p2 += 2; - p = out.find(" ", p2); - list_EXISTING_KEY_LIST.SetItemText(row, 0, toUTF16(out.substr(p2, p - p2)).c_str()); - - p++; - p2 = out.find("\n", p); - string::size_type p3 = out.substr(p, p2 - p).find("["); - if (p3 != string::npos) { - p3 += p; - p2 = p3; - p2--; - p3++; - p3 += mir_strlen("expires: "); - string::size_type p4 = out.find("]", p3); - list_EXISTING_KEY_LIST.SetItemText(row, 4, toUTF16(out.substr(p3, p4 - p3)).c_str()); - } - else p2--; - - list_EXISTING_KEY_LIST.SetItemText(row, 3, toUTF16(out.substr(p, p2 - p)).c_str()); - - p = out.find("uid ", p); - p += mir_strlen("uid "); - p2 = out.find("\n", p); - p3 = out.substr(p, p2 - p).find("<"); - if (p3 != string::npos) { - p3 += p; - p2 = p3; - p2--; - p3++; - string::size_type p4 = out.find(">", p3); - list_EXISTING_KEY_LIST.SetItemText(row, 1, toUTF16(out.substr(p3, p4 - p3)).c_str()); - } - else p2--; - - p = out.find_first_not_of(" ", p); - list_EXISTING_KEY_LIST.SetItemText(row, 2, toUTF16(out.substr(p, p2 - p)).c_str()); - i++; - } - - if (list_EXISTING_KEY_LIST.GetItemCount()) { - list_EXISTING_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE); - list_EXISTING_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE); - } - return true; - } - - bool OnApply() override - { - int i = list_EXISTING_KEY_LIST.GetSelectionMark(); - if (i == -1) - return false; //TODO: error message - - list_EXISTING_KEY_LIST.GetItemText(i, 0, id, _countof(id)); - extern CCtrlEdit *edit_p_PubKeyEdit; - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(id); - if (!gpg_launcher(params)) - return false; - if (params.result == pxNotFound) - return false; - - string out(params.out); - size_t p1 = out.find("-----BEGIN PGP PUBLIC KEY BLOCK-----"); - if (p1 != std::string::npos) { - size_t p2 = out.find("-----END PGP PUBLIC KEY BLOCK-----", p1); - if (p2 != std::string::npos) { - p2 += mir_strlen("-----END PGP PUBLIC KEY BLOCK-----"); - out = out.substr(p1, p2 - p1); - if (edit_p_PubKeyEdit) - edit_p_PubKeyEdit->SetText(_A2T(out.c_str())); - } - else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK); - } - else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK); - - return true; - } - - void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); - } - - void onChange_EXISTING_KEY_LIST(CCtrlListView::TEventInfo *) - { - EnableWindow(GetDlgItem(m_hwnd, IDOK), TRUE); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// Import key dialog - -class CDlgImportKey : public CDlgBase -{ - MCONTACT hContact; - CCtrlCombo combo_KEYSERVER; - CCtrlButton btn_IMPORT; - -public: - CDlgImportKey(MCONTACT _hContact) : - CDlgBase(g_plugin, IDD_IMPORT_KEY), - combo_KEYSERVER(this, IDC_KEYSERVER), - btn_IMPORT(this, IDC_IMPORT) - { - hContact = _hContact; - btn_IMPORT.OnClick = Callback(this, &CDlgImportKey::onClick_IMPORT); - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow"); - - combo_KEYSERVER.AddString(L"subkeys.pgp.net"); - combo_KEYSERVER.AddString(L"keys.gnupg.net"); - return true; - } - - void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow"); - } - - void onClick_IMPORT(CCtrlButton *) - { - gpg_execution_params params; - params.addParam(L"--keyserver"); - params.addParam(combo_KEYSERVER.GetText()); - params.addParam(L"--recv-keys"); - params.addParam(toUTF16(globals.hcontact_data[hContact].key_in_prescense)); - gpg_launcher(params); - - MessageBoxA(nullptr, params.out.c_str(), "GPG output", MB_OK); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// COptGpgMainDlg class - -static class COptGpgMainDlg *g_pMainDlg; - -class COptGpgMainDlg : public CDlgBase -{ - bool old_bFileTransfers = g_plugin.bFileTransfers; - - CCtrlListView list_USERLIST; - CCtrlData lbl_CURRENT_KEY; - CCtrlEdit edit_LOG_FILE_EDIT; - CCtrlCheck check_DEBUG_LOG, check_JABBER_API, check_AUTO_EXCHANGE, check_FILE_TRANSFERS; - CCtrlButton btn_DELETE_KEY_BUTTON, btn_SELECT_KEY, btn_SAVE_KEY_BUTTON, btn_COPY_KEY, btn_LOG_FILE_SET; - -public: - COptGpgMainDlg() : CDlgBase(g_plugin, IDD_OPT_GPG), - list_USERLIST(this, IDC_USERLIST), lbl_CURRENT_KEY(this, IDC_CURRENT_KEY), edit_LOG_FILE_EDIT(this, IDC_LOG_FILE_EDIT), - check_DEBUG_LOG(this, IDC_DEBUG_LOG), check_JABBER_API(this, IDC_JABBER_API), check_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE), check_FILE_TRANSFERS(this, IDC_FILE_TRANSFERS), - btn_DELETE_KEY_BUTTON(this, IDC_DELETE_KEY_BUTTON), btn_SELECT_KEY(this, IDC_SELECT_KEY), btn_SAVE_KEY_BUTTON(this, IDC_SAVE_KEY_BUTTON), btn_COPY_KEY(this, IDC_COPY_KEY), btn_LOG_FILE_SET(this, IDC_LOG_FILE_SET) - { - btn_DELETE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_DELETE_KEY_BUTTON); - btn_SELECT_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_SELECT_KEY); - btn_SAVE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_SAVE_KEY_BUTTON); - btn_COPY_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_COPY_KEY); - btn_LOG_FILE_SET.OnClick = Callback(this, &COptGpgMainDlg::onClick_LOG_FILE_SET); - - check_JABBER_API.OnChange = Callback(this, &COptGpgMainDlg::onChange_JABBER_API); - - list_USERLIST.OnItemChanged = Callback(this, &COptGpgMainDlg::onItemChanged_USERLIST); - - CreateLink(check_DEBUG_LOG, g_plugin.bDebugLog); - CreateLink(check_JABBER_API, g_plugin.bJabberAPI); - CreateLink(check_AUTO_EXCHANGE, g_plugin.bAutoExchange); - CreateLink(check_FILE_TRANSFERS, g_plugin.bFileTransfers); - } - - bool OnInitDialog() override - { - g_pMainDlg = this; - - list_USERLIST.AddColumn(0, TranslateT("Contact"), 60); - list_USERLIST.AddColumn(1, TranslateT("Key ID"), 50); - list_USERLIST.AddColumn(2, TranslateT("Name"), 50); - list_USERLIST.AddColumn(3, TranslateT("Email"), 50); - list_USERLIST.AddColumn(4, TranslateT("Account"), 60); - list_USERLIST.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); - - for (auto &hContact : Contacts()) { - if (!isContactHaveKey(hContact)) - continue; - - auto *pa = Proto_GetAccount(Proto_GetBaseAccountName(hContact)); - if (pa == nullptr) - continue; - - wchar_t *name = Clist_GetContactDisplayName(hContact); - - int row = list_USERLIST.AddItem(L"", 0, hContact); - list_USERLIST.SetItemText(row, 0, name); - - list_USERLIST.SetItemText(row, 4, pa->tszAccountName); - - CMStringW tmp = g_plugin.getMStringW(hContact, "KeyID", L"not set"); - list_USERLIST.SetItemText(row, 1, tmp); - - tmp = g_plugin.getMStringW(hContact, "KeyMainName", L"not set"); - list_USERLIST.SetItemText(row, 2, tmp); - - tmp = g_plugin.getMStringW(hContact, "KeyMainEmail", L"not set"); - list_USERLIST.SetItemText(row, 3, tmp); - - if (g_plugin.getByte(hContact, "GPGEncryption", 0)) - list_USERLIST.SetCheckState(row, 1); - } - - SetListAutoSize(); - - edit_LOG_FILE_EDIT.SetText(ptrW(g_plugin.getWStringA("szLogFilePath", L""))); - - check_JABBER_API.Enable(); - check_AUTO_EXCHANGE.Enable(g_plugin.bJabberAPI); - - lbl_CURRENT_KEY.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("Default private key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get())); - - check_JABBER_API.SetState(g_plugin.getByte("bJabberAPI", 1)); - check_FILE_TRANSFERS.SetState(g_plugin.getByte("bFileTransfers", 0)); - check_AUTO_EXCHANGE.SetState(g_plugin.getByte("bAutoExchange", 0)); - - //TODO: get rid of following s..t - //////////////// - hwndCurKey_p = lbl_CURRENT_KEY.GetHwnd(); - //////////////// - return true; - } - - bool OnApply() override - { - globals.debuglog.init(); - - if (g_plugin.bFileTransfers != old_bFileTransfers) - g_plugin.bSameAction = false; - - g_plugin.setWString("szLogFilePath", ptrW(edit_LOG_FILE_EDIT.GetText())); - return true; - } - - void OnDestroy() override - { - hwndCurKey_p = nullptr; - g_pMainDlg = nullptr; - } - - void onClick_DELETE_KEY_BUTTON(CCtrlButton*) - { - int idx = list_USERLIST.GetSelectionMark(); - if (idx == -1) - return; - - bool keep = false; - bool ismetacontact = false; - MCONTACT meta = NULL; - MCONTACT hContact = list_USERLIST.GetItemData(idx); - if (db_mc_isMeta(hContact)) { - meta = hContact; - hContact = metaGetMostOnline(hContact); - ismetacontact = true; - } - else if ((meta = db_mc_getMeta(hContact)) != NULL) { - hContact = metaGetMostOnline(meta); - ismetacontact = true; - } - - CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID")); - for (auto &hcnttmp : Contacts()) { - if (hcnttmp != hContact) { - ptrA tmp2(g_plugin.getStringA(hcnttmp, "KeyID")); - if (!mir_strcmp(tmp, tmp2)) { - keep = true; - break; - } - } - } - - if (!keep) - if (MessageBox(nullptr, TranslateT("This key is not used by any contact. Do you want to remove it from public keyring?"), TranslateT("Key info"), MB_YESNO) == IDYES) { - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"--delete-key"); - params.addParam(_A2T(tmp).get()); - if (!gpg_launcher(params)) - return; - - if (params.result == pxNotFound) - return; - - if (params.out.Find("--delete-secret-keys") != -1) - MessageBox(nullptr, TranslateT("we have secret key for this public key, do not removing from GPG keyring"), TranslateT("info"), MB_OK); - else - MessageBox(nullptr, TranslateT("Key removed from GPG keyring"), TranslateT("info"), MB_OK); - } - - if (ismetacontact) { - if (MessageBox(nullptr, TranslateT("Do you want to remove key from entire metacontact (all subcontacts)?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { - MCONTACT hcnt = NULL; - int count = db_mc_getSubCount(meta); - for (int i = 0; i < count; i++) { - hcnt = db_mc_getSub(meta, i); - if (hcnt) { - g_plugin.delSetting(hcnt, "KeyID"); - g_plugin.delSetting(hcnt, "GPGPubKey"); - g_plugin.delSetting(hcnt, "KeyMainName"); - g_plugin.delSetting(hcnt, "KeyType"); - g_plugin.delSetting(hcnt, "KeyMainEmail"); - g_plugin.delSetting(hcnt, "KeyComment"); - setSrmmIcon(hcnt); - } - } - } - else { - g_plugin.delSetting(hContact, "KeyID"); - g_plugin.delSetting(hContact, "GPGPubKey"); - g_plugin.delSetting(hContact, "KeyMainName"); - g_plugin.delSetting(hContact, "KeyType"); - g_plugin.delSetting(hContact, "KeyMainEmail"); - g_plugin.delSetting(hContact, "KeyComment"); - setSrmmIcon(hContact); - } - } - else { - g_plugin.delSetting(hContact, "KeyID"); - g_plugin.delSetting(hContact, "GPGPubKey"); - g_plugin.delSetting(hContact, "KeyMainName"); - g_plugin.delSetting(hContact, "KeyType"); - g_plugin.delSetting(hContact, "KeyMainEmail"); - g_plugin.delSetting(hContact, "KeyComment"); - setSrmmIcon(hContact); - } - - list_USERLIST.SetItemText(idx, 3, TranslateT("not set")); - list_USERLIST.SetItemText(idx, 2, TranslateT("not set")); - list_USERLIST.SetItemText(idx, 1, TranslateT("not set")); - } - - void onClick_SELECT_KEY(CCtrlButton*) - { - ShowFirstRunDialog(); - } - - void onClick_SAVE_KEY_BUTTON(CCtrlButton*) - { - int idx = list_USERLIST.GetSelectionMark(); - if (idx == -1) - return; - - MCONTACT hContact = list_USERLIST.GetItemData(idx); - ptrW tmp(GetFilePath(TranslateT("Export public key"), L"*", TranslateT(".asc pubkey file"), true)); - if (tmp) { - CMStringW str(g_plugin.getMStringW(hContact, "GPGPubKey")); - str.Replace(L"\r", L""); - - wfstream f(tmp, std::ios::out); - f << str.c_str(); - f.close(); - } - } - - void onClick_COPY_KEY(CCtrlButton *) - { - CMStringW str(g_plugin.getMStringW("GPGPubKey")); - str.Replace(L"\n", L"\r\n"); - Utils_ClipboardCopy(str); - } - - void onClick_LOG_FILE_SET(CCtrlButton*) - { - edit_LOG_FILE_EDIT.SetText(ptrW(GetFilePath(TranslateT("Set log file"), L"*", TranslateT("LOG files"), 1))); - } - - void onChange_JABBER_API(CCtrlCheck *chk) - { - check_AUTO_EXCHANGE.Enable(chk->GetState()); - } - - void onItemChanged_USERLIST(CCtrlListView::TEventInfo *ev) - { - NMLISTVIEW *hdr = ev->nmlv; - if (hdr->iItem == -1) - return; - - MCONTACT hContact = list_USERLIST.GetItemData(hdr->iItem); - if (list_USERLIST.GetCheckState(hdr->iItem)) - g_plugin.setByte(hContact, "GPGEncryption", 1); - else - g_plugin.setByte(hContact, "GPGEncryption", 0); - setSrmmIcon(hContact); - } - - void SetLineText(int i, const wchar_t *pwszText) - { - int idx = list_USERLIST.GetSelectionMark(); - if (idx != -1) - list_USERLIST.SetItemText(idx, i, pwszText); - } - - void SetListAutoSize() - { - if (list_USERLIST.GetItemCount() == 0) - return; - - list_USERLIST.SetColumnWidth(0, LVSCW_AUTOSIZE); - list_USERLIST.SetColumnWidth(1, LVSCW_AUTOSIZE); - list_USERLIST.SetColumnWidth(2, LVSCW_AUTOSIZE); - list_USERLIST.SetColumnWidth(3, LVSCW_AUTOSIZE); - list_USERLIST.SetColumnWidth(4, LVSCW_AUTOSIZE); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// COptGpgBinDlg class - -class COptGpgBinDlg : public CDlgBase -{ - CCtrlEdit edit_BIN_PATH, edit_HOME_DIR; - CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR; - -public: - COptGpgBinDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_BIN), - edit_BIN_PATH(this, IDC_BIN_PATH), edit_HOME_DIR(this, IDC_HOME_DIR), - btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR) - { - btn_SET_BIN_PATH.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_BIN_PATH); - btn_SET_HOME_DIR.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_HOME_DIR); - - } - - bool OnInitDialog() override - { - edit_BIN_PATH.SetText(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); - edit_HOME_DIR.SetText(g_plugin.getMStringW("szHomePath", L"gpg")); - return true; - } - - bool OnApply() override - { - g_plugin.setWString("szGpgBinPath", ptrW(edit_BIN_PATH.GetText())); - - ptrW wszHomeDir(edit_HOME_DIR.GetText()); - while (wszHomeDir[mir_wstrlen(wszHomeDir) - 1] == '\\') - wszHomeDir[mir_wstrlen(wszHomeDir) - 1] = '\0'; - g_plugin.setWString("szHomePath", wszHomeDir); - return true; - } - - void onClick_SET_BIN_PATH(CCtrlButton*) - { - GetFilePath(TranslateT("Choose gpg.exe"), "szGpgBinPath", L"*.exe", TranslateT("EXE Executables")); - CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); - edit_BIN_PATH.SetText(tmp); - bool gpg_exists = false; - { - if (_waccess(tmp, 0) != -1) - gpg_exists = true; - if (gpg_exists) { - bool bad_version = false; - CMStringW tmp_path = g_plugin.getMStringW("szGpgBinPath", L""); - g_plugin.setWString("szGpgBinPath", tmp); - - gpg_execution_params params; - params.addParam(L"--version"); - - bool old_gpg_state = globals.gpg_valid; - globals.gpg_valid = true; - gpg_launcher(params); - globals.gpg_valid = old_gpg_state; - g_plugin.setWString("szGpgBinPath", tmp_path); - - int p1 = params.out.Find("(GnuPG) "); - if (p1 != string::npos) { - p1 += mir_strlen("(GnuPG) "); - if (params.out[p1] != '1') - bad_version = true; - } - else { - bad_version = false; - MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); - } - } - } - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - if (tmp.Find(mir_path, 0) == 0) { - CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); - edit_BIN_PATH.SetText(path); - } - } - - void onClick_SET_HOME_DIR(CCtrlButton*) - { - GetFolderPath(TranslateT("Set home directory")); - CMStringW tmp(g_plugin.getMStringW("szHomePath", L"")); - edit_HOME_DIR.SetText(tmp); - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - if (tmp.Find(mir_path, 0) == 0) { - CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); - edit_HOME_DIR.SetText(tmp); - } - } -}; - -class COptGpgMsgDlg : public CDlgBase -{ - CCtrlCheck check_APPEND_TAGS, check_STRIP_TAGS; - CCtrlEdit edit_IN_OPEN_TAG, edit_IN_CLOSE_TAG, edit_OUT_OPEN_TAG, edit_OUT_CLOSE_TAG; - -public: - COptGpgMsgDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_MESSAGES), - check_APPEND_TAGS(this, IDC_APPEND_TAGS), check_STRIP_TAGS(this, IDC_STRIP_TAGS), - edit_IN_OPEN_TAG(this, IDC_IN_OPEN_TAG), edit_IN_CLOSE_TAG(this, IDC_IN_CLOSE_TAG), edit_OUT_OPEN_TAG(this, IDC_OUT_OPEN_TAG), edit_OUT_CLOSE_TAG(this, IDC_OUT_CLOSE_TAG) - { - CreateLink(check_STRIP_TAGS, g_plugin.bStripTags); - CreateLink(check_APPEND_TAGS, g_plugin.bAppendTags); - } - - bool OnInitDialog() override - { - edit_IN_OPEN_TAG.SetText(g_plugin.getMStringW("szInOpenTag", L"")); - edit_IN_CLOSE_TAG.SetText(g_plugin.getMStringW("szInCloseTag", L"")); - edit_OUT_OPEN_TAG.SetText(g_plugin.getMStringW("szOutOpenTag", L"")); - edit_OUT_CLOSE_TAG.SetText(g_plugin.getMStringW("szOutCloseTag", L"")); - return true; - } - - bool OnApply() override - { - ptrW tmp(edit_IN_OPEN_TAG.GetText()); - g_plugin.setWString("szInOpenTag", tmp); - globals.wszInopentag = tmp; - - tmp = edit_IN_CLOSE_TAG.GetText(); - g_plugin.setWString("szInCloseTag", tmp); - globals.wszInclosetag = tmp; - - tmp = mir_wstrdup(edit_OUT_OPEN_TAG.GetText()); - g_plugin.setWString("szOutOpenTag", tmp); - globals.wszOutopentag = tmp; - - tmp = mir_wstrdup(edit_OUT_CLOSE_TAG.GetText()); - g_plugin.setWString("szOutCloseTag", tmp); - globals.wszOutclosetag = tmp; - return true; - } -}; - -class COptGpgAdvDlg : public CDlgBase -{ - CCtrlButton btn_EXPORT, btn_IMPORT; - CCtrlCheck chkPresenceSub, chkSendErrors; - -public: - COptGpgAdvDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_ADVANCED), - btn_EXPORT(this, IDC_EXPORT), - btn_IMPORT(this, IDC_IMPORT), - chkSendErrors(this, IDC_SEND_ERRORS), - chkPresenceSub(this, IDC_PRESCENSE_SUBSCRIPTION) - { - btn_EXPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_EXPORT); - btn_IMPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_IMPORT); - - CreateLink(chkSendErrors, g_plugin.bSendErrorMessages); - CreateLink(chkPresenceSub, g_plugin.bPresenceSigning); - } - - bool OnInitDialog() override - { - chkPresenceSub.Enable(g_plugin.bJabberAPI); - return true; - } - - void onClick_EXPORT(CCtrlButton*) - { - INT_PTR ExportGpGKeys(WPARAM w, LPARAM l); - ExportGpGKeys(NULL, NULL); - } - - void onClick_IMPORT(CCtrlButton*) - { - INT_PTR ImportGpGKeys(WPARAM w, LPARAM l); - ImportGpGKeys(NULL, NULL); - } -}; - -CCtrlEdit *edit_p_PubKeyEdit = nullptr; - -static LRESULT CALLBACK editctrl_ctrl_a(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_KEYDOWN: - if (wParam == 0x41 && GetKeyState(VK_CONTROL) < 0) - SendMessage(hwndDlg, EM_SETSEL, 0, -1); - return 0; - } - return mir_callNextSubclass(hwndDlg, editctrl_ctrl_a, msg, wParam, lParam); -} - -class CDlgLoadPubKeyDlg : public CDlgBase -{ - MCONTACT hContact; - wstring key_buf; - wstring::size_type ws1 = 0, ws2 = 0; - CCtrlCheck chk_ENABLE_ENCRYPTION; - CCtrlButton btn_SELECT_EXISTING, btn_OK, btn_LOAD_FROM_FILE, btn_IMPORT; - CCtrlEdit edit_PUBLIC_KEY_EDIT; - -public: - CDlgLoadPubKeyDlg(MCONTACT _p1) : - CDlgBase(g_plugin, IDD_LOAD_PUBLIC_KEY), - hContact(_p1), - chk_ENABLE_ENCRYPTION(this, IDC_ENABLE_ENCRYPTION), - btn_SELECT_EXISTING(this, IDC_SELECT_EXISTING), btn_OK(this, ID_OK), btn_LOAD_FROM_FILE(this, ID_LOAD_FROM_FILE), btn_IMPORT(this, IDC_IMPORT), - edit_PUBLIC_KEY_EDIT(this, IDC_PUBLIC_KEY_EDIT) - { - btn_SELECT_EXISTING.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_SELECT_EXISTING); - btn_OK.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_OK); - btn_LOAD_FROM_FILE.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_LOAD_FROM_FILE); - btn_IMPORT.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_IMPORT); - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); - - mir_subclassWindow(GetDlgItem(m_hwnd, IDC_PUBLIC_KEY_EDIT), editctrl_ctrl_a); - MCONTACT hcnt = db_mc_tryMeta(hContact); - { - wstring msg = TranslateT("Load Public GPG Key for "); - msg += Clist_GetContactDisplayName(hcnt, 0); - this->SetCaption(msg.c_str()); - } - if (!hcnt) { - btn_SELECT_EXISTING.Disable(); - chk_ENABLE_ENCRYPTION.Disable(); - } - if (isContactSecured(hcnt)) - chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn off encryption")); - else { - chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn on encryption")); - chk_ENABLE_ENCRYPTION.SetState(1); - } - if (hcnt) { - wstring str = ptrW(g_plugin.getWStringA(hcnt, "GPGPubKey", L"")); - if (!str.empty()) { - wstring::size_type p = 0, stop = 0; - for (;;) { - if ((p = str.find(L"\n", p + 2)) != wstring::npos) { - if (p > stop) { - stop = p; - str.insert(p, L"\r"); - } - else break; - } - } - } - - if (!globals.hcontact_data[hcnt].key_in_prescense.empty()) { - if (g_plugin.getMStringA(hcnt, "KeyID").IsEmpty()) { - gpg_execution_params params; - params.addParam(L"--export"); - params.addParam(L"-a"); - params.addParam(toUTF16(globals.hcontact_data[hcnt].key_in_prescense)); - gpg_launcher(params); //TODO: handle errors - - if ((params.out.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----") != -1) && (params.out.Find("-----END PGP PUBLIC KEY BLOCK-----") != -1)) { - params.out.Replace("\n", "\r\n"); - - wchar_t *tmp3 = mir_a2u(params.out.c_str()); - str.clear(); - str.append(tmp3); - mir_free(tmp3); - string msg = Translate("Load Public GPG Key for "); - msg += _T2A(Clist_GetContactDisplayName(hcnt)); - msg += " (Key ID: "; - msg += globals.hcontact_data[hcnt].key_in_prescense; - msg += Translate(" found in presence, and exists in keyring.)"); - SetCaption(toUTF16(msg).c_str()); - } - else { - string msg = Translate("Load Public GPG Key (Key ID: "); - msg += globals.hcontact_data[hcnt].key_in_prescense; - msg += Translate(" found in presence.)"); - SetCaption(toUTF16(msg).c_str()); - btn_IMPORT.Enable(); - } - } - } - - edit_PUBLIC_KEY_EDIT.SetText(!str.empty() ? str.c_str() : L""); - } - edit_p_PubKeyEdit = &edit_PUBLIC_KEY_EDIT; - return true; - } - - virtual void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); - edit_p_PubKeyEdit = nullptr; - } - - void onClick_SELECT_EXISTING(CCtrlButton*) - { - (new CDlgLoadExistingKey())->Show(); - } - - void onClick_OK(CCtrlButton*) - { - wchar_t *tmp = mir_wstrdup(edit_PUBLIC_KEY_EDIT.GetText()); - wchar_t *begin, *end; - key_buf.append(tmp); - key_buf.append(L"\n"); //no new line at end of file ) - mir_free(tmp); - while ((ws1 = key_buf.find(L"\r", ws1)) != wstring::npos) { - key_buf.erase(ws1, 1); //remove windows specific trash - } - ws1 = 0; - if (((ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) { - begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PUBLIC KEY BLOCK-----") + 1)); - mir_wstrcpy(begin, L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); - end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----") + 1)); - mir_wstrcpy(end, L"-----END PGP PUBLIC KEY BLOCK-----"); - } - else if (((ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) { - begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PRIVATE KEY BLOCK-----") + 1)); - mir_wstrcpy(begin, L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); - end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----") + 1)); - mir_wstrcpy(end, L"-----END PGP PRIVATE KEY BLOCK-----"); - } - else { - MessageBox(nullptr, TranslateT("This is not public or private key"), L"INFO", MB_OK); - return; - } - ws2 += mir_wstrlen(end); - bool allsubcontacts = false; - { - if (db_mc_isMeta(hContact)) { - if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { - allsubcontacts = true; - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setWString(hcnt, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); - } - } - else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); - } - else g_plugin.setWString(hContact, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); - } - tmp = (wchar_t*)mir_alloc(sizeof(wchar_t) * (key_buf.length() + 1)); - mir_wstrcpy(tmp, key_buf.substr(ws1, ws2 - ws1).c_str()); - { //gpg execute block - std::vector cmd; - CMStringW tmp2; - { - MCONTACT hcnt = db_mc_tryMeta(hContact); - tmp2 = g_plugin.getMStringW("szHomePath"); - tmp2 += L"\\temporary_exported.asc"; - boost::filesystem::remove(tmp2.c_str()); - - wfstream f(tmp2, std::ios::out); - CMStringW str = g_plugin.getMStringW(hcnt, "GPGPubKey"); - str.Replace(L"\r", L""); - f << str.c_str(); - f.close(); - } - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(tmp2.c_str()); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - mir_free(begin); - mir_free(end); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.delSetting(hcnt, "bAlwatsTrust"); - } - } - else g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust"); - } - else g_plugin.delSetting(hContact, "bAlwatsTrust"); - } - - string output(params.out); - { - if (output.find("already in secret keyring") != string::npos) { - MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK); - boost::filesystem::remove(tmp2.c_str()); - return; - } - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - { - char *tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); - mir_strcpy(tmp3, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp3, nullptr); - { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyID", tmp3); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyID", tmp3); - } - else g_plugin.setString(hContact, "KeyID", tmp3); - } - mir_free(tmp3); - } - - if (hContact && g_pMainDlg) - g_pMainDlg->SetLineText(1, toUTF16(output.substr(s, s2 - s)).c_str()); - - s = output.find("“", s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else - s += 3; - bool uncommon = false; - if ((s2 = output.find("(", s)) == string::npos) { - if ((s2 = output.find("<", s)) == string::npos) { - s2 = output.find("”", s); - uncommon = true; - } - } - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s2 != string::npos && s != string::npos) { - { - char *tmp3 = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); - mir_strcpy(tmp3, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); - mir_utf8decode(tmp3, nullptr); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", output.substr(s, s2 - s - 1).c_str()); - } - else g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); - } - mir_free(tmp3); - } - - if (hContact && g_pMainDlg) - g_pMainDlg->SetLineText(2, toUTF16(output.substr(s, s2 - s - 1)).c_str()); - - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (s != string::npos && s2 != string::npos) { - if (output[s] == ')') { - char *tmp3 = (char*)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); - mir_strcpy(tmp3, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp3, nullptr); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyComment", output.substr(s2, s - s2).c_str()); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", output.substr(s2, s - s2).c_str()); - } - else g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str()); - } - mir_free(tmp3); - s += 3; - s2 = output.find(">", s); - tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); - mir_strcpy(tmp3, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp3, nullptr); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s, s2 - s).c_str()); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s, s2 - s).c_str()); - } - else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str()); - } - mir_free(tmp3); - - if (hContact && g_pMainDlg) - g_pMainDlg->SetLineText(3, toUTF16(output.substr(s, s2 - s)).c_str()); - } - else { - char *tmp3 = (char*)mir_alloc(output.substr(s2, s - s2).length() + 1); - mir_strcpy(tmp3, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp3, nullptr); - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - } - } - else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str()); - } - else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - } - mir_free(tmp3); - - if (hContact && g_pMainDlg) - g_pMainDlg->SetLineText(3, toUTF16(output.substr(s2, s - s2)).c_str()); - } - } - } - if (hContact && g_pMainDlg) - g_pMainDlg->SetListAutoSize(); - } - if (!hContact) { - gpg_execution_params params2; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(g_plugin.getMStringW(hContact, "KeyID").c_str()); - if (!gpg_launcher(params2)) - return; - if (params2.result == pxNotFound) - return; - - params2.out.Remove('\r'); - g_plugin.setString(hContact, "GPGPubKey", params2.out.c_str()); - } - MessageBoxA(nullptr, output.c_str(), "", MB_OK); - boost::filesystem::remove(tmp2.c_str()); - } - key_buf.clear(); - if (chk_ENABLE_ENCRYPTION.GetState()) { - if (hContact) { - if (db_mc_isMeta(hContact)) { - if (allsubcontacts) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) { - g_plugin.setByte(hcnt, "GPGEncryption", !isContactSecured(hcnt)); - setSrmmIcon(hContact); - } - } - } - else g_plugin.setByte(metaGetMostOnline(hContact), "GPGEncryption", !isContactSecured(hContact)); - } - else g_plugin.setByte(hContact, "GPGEncryption", !isContactSecured(hContact)); - } - } - this->Close(); - } - - void onClick_LOAD_FROM_FILE(CCtrlButton *) - { - ptrW tmp(GetFilePath(TranslateT("Set file containing GPG public key"), L"*", TranslateT("GPG public key file"))); - if (!tmp) - return; - - wfstream f(tmp, std::ios::in | std::ios::ate | std::ios::binary); - if (!f.is_open()) { - MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); - return; - } - if (f.is_open()) { - std::wifstream::pos_type size = f.tellg(); - wchar_t *temp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1]; - f.seekg(0, std::ios::beg); - f.read(temp, size); - temp[size] = '\0'; - key_buf.append(temp); - delete[] temp; - f.close(); - } - if (key_buf.empty()) { - key_buf.clear(); - if (globals.debuglog) - globals.debuglog << "info: Failed to read key file"; - return; - } - ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----"); - ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); - if (ws2 == wstring::npos || ws1 == wstring::npos) { - ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----"); - ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); - } - if (ws2 == wstring::npos || ws1 == wstring::npos) { - MessageBox(nullptr, TranslateT("There is no public or private key."), TranslateT("Info"), MB_OK); - return; - } - ws2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); - edit_PUBLIC_KEY_EDIT.SetText(key_buf.substr(ws1, ws2 - ws1).c_str()); - key_buf.clear(); - } - - void onClick_IMPORT(CCtrlButton *) - { - CDlgImportKey *d = new CDlgImportKey(hContact); - d->Show(); - } -}; - - -void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal) -{ - CDlgLoadPubKeyDlg *d = new CDlgLoadPubKeyDlg(hContact); - if (bModal) - d->DoModal(); - else - d->Show(); -} - -int GpgOptInit(WPARAM wParam, LPARAM) -{ - OPTIONSDIALOGPAGE odp = {}; - odp.szGroup.w = LPGENW("Services"); - odp.szTitle.w = _T(MODULENAME); - - odp.szTab.w = LPGENW("Main"); - odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE; - odp.pDialog = new COptGpgMainDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.szTab.w = LPGENW("GnuPG Variables"); - odp.pDialog = new COptGpgBinDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.szTab.w = LPGENW("Messages"); - odp.pDialog = new COptGpgMsgDlg(); - g_plugin.addOptions(wParam, &odp); - - odp.szTab.w = LPGENW("Advanced"); - odp.pDialog = new COptGpgAdvDlg(); - g_plugin.addOptions(wParam, &odp); - return 0; -} +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +globals_s globals; + +HWND hwndCurKey_p = nullptr; + +///////////////////////////////////////////////////////////////////////////////////////// +// Load existing key dialog + +class CDlgLoadExistingKey : public CDlgBase +{ + wchar_t id[16]; + CCtrlListView list_EXISTING_KEY_LIST; + +public: + CDlgLoadExistingKey() : + CDlgBase(g_plugin, IDD_LOAD_EXISTING_KEY), + list_EXISTING_KEY_LIST(this, IDC_EXISTING_KEY_LIST) + { + id[0] = 0; + + list_EXISTING_KEY_LIST.OnClick = Callback(this, &CDlgLoadExistingKey::onChange_EXISTING_KEY_LIST); + } + + bool OnInitDialog() override + { + Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); + + list_EXISTING_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50); + list_EXISTING_KEY_LIST.AddColumn(1, TranslateT("Email"), 30); + list_EXISTING_KEY_LIST.AddColumn(2, TranslateT("Name"), 250); + list_EXISTING_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30); + list_EXISTING_KEY_LIST.AddColumn(4, TranslateT("Expiration date"), 30); + list_EXISTING_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30); + list_EXISTING_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); + + // parse gpg output + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--list-keys"); + if (!gpg_launcher(params)) + return false; + if (params.result == pxNotFound) + return false; + + int i = 1; + string out(params.out); + string::size_type p = 0, p2 = 0, stop = 0; + while (p != string::npos) { + if ((p = out.find("pub ", p)) == string::npos) + break; + p += 5; + if (p < stop) + break; + stop = p; + p2 = out.find("/", p) - 1; + + int row = list_EXISTING_KEY_LIST.AddItem(L"", 0); + list_EXISTING_KEY_LIST.SetItemText(row, 5, toUTF16(out.substr(p, p2 - p)).c_str()); + + p2 += 2; + p = out.find(" ", p2); + list_EXISTING_KEY_LIST.SetItemText(row, 0, toUTF16(out.substr(p2, p - p2)).c_str()); + + p++; + p2 = out.find("\n", p); + string::size_type p3 = out.substr(p, p2 - p).find("["); + if (p3 != string::npos) { + p3 += p; + p2 = p3; + p2--; + p3++; + p3 += mir_strlen("expires: "); + string::size_type p4 = out.find("]", p3); + list_EXISTING_KEY_LIST.SetItemText(row, 4, toUTF16(out.substr(p3, p4 - p3)).c_str()); + } + else p2--; + + list_EXISTING_KEY_LIST.SetItemText(row, 3, toUTF16(out.substr(p, p2 - p)).c_str()); + + p = out.find("uid ", p); + p += mir_strlen("uid "); + p2 = out.find("\n", p); + p3 = out.substr(p, p2 - p).find("<"); + if (p3 != string::npos) { + p3 += p; + p2 = p3; + p2--; + p3++; + string::size_type p4 = out.find(">", p3); + list_EXISTING_KEY_LIST.SetItemText(row, 1, toUTF16(out.substr(p3, p4 - p3)).c_str()); + } + else p2--; + + p = out.find_first_not_of(" ", p); + list_EXISTING_KEY_LIST.SetItemText(row, 2, toUTF16(out.substr(p, p2 - p)).c_str()); + i++; + } + + if (list_EXISTING_KEY_LIST.GetItemCount()) { + list_EXISTING_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE); + list_EXISTING_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE); + list_EXISTING_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE); + list_EXISTING_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE); + list_EXISTING_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE); + list_EXISTING_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE); + } + return true; + } + + bool OnApply() override + { + int i = list_EXISTING_KEY_LIST.GetSelectionMark(); + if (i == -1) + return false; //TODO: error message + + list_EXISTING_KEY_LIST.GetItemText(i, 0, id, _countof(id)); + extern CCtrlEdit *edit_p_PubKeyEdit; + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"-a"); + params.addParam(L"--export"); + params.addParam(id); + if (!gpg_launcher(params)) + return false; + if (params.result == pxNotFound) + return false; + + string out(params.out); + size_t p1 = out.find("-----BEGIN PGP PUBLIC KEY BLOCK-----"); + if (p1 != std::string::npos) { + size_t p2 = out.find("-----END PGP PUBLIC KEY BLOCK-----", p1); + if (p2 != std::string::npos) { + p2 += mir_strlen("-----END PGP PUBLIC KEY BLOCK-----"); + out = out.substr(p1, p2 - p1); + if (edit_p_PubKeyEdit) + edit_p_PubKeyEdit->SetText(_A2T(out.c_str())); + } + else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK); + } + else MessageBox(nullptr, TranslateT("Failed to export public key."), TranslateT("Error"), MB_OK); + + return true; + } + + void OnDestroy() override + { + Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); + } + + void onChange_EXISTING_KEY_LIST(CCtrlListView::TEventInfo *) + { + EnableWindow(GetDlgItem(m_hwnd, IDOK), TRUE); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// Import key dialog + +class CDlgImportKey : public CDlgBase +{ + MCONTACT hContact; + CCtrlCombo combo_KEYSERVER; + CCtrlButton btn_IMPORT; + +public: + CDlgImportKey(MCONTACT _hContact) : + CDlgBase(g_plugin, IDD_IMPORT_KEY), + combo_KEYSERVER(this, IDC_KEYSERVER), + btn_IMPORT(this, IDC_IMPORT) + { + hContact = _hContact; + btn_IMPORT.OnClick = Callback(this, &CDlgImportKey::onClick_IMPORT); + } + + bool OnInitDialog() override + { + Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow"); + + combo_KEYSERVER.AddString(L"subkeys.pgp.net"); + combo_KEYSERVER.AddString(L"keys.gnupg.net"); + return true; + } + + void OnDestroy() override + { + Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "ImportKeyWindow"); + } + + void onClick_IMPORT(CCtrlButton *) + { + gpg_execution_params params; + params.addParam(L"--keyserver"); + params.addParam(combo_KEYSERVER.GetText()); + params.addParam(L"--recv-keys"); + params.addParam(toUTF16(globals.hcontact_data[hContact].key_in_prescense)); + gpg_launcher(params); + + MessageBoxA(nullptr, params.out.c_str(), "GPG output", MB_OK); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// COptGpgMainDlg class + +static class COptGpgMainDlg *g_pMainDlg; + +class COptGpgMainDlg : public CDlgBase +{ + bool old_bFileTransfers = g_plugin.bFileTransfers; + + CCtrlListView list_USERLIST; + CCtrlData lbl_CURRENT_KEY; + CCtrlEdit edit_LOG_FILE_EDIT; + CCtrlCheck check_DEBUG_LOG, check_JABBER_API, check_AUTO_EXCHANGE, check_FILE_TRANSFERS; + CCtrlButton btn_DELETE_KEY_BUTTON, btn_SELECT_KEY, btn_SAVE_KEY_BUTTON, btn_COPY_KEY, btn_LOG_FILE_SET; + +public: + COptGpgMainDlg() : CDlgBase(g_plugin, IDD_OPT_GPG), + list_USERLIST(this, IDC_USERLIST), lbl_CURRENT_KEY(this, IDC_CURRENT_KEY), edit_LOG_FILE_EDIT(this, IDC_LOG_FILE_EDIT), + check_DEBUG_LOG(this, IDC_DEBUG_LOG), check_JABBER_API(this, IDC_JABBER_API), check_AUTO_EXCHANGE(this, IDC_AUTO_EXCHANGE), check_FILE_TRANSFERS(this, IDC_FILE_TRANSFERS), + btn_DELETE_KEY_BUTTON(this, IDC_DELETE_KEY_BUTTON), btn_SELECT_KEY(this, IDC_SELECT_KEY), btn_SAVE_KEY_BUTTON(this, IDC_SAVE_KEY_BUTTON), btn_COPY_KEY(this, IDC_COPY_KEY), btn_LOG_FILE_SET(this, IDC_LOG_FILE_SET) + { + btn_DELETE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_DELETE_KEY_BUTTON); + btn_SELECT_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_SELECT_KEY); + btn_SAVE_KEY_BUTTON.OnClick = Callback(this, &COptGpgMainDlg::onClick_SAVE_KEY_BUTTON); + btn_COPY_KEY.OnClick = Callback(this, &COptGpgMainDlg::onClick_COPY_KEY); + btn_LOG_FILE_SET.OnClick = Callback(this, &COptGpgMainDlg::onClick_LOG_FILE_SET); + + check_JABBER_API.OnChange = Callback(this, &COptGpgMainDlg::onChange_JABBER_API); + + list_USERLIST.OnItemChanged = Callback(this, &COptGpgMainDlg::onItemChanged_USERLIST); + + CreateLink(check_DEBUG_LOG, g_plugin.bDebugLog); + CreateLink(check_JABBER_API, g_plugin.bJabberAPI); + CreateLink(check_AUTO_EXCHANGE, g_plugin.bAutoExchange); + CreateLink(check_FILE_TRANSFERS, g_plugin.bFileTransfers); + } + + bool OnInitDialog() override + { + g_pMainDlg = this; + + list_USERLIST.AddColumn(0, TranslateT("Contact"), 60); + list_USERLIST.AddColumn(1, TranslateT("Key ID"), 50); + list_USERLIST.AddColumn(2, TranslateT("Name"), 50); + list_USERLIST.AddColumn(3, TranslateT("Email"), 50); + list_USERLIST.AddColumn(4, TranslateT("Account"), 60); + list_USERLIST.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); + + for (auto &hContact : Contacts()) { + if (!isContactHaveKey(hContact)) + continue; + + auto *pa = Proto_GetAccount(Proto_GetBaseAccountName(hContact)); + if (pa == nullptr) + continue; + + wchar_t *name = Clist_GetContactDisplayName(hContact); + + int row = list_USERLIST.AddItem(L"", 0, hContact); + list_USERLIST.SetItemText(row, 0, name); + + list_USERLIST.SetItemText(row, 4, pa->tszAccountName); + + CMStringW tmp = g_plugin.getMStringW(hContact, "KeyID", L"not set"); + list_USERLIST.SetItemText(row, 1, tmp); + + tmp = g_plugin.getMStringW(hContact, "KeyMainName", L"not set"); + list_USERLIST.SetItemText(row, 2, tmp); + + tmp = g_plugin.getMStringW(hContact, "KeyMainEmail", L"not set"); + list_USERLIST.SetItemText(row, 3, tmp); + + if (g_plugin.getByte(hContact, "GPGEncryption", 0)) + list_USERLIST.SetCheckState(row, 1); + } + + SetListAutoSize(); + + edit_LOG_FILE_EDIT.SetText(ptrW(g_plugin.getWStringA("szLogFilePath", L""))); + + check_JABBER_API.Enable(); + check_AUTO_EXCHANGE.Enable(g_plugin.bJabberAPI); + + lbl_CURRENT_KEY.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("Default private key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get())); + + check_JABBER_API.SetState(g_plugin.getByte("bJabberAPI", 1)); + check_FILE_TRANSFERS.SetState(g_plugin.getByte("bFileTransfers", 0)); + check_AUTO_EXCHANGE.SetState(g_plugin.getByte("bAutoExchange", 0)); + + //TODO: get rid of following s..t + //////////////// + hwndCurKey_p = lbl_CURRENT_KEY.GetHwnd(); + //////////////// + return true; + } + + bool OnApply() override + { + globals.debuglog.init(); + + if (g_plugin.bFileTransfers != old_bFileTransfers) + g_plugin.bSameAction = false; + + g_plugin.setWString("szLogFilePath", ptrW(edit_LOG_FILE_EDIT.GetText())); + return true; + } + + void OnDestroy() override + { + hwndCurKey_p = nullptr; + g_pMainDlg = nullptr; + } + + void onClick_DELETE_KEY_BUTTON(CCtrlButton*) + { + int idx = list_USERLIST.GetSelectionMark(); + if (idx == -1) + return; + + bool keep = false; + bool ismetacontact = false; + MCONTACT meta = NULL; + MCONTACT hContact = list_USERLIST.GetItemData(idx); + if (db_mc_isMeta(hContact)) { + meta = hContact; + hContact = metaGetMostOnline(hContact); + ismetacontact = true; + } + else if ((meta = db_mc_getMeta(hContact)) != NULL) { + hContact = metaGetMostOnline(meta); + ismetacontact = true; + } + + CMStringA tmp(g_plugin.getMStringA(hContact, "KeyID")); + for (auto &hcnttmp : Contacts()) { + if (hcnttmp != hContact) { + ptrA tmp2(g_plugin.getStringA(hcnttmp, "KeyID")); + if (!mir_strcmp(tmp, tmp2)) { + keep = true; + break; + } + } + } + + if (!keep) + if (MessageBox(nullptr, TranslateT("This key is not used by any contact. Do you want to remove it from public keyring?"), TranslateT("Key info"), MB_YESNO) == IDYES) { + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--yes"); + params.addParam(L"--delete-key"); + params.addParam(_A2T(tmp).get()); + if (!gpg_launcher(params)) + return; + + if (params.result == pxNotFound) + return; + + if (params.out.Find("--delete-secret-keys") != -1) + MessageBox(nullptr, TranslateT("we have secret key for this public key, do not removing from GPG keyring"), TranslateT("info"), MB_OK); + else + MessageBox(nullptr, TranslateT("Key removed from GPG keyring"), TranslateT("info"), MB_OK); + } + + if (ismetacontact) { + if (MessageBox(nullptr, TranslateT("Do you want to remove key from entire metacontact (all subcontacts)?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { + MCONTACT hcnt = NULL; + int count = db_mc_getSubCount(meta); + for (int i = 0; i < count; i++) { + hcnt = db_mc_getSub(meta, i); + if (hcnt) { + g_plugin.delSetting(hcnt, "KeyID"); + g_plugin.delSetting(hcnt, "GPGPubKey"); + g_plugin.delSetting(hcnt, "KeyMainName"); + g_plugin.delSetting(hcnt, "KeyType"); + g_plugin.delSetting(hcnt, "KeyMainEmail"); + g_plugin.delSetting(hcnt, "KeyComment"); + setSrmmIcon(hcnt); + } + } + } + else { + g_plugin.delSetting(hContact, "KeyID"); + g_plugin.delSetting(hContact, "GPGPubKey"); + g_plugin.delSetting(hContact, "KeyMainName"); + g_plugin.delSetting(hContact, "KeyType"); + g_plugin.delSetting(hContact, "KeyMainEmail"); + g_plugin.delSetting(hContact, "KeyComment"); + setSrmmIcon(hContact); + } + } + else { + g_plugin.delSetting(hContact, "KeyID"); + g_plugin.delSetting(hContact, "GPGPubKey"); + g_plugin.delSetting(hContact, "KeyMainName"); + g_plugin.delSetting(hContact, "KeyType"); + g_plugin.delSetting(hContact, "KeyMainEmail"); + g_plugin.delSetting(hContact, "KeyComment"); + setSrmmIcon(hContact); + } + + list_USERLIST.SetItemText(idx, 3, TranslateT("not set")); + list_USERLIST.SetItemText(idx, 2, TranslateT("not set")); + list_USERLIST.SetItemText(idx, 1, TranslateT("not set")); + } + + void onClick_SELECT_KEY(CCtrlButton*) + { + ShowFirstRunDialog(); + } + + void onClick_SAVE_KEY_BUTTON(CCtrlButton*) + { + int idx = list_USERLIST.GetSelectionMark(); + if (idx == -1) + return; + + MCONTACT hContact = list_USERLIST.GetItemData(idx); + ptrW tmp(GetFilePath(TranslateT("Export public key"), L"*", TranslateT(".asc pubkey file"), true)); + if (tmp) { + CMStringW str(g_plugin.getMStringW(hContact, "GPGPubKey")); + str.Replace(L"\r", L""); + + wfstream f(tmp, std::ios::out); + f << str.c_str(); + f.close(); + } + } + + void onClick_COPY_KEY(CCtrlButton *) + { + CMStringW str(g_plugin.getMStringW("GPGPubKey")); + str.Replace(L"\n", L"\r\n"); + Utils_ClipboardCopy(str); + } + + void onClick_LOG_FILE_SET(CCtrlButton*) + { + edit_LOG_FILE_EDIT.SetText(ptrW(GetFilePath(TranslateT("Set log file"), L"*", TranslateT("LOG files"), 1))); + } + + void onChange_JABBER_API(CCtrlCheck *chk) + { + check_AUTO_EXCHANGE.Enable(chk->GetState()); + } + + void onItemChanged_USERLIST(CCtrlListView::TEventInfo *ev) + { + NMLISTVIEW *hdr = ev->nmlv; + if (hdr->iItem == -1) + return; + + MCONTACT hContact = list_USERLIST.GetItemData(hdr->iItem); + if (list_USERLIST.GetCheckState(hdr->iItem)) + g_plugin.setByte(hContact, "GPGEncryption", 1); + else + g_plugin.setByte(hContact, "GPGEncryption", 0); + setSrmmIcon(hContact); + } + + void SetLineText(int i, const wchar_t *pwszText) + { + int idx = list_USERLIST.GetSelectionMark(); + if (idx != -1) + list_USERLIST.SetItemText(idx, i, pwszText); + } + + void SetListAutoSize() + { + if (list_USERLIST.GetItemCount() == 0) + return; + + list_USERLIST.SetColumnWidth(0, LVSCW_AUTOSIZE); + list_USERLIST.SetColumnWidth(1, LVSCW_AUTOSIZE); + list_USERLIST.SetColumnWidth(2, LVSCW_AUTOSIZE); + list_USERLIST.SetColumnWidth(3, LVSCW_AUTOSIZE); + list_USERLIST.SetColumnWidth(4, LVSCW_AUTOSIZE); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// COptGpgBinDlg class + +class COptGpgBinDlg : public CDlgBase +{ + CCtrlEdit edit_BIN_PATH, edit_HOME_DIR; + CCtrlButton btn_SET_BIN_PATH, btn_SET_HOME_DIR; + +public: + COptGpgBinDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_BIN), + edit_BIN_PATH(this, IDC_BIN_PATH), edit_HOME_DIR(this, IDC_HOME_DIR), + btn_SET_BIN_PATH(this, IDC_SET_BIN_PATH), btn_SET_HOME_DIR(this, IDC_SET_HOME_DIR) + { + btn_SET_BIN_PATH.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_BIN_PATH); + btn_SET_HOME_DIR.OnClick = Callback(this, &COptGpgBinDlg::onClick_SET_HOME_DIR); + + } + + bool OnInitDialog() override + { + edit_BIN_PATH.SetText(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); + edit_HOME_DIR.SetText(g_plugin.getMStringW("szHomePath", L"gpg")); + return true; + } + + bool OnApply() override + { + g_plugin.setWString("szGpgBinPath", ptrW(edit_BIN_PATH.GetText())); + + ptrW wszHomeDir(edit_HOME_DIR.GetText()); + while (wszHomeDir[mir_wstrlen(wszHomeDir) - 1] == '\\') + wszHomeDir[mir_wstrlen(wszHomeDir) - 1] = '\0'; + g_plugin.setWString("szHomePath", wszHomeDir); + return true; + } + + void onClick_SET_BIN_PATH(CCtrlButton*) + { + GetFilePath(TranslateT("Choose gpg.exe"), "szGpgBinPath", L"*.exe", TranslateT("EXE Executables")); + CMStringW tmp(g_plugin.getMStringW("szGpgBinPath", L"gpg.exe")); + edit_BIN_PATH.SetText(tmp); + bool gpg_exists = false; + { + if (_waccess(tmp, 0) != -1) + gpg_exists = true; + if (gpg_exists) { + bool bad_version = false; + CMStringW tmp_path = g_plugin.getMStringW("szGpgBinPath", L""); + g_plugin.setWString("szGpgBinPath", tmp); + + gpg_execution_params params; + params.addParam(L"--version"); + + bool old_gpg_state = globals.gpg_valid; + globals.gpg_valid = true; + gpg_launcher(params); + globals.gpg_valid = old_gpg_state; + g_plugin.setWString("szGpgBinPath", tmp_path); + + int p1 = params.out.Find("(GnuPG) "); + if (p1 != string::npos) { + p1 += mir_strlen("(GnuPG) "); + if (params.out[p1] != '1') + bad_version = true; + } + else { + bad_version = false; + MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); + } + } + } + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + if (tmp.Find(mir_path, 0) == 0) { + CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); + edit_BIN_PATH.SetText(path); + } + } + + void onClick_SET_HOME_DIR(CCtrlButton*) + { + GetFolderPath(TranslateT("Set home directory")); + CMStringW tmp(g_plugin.getMStringW("szHomePath", L"")); + edit_HOME_DIR.SetText(tmp); + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + if (tmp.Find(mir_path, 0) == 0) { + CMStringW path = tmp.Mid(mir_wstrlen(mir_path)); + edit_HOME_DIR.SetText(tmp); + } + } +}; + +class COptGpgMsgDlg : public CDlgBase +{ + CCtrlCheck check_APPEND_TAGS, check_STRIP_TAGS; + CCtrlEdit edit_IN_OPEN_TAG, edit_IN_CLOSE_TAG, edit_OUT_OPEN_TAG, edit_OUT_CLOSE_TAG; + +public: + COptGpgMsgDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_MESSAGES), + check_APPEND_TAGS(this, IDC_APPEND_TAGS), check_STRIP_TAGS(this, IDC_STRIP_TAGS), + edit_IN_OPEN_TAG(this, IDC_IN_OPEN_TAG), edit_IN_CLOSE_TAG(this, IDC_IN_CLOSE_TAG), edit_OUT_OPEN_TAG(this, IDC_OUT_OPEN_TAG), edit_OUT_CLOSE_TAG(this, IDC_OUT_CLOSE_TAG) + { + CreateLink(check_STRIP_TAGS, g_plugin.bStripTags); + CreateLink(check_APPEND_TAGS, g_plugin.bAppendTags); + } + + bool OnInitDialog() override + { + edit_IN_OPEN_TAG.SetText(g_plugin.getMStringW("szInOpenTag", L"")); + edit_IN_CLOSE_TAG.SetText(g_plugin.getMStringW("szInCloseTag", L"")); + edit_OUT_OPEN_TAG.SetText(g_plugin.getMStringW("szOutOpenTag", L"")); + edit_OUT_CLOSE_TAG.SetText(g_plugin.getMStringW("szOutCloseTag", L"")); + return true; + } + + bool OnApply() override + { + ptrW tmp(edit_IN_OPEN_TAG.GetText()); + g_plugin.setWString("szInOpenTag", tmp); + globals.wszInopentag = tmp; + + tmp = edit_IN_CLOSE_TAG.GetText(); + g_plugin.setWString("szInCloseTag", tmp); + globals.wszInclosetag = tmp; + + tmp = mir_wstrdup(edit_OUT_OPEN_TAG.GetText()); + g_plugin.setWString("szOutOpenTag", tmp); + globals.wszOutopentag = tmp; + + tmp = mir_wstrdup(edit_OUT_CLOSE_TAG.GetText()); + g_plugin.setWString("szOutCloseTag", tmp); + globals.wszOutclosetag = tmp; + return true; + } +}; + +class COptGpgAdvDlg : public CDlgBase +{ + CCtrlButton btn_EXPORT, btn_IMPORT; + CCtrlCheck chkPresenceSub, chkSendErrors; + +public: + COptGpgAdvDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_ADVANCED), + btn_EXPORT(this, IDC_EXPORT), + btn_IMPORT(this, IDC_IMPORT), + chkSendErrors(this, IDC_SEND_ERRORS), + chkPresenceSub(this, IDC_PRESCENSE_SUBSCRIPTION) + { + btn_EXPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_EXPORT); + btn_IMPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_IMPORT); + + CreateLink(chkSendErrors, g_plugin.bSendErrorMessages); + CreateLink(chkPresenceSub, g_plugin.bPresenceSigning); + } + + bool OnInitDialog() override + { + chkPresenceSub.Enable(g_plugin.bJabberAPI); + return true; + } + + void onClick_EXPORT(CCtrlButton*) + { + INT_PTR ExportGpGKeys(WPARAM w, LPARAM l); + ExportGpGKeys(NULL, NULL); + } + + void onClick_IMPORT(CCtrlButton*) + { + INT_PTR ImportGpGKeys(WPARAM w, LPARAM l); + ImportGpGKeys(NULL, NULL); + } +}; + +CCtrlEdit *edit_p_PubKeyEdit = nullptr; + +static LRESULT CALLBACK editctrl_ctrl_a(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_KEYDOWN: + if (wParam == 0x41 && GetKeyState(VK_CONTROL) < 0) + SendMessage(hwndDlg, EM_SETSEL, 0, -1); + return 0; + } + return mir_callNextSubclass(hwndDlg, editctrl_ctrl_a, msg, wParam, lParam); +} + +class CDlgLoadPubKeyDlg : public CDlgBase +{ + MCONTACT hContact; + wstring key_buf; + wstring::size_type ws1 = 0, ws2 = 0; + CCtrlCheck chk_ENABLE_ENCRYPTION; + CCtrlButton btn_SELECT_EXISTING, btn_OK, btn_LOAD_FROM_FILE, btn_IMPORT; + CCtrlEdit edit_PUBLIC_KEY_EDIT; + +public: + CDlgLoadPubKeyDlg(MCONTACT _p1) : + CDlgBase(g_plugin, IDD_LOAD_PUBLIC_KEY), + hContact(_p1), + chk_ENABLE_ENCRYPTION(this, IDC_ENABLE_ENCRYPTION), + btn_SELECT_EXISTING(this, IDC_SELECT_EXISTING), btn_OK(this, ID_OK), btn_LOAD_FROM_FILE(this, ID_LOAD_FROM_FILE), btn_IMPORT(this, IDC_IMPORT), + edit_PUBLIC_KEY_EDIT(this, IDC_PUBLIC_KEY_EDIT) + { + btn_SELECT_EXISTING.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_SELECT_EXISTING); + btn_OK.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_OK); + btn_LOAD_FROM_FILE.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_LOAD_FROM_FILE); + btn_IMPORT.OnClick = Callback(this, &CDlgLoadPubKeyDlg::onClick_IMPORT); + } + + bool OnInitDialog() override + { + Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); + + mir_subclassWindow(GetDlgItem(m_hwnd, IDC_PUBLIC_KEY_EDIT), editctrl_ctrl_a); + MCONTACT hcnt = db_mc_tryMeta(hContact); + { + wstring msg = TranslateT("Load Public GPG Key for "); + msg += Clist_GetContactDisplayName(hcnt, 0); + this->SetCaption(msg.c_str()); + } + if (!hcnt) { + btn_SELECT_EXISTING.Disable(); + chk_ENABLE_ENCRYPTION.Disable(); + } + if (isContactSecured(hcnt)) + chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn off encryption")); + else { + chk_ENABLE_ENCRYPTION.SetText(TranslateT("Turn on encryption")); + chk_ENABLE_ENCRYPTION.SetState(1); + } + if (hcnt) { + wstring str = ptrW(g_plugin.getWStringA(hcnt, "GPGPubKey", L"")); + if (!str.empty()) { + wstring::size_type p = 0, stop = 0; + for (;;) { + if ((p = str.find(L"\n", p + 2)) != wstring::npos) { + if (p > stop) { + stop = p; + str.insert(p, L"\r"); + } + else break; + } + } + } + + if (!globals.hcontact_data[hcnt].key_in_prescense.empty()) { + if (g_plugin.getMStringA(hcnt, "KeyID").IsEmpty()) { + gpg_execution_params params; + params.addParam(L"--export"); + params.addParam(L"-a"); + params.addParam(toUTF16(globals.hcontact_data[hcnt].key_in_prescense)); + gpg_launcher(params); //TODO: handle errors + + if ((params.out.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----") != -1) && (params.out.Find("-----END PGP PUBLIC KEY BLOCK-----") != -1)) { + params.out.Replace("\n", "\r\n"); + + wchar_t *tmp3 = mir_a2u(params.out.c_str()); + str.clear(); + str.append(tmp3); + mir_free(tmp3); + string msg = Translate("Load Public GPG Key for "); + msg += _T2A(Clist_GetContactDisplayName(hcnt)); + msg += " (Key ID: "; + msg += globals.hcontact_data[hcnt].key_in_prescense; + msg += Translate(" found in presence, and exists in keyring.)"); + SetCaption(toUTF16(msg).c_str()); + } + else { + string msg = Translate("Load Public GPG Key (Key ID: "); + msg += globals.hcontact_data[hcnt].key_in_prescense; + msg += Translate(" found in presence.)"); + SetCaption(toUTF16(msg).c_str()); + btn_IMPORT.Enable(); + } + } + } + + edit_PUBLIC_KEY_EDIT.SetText(!str.empty() ? str.c_str() : L""); + } + edit_p_PubKeyEdit = &edit_PUBLIC_KEY_EDIT; + return true; + } + + virtual void OnDestroy() override + { + Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "LoadKeyWindow"); + edit_p_PubKeyEdit = nullptr; + } + + void onClick_SELECT_EXISTING(CCtrlButton*) + { + (new CDlgLoadExistingKey())->Show(); + } + + void onClick_OK(CCtrlButton*) + { + wchar_t *tmp = mir_wstrdup(edit_PUBLIC_KEY_EDIT.GetText()); + wchar_t *begin, *end; + key_buf.append(tmp); + key_buf.append(L"\n"); //no new line at end of file ) + mir_free(tmp); + while ((ws1 = key_buf.find(L"\r", ws1)) != wstring::npos) { + key_buf.erase(ws1, 1); //remove windows specific trash + } + ws1 = 0; + if (((ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----")) != wstring::npos)) { + begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PUBLIC KEY BLOCK-----") + 1)); + mir_wstrcpy(begin, L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); + end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----") + 1)); + mir_wstrcpy(end, L"-----END PGP PUBLIC KEY BLOCK-----"); + } + else if (((ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----")) != wstring::npos) && ((ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----")) != wstring::npos)) { + begin = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----BEGIN PGP PRIVATE KEY BLOCK-----") + 1)); + mir_wstrcpy(begin, L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); + end = (wchar_t*)mir_alloc(sizeof(wchar_t) * (mir_wstrlen(L"-----END PGP PRIVATE KEY BLOCK-----") + 1)); + mir_wstrcpy(end, L"-----END PGP PRIVATE KEY BLOCK-----"); + } + else { + MessageBox(nullptr, TranslateT("This is not public or private key"), L"INFO", MB_OK); + return; + } + ws2 += mir_wstrlen(end); + bool allsubcontacts = false; + { + if (db_mc_isMeta(hContact)) { + if (MessageBox(nullptr, TranslateT("Do you want to load key for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { + allsubcontacts = true; + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.setWString(hcnt, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); + } + } + else g_plugin.setWString(metaGetMostOnline(hContact), "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); + } + else g_plugin.setWString(hContact, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); + } + tmp = (wchar_t*)mir_alloc(sizeof(wchar_t) * (key_buf.length() + 1)); + mir_wstrcpy(tmp, key_buf.substr(ws1, ws2 - ws1).c_str()); + { //gpg execute block + std::vector cmd; + CMStringW tmp2; + { + MCONTACT hcnt = db_mc_tryMeta(hContact); + tmp2 = g_plugin.getMStringW("szHomePath"); + tmp2 += L"\\temporary_exported.asc"; + boost::filesystem::remove(tmp2.c_str()); + + wfstream f(tmp2, std::ios::out); + CMStringW str = g_plugin.getMStringW(hcnt, "GPGPubKey"); + str.Replace(L"\r", L""); + f << str.c_str(); + f.close(); + } + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--import"); + params.addParam(tmp2.c_str()); + if (!gpg_launcher(params)) + return; + if (params.result == pxNotFound) + return; + + mir_free(begin); + mir_free(end); + if (hContact) { + if (db_mc_isMeta(hContact)) { + if (allsubcontacts) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.delSetting(hcnt, "bAlwatsTrust"); + } + } + else g_plugin.delSetting(metaGetMostOnline(hContact), "bAlwatsTrust"); + } + else g_plugin.delSetting(hContact, "bAlwatsTrust"); + } + + string output(params.out); + { + if (output.find("already in secret keyring") != string::npos) { + MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK); + boost::filesystem::remove(tmp2.c_str()); + return; + } + string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); + string::size_type s2 = output.find(":", s); + { + char *tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); + mir_strcpy(tmp3, output.substr(s, s2 - s).c_str()); + mir_utf8decode(tmp3, nullptr); + { + if (db_mc_isMeta(hContact)) { + if (allsubcontacts) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.setString(hcnt, "KeyID", tmp3); + } + } + else g_plugin.setString(metaGetMostOnline(hContact), "KeyID", tmp3); + } + else g_plugin.setString(hContact, "KeyID", tmp3); + } + mir_free(tmp3); + } + + if (hContact && g_pMainDlg) + g_pMainDlg->SetLineText(1, toUTF16(output.substr(s, s2 - s)).c_str()); + + s = output.find("“", s2); + if (s == string::npos) { + s = output.find("\"", s2); + s += 1; + } + else + s += 3; + bool uncommon = false; + if ((s2 = output.find("(", s)) == string::npos) { + if ((s2 = output.find("<", s)) == string::npos) { + s2 = output.find("”", s); + uncommon = true; + } + } + else if (s2 > output.find("<", s)) + s2 = output.find("<", s); + if (s2 != string::npos && s != string::npos) { + { + char *tmp3 = (char*)mir_alloc(sizeof(char)*(output.substr(s, s2 - s - (uncommon ? 1 : 0)).length() + 1)); + mir_strcpy(tmp3, output.substr(s, s2 - s - (uncommon ? 1 : 0)).c_str()); + mir_utf8decode(tmp3, nullptr); + if (hContact) { + if (db_mc_isMeta(hContact)) { + if (allsubcontacts) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.setString(hcnt, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); + } + } + else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainName", output.substr(s, s2 - s - 1).c_str()); + } + else g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); + } + mir_free(tmp3); + } + + if (hContact && g_pMainDlg) + g_pMainDlg->SetLineText(2, toUTF16(output.substr(s, s2 - s - 1)).c_str()); + + if ((s = output.find(")", s2)) == string::npos) + s = output.find(">", s2); + else if (s > output.find(">", s2)) + s = output.find(">", s2); + s2++; + if (s != string::npos && s2 != string::npos) { + if (output[s] == ')') { + char *tmp3 = (char*)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); + mir_strcpy(tmp3, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp3, nullptr); + if (hContact) { + if (db_mc_isMeta(hContact)) { + if (allsubcontacts) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.setString(hcnt, "KeyComment", output.substr(s2, s - s2).c_str()); + } + } + else g_plugin.setString(metaGetMostOnline(hContact), "KeyComment", output.substr(s2, s - s2).c_str()); + } + else g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str()); + } + mir_free(tmp3); + s += 3; + s2 = output.find(">", s); + tmp3 = (char*)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); + mir_strcpy(tmp3, output.substr(s, s2 - s).c_str()); + mir_utf8decode(tmp3, nullptr); + if (hContact) { + if (db_mc_isMeta(hContact)) { + if (allsubcontacts) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s, s2 - s).c_str()); + } + } + else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s, s2 - s).c_str()); + } + else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str()); + } + mir_free(tmp3); + + if (hContact && g_pMainDlg) + g_pMainDlg->SetLineText(3, toUTF16(output.substr(s, s2 - s)).c_str()); + } + else { + char *tmp3 = (char*)mir_alloc(output.substr(s2, s - s2).length() + 1); + mir_strcpy(tmp3, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp3, nullptr); + if (hContact) { + if (db_mc_isMeta(hContact)) { + if (allsubcontacts) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.setString(hcnt, "KeyMainEmail", output.substr(s2, s - s2).c_str()); + } + } + else g_plugin.setString(metaGetMostOnline(hContact), "KeyMainEmail", output.substr(s2, s - s2).c_str()); + } + else g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); + } + mir_free(tmp3); + + if (hContact && g_pMainDlg) + g_pMainDlg->SetLineText(3, toUTF16(output.substr(s2, s - s2)).c_str()); + } + } + } + if (hContact && g_pMainDlg) + g_pMainDlg->SetListAutoSize(); + } + if (!hContact) { + gpg_execution_params params2; + params.addParam(L"--batch"); + params.addParam(L"-a"); + params.addParam(L"--export"); + params.addParam(g_plugin.getMStringW(hContact, "KeyID").c_str()); + if (!gpg_launcher(params2)) + return; + if (params2.result == pxNotFound) + return; + + params2.out.Remove('\r'); + g_plugin.setString(hContact, "GPGPubKey", params2.out.c_str()); + } + MessageBoxA(nullptr, output.c_str(), "", MB_OK); + boost::filesystem::remove(tmp2.c_str()); + } + key_buf.clear(); + if (chk_ENABLE_ENCRYPTION.GetState()) { + if (hContact) { + if (db_mc_isMeta(hContact)) { + if (allsubcontacts) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) { + g_plugin.setByte(hcnt, "GPGEncryption", !isContactSecured(hcnt)); + setSrmmIcon(hContact); + } + } + } + else g_plugin.setByte(metaGetMostOnline(hContact), "GPGEncryption", !isContactSecured(hContact)); + } + else g_plugin.setByte(hContact, "GPGEncryption", !isContactSecured(hContact)); + } + } + this->Close(); + } + + void onClick_LOAD_FROM_FILE(CCtrlButton *) + { + ptrW tmp(GetFilePath(TranslateT("Set file containing GPG public key"), L"*", TranslateT("GPG public key file"))); + if (!tmp) + return; + + wfstream f(tmp, std::ios::in | std::ios::ate | std::ios::binary); + if (!f.is_open()) { + MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); + return; + } + if (f.is_open()) { + std::wifstream::pos_type size = f.tellg(); + wchar_t *temp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1]; + f.seekg(0, std::ios::beg); + f.read(temp, size); + temp[size] = '\0'; + key_buf.append(temp); + delete[] temp; + f.close(); + } + if (key_buf.empty()) { + key_buf.clear(); + if (globals.debuglog) + globals.debuglog << "info: Failed to read key file"; + return; + } + ws2 = key_buf.find(L"-----END PGP PUBLIC KEY BLOCK-----"); + ws1 = key_buf.find(L"-----BEGIN PGP PUBLIC KEY BLOCK-----"); + if (ws2 == wstring::npos || ws1 == wstring::npos) { + ws2 = key_buf.find(L"-----END PGP PRIVATE KEY BLOCK-----"); + ws1 = key_buf.find(L"-----BEGIN PGP PRIVATE KEY BLOCK-----"); + } + if (ws2 == wstring::npos || ws1 == wstring::npos) { + MessageBox(nullptr, TranslateT("There is no public or private key."), TranslateT("Info"), MB_OK); + return; + } + ws2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); + edit_PUBLIC_KEY_EDIT.SetText(key_buf.substr(ws1, ws2 - ws1).c_str()); + key_buf.clear(); + } + + void onClick_IMPORT(CCtrlButton *) + { + CDlgImportKey *d = new CDlgImportKey(hContact); + d->Show(); + } +}; + + +void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal) +{ + CDlgLoadPubKeyDlg *d = new CDlgLoadPubKeyDlg(hContact); + if (bModal) + d->DoModal(); + else + d->Show(); +} + +int GpgOptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.szGroup.w = LPGENW("Services"); + odp.szTitle.w = _T(MODULENAME); + + odp.szTab.w = LPGENW("Main"); + odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE; + odp.pDialog = new COptGpgMainDlg(); + g_plugin.addOptions(wParam, &odp); + + odp.szTab.w = LPGENW("GnuPG Variables"); + odp.pDialog = new COptGpgBinDlg(); + g_plugin.addOptions(wParam, &odp); + + odp.szTab.w = LPGENW("Messages"); + odp.pDialog = new COptGpgMsgDlg(); + g_plugin.addOptions(wParam, &odp); + + odp.szTab.w = LPGENW("Advanced"); + odp.pDialog = new COptGpgAdvDlg(); + g_plugin.addOptions(wParam, &odp); + return 0; +} diff --git a/plugins/New_GPG/src/options.h b/plugins/New_GPG/src/options.h index 45c92f914a..b7d98935c2 100644 --- a/plugins/New_GPG/src/options.h +++ b/plugins/New_GPG/src/options.h @@ -1,22 +1,22 @@ -// Copyright © 2017-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef OPTIONS_H -#define OPTIONS_H - - - +// Copyright © 2017-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef OPTIONS_H +#define OPTIONS_H + + + #endif \ No newline at end of file diff --git a/plugins/New_GPG/src/srmm.cpp b/plugins/New_GPG/src/srmm.cpp index 589dda598d..ee575c4947 100644 --- a/plugins/New_GPG/src/srmm.cpp +++ b/plugins/New_GPG/src/srmm.cpp @@ -1,78 +1,78 @@ -// Copyright © 2010-22 SecureIM developers (baloo and others), sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -static void ToggleIcon(MCONTACT hContact) -{ - MCONTACT hMeta = NULL; - if (db_mc_isMeta(hContact)) { - hMeta = hContact; - hContact = metaGetMostOnline(hContact); // возьмем тот, через который пойдет сообщение - } - else if (db_mc_isSub(hContact)) - hMeta = db_mc_getMeta(hContact); - - int enc = g_plugin.getByte(hContact, "GPGEncryption"); - if (enc) { - g_plugin.setByte(hContact, "GPGEncryption", 0); - if (hMeta) - g_plugin.setByte(hMeta, "GPGEncryption", 0); - setSrmmIcon(hContact); - } - else if (!enc) { - if (!isContactHaveKey(hContact)) - ShowLoadPublicKeyDialog(hContact, false); - else { - g_plugin.setByte(hContact, "GPGEncryption", 1); - if (hMeta) - g_plugin.setByte(hMeta, "GPGEncryption", 1); - setSrmmIcon(hContact); - return; - } - - if (isContactHaveKey(hContact)) { - g_plugin.setByte(hContact, "GPGEncryption", 1); - if (hMeta) - g_plugin.setByte(hMeta, "GPGEncryption", 1); - setSrmmIcon(hContact); - } - } -} - -int __cdecl onWindowEvent(WPARAM, LPARAM lParam) -{ - MessageWindowEventData *mwd = (MessageWindowEventData *)lParam; - if (mwd->uType == MSG_WINDOW_EVT_OPEN || mwd->uType == MSG_WINDOW_EVT_OPENING) - if (isContactHaveKey(mwd->hContact)) - setSrmmIcon(mwd->hContact); - return 0; -} - -int __cdecl onIconPressed(WPARAM hContact, LPARAM lParam) -{ - StatusIconClickData *sicd = (StatusIconClickData *)lParam; - if (!mir_strcmp(sicd->szModule, MODULENAME)) - ToggleIcon(hContact); - - return 0; -} - -int __cdecl onExtraIconPressed(WPARAM hContact, LPARAM, LPARAM) -{ - ToggleIcon(hContact); - return 0; -} +// Copyright © 2010-23 SecureIM developers (baloo and others), sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +static void ToggleIcon(MCONTACT hContact) +{ + MCONTACT hMeta = NULL; + if (db_mc_isMeta(hContact)) { + hMeta = hContact; + hContact = metaGetMostOnline(hContact); // возьмем тот, через который пойдет сообщение + } + else if (db_mc_isSub(hContact)) + hMeta = db_mc_getMeta(hContact); + + int enc = g_plugin.getByte(hContact, "GPGEncryption"); + if (enc) { + g_plugin.setByte(hContact, "GPGEncryption", 0); + if (hMeta) + g_plugin.setByte(hMeta, "GPGEncryption", 0); + setSrmmIcon(hContact); + } + else if (!enc) { + if (!isContactHaveKey(hContact)) + ShowLoadPublicKeyDialog(hContact, false); + else { + g_plugin.setByte(hContact, "GPGEncryption", 1); + if (hMeta) + g_plugin.setByte(hMeta, "GPGEncryption", 1); + setSrmmIcon(hContact); + return; + } + + if (isContactHaveKey(hContact)) { + g_plugin.setByte(hContact, "GPGEncryption", 1); + if (hMeta) + g_plugin.setByte(hMeta, "GPGEncryption", 1); + setSrmmIcon(hContact); + } + } +} + +int __cdecl onWindowEvent(WPARAM, LPARAM lParam) +{ + MessageWindowEventData *mwd = (MessageWindowEventData *)lParam; + if (mwd->uType == MSG_WINDOW_EVT_OPEN || mwd->uType == MSG_WINDOW_EVT_OPENING) + if (isContactHaveKey(mwd->hContact)) + setSrmmIcon(mwd->hContact); + return 0; +} + +int __cdecl onIconPressed(WPARAM hContact, LPARAM lParam) +{ + StatusIconClickData *sicd = (StatusIconClickData *)lParam; + if (!mir_strcmp(sicd->szModule, MODULENAME)) + ToggleIcon(hContact); + + return 0; +} + +int __cdecl onExtraIconPressed(WPARAM hContact, LPARAM, LPARAM) +{ + ToggleIcon(hContact); + return 0; +} diff --git a/plugins/New_GPG/src/stdafx.cxx b/plugins/New_GPG/src/stdafx.cxx index d265a4c02e..8c570f6949 100644 --- a/plugins/New_GPG/src/stdafx.cxx +++ b/plugins/New_GPG/src/stdafx.cxx @@ -1,18 +1,18 @@ -/* -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation version 2 -of the License. - -This program 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 General Public License -along with this program. If not, see . -*/ - -#include "stdafx.h" +/* +Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program 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 General Public License +along with this program. If not, see . +*/ + +#include "stdafx.h" diff --git a/plugins/New_GPG/src/stdafx.h b/plugins/New_GPG/src/stdafx.h index 3553a65c41..5abc9c16f7 100644 --- a/plugins/New_GPG/src/stdafx.h +++ b/plugins/New_GPG/src/stdafx.h @@ -1,98 +1,98 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef COMMONHEADERS_H -#define COMMONHEADERS_H - -#pragma warning(disable:4512 4267 4127) - -#define WIN32_LEAN_AND_MEAN -#define _SCL_SECURE_NO_WARNINGS - -#include - -// windows -#include -#include -#include -#include - -// c++ -#include -using std::map; -#include -using std::list; -#include -using std::string; -using std::wstring; -#include -using std::wfstream; -using std::fstream; - -// boost -#include -#include -#include -#include -#include - -// boost process -#include -#include - -// miranda -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -struct CMPlugin : public PLUGIN -{ - CMOption bJabberAPI, bPresenceSigning, bFileTransfers, bAutoExchange, bSameAction, bAppendTags, bStripTags, bDebugLog, bSendErrorMessages; - HANDLE hCLIcon = nullptr; - HGENMENU hToggleEncryption = nullptr, hSendKey = nullptr; - - CMPlugin(); - - int Load() override; - int Unload() override; -}; - -// internal -#include "resource.h" -#include "version.h" -#include "constants.h" -#include "log.h" -#include "utilities.h" -#include "gpg_wrapper.h" -#include "jabber_account.h" -#include "metacontacts.h" -#include "ui.h" -#include "options.h" -#include "globals.h" - -#endif +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef COMMONHEADERS_H +#define COMMONHEADERS_H + +#pragma warning(disable:4512 4267 4127) + +#define WIN32_LEAN_AND_MEAN +#define _SCL_SECURE_NO_WARNINGS + +#include + +// windows +#include +#include +#include +#include + +// c++ +#include +using std::map; +#include +using std::list; +#include +using std::string; +using std::wstring; +#include +using std::wfstream; +using std::fstream; + +// boost +#include +#include +#include +#include +#include + +// boost process +#include +#include + +// miranda +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct CMPlugin : public PLUGIN +{ + CMOption bJabberAPI, bPresenceSigning, bFileTransfers, bAutoExchange, bSameAction, bAppendTags, bStripTags, bDebugLog, bSendErrorMessages; + HANDLE hCLIcon = nullptr; + HGENMENU hToggleEncryption = nullptr, hSendKey = nullptr; + + CMPlugin(); + + int Load() override; + int Unload() override; +}; + +// internal +#include "resource.h" +#include "version.h" +#include "constants.h" +#include "log.h" +#include "utilities.h" +#include "gpg_wrapper.h" +#include "jabber_account.h" +#include "metacontacts.h" +#include "ui.h" +#include "options.h" +#include "globals.h" + +#endif diff --git a/plugins/New_GPG/src/ui.cpp b/plugins/New_GPG/src/ui.cpp index 78ff38af92..2118ce723a 100644 --- a/plugins/New_GPG/src/ui.cpp +++ b/plugins/New_GPG/src/ui.cpp @@ -1,902 +1,902 @@ -// Copyright (c) 2017-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// - -bool CDlgEncryptedFileMsgBox::OnInitDialog() -{ - globals.bDecryptFiles = false; - return true; -} - -CDlgEncryptedFileMsgBox::CDlgEncryptedFileMsgBox() : - CDlgBase(g_plugin, IDD_ENCRYPTED_FILE_MSG_BOX), - chk_REMEMBER(this, IDC_REMEMBER), - btn_IGNORE(this, IDC_IGNORE), - btn_DECRYPT(this, IDC_DECRYPT) -{ - btn_IGNORE.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_IGNORE); - btn_DECRYPT.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_DECRYPT); -} - -void CDlgEncryptedFileMsgBox::onClick_IGNORE(CCtrlButton*) -{ - if (chk_REMEMBER.GetState()) - g_plugin.bSameAction = true; - - this->Close(); -} - -void CDlgEncryptedFileMsgBox::onClick_DECRYPT(CCtrlButton*) -{ - globals.bDecryptFiles = true; - if (chk_REMEMBER.GetState()) { - g_plugin.bFileTransfers = true; - g_plugin.bSameAction = false; - } - this->Close(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CDlgExportKeysMsgBox : public CDlgBase -{ - CCtrlCheck chk_PUBLIC, chk_PRIVATE, chk_ALL; - -public: - CDlgExportKeysMsgBox() : - CDlgBase(g_plugin, IDD_EXPORT_TYPE), - chk_PUBLIC(this, IDC_PUBLIC), - chk_PRIVATE(this, IDC_PRIVATE), - chk_ALL(this, IDC_ALL) - { - } - - bool OnInitDialog() override - { - chk_PUBLIC.SetState(true); - return true; - } - - bool OnApply() override - { - if (chk_PUBLIC.GetState()) - ExportGpGKeysFunc(0); - else if (chk_PRIVATE.GetState()) - ExportGpGKeysFunc(1); - else if (chk_ALL.GetState()) - ExportGpGKeysFunc(2); - return true; - } -}; - -INT_PTR ExportGpGKeys(WPARAM, LPARAM) -{ - (new CDlgExportKeysMsgBox())->Show(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -class CDlgChangePasswdMsgBox : public CDlgBase //always modal -{ - CCtrlEdit edit_NEW_PASSWD1, edit_NEW_PASSWD2, edit_OLD_PASSWD; - -public: - CDlgChangePasswdMsgBox() : - CDlgBase(g_plugin, IDD_CHANGE_PASSWD), - edit_NEW_PASSWD1(this, IDC_NEW_PASSWD1), - edit_NEW_PASSWD2(this, IDC_NEW_PASSWD2), - edit_OLD_PASSWD(this, IDC_OLD_PASSWD) - { - } - - bool OnApply() override - { - //TODO: show some prgress - if (mir_wstrcmp(edit_NEW_PASSWD1.GetText(), edit_NEW_PASSWD2.GetText())) { - MessageBox(m_hwnd, TranslateT("New passwords do not match"), TranslateT("Error"), MB_OK); - return false; - } - - std::string old_pass, new_pass; - new_pass = toUTF8(ptrW(edit_NEW_PASSWD1.GetText()).get()); - old_pass = toUTF8(ptrW(edit_OLD_PASSWD.GetText()).get()); - - bool old_pass_match = false; - if (!mir_strcmp(ptrA(g_plugin.getUStringA("szKeyPassword")), old_pass.c_str())) - old_pass_match = true; - - if (!old_pass_match) { - if (globals.key_id_global[0]) { - string dbsetting = "szKey_"; - dbsetting += toUTF8(globals.key_id_global); - dbsetting += "_Password"; - ptrA pass(g_plugin.getUStringA(dbsetting.c_str())); - if (!mir_strcmp(pass, old_pass.c_str())) - old_pass_match = true; - } - } - - if (!old_pass_match) - if (MessageBox(m_hwnd, TranslateT("Old password does not match, you can continue, but GPG will reject wrong password.\nDo you want to continue?"), TranslateT("Error"), MB_YESNO) == IDNO) - return false; - - gpg_execution_params_pass params(old_pass, new_pass); - params.addParam(L"--edit-key"); - params.addParam(globals.key_id_global); - params.addParam(L"passwd"); - - HANDLE hThread = mir_forkThread(&pxEexcute_passwd_change_thread, ¶ms); - if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) { - if (params.child) - params.child->terminate(); - if (globals.debuglog) - globals.debuglog << "GPG execution timed out, aborted"; - return true; - } - - return params.result != pxNotFound; - } -}; - -void ShowChangePasswdDlg() -{ - CDlgChangePasswdMsgBox *d = new CDlgChangePasswdMsgBox; - d->DoModal(); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// New key generation dialog - -class CDlgKeyGen : public CDlgBase -{ - CCtrlCombo combo_KEY_TYPE; - CCtrlEdit edit_KEY_LENGTH, edit_KEY_PASSWD, edit_KEY_REAL_NAME, edit_KEY_EMAIL, edit_KEY_COMMENT, edit_KEY_EXPIRE_DATE; - CCtrlData lbl_GENERATING_TEXT; - -public: - CDlgKeyGen() : - CDlgBase(g_plugin, IDD_KEY_GEN), - combo_KEY_TYPE(this, IDC_KEY_TYPE), - edit_KEY_LENGTH(this, IDC_KEY_LENGTH), - edit_KEY_PASSWD(this, IDC_KEY_PASSWD), - edit_KEY_REAL_NAME(this, IDC_KEY_REAL_NAME), - edit_KEY_EMAIL(this, IDC_KEY_EMAIL), - edit_KEY_COMMENT(this, IDC_KEY_COMMENT), - edit_KEY_EXPIRE_DATE(this, IDC_KEY_EXPIRE_DATE), - lbl_GENERATING_TEXT(this, IDC_GENERATING_TEXT) - { - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow"); - SetCaption(TranslateT("Key Generation dialog")); - - combo_KEY_TYPE.AddString(L"RSA"); - combo_KEY_TYPE.AddString(L"DSA"); - combo_KEY_TYPE.SelectString(L"RSA"); - edit_KEY_EXPIRE_DATE.SetText(L"0"); - edit_KEY_LENGTH.SetText(L"4096"); - return true; - } - - bool OnApply() override - { - // data sanity checks - ptrW tmp(combo_KEY_TYPE.GetText()); - if (mir_wstrlen(tmp) < 3) { - MessageBox(nullptr, TranslateT("You must set encryption algorithm first"), TranslateT("Error"), MB_OK); - return false; - } - - tmp = edit_KEY_LENGTH.GetText(); - int length = _wtoi(tmp); - if (length < 1024 || length > 4096) { - MessageBox(nullptr, TranslateT("Key length must be of length from 1024 to 4096 bits"), TranslateT("Error"), MB_OK); - return false; - } - - tmp = edit_KEY_EXPIRE_DATE.GetText(); - if (mir_wstrlen(tmp) != 10 && tmp[0] != '0') { - MessageBox(nullptr, TranslateT("Invalid date"), TranslateT("Error"), MB_OK); - return false; - } - - tmp = edit_KEY_REAL_NAME.GetText(); - if (mir_wstrlen(tmp) < 4) { - MessageBox(nullptr, TranslateT("Name must contain at least 4 characters"), TranslateT("Error"), MB_OK); - return false; - } - if (wcschr(tmp, '(') || wcschr(tmp, ')')) { - MessageBox(nullptr, TranslateT("Name cannot contain '(' or ')'"), TranslateT("Error"), MB_OK); - return false; - } - - tmp = edit_KEY_EMAIL.GetText(); - if (mir_wstrlen(tmp) < 5 || !wcschr(tmp, '@')) { - MessageBox(nullptr, TranslateT("Invalid Email"), TranslateT("Error"), MB_OK); - return false; - } - - // generating key file - CMStringW path = g_plugin.getMStringW("szHomePath"); - path += L"\\new_key"; - wfstream f(path.c_str(), std::ios::out); - if (!f.is_open()) { - MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); - return false; - } - - f << "Key-Type: "; - char *tmp2 = mir_u2a(combo_KEY_TYPE.GetText()); - char *subkeytype = (char *)mir_alloc(6); - if (strstr(tmp2, "RSA")) - mir_strcpy(subkeytype, "RSA"); - else if (strstr(tmp2, "DSA")) //this is useless check for now, but it will be required if someone add another key types support - mir_strcpy(subkeytype, "ELG-E"); - f << tmp2; - mir_free(tmp2); - f << "\n"; - f << "Key-Length: "; - f << _wtoi(edit_KEY_LENGTH.GetText()); - f << "\n"; - f << "Subkey-Length: "; - f << _wtoi(edit_KEY_LENGTH.GetText()); - f << "\n"; - f << "Subkey-Type: "; - f << subkeytype; - mir_free(subkeytype); - f << "\n"; - if (edit_KEY_PASSWD.GetText()[0]) { - f << "Passphrase: "; - f << toUTF8(edit_KEY_PASSWD.GetText()).c_str(); - f << "\n"; - } - f << "Name-Real: "; - f << toUTF8(edit_KEY_REAL_NAME.GetText()).c_str(); - f << "\n"; - if (edit_KEY_COMMENT.GetText()[0]) { - f << "Name-Comment: "; - f << toUTF8(edit_KEY_COMMENT.GetText()).c_str(); - f << "\n"; - } - f << "Name-Email: "; - f << toUTF8(edit_KEY_EMAIL.GetText()).c_str(); - f << "\n"; - f << "Expire-Date: "; - f << toUTF8(edit_KEY_EXPIRE_DATE.GetText()).c_str(); - f << "\n"; - f.close(); - - lbl_GENERATING_TEXT.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE); - lbl_GENERATING_TEXT.SetText(TranslateT("Generating new key, please wait...")); - combo_KEY_TYPE.Disable(); - edit_KEY_LENGTH.Disable(); - edit_KEY_PASSWD.Disable(); - edit_KEY_REAL_NAME.Disable(); - edit_KEY_EMAIL.Disable(); - edit_KEY_COMMENT.Disable(); - edit_KEY_EXPIRE_DATE.Disable(); - - // gpg execution - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"--gen-key"); - params.addParam(path.c_str()); - params.bNoOutput = true; - if (!gpg_launcher(params, boost::posix_time::minutes(10))) - return false; - if (params.result == pxNotFound) - return false; - - boost::filesystem::remove(path.c_str()); - return true; - } - - void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow"); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// First run dialog - -class CDlgFirstRun : public CDlgBase -{ - void refresh_key_list() - { - list_KEY_LIST.DeleteAllItems(); - int i = 1; - - // parse gpg output - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--list-secret-keys"); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - wstring::size_type p = 0, p2 = 0, stop = 0; - string out(params.out); - while (p != string::npos) { - if ((p = out.find("sec ", p)) == string::npos) - break; - p += 5; - if (p < stop) - break; - stop = p; - p2 = out.find("/", p) - 1; - wchar_t *key_len = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()), *creation_date = nullptr, *expire_date = nullptr; - p2 += 2; - p = out.find(" ", p2); - std::wstring key_id = toUTF16(out.substr(p2, p - p2)); - p += 1; - p2 = out.find(" ", p); - std::string::size_type p3 = out.find("\n", p); - if ((p2 != std::string::npos) && (p3 < p2)) { - p2 = p3; - creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p - 1)).c_str()); - } - else { - creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); - p2 = out.find("[", p2); - p2 = out.find("expires:", p2); - p2 += mir_strlen("expires:"); - if (p2 != std::string::npos) { - p2++; - p = p2; - p2 = out.find("]", p); - expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); - //check expiration - bool expired = false; - { - boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); - wchar_t buf[5]; - wcsncpy_s(buf, expire_date, _TRUNCATE); - int year = _wtoi(buf); - if (year < now.date().year()) - expired = true; - else if (year == now.date().year()) { - wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); - int month = _wtoi(buf); - if (month < now.date().month()) - expired = true; - else if (month == now.date().month()) { - wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); - unsigned day = _wtoi(buf); - if (day <= now.date().day_number()) - expired = true; - } - } - } - if (expired) { - mir_free(key_len); - mir_free(creation_date); - mir_free(expire_date); - //mimic normal behaviour - p = out.find("uid ", p); - p2 = out.find_first_not_of(" ", p + 5); - p = out.find("<", p2); - p++; - //p2 = out.find(">", p); - // - continue; //does not add to key list - } - } - } - int row = list_KEY_LIST.AddItem(L"", 0); - list_KEY_LIST.SetItemText(row, 3, creation_date); - mir_free(creation_date); - if (expire_date) { - list_KEY_LIST.SetItemText(row, 4, expire_date); - mir_free(expire_date); - } - list_KEY_LIST.SetItemText(row, 5, key_len); - mir_free(key_len); - list_KEY_LIST.SetItemText(row, 0, (wchar_t *)key_id.c_str()); - p = out.find("uid ", p); - p2 = out.find_first_not_of(" ", p + 5); - p = out.find("<", p2); - - wstring tmp = toUTF16(out.substr(p2, p - p2)); - list_KEY_LIST.SetItemText(row, 2, (wchar_t *)tmp.c_str()); - - p++; - p2 = out.find(">", p); - - tmp = toUTF16(out.substr(p, p2 - p)); - list_KEY_LIST.SetItemText(row, 1, (wchar_t *)tmp.c_str()); - - // get accounts - std::wstring accs; - for (auto &pa : Accounts()) { - std::string setting = pa->szModuleName; - setting += "_KeyID"; - ptrW str(g_plugin.getWStringA(setting.c_str(), L"")); - if (key_id == str.get()) { - if (!accs.empty()) - accs += L","; - accs += pa->tszAccountName; - } - } - list_KEY_LIST.SetItemText(row, 6, accs.c_str()); - } - i++; - list_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE); - list_KEY_LIST.SetColumnWidth(6, LVSCW_AUTOSIZE); - } - - CCtrlListView list_KEY_LIST; - CCtrlButton btn_COPY_PUBKEY, btn_EXPORT_PRIVATE, btn_CHANGE_PASSWD, btn_GENERATE_RANDOM, btn_GENERATE_KEY, btn_OTHER, btn_DELETE_KEY, btn_OK; - CCtrlEdit edit_KEY_PASSWORD; - CCtrlCombo combo_ACCOUNT; - CCtrlData lbl_KEY_ID, lbl_GENERATING_KEY; - wchar_t fp[16]; - const char *m_szCurrAcc = nullptr; - -public: - CDlgFirstRun() : - CDlgBase(g_plugin, IDD_FIRST_RUN), - list_KEY_LIST(this, IDC_KEY_LIST), - btn_COPY_PUBKEY(this, IDC_COPY_PUBKEY), - btn_EXPORT_PRIVATE(this, IDC_EXPORT_PRIVATE), - btn_CHANGE_PASSWD(this, IDC_CHANGE_PASSWD), - btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM), - btn_GENERATE_KEY(this, IDC_GENERATE_KEY), - btn_OTHER(this, IDC_OTHER), - btn_DELETE_KEY(this, IDC_DELETE_KEY), - btn_OK(this, ID_OK), - edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD), - combo_ACCOUNT(this, IDC_ACCOUNT), - lbl_KEY_ID(this, IDC_KEY_ID), - lbl_GENERATING_KEY(this, IDC_GENERATING_KEY) - { - fp[0] = 0; - - btn_COPY_PUBKEY.OnClick = Callback(this, &CDlgFirstRun::onClick_COPY_PUBKEY); - btn_EXPORT_PRIVATE.OnClick = Callback(this, &CDlgFirstRun::onClick_EXPORT_PRIVATE); - btn_CHANGE_PASSWD.OnClick = Callback(this, &CDlgFirstRun::onClick_CHANGE_PASSWD); - btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_RANDOM); - btn_GENERATE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_KEY); - btn_OTHER.OnClick = Callback(this, &CDlgFirstRun::onClick_OTHER); - btn_DELETE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_DELETE_KEY); - btn_OK.OnClick = Callback(this, &CDlgFirstRun::onClick_OK); - - combo_ACCOUNT.OnChange = Callback(this, &CDlgFirstRun::onChange_ACCOUNT); - - list_KEY_LIST.OnClick = Callback(this, &CDlgFirstRun::onChange_KEY_LIST); - } - - bool OnInitDialog() override - { - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow"); - SetCaption(TranslateT("Bind own keys to accounts")); - btn_COPY_PUBKEY.Disable(); - btn_EXPORT_PRIVATE.Disable(); - btn_CHANGE_PASSWD.Disable(); - - list_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50); - list_KEY_LIST.AddColumn(1, TranslateT("Email"), 30); - list_KEY_LIST.AddColumn(2, TranslateT("Name"), 250); - list_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30); - list_KEY_LIST.AddColumn(4, TranslateT("Expire date"), 30); - list_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30); - list_KEY_LIST.AddColumn(6, TranslateT("Accounts"), 30); - list_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); - - refresh_key_list(); - - combo_ACCOUNT.AddString(TranslateT("Default")); - - for (auto &pa : Accounts()) { - if (StriStr(pa->szModuleName, "metacontacts")) - continue; - if (StriStr(pa->szModuleName, "weather")) - continue; - - combo_ACCOUNT.AddString(pa->tszAccountName, (LPARAM)pa->szModuleName); - } - combo_ACCOUNT.SetCurSel(0); - - lbl_KEY_ID.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get())); - return true; - } - - void OnDestroy() override - { - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow"); - } - - void onClick_COPY_PUBKEY(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(fp); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - params.out.Remove('\r'); - Utils_ClipboardCopy(params.out); - } - - void onClick_EXPORT_PRIVATE(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - ptrW p(GetFilePath(L"Choose file to export key", L"*", L"Any file", true)); - if (!p || !p[0]) - return; - - std::ofstream file; - file.open(p, std::ios::trunc | std::ios::out); - if (!file.is_open()) - return; //TODO: handle error - - list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export-secret-keys"); - params.addParam(fp); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - params.out.Remove('\r'); - file << params.out.c_str(); - if (file.is_open()) - file.close(); - } - - void onClick_CHANGE_PASSWD(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - list_KEY_LIST.GetItemText(i, 0, globals.key_id_global, _countof(globals.key_id_global)); - - // temporary code follows - std::string old_pass, new_pass; - - gpg_execution_params_pass params(old_pass, new_pass); - params.addParam(L"--edit-key"); - params.addParam(globals.key_id_global); - params.addParam(L"passwd"); - - HANDLE hThread = mir_forkThread(pxEexcute_passwd_change_thread, ¶ms); - if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) { - if (params.child) - params.child->terminate(); - if (globals.debuglog) - globals.debuglog << "GPG execution timed out, aborted"; - this->Close(); - } - } - - void onClick_GENERATE_RANDOM(CCtrlButton *) - { - lbl_GENERATING_KEY.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE); - lbl_GENERATING_KEY.SetText(TranslateT("Generating new random key, please wait")); - btn_GENERATE_KEY.Disable(); - btn_OTHER.Disable(); - btn_DELETE_KEY.Disable(); - list_KEY_LIST.Disable(); - btn_GENERATE_RANDOM.Disable(); - gpg_use_new_random_key(m_szCurrAcc); - this->Close(); - } - - void onClick_GENERATE_KEY(CCtrlButton *) - { - CDlgKeyGen().DoModal(); - refresh_key_list(); - } - - void onClick_OTHER(CCtrlButton *) - { - ShowLoadPublicKeyDialog(0, true); - refresh_key_list(); - } - - void onClick_DELETE_KEY(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); - { - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--fingerprint"); - params.addParam(fp); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - int s = params.out.Find("Key fingerprint = "); - s += mir_strlen("Key fingerprint = "); - int s2 = params.out.Find("\n", s); - - CMStringW tmp = params.out.Mid(s, s2 - s); - tmp.Remove(' '); - - gpg_execution_params params2; - params2.addParam(L"--batch"); - params2.addParam(L"--delete-secret-and-public-key"); - params2.addParam(L"--fingerprint"); - params2.addParam(tmp.c_str()); - - if (!gpg_launcher(params2)) - return; - if (params2.result == pxNotFound) - return; - } - - if (m_szCurrAcc == nullptr) { - g_plugin.delSetting("GPGPubKey"); - g_plugin.delSetting("KeyID"); - g_plugin.delSetting("KeyComment"); - g_plugin.delSetting("KeyMainName"); - g_plugin.delSetting("KeyMainEmail"); - g_plugin.delSetting("KeyType"); - } - else { - CMStringA acc_str = m_szCurrAcc; - g_plugin.delSetting(acc_str + "_GPGPubKey"); - g_plugin.delSetting(acc_str + "_KeyMainName"); - g_plugin.delSetting(acc_str + "_KeyID"); - g_plugin.delSetting(acc_str + "_KeyComment"); - g_plugin.delSetting(acc_str + "_KeyMainEmail"); - g_plugin.delSetting(acc_str + "_KeyType"); - } - - list_KEY_LIST.DeleteItem(i); - } - - void onClick_OK(CCtrlButton *) - { - int i = list_KEY_LIST.GetSelectionMark(); - if (i == -1) - return; - - list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); - wchar_t name[65]; - list_KEY_LIST.GetItemText(i, 2, name, 64); - { - if (wcschr(name, '(')) { - wstring str = name; - wstring::size_type p = str.find(L"(") - 1; - mir_wstrcpy(name, str.substr(0, p).c_str()); - } - } - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(fp); - if (!gpg_launcher(params)) - return; - if (params.result == pxNotFound) - return; - - params.out.Remove('\r'); - - if (m_szCurrAcc == nullptr) { - g_plugin.setString("GPGPubKey", params.out.c_str()); - g_plugin.setWString("KeyMainName", name); - g_plugin.setWString("KeyID", fp); - - wstring keyinfo = TranslateT("Default private key ID"); - keyinfo += L": "; - keyinfo += (fp[0]) ? fp : L"not set"; - extern HWND hwndCurKey_p; - SetWindowText(hwndCurKey_p, keyinfo.c_str()); - } - else { - CMStringA acc_str = m_szCurrAcc; - g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str()); - g_plugin.setWString(acc_str + "_KeyMainName", name); - g_plugin.setWString(acc_str + "_KeyID", fp); - } - - ptrW passwd(edit_KEY_PASSWORD.GetText()); - if (mir_wstrlen(passwd)) { - string dbsetting = "szKey_"; - dbsetting += _T2A(fp); - dbsetting += "_Password"; - g_plugin.setWString(dbsetting.c_str(), passwd); - } - - //bAutoExchange = CheckStateStoreDB(hwndDlg, IDC_AUTO_EXCHANGE, "bAutoExchange") != 0; //TODO: check is it just typo, or doing something - globals.gpg_valid = isGPGValid(); - globals.gpg_keyexist = isGPGKeyExist(); - DestroyWindow(m_hwnd); - } - - void onChange_ACCOUNT(CCtrlCombo *pCombo) - { - CMStringW keyinfo = TranslateT("key ID"); - keyinfo += ": "; - - m_szCurrAcc = (const char *)pCombo->GetCurData(); - if (m_szCurrAcc == nullptr) { - keyinfo += g_plugin.getMStringW("KeyID", TranslateT("not set")); - } - else { - std::string acc_str = m_szCurrAcc; - acc_str += "_KeyID"; - keyinfo += g_plugin.getMStringW(acc_str.c_str(), TranslateT("not set")); - } - lbl_KEY_ID.SetText(keyinfo); - } - - void onChange_KEY_LIST(CCtrlListView::TEventInfo *ev) - { - if (ev->nmlv) { - NMLISTVIEW *hdr = ev->nmlv; - - if (hdr->hdr.code == NM_CLICK) { - btn_OK.Enable(); - btn_COPY_PUBKEY.Enable(); - btn_EXPORT_PRIVATE.Enable(); - btn_CHANGE_PASSWD.Enable(); - } - } - } -}; - -void ShowFirstRunDialog() -{ - CDlgFirstRun().DoModal(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CDlgNewKey::CDlgNewKey(MCONTACT _hContact, wstring _new_key) : - CDlgBase(g_plugin, IDD_NEW_KEY), - lbl_KEY_FROM(this, IDC_KEY_FROM), - lbl_MESSAGE(this, IDC_MESSAGE), - btn_IMPORT(this, ID_IMPORT), - btn_IMPORT_AND_USE(this, IDC_IMPORT_AND_USE), - btn_IGNORE_KEY(this, IDC_IGNORE_KEY) -{ - hContact = _hContact; - new_key = _new_key; - btn_IMPORT.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT); - btn_IMPORT_AND_USE.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT_AND_USE); - btn_IGNORE_KEY.OnClick = Callback(this, &CDlgNewKey::onClick_IGNORE_KEY); -} - -bool CDlgNewKey::OnInitDialog() -{ - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow"); - - CMStringW tmp = g_plugin.getMStringW(hContact, "GPGPubKey"); - lbl_MESSAGE.SetText(!tmp.IsEmpty() ? TranslateT("There is existing key for contact, would you like to replace it with new key?") : TranslateT("New public key was received, do you want to import it?")); - btn_IMPORT_AND_USE.Enable(g_plugin.getByte(hContact, "GPGEncryption", 0)); - btn_IMPORT.SetText(!tmp.IsEmpty() ? TranslateT("Replace") : TranslateT("Accept")); - - tmp.Format(TranslateT("Received key from %s"), Clist_GetContactDisplayName(hContact)); - lbl_KEY_FROM.SetText(tmp); - return true; -} - -void CDlgNewKey::OnDestroy() -{ - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow"); -} - -void CDlgNewKey::onClick_IMPORT(CCtrlButton*) -{ - ImportKey(hContact, new_key); - this->Close(); -} - -void CDlgNewKey::onClick_IMPORT_AND_USE(CCtrlButton*) -{ - ImportKey(hContact, new_key); - g_plugin.setByte(hContact, "GPGEncryption", 1); - setSrmmIcon(hContact); - this->Close(); -} - -void CDlgNewKey::onClick_IGNORE_KEY(CCtrlButton*) -{ - this->Close(); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -CDlgKeyPasswordMsgBox::CDlgKeyPasswordMsgBox(MCONTACT _hContact) : - CDlgBase(g_plugin, IDD_KEY_PASSWD), - lbl_KEYID(this, IDC_KEYID), - edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD), - chk_DEFAULT_PASSWORD(this, IDC_DEFAULT_PASSWORD), - chk_SAVE_PASSWORD(this, IDC_SAVE_PASSWORD) -{ - hContact = _hContact; -} - -bool CDlgKeyPasswordMsgBox::OnInitDialog() -{ - Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow"); - - CMStringW questionstr = TranslateT("Please enter password for key with ID: "); - questionstr += g_plugin.getMStringW(hContact, "InKeyID"); - lbl_KEYID.SetText(questionstr.c_str()); - - chk_DEFAULT_PASSWORD.Disable(); - return true; -} - -bool CDlgKeyPasswordMsgBox::OnApply() -{ - ptrW tmp(edit_KEY_PASSWORD.GetText()); - if (tmp && tmp[0]) { - if (chk_SAVE_PASSWORD.GetState()) { - inkeyid = g_plugin.getStringA(hContact, "InKeyID", ""); - if (inkeyid && inkeyid[0] && !chk_DEFAULT_PASSWORD.GetState()) { - string dbsetting = "szKey_"; - dbsetting += inkeyid; - dbsetting += "_Password"; - g_plugin.setWString(dbsetting.c_str(), tmp); - } - else g_plugin.setWString("szKeyPassword", tmp); - } - globals.wszPassword = tmp; - } - mir_free(inkeyid); - return true; -} - -void CDlgKeyPasswordMsgBox::OnDestroy() -{ - if (!m_bSucceeded) - globals._terminate = true; - - mir_free(inkeyid); - Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow"); -} +// Copyright (c) 2017-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// + +bool CDlgEncryptedFileMsgBox::OnInitDialog() +{ + globals.bDecryptFiles = false; + return true; +} + +CDlgEncryptedFileMsgBox::CDlgEncryptedFileMsgBox() : + CDlgBase(g_plugin, IDD_ENCRYPTED_FILE_MSG_BOX), + chk_REMEMBER(this, IDC_REMEMBER), + btn_IGNORE(this, IDC_IGNORE), + btn_DECRYPT(this, IDC_DECRYPT) +{ + btn_IGNORE.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_IGNORE); + btn_DECRYPT.OnClick = Callback(this, &CDlgEncryptedFileMsgBox::onClick_DECRYPT); +} + +void CDlgEncryptedFileMsgBox::onClick_IGNORE(CCtrlButton*) +{ + if (chk_REMEMBER.GetState()) + g_plugin.bSameAction = true; + + this->Close(); +} + +void CDlgEncryptedFileMsgBox::onClick_DECRYPT(CCtrlButton*) +{ + globals.bDecryptFiles = true; + if (chk_REMEMBER.GetState()) { + g_plugin.bFileTransfers = true; + g_plugin.bSameAction = false; + } + this->Close(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +class CDlgExportKeysMsgBox : public CDlgBase +{ + CCtrlCheck chk_PUBLIC, chk_PRIVATE, chk_ALL; + +public: + CDlgExportKeysMsgBox() : + CDlgBase(g_plugin, IDD_EXPORT_TYPE), + chk_PUBLIC(this, IDC_PUBLIC), + chk_PRIVATE(this, IDC_PRIVATE), + chk_ALL(this, IDC_ALL) + { + } + + bool OnInitDialog() override + { + chk_PUBLIC.SetState(true); + return true; + } + + bool OnApply() override + { + if (chk_PUBLIC.GetState()) + ExportGpGKeysFunc(0); + else if (chk_PRIVATE.GetState()) + ExportGpGKeysFunc(1); + else if (chk_ALL.GetState()) + ExportGpGKeysFunc(2); + return true; + } +}; + +INT_PTR ExportGpGKeys(WPARAM, LPARAM) +{ + (new CDlgExportKeysMsgBox())->Show(); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +class CDlgChangePasswdMsgBox : public CDlgBase //always modal +{ + CCtrlEdit edit_NEW_PASSWD1, edit_NEW_PASSWD2, edit_OLD_PASSWD; + +public: + CDlgChangePasswdMsgBox() : + CDlgBase(g_plugin, IDD_CHANGE_PASSWD), + edit_NEW_PASSWD1(this, IDC_NEW_PASSWD1), + edit_NEW_PASSWD2(this, IDC_NEW_PASSWD2), + edit_OLD_PASSWD(this, IDC_OLD_PASSWD) + { + } + + bool OnApply() override + { + //TODO: show some prgress + if (mir_wstrcmp(edit_NEW_PASSWD1.GetText(), edit_NEW_PASSWD2.GetText())) { + MessageBox(m_hwnd, TranslateT("New passwords do not match"), TranslateT("Error"), MB_OK); + return false; + } + + std::string old_pass, new_pass; + new_pass = toUTF8(ptrW(edit_NEW_PASSWD1.GetText()).get()); + old_pass = toUTF8(ptrW(edit_OLD_PASSWD.GetText()).get()); + + bool old_pass_match = false; + if (!mir_strcmp(ptrA(g_plugin.getUStringA("szKeyPassword")), old_pass.c_str())) + old_pass_match = true; + + if (!old_pass_match) { + if (globals.key_id_global[0]) { + string dbsetting = "szKey_"; + dbsetting += toUTF8(globals.key_id_global); + dbsetting += "_Password"; + ptrA pass(g_plugin.getUStringA(dbsetting.c_str())); + if (!mir_strcmp(pass, old_pass.c_str())) + old_pass_match = true; + } + } + + if (!old_pass_match) + if (MessageBox(m_hwnd, TranslateT("Old password does not match, you can continue, but GPG will reject wrong password.\nDo you want to continue?"), TranslateT("Error"), MB_YESNO) == IDNO) + return false; + + gpg_execution_params_pass params(old_pass, new_pass); + params.addParam(L"--edit-key"); + params.addParam(globals.key_id_global); + params.addParam(L"passwd"); + + HANDLE hThread = mir_forkThread(&pxEexcute_passwd_change_thread, ¶ms); + if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) { + if (params.child) + params.child->terminate(); + if (globals.debuglog) + globals.debuglog << "GPG execution timed out, aborted"; + return true; + } + + return params.result != pxNotFound; + } +}; + +void ShowChangePasswdDlg() +{ + CDlgChangePasswdMsgBox *d = new CDlgChangePasswdMsgBox; + d->DoModal(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// New key generation dialog + +class CDlgKeyGen : public CDlgBase +{ + CCtrlCombo combo_KEY_TYPE; + CCtrlEdit edit_KEY_LENGTH, edit_KEY_PASSWD, edit_KEY_REAL_NAME, edit_KEY_EMAIL, edit_KEY_COMMENT, edit_KEY_EXPIRE_DATE; + CCtrlData lbl_GENERATING_TEXT; + +public: + CDlgKeyGen() : + CDlgBase(g_plugin, IDD_KEY_GEN), + combo_KEY_TYPE(this, IDC_KEY_TYPE), + edit_KEY_LENGTH(this, IDC_KEY_LENGTH), + edit_KEY_PASSWD(this, IDC_KEY_PASSWD), + edit_KEY_REAL_NAME(this, IDC_KEY_REAL_NAME), + edit_KEY_EMAIL(this, IDC_KEY_EMAIL), + edit_KEY_COMMENT(this, IDC_KEY_COMMENT), + edit_KEY_EXPIRE_DATE(this, IDC_KEY_EXPIRE_DATE), + lbl_GENERATING_TEXT(this, IDC_GENERATING_TEXT) + { + } + + bool OnInitDialog() override + { + Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow"); + SetCaption(TranslateT("Key Generation dialog")); + + combo_KEY_TYPE.AddString(L"RSA"); + combo_KEY_TYPE.AddString(L"DSA"); + combo_KEY_TYPE.SelectString(L"RSA"); + edit_KEY_EXPIRE_DATE.SetText(L"0"); + edit_KEY_LENGTH.SetText(L"4096"); + return true; + } + + bool OnApply() override + { + // data sanity checks + ptrW tmp(combo_KEY_TYPE.GetText()); + if (mir_wstrlen(tmp) < 3) { + MessageBox(nullptr, TranslateT("You must set encryption algorithm first"), TranslateT("Error"), MB_OK); + return false; + } + + tmp = edit_KEY_LENGTH.GetText(); + int length = _wtoi(tmp); + if (length < 1024 || length > 4096) { + MessageBox(nullptr, TranslateT("Key length must be of length from 1024 to 4096 bits"), TranslateT("Error"), MB_OK); + return false; + } + + tmp = edit_KEY_EXPIRE_DATE.GetText(); + if (mir_wstrlen(tmp) != 10 && tmp[0] != '0') { + MessageBox(nullptr, TranslateT("Invalid date"), TranslateT("Error"), MB_OK); + return false; + } + + tmp = edit_KEY_REAL_NAME.GetText(); + if (mir_wstrlen(tmp) < 4) { + MessageBox(nullptr, TranslateT("Name must contain at least 4 characters"), TranslateT("Error"), MB_OK); + return false; + } + if (wcschr(tmp, '(') || wcschr(tmp, ')')) { + MessageBox(nullptr, TranslateT("Name cannot contain '(' or ')'"), TranslateT("Error"), MB_OK); + return false; + } + + tmp = edit_KEY_EMAIL.GetText(); + if (mir_wstrlen(tmp) < 5 || !wcschr(tmp, '@')) { + MessageBox(nullptr, TranslateT("Invalid Email"), TranslateT("Error"), MB_OK); + return false; + } + + // generating key file + CMStringW path = g_plugin.getMStringW("szHomePath"); + path += L"\\new_key"; + wfstream f(path.c_str(), std::ios::out); + if (!f.is_open()) { + MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); + return false; + } + + f << "Key-Type: "; + char *tmp2 = mir_u2a(combo_KEY_TYPE.GetText()); + char *subkeytype = (char *)mir_alloc(6); + if (strstr(tmp2, "RSA")) + mir_strcpy(subkeytype, "RSA"); + else if (strstr(tmp2, "DSA")) //this is useless check for now, but it will be required if someone add another key types support + mir_strcpy(subkeytype, "ELG-E"); + f << tmp2; + mir_free(tmp2); + f << "\n"; + f << "Key-Length: "; + f << _wtoi(edit_KEY_LENGTH.GetText()); + f << "\n"; + f << "Subkey-Length: "; + f << _wtoi(edit_KEY_LENGTH.GetText()); + f << "\n"; + f << "Subkey-Type: "; + f << subkeytype; + mir_free(subkeytype); + f << "\n"; + if (edit_KEY_PASSWD.GetText()[0]) { + f << "Passphrase: "; + f << toUTF8(edit_KEY_PASSWD.GetText()).c_str(); + f << "\n"; + } + f << "Name-Real: "; + f << toUTF8(edit_KEY_REAL_NAME.GetText()).c_str(); + f << "\n"; + if (edit_KEY_COMMENT.GetText()[0]) { + f << "Name-Comment: "; + f << toUTF8(edit_KEY_COMMENT.GetText()).c_str(); + f << "\n"; + } + f << "Name-Email: "; + f << toUTF8(edit_KEY_EMAIL.GetText()).c_str(); + f << "\n"; + f << "Expire-Date: "; + f << toUTF8(edit_KEY_EXPIRE_DATE.GetText()).c_str(); + f << "\n"; + f.close(); + + lbl_GENERATING_TEXT.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE); + lbl_GENERATING_TEXT.SetText(TranslateT("Generating new key, please wait...")); + combo_KEY_TYPE.Disable(); + edit_KEY_LENGTH.Disable(); + edit_KEY_PASSWD.Disable(); + edit_KEY_REAL_NAME.Disable(); + edit_KEY_EMAIL.Disable(); + edit_KEY_COMMENT.Disable(); + edit_KEY_EXPIRE_DATE.Disable(); + + // gpg execution + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--yes"); + params.addParam(L"--gen-key"); + params.addParam(path.c_str()); + params.bNoOutput = true; + if (!gpg_launcher(params, boost::posix_time::minutes(10))) + return false; + if (params.result == pxNotFound) + return false; + + boost::filesystem::remove(path.c_str()); + return true; + } + + void OnDestroy() override + { + Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "KeygenWindow"); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// First run dialog + +class CDlgFirstRun : public CDlgBase +{ + void refresh_key_list() + { + list_KEY_LIST.DeleteAllItems(); + int i = 1; + + // parse gpg output + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--list-secret-keys"); + if (!gpg_launcher(params)) + return; + if (params.result == pxNotFound) + return; + + wstring::size_type p = 0, p2 = 0, stop = 0; + string out(params.out); + while (p != string::npos) { + if ((p = out.find("sec ", p)) == string::npos) + break; + p += 5; + if (p < stop) + break; + stop = p; + p2 = out.find("/", p) - 1; + wchar_t *key_len = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()), *creation_date = nullptr, *expire_date = nullptr; + p2 += 2; + p = out.find(" ", p2); + std::wstring key_id = toUTF16(out.substr(p2, p - p2)); + p += 1; + p2 = out.find(" ", p); + std::string::size_type p3 = out.find("\n", p); + if ((p2 != std::string::npos) && (p3 < p2)) { + p2 = p3; + creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p - 1)).c_str()); + } + else { + creation_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); + p2 = out.find("[", p2); + p2 = out.find("expires:", p2); + p2 += mir_strlen("expires:"); + if (p2 != std::string::npos) { + p2++; + p = p2; + p2 = out.find("]", p); + expire_date = mir_wstrdup(toUTF16(out.substr(p, p2 - p)).c_str()); + //check expiration + bool expired = false; + { + boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); + wchar_t buf[5]; + wcsncpy_s(buf, expire_date, _TRUNCATE); + int year = _wtoi(buf); + if (year < now.date().year()) + expired = true; + else if (year == now.date().year()) { + wcsncpy_s(buf, (expire_date + 5), _TRUNCATE); + int month = _wtoi(buf); + if (month < now.date().month()) + expired = true; + else if (month == now.date().month()) { + wcsncpy_s(buf, (expire_date + 8), _TRUNCATE); + unsigned day = _wtoi(buf); + if (day <= now.date().day_number()) + expired = true; + } + } + } + if (expired) { + mir_free(key_len); + mir_free(creation_date); + mir_free(expire_date); + //mimic normal behaviour + p = out.find("uid ", p); + p2 = out.find_first_not_of(" ", p + 5); + p = out.find("<", p2); + p++; + //p2 = out.find(">", p); + // + continue; //does not add to key list + } + } + } + int row = list_KEY_LIST.AddItem(L"", 0); + list_KEY_LIST.SetItemText(row, 3, creation_date); + mir_free(creation_date); + if (expire_date) { + list_KEY_LIST.SetItemText(row, 4, expire_date); + mir_free(expire_date); + } + list_KEY_LIST.SetItemText(row, 5, key_len); + mir_free(key_len); + list_KEY_LIST.SetItemText(row, 0, (wchar_t *)key_id.c_str()); + p = out.find("uid ", p); + p2 = out.find_first_not_of(" ", p + 5); + p = out.find("<", p2); + + wstring tmp = toUTF16(out.substr(p2, p - p2)); + list_KEY_LIST.SetItemText(row, 2, (wchar_t *)tmp.c_str()); + + p++; + p2 = out.find(">", p); + + tmp = toUTF16(out.substr(p, p2 - p)); + list_KEY_LIST.SetItemText(row, 1, (wchar_t *)tmp.c_str()); + + // get accounts + std::wstring accs; + for (auto &pa : Accounts()) { + std::string setting = pa->szModuleName; + setting += "_KeyID"; + ptrW str(g_plugin.getWStringA(setting.c_str(), L"")); + if (key_id == str.get()) { + if (!accs.empty()) + accs += L","; + accs += pa->tszAccountName; + } + } + list_KEY_LIST.SetItemText(row, 6, accs.c_str()); + } + i++; + list_KEY_LIST.SetColumnWidth(0, LVSCW_AUTOSIZE); + list_KEY_LIST.SetColumnWidth(1, LVSCW_AUTOSIZE); + list_KEY_LIST.SetColumnWidth(2, LVSCW_AUTOSIZE); + list_KEY_LIST.SetColumnWidth(3, LVSCW_AUTOSIZE); + list_KEY_LIST.SetColumnWidth(4, LVSCW_AUTOSIZE); + list_KEY_LIST.SetColumnWidth(5, LVSCW_AUTOSIZE); + list_KEY_LIST.SetColumnWidth(6, LVSCW_AUTOSIZE); + } + + CCtrlListView list_KEY_LIST; + CCtrlButton btn_COPY_PUBKEY, btn_EXPORT_PRIVATE, btn_CHANGE_PASSWD, btn_GENERATE_RANDOM, btn_GENERATE_KEY, btn_OTHER, btn_DELETE_KEY, btn_OK; + CCtrlEdit edit_KEY_PASSWORD; + CCtrlCombo combo_ACCOUNT; + CCtrlData lbl_KEY_ID, lbl_GENERATING_KEY; + wchar_t fp[16]; + const char *m_szCurrAcc = nullptr; + +public: + CDlgFirstRun() : + CDlgBase(g_plugin, IDD_FIRST_RUN), + list_KEY_LIST(this, IDC_KEY_LIST), + btn_COPY_PUBKEY(this, IDC_COPY_PUBKEY), + btn_EXPORT_PRIVATE(this, IDC_EXPORT_PRIVATE), + btn_CHANGE_PASSWD(this, IDC_CHANGE_PASSWD), + btn_GENERATE_RANDOM(this, IDC_GENERATE_RANDOM), + btn_GENERATE_KEY(this, IDC_GENERATE_KEY), + btn_OTHER(this, IDC_OTHER), + btn_DELETE_KEY(this, IDC_DELETE_KEY), + btn_OK(this, ID_OK), + edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD), + combo_ACCOUNT(this, IDC_ACCOUNT), + lbl_KEY_ID(this, IDC_KEY_ID), + lbl_GENERATING_KEY(this, IDC_GENERATING_KEY) + { + fp[0] = 0; + + btn_COPY_PUBKEY.OnClick = Callback(this, &CDlgFirstRun::onClick_COPY_PUBKEY); + btn_EXPORT_PRIVATE.OnClick = Callback(this, &CDlgFirstRun::onClick_EXPORT_PRIVATE); + btn_CHANGE_PASSWD.OnClick = Callback(this, &CDlgFirstRun::onClick_CHANGE_PASSWD); + btn_GENERATE_RANDOM.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_RANDOM); + btn_GENERATE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_GENERATE_KEY); + btn_OTHER.OnClick = Callback(this, &CDlgFirstRun::onClick_OTHER); + btn_DELETE_KEY.OnClick = Callback(this, &CDlgFirstRun::onClick_DELETE_KEY); + btn_OK.OnClick = Callback(this, &CDlgFirstRun::onClick_OK); + + combo_ACCOUNT.OnChange = Callback(this, &CDlgFirstRun::onChange_ACCOUNT); + + list_KEY_LIST.OnClick = Callback(this, &CDlgFirstRun::onChange_KEY_LIST); + } + + bool OnInitDialog() override + { + Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow"); + SetCaption(TranslateT("Bind own keys to accounts")); + btn_COPY_PUBKEY.Disable(); + btn_EXPORT_PRIVATE.Disable(); + btn_CHANGE_PASSWD.Disable(); + + list_KEY_LIST.AddColumn(0, TranslateT("Key ID"), 50); + list_KEY_LIST.AddColumn(1, TranslateT("Email"), 30); + list_KEY_LIST.AddColumn(2, TranslateT("Name"), 250); + list_KEY_LIST.AddColumn(3, TranslateT("Creation date"), 30); + list_KEY_LIST.AddColumn(4, TranslateT("Expire date"), 30); + list_KEY_LIST.AddColumn(5, TranslateT("Key length"), 30); + list_KEY_LIST.AddColumn(6, TranslateT("Accounts"), 30); + list_KEY_LIST.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); + + refresh_key_list(); + + combo_ACCOUNT.AddString(TranslateT("Default")); + + for (auto &pa : Accounts()) { + if (StriStr(pa->szModuleName, "metacontacts")) + continue; + if (StriStr(pa->szModuleName, "weather")) + continue; + + combo_ACCOUNT.AddString(pa->tszAccountName, (LPARAM)pa->szModuleName); + } + combo_ACCOUNT.SetCurSel(0); + + lbl_KEY_ID.SetText(CMStringW(FORMAT, L"%s: %s", TranslateT("key ID"), ptrW(g_plugin.getWStringA("KeyID", TranslateT("not set"))).get())); + return true; + } + + void OnDestroy() override + { + Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "FirstrunWindow"); + } + + void onClick_COPY_PUBKEY(CCtrlButton *) + { + int i = list_KEY_LIST.GetSelectionMark(); + if (i == -1) + return; + + list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"-a"); + params.addParam(L"--export"); + params.addParam(fp); + if (!gpg_launcher(params)) + return; + if (params.result == pxNotFound) + return; + + params.out.Remove('\r'); + Utils_ClipboardCopy(params.out); + } + + void onClick_EXPORT_PRIVATE(CCtrlButton *) + { + int i = list_KEY_LIST.GetSelectionMark(); + if (i == -1) + return; + + ptrW p(GetFilePath(L"Choose file to export key", L"*", L"Any file", true)); + if (!p || !p[0]) + return; + + std::ofstream file; + file.open(p, std::ios::trunc | std::ios::out); + if (!file.is_open()) + return; //TODO: handle error + + list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"-a"); + params.addParam(L"--export-secret-keys"); + params.addParam(fp); + if (!gpg_launcher(params)) + return; + if (params.result == pxNotFound) + return; + + params.out.Remove('\r'); + file << params.out.c_str(); + if (file.is_open()) + file.close(); + } + + void onClick_CHANGE_PASSWD(CCtrlButton *) + { + int i = list_KEY_LIST.GetSelectionMark(); + if (i == -1) + return; + + list_KEY_LIST.GetItemText(i, 0, globals.key_id_global, _countof(globals.key_id_global)); + + // temporary code follows + std::string old_pass, new_pass; + + gpg_execution_params_pass params(old_pass, new_pass); + params.addParam(L"--edit-key"); + params.addParam(globals.key_id_global); + params.addParam(L"passwd"); + + HANDLE hThread = mir_forkThread(pxEexcute_passwd_change_thread, ¶ms); + if (WaitForSingleObject(hThread, 600000) != WAIT_OBJECT_0) { + if (params.child) + params.child->terminate(); + if (globals.debuglog) + globals.debuglog << "GPG execution timed out, aborted"; + this->Close(); + } + } + + void onClick_GENERATE_RANDOM(CCtrlButton *) + { + lbl_GENERATING_KEY.SendMsg(WM_SETFONT, (WPARAM)globals.bold_font, TRUE); + lbl_GENERATING_KEY.SetText(TranslateT("Generating new random key, please wait")); + btn_GENERATE_KEY.Disable(); + btn_OTHER.Disable(); + btn_DELETE_KEY.Disable(); + list_KEY_LIST.Disable(); + btn_GENERATE_RANDOM.Disable(); + gpg_use_new_random_key(m_szCurrAcc); + this->Close(); + } + + void onClick_GENERATE_KEY(CCtrlButton *) + { + CDlgKeyGen().DoModal(); + refresh_key_list(); + } + + void onClick_OTHER(CCtrlButton *) + { + ShowLoadPublicKeyDialog(0, true); + refresh_key_list(); + } + + void onClick_DELETE_KEY(CCtrlButton *) + { + int i = list_KEY_LIST.GetSelectionMark(); + if (i == -1) + return; + + list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); + { + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--fingerprint"); + params.addParam(fp); + if (!gpg_launcher(params)) + return; + if (params.result == pxNotFound) + return; + + int s = params.out.Find("Key fingerprint = "); + s += mir_strlen("Key fingerprint = "); + int s2 = params.out.Find("\n", s); + + CMStringW tmp = params.out.Mid(s, s2 - s); + tmp.Remove(' '); + + gpg_execution_params params2; + params2.addParam(L"--batch"); + params2.addParam(L"--delete-secret-and-public-key"); + params2.addParam(L"--fingerprint"); + params2.addParam(tmp.c_str()); + + if (!gpg_launcher(params2)) + return; + if (params2.result == pxNotFound) + return; + } + + if (m_szCurrAcc == nullptr) { + g_plugin.delSetting("GPGPubKey"); + g_plugin.delSetting("KeyID"); + g_plugin.delSetting("KeyComment"); + g_plugin.delSetting("KeyMainName"); + g_plugin.delSetting("KeyMainEmail"); + g_plugin.delSetting("KeyType"); + } + else { + CMStringA acc_str = m_szCurrAcc; + g_plugin.delSetting(acc_str + "_GPGPubKey"); + g_plugin.delSetting(acc_str + "_KeyMainName"); + g_plugin.delSetting(acc_str + "_KeyID"); + g_plugin.delSetting(acc_str + "_KeyComment"); + g_plugin.delSetting(acc_str + "_KeyMainEmail"); + g_plugin.delSetting(acc_str + "_KeyType"); + } + + list_KEY_LIST.DeleteItem(i); + } + + void onClick_OK(CCtrlButton *) + { + int i = list_KEY_LIST.GetSelectionMark(); + if (i == -1) + return; + + list_KEY_LIST.GetItemText(i, 0, fp, _countof(fp)); + wchar_t name[65]; + list_KEY_LIST.GetItemText(i, 2, name, 64); + { + if (wcschr(name, '(')) { + wstring str = name; + wstring::size_type p = str.find(L"(") - 1; + mir_wstrcpy(name, str.substr(0, p).c_str()); + } + } + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"-a"); + params.addParam(L"--export"); + params.addParam(fp); + if (!gpg_launcher(params)) + return; + if (params.result == pxNotFound) + return; + + params.out.Remove('\r'); + + if (m_szCurrAcc == nullptr) { + g_plugin.setString("GPGPubKey", params.out.c_str()); + g_plugin.setWString("KeyMainName", name); + g_plugin.setWString("KeyID", fp); + + wstring keyinfo = TranslateT("Default private key ID"); + keyinfo += L": "; + keyinfo += (fp[0]) ? fp : L"not set"; + extern HWND hwndCurKey_p; + SetWindowText(hwndCurKey_p, keyinfo.c_str()); + } + else { + CMStringA acc_str = m_szCurrAcc; + g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str()); + g_plugin.setWString(acc_str + "_KeyMainName", name); + g_plugin.setWString(acc_str + "_KeyID", fp); + } + + ptrW passwd(edit_KEY_PASSWORD.GetText()); + if (mir_wstrlen(passwd)) { + string dbsetting = "szKey_"; + dbsetting += _T2A(fp); + dbsetting += "_Password"; + g_plugin.setWString(dbsetting.c_str(), passwd); + } + + //bAutoExchange = CheckStateStoreDB(hwndDlg, IDC_AUTO_EXCHANGE, "bAutoExchange") != 0; //TODO: check is it just typo, or doing something + globals.gpg_valid = isGPGValid(); + globals.gpg_keyexist = isGPGKeyExist(); + DestroyWindow(m_hwnd); + } + + void onChange_ACCOUNT(CCtrlCombo *pCombo) + { + CMStringW keyinfo = TranslateT("key ID"); + keyinfo += ": "; + + m_szCurrAcc = (const char *)pCombo->GetCurData(); + if (m_szCurrAcc == nullptr) { + keyinfo += g_plugin.getMStringW("KeyID", TranslateT("not set")); + } + else { + std::string acc_str = m_szCurrAcc; + acc_str += "_KeyID"; + keyinfo += g_plugin.getMStringW(acc_str.c_str(), TranslateT("not set")); + } + lbl_KEY_ID.SetText(keyinfo); + } + + void onChange_KEY_LIST(CCtrlListView::TEventInfo *ev) + { + if (ev->nmlv) { + NMLISTVIEW *hdr = ev->nmlv; + + if (hdr->hdr.code == NM_CLICK) { + btn_OK.Enable(); + btn_COPY_PUBKEY.Enable(); + btn_EXPORT_PRIVATE.Enable(); + btn_CHANGE_PASSWD.Enable(); + } + } + } +}; + +void ShowFirstRunDialog() +{ + CDlgFirstRun().DoModal(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CDlgNewKey::CDlgNewKey(MCONTACT _hContact, wstring _new_key) : + CDlgBase(g_plugin, IDD_NEW_KEY), + lbl_KEY_FROM(this, IDC_KEY_FROM), + lbl_MESSAGE(this, IDC_MESSAGE), + btn_IMPORT(this, ID_IMPORT), + btn_IMPORT_AND_USE(this, IDC_IMPORT_AND_USE), + btn_IGNORE_KEY(this, IDC_IGNORE_KEY) +{ + hContact = _hContact; + new_key = _new_key; + btn_IMPORT.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT); + btn_IMPORT_AND_USE.OnClick = Callback(this, &CDlgNewKey::onClick_IMPORT_AND_USE); + btn_IGNORE_KEY.OnClick = Callback(this, &CDlgNewKey::onClick_IGNORE_KEY); +} + +bool CDlgNewKey::OnInitDialog() +{ + Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow"); + + CMStringW tmp = g_plugin.getMStringW(hContact, "GPGPubKey"); + lbl_MESSAGE.SetText(!tmp.IsEmpty() ? TranslateT("There is existing key for contact, would you like to replace it with new key?") : TranslateT("New public key was received, do you want to import it?")); + btn_IMPORT_AND_USE.Enable(g_plugin.getByte(hContact, "GPGEncryption", 0)); + btn_IMPORT.SetText(!tmp.IsEmpty() ? TranslateT("Replace") : TranslateT("Accept")); + + tmp.Format(TranslateT("Received key from %s"), Clist_GetContactDisplayName(hContact)); + lbl_KEY_FROM.SetText(tmp); + return true; +} + +void CDlgNewKey::OnDestroy() +{ + Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "NewKeyWindow"); +} + +void CDlgNewKey::onClick_IMPORT(CCtrlButton*) +{ + ImportKey(hContact, new_key); + this->Close(); +} + +void CDlgNewKey::onClick_IMPORT_AND_USE(CCtrlButton*) +{ + ImportKey(hContact, new_key); + g_plugin.setByte(hContact, "GPGEncryption", 1); + setSrmmIcon(hContact); + this->Close(); +} + +void CDlgNewKey::onClick_IGNORE_KEY(CCtrlButton*) +{ + this->Close(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CDlgKeyPasswordMsgBox::CDlgKeyPasswordMsgBox(MCONTACT _hContact) : + CDlgBase(g_plugin, IDD_KEY_PASSWD), + lbl_KEYID(this, IDC_KEYID), + edit_KEY_PASSWORD(this, IDC_KEY_PASSWORD), + chk_DEFAULT_PASSWORD(this, IDC_DEFAULT_PASSWORD), + chk_SAVE_PASSWORD(this, IDC_SAVE_PASSWORD) +{ + hContact = _hContact; +} + +bool CDlgKeyPasswordMsgBox::OnInitDialog() +{ + Utils_RestoreWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow"); + + CMStringW questionstr = TranslateT("Please enter password for key with ID: "); + questionstr += g_plugin.getMStringW(hContact, "InKeyID"); + lbl_KEYID.SetText(questionstr.c_str()); + + chk_DEFAULT_PASSWORD.Disable(); + return true; +} + +bool CDlgKeyPasswordMsgBox::OnApply() +{ + ptrW tmp(edit_KEY_PASSWORD.GetText()); + if (tmp && tmp[0]) { + if (chk_SAVE_PASSWORD.GetState()) { + inkeyid = g_plugin.getStringA(hContact, "InKeyID", ""); + if (inkeyid && inkeyid[0] && !chk_DEFAULT_PASSWORD.GetState()) { + string dbsetting = "szKey_"; + dbsetting += inkeyid; + dbsetting += "_Password"; + g_plugin.setWString(dbsetting.c_str(), tmp); + } + else g_plugin.setWString("szKeyPassword", tmp); + } + globals.wszPassword = tmp; + } + mir_free(inkeyid); + return true; +} + +void CDlgKeyPasswordMsgBox::OnDestroy() +{ + if (!m_bSucceeded) + globals._terminate = true; + + mir_free(inkeyid); + Utils_SaveWindowPosition(m_hwnd, 0, MODULENAME, "PasswordWindow"); +} diff --git a/plugins/New_GPG/src/ui.h b/plugins/New_GPG/src/ui.h index 1e30441b83..1e5c936716 100644 --- a/plugins/New_GPG/src/ui.h +++ b/plugins/New_GPG/src/ui.h @@ -1,69 +1,69 @@ -// Copyright © 2017-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef UI_H -#define UI_H - -void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal); -void ShowFirstRunDialog(); - -class CDlgEncryptedFileMsgBox : public CDlgBase -{ - CCtrlCheck chk_REMEMBER; - CCtrlButton btn_IGNORE, btn_DECRYPT; - -public: - CDlgEncryptedFileMsgBox(); - bool OnInitDialog() override; - - void onClick_IGNORE(CCtrlButton*); - void onClick_DECRYPT(CCtrlButton*); -}; - -class CDlgNewKey : public CDlgBase -{ - wstring new_key; - MCONTACT hContact; - CCtrlData lbl_KEY_FROM, lbl_MESSAGE; - CCtrlButton btn_IMPORT, btn_IMPORT_AND_USE, btn_IGNORE_KEY; - -public: - CDlgNewKey(MCONTACT hContact, wstring new_key); - bool OnInitDialog() override; - void OnDestroy() override; - - void onClick_IMPORT(CCtrlButton*); - void onClick_IMPORT_AND_USE(CCtrlButton*); - void onClick_IGNORE_KEY(CCtrlButton*); -}; - -class CDlgKeyPasswordMsgBox : public CDlgBase //always modal -{ - char *inkeyid = nullptr; - MCONTACT hContact; - CCtrlData lbl_KEYID; - CCtrlEdit edit_KEY_PASSWORD; - CCtrlCheck chk_DEFAULT_PASSWORD, chk_SAVE_PASSWORD; - -public: - CDlgKeyPasswordMsgBox(MCONTACT _hContact); - - bool OnInitDialog() override; - bool OnApply() override; - void OnDestroy() override; -}; - +// Copyright © 2017-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef UI_H +#define UI_H + +void ShowLoadPublicKeyDialog(MCONTACT hContact, bool bModal); +void ShowFirstRunDialog(); + +class CDlgEncryptedFileMsgBox : public CDlgBase +{ + CCtrlCheck chk_REMEMBER; + CCtrlButton btn_IGNORE, btn_DECRYPT; + +public: + CDlgEncryptedFileMsgBox(); + bool OnInitDialog() override; + + void onClick_IGNORE(CCtrlButton*); + void onClick_DECRYPT(CCtrlButton*); +}; + +class CDlgNewKey : public CDlgBase +{ + wstring new_key; + MCONTACT hContact; + CCtrlData lbl_KEY_FROM, lbl_MESSAGE; + CCtrlButton btn_IMPORT, btn_IMPORT_AND_USE, btn_IGNORE_KEY; + +public: + CDlgNewKey(MCONTACT hContact, wstring new_key); + bool OnInitDialog() override; + void OnDestroy() override; + + void onClick_IMPORT(CCtrlButton*); + void onClick_IMPORT_AND_USE(CCtrlButton*); + void onClick_IGNORE_KEY(CCtrlButton*); +}; + +class CDlgKeyPasswordMsgBox : public CDlgBase //always modal +{ + char *inkeyid = nullptr; + MCONTACT hContact; + CCtrlData lbl_KEYID; + CCtrlEdit edit_KEY_PASSWORD; + CCtrlCheck chk_DEFAULT_PASSWORD, chk_SAVE_PASSWORD; + +public: + CDlgKeyPasswordMsgBox(MCONTACT _hContact); + + bool OnInitDialog() override; + bool OnApply() override; + void OnDestroy() override; +}; + #endif // UI_H \ No newline at end of file diff --git a/plugins/New_GPG/src/utilities.cpp b/plugins/New_GPG/src/utilities.cpp index f6466f89b6..cefc7ade47 100644 --- a/plugins/New_GPG/src/utilities.cpp +++ b/plugins/New_GPG/src/utilities.cpp @@ -1,1428 +1,1428 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "stdafx.h" - -#include "utf8.h" - -void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc) -{ - wchar_t str[MAX_PATH + 2] = {}; - OPENFILENAME ofn = {}; - wchar_t filter[512], *pfilter; - ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName); - ofn.Flags = OFN_EXPLORER; - ofn.lpstrTitle = TranslateW(WindowTittle); - wcsncpy(filter, TranslateW(szExtDesc), _countof(filter) - 1); - pfilter = filter + mir_wstrlen(filter) + 1; - mir_wstrcpy(pfilter, szExt); - pfilter[mir_wstrlen(pfilter) + 1] = '\0'; - pfilter[mir_wstrlen(pfilter) + 2] = '\0'; - ofn.lpstrFilter = filter; - wcsncpy(str, g_plugin.getMStringW(szSetting), _countof(str) - 1); - if (mir_wstrlen(str) < 2) - str[0] = '\0'; - ofn.lpstrFile = str; - ofn.nMaxFile = _MAX_PATH; - ofn.nMaxFileTitle = MAX_PATH; - if (GetOpenFileName(&ofn)) - g_plugin.setWString(szSetting, str); -} - -wchar_t* GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file) -{ - wchar_t str[MAX_PATH + 1]; - OPENFILENAME ofn = {}; - wchar_t filter[512], *pfilter; - ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName); - ofn.Flags = OFN_EXPLORER; - ofn.lpstrTitle = TranslateW(WindowTittle); - mir_wstrcpy(filter, TranslateW(szExtDesc)); - pfilter = filter + mir_wstrlen(filter) + 1; - mir_wstrcpy(pfilter, szExt); - pfilter[mir_wstrlen(pfilter) + 1] = '\0'; - pfilter[mir_wstrlen(pfilter) + 2] = '\0'; - ofn.lpstrFilter = filter; - mir_wstrcpy(str, L""); - if (mir_wstrlen(str) < 2) - str[0] = '\0'; - ofn.lpstrFile = str; - ofn.nMaxFile = _MAX_PATH; - ofn.nMaxFileTitle = MAX_PATH; - if (!save_file) { - if (!GetOpenFileName(&ofn)) - return nullptr; - } - else { - if (!GetSaveFileName(&ofn)) - return nullptr; - } - return mir_wstrdup(str); -} - -void GetFolderPath(wchar_t *WindowTittle) -{ - BROWSEINFO pbi = {}; - pbi.lpszTitle = WindowTittle; - pbi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE; - LPITEMIDLIST pidl = SHBrowseForFolder(&pbi); - if (pidl != nullptr) { - wchar_t path[MAX_PATH]; - if (SHGetPathFromIDList(pidl, path)) { - g_plugin.setWString("szHomePath", path); - } - IMalloc * imalloc = nullptr; - if (SUCCEEDED(SHGetMalloc(&imalloc))) { - imalloc->Free(pidl); - imalloc->Release(); - } - } -} - -INT_PTR LoadKey(WPARAM w, LPARAM) -{ - ShowLoadPublicKeyDialog((MCONTACT)w, false); - return 0; -} - -INT_PTR SendKey(WPARAM w, LPARAM) -{ - MCONTACT hContact = db_mc_tryMeta(w); - std::string key_id_str; - - LPSTR proto = Proto_GetBaseAccountName(hContact); - PROTOACCOUNT *acc = Proto_GetAccount(proto); - std::string acc_str; - if (acc) { - acc_str = acc->szModuleName; - key_id_str = acc_str; - key_id_str += "_KeyID"; - acc_str += "_GPGPubKey"; - } - - CMStringA szMessage = g_plugin.getMStringA(acc_str.empty() ? "GPGPubKey" : acc_str.c_str()); - if (szMessage.IsEmpty()) - szMessage = g_plugin.getMStringA("GPGPubKey"); //try to get default key as fallback in any way - - if (!szMessage.IsEmpty()) { - uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0); - g_plugin.setByte(hContact, "GPGEncryption", 0); - ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)szMessage.c_str()); - std::string msg = "Public key "; - CMStringA keyid = g_plugin.getMStringA(key_id_str.c_str()); - if (keyid.IsEmpty()) - keyid = g_plugin.getMStringA("KeyID"); - msg += keyid; - msg += " sent"; - - HistoryLog(hContact, msg.c_str(), DBEF_SENT); - g_plugin.setByte(hContact, "GPGEncryption", enc); - } - - return 0; -} - -INT_PTR ToggleEncryption(WPARAM w, LPARAM) -{ - MCONTACT hContact = (MCONTACT)w; - uint8_t enc; - if (db_mc_isMeta(hContact)) { - enc = g_plugin.getByte(metaGetMostOnline(hContact), "GPGEncryption"); - if (MessageBox(nullptr, TranslateT("Do you want to toggle encryption for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { - int count = db_mc_getSubCount(hContact); - for (int i = 0; i < count; i++) { - MCONTACT hcnt = db_mc_getSub(hContact, i); - if (hcnt) - g_plugin.getByte(hcnt, "GPGEncryption", enc ? 0 : 1); - } - g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1); - } - } - else { - enc = g_plugin.getByte(hContact, "GPGEncryption", 0); - g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1); - } - setSrmmIcon(hContact); - return 0; -} - -int OnPreBuildContactMenu(WPARAM w, LPARAM) -{ - MCONTACT hContact = db_mc_tryMeta(w); - { - CMenuItem mi2(&g_plugin); - LPSTR proto = Proto_GetBaseAccountName(hContact); - PROTOACCOUNT *acc = Proto_GetAccount(proto); - std::string setting; - if (acc) { - setting = acc->szModuleName; - setting += "_KeyID"; - } - - CMStringA keyid = g_plugin.getMStringA(setting.c_str()); - if (keyid.IsEmpty()) - keyid = g_plugin.getMStringA("KeyID"); - - wchar_t buf[128] = { 0 }; - mir_snwprintf(buf, L"%s: %S", TranslateT("Send public key"), keyid.c_str()); - Menu_ModifyItem(g_plugin.hSendKey, buf); - } - - int flags; - CMStringA tmp = g_plugin.getMStringW(hContact, "GPGPubKey"); - if (tmp.IsEmpty()) { - g_plugin.delSetting(hContact, "GPGEncryption"); - flags = CMIF_GRAYED; - } - else flags = 0; - - if (g_plugin.getByte(hContact, "GPGEncryption")) - Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn off GPG encryption"), g_plugin.getIconHandle(IDI_SECURED), flags); - else - Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn on GPG encryption"), g_plugin.getIconHandle(IDI_UNSECURED), flags); - return 0; -} - -list transfers; - -int onProtoAck(WPARAM, LPARAM l) -{ - ACKDATA *ack = (ACKDATA*)l; - if (ack->type == ACKTYPE_FILE) { - switch (ack->result) { - case ACKRESULT_DENIED: case ACKRESULT_FAILED: - break; - case ACKRESULT_SUCCESS: - if (ack->hProcess) { - PROTOFILETRANSFERSTATUS *f = (PROTOFILETRANSFERSTATUS*)ack->hProcess; - if (f == nullptr) - break; - - if ((f->flags & PFTS_SENDING) != PFTS_SENDING) { - wchar_t *filename = nullptr; - if (f->flags & PFTS_UNICODE) { - if (f->szCurrentFile.w && f->szCurrentFile.w[0]) - filename = mir_wstrdup(f->szCurrentFile.w); - if (!filename) - return 0; - } - else { - if (f->szCurrentFile.a && f->szCurrentFile.a[0]) - filename = mir_utf8decodeW(f->szCurrentFile.a); - if (!filename) - return 0; - } - if (wcsstr(filename, L".gpg")) { //decrypt it - //process encrypted file - if (!g_plugin.bFileTransfers && !g_plugin.bSameAction) { - void ShowEncryptedFileMsgBox(); - ShowEncryptedFileMsgBox(); - } - if (!g_plugin.bFileTransfers && g_plugin.bSameAction) - return 0; - if (!globals.bDecryptFiles) - return 0; - HistoryLog(ack->hContact, "Received encrypted file, trying to decrypt"); - if (!boost::filesystem::exists(f->szCurrentFile.w)) - return 0; - - gpg_execution_params params; - params.addParam(L"-o"); - wstring file = filename; - wstring::size_type p1 = file.rfind(L".gpg"); - file.erase(p1, mir_wstrlen(L".gpg")); - if (boost::filesystem::exists(file)) { - if (MessageBox(nullptr, TranslateT("Target file exists, do you want to replace it?"), TranslateT("Warning"), MB_YESNO) == IDNO) - return 0; - } - params.addParam(file); - boost::filesystem::remove(file); - { - // password - CMStringW pass; - CMStringA keyid = g_plugin.getMStringA(ack->hContact, "KeyID"); - if (!keyid.IsEmpty()) { - string dbsetting = "szKey_"; - dbsetting += keyid; - dbsetting += "_Password"; - pass = g_plugin.getMStringW(dbsetting.c_str()); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password in database for key ID: " + string(keyid.c_str()) + ", trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; - } - else { - pass = g_plugin.getMStringW("szKeyPassword"); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; - } - if (!pass.IsEmpty()) { - params.addParam(L"--passphrase"); - params.addParam(pass.c_str()); - } - else if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; - params.addParam(L"--passphrase"); - params.addParam(globals.wszPassword.c_str()); - } - else if (globals.debuglog) - globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " without password"; - } - params.addParam(L"-d"); - params.addParam(filename); - if (!gpg_launcher(params, boost::posix_time::minutes(15))) - return 0; - - string out(params.out); - while (out.find("public key decryption failed: bad passphrase") != string::npos) { - if (globals.debuglog) - globals.debuglog << "info: failed to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " password needed, trying to get one"; - if (globals._terminate) - break; - { //save inkey id - string::size_type s = out.find(" encrypted with "); - s = out.find(" ID ", s); - s += mir_strlen(" ID "); - string::size_type s2 = out.find(",", s); - if (db_mc_isMeta(ack->hContact)) - g_plugin.setString(metaGetMostOnline(ack->hContact), "InKeyID", out.substr(s, s2 - s).c_str()); - else - g_plugin.setString(ack->hContact, "InKeyID", out.substr(s, s2 - s).c_str()); - } - - CDlgKeyPasswordMsgBox(ack->hContact).DoModal(); - - if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)); - - params.addParam(L"--passphrase"); - params.addParam(globals.wszPassword.c_str()); - } - - if (!gpg_launcher(params, boost::posix_time::seconds(15))) - return 0; - if (params.result == pxNotFound) - return 0; - } - if (params.result == pxSuccess) - boost::filesystem::remove(filename); - } - mir_free(filename); - } - } - break; - } - } - else if (ack->type == ACKTYPE_MESSAGE) { - extern std::list sent_msgs; - if (!sent_msgs.empty()) { - if (ack->result == ACKRESULT_FAILED) { - std::list::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess); - if (it != sent_msgs.end()) - HistoryLog(ack->hContact, "Failed to send encrypted message"); - } - else if (ack->result == ACKRESULT_SUCCESS) { - std::list::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess); - if (it != sent_msgs.end()) - sent_msgs.erase(it); - } - } - } - return 0; -} - -std::wstring encrypt_file(MCONTACT hContact, wchar_t *filename) -{ - MCONTACT hcnt = db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact; - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--tes"); - params.addParam(L"-r"); - - CMStringW keyid = g_plugin.getMStringW(hcnt, "KeyID"); - wchar_t *name = wcsrchr(filename, '\\'); - if (!name) - name = filename; - else - name++; - wchar_t *file_out = new wchar_t[mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1]; - mir_snwprintf(file_out, mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1, L"%s.gpg", name); - params.addParam(keyid.c_str()); - - if (g_plugin.getByte(hcnt, "bAlwaysTrust")) { - params.addParam(L"--trust-model"); - params.addParam(L"always"); - } - - params.addParam(L"-o"); - wchar_t *temp = _tgetenv(L"TEMP"); - params.addParam(wstring(temp) + L"\\" + file_out); - wstring path_out = temp; - path_out += L"\\"; - path_out += file_out; - boost::filesystem::remove(path_out); - params.addParam(L"-e"); - params.addParam(filename); - delete[] file_out; - - if (!gpg_launcher(params, boost::posix_time::minutes(3))) - return nullptr; - - if (params.out.Find("There is no assurance this key belongs to the named user") != -1) { - if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO)) - return nullptr; - - g_plugin.setByte(hcnt, "bAlwaysTrust", 1); - - params.addParam(L"--trust-model"); - params.addParam(L"always"); - if (!gpg_launcher(params, boost::posix_time::minutes(3))) - return nullptr; - } - return path_out; -} - -//from secureim partially -INT_PTR onSendFile(WPARAM w, LPARAM l) -{ - CCSDATA *ccs = (CCSDATA*)l; - if (!g_plugin.bFileTransfers) - return Proto_ChainSend(w, ccs); - - if (isContactSecured(ccs->hContact)) { - char *proto = Proto_GetBaseAccountName(ccs->hContact); - bool cap_found = false, supported_proto = false; - ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", "")); - if (jid[0]) { - for (auto p : globals.Accounts) { - ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); - if (caps) { - supported_proto = true; - string str; - for (int i = 0;; i++) { - str.push_back(caps[i]); - if (caps[i] == '\0') - if (caps[i + 1] == '\0') - break; - } - - if (str.find("GPG_Encrypted_FileTransfers:0") != string::npos) - cap_found = true; - } - } - } - - if (supported_proto && !cap_found) { - if (MessageBox(nullptr, TranslateT("Capability to decrypt file not found on other side.\nRecipient may be unable to decrypt file(s).\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO) - return Proto_ChainSend(w, ccs); - } - if (!supported_proto) { - if (MessageBox(nullptr, TranslateT("Unable to check encryption support on other side.\nRecipient may be unable to decrypt file(s).\nCurrently capability check supported only for ICQ and Jabber protocols.\nIt will work for any other proto if Miranda with New_GPG is used on other side.\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO) - return Proto_ChainSend(w, ccs); - } - HistoryLog(ccs->hContact, TranslateU("encrypting file for transfer"), DBEF_SENT); - if (StriStr(ccs->szProtoService, "/sendfilew")) { - wchar_t **file = (wchar_t **)ccs->lParam; - for (int i = 0; file[i]; i++) { - if (!boost::filesystem::exists(file[i])) - return 0; //we do not want to send file unencrypted (sometimes ack have wrong info) - if (wcsstr(file[i], L".gpg")) - continue; - std::wstring path_out = encrypt_file(ccs->hContact, file[i]); - mir_free(file[i]); - file[i] = mir_wstrdup(path_out.c_str()); - transfers.push_back(path_out); - } - } - else { - char **file = (char**)ccs->lParam; - for (int i = 0; file[i]; i++) { - if (!boost::filesystem::exists(file[i])) - return 0; //we do not want to send file unencrypted (sometimes ack have wrong info) - if (strstr(file[i], ".gpg")) - continue; - wchar_t *tmp = mir_utf8decodeW(file[i]); - std::wstring path_out = encrypt_file(ccs->hContact, tmp); - mir_free(tmp); - char* tmp2 = mir_utf8encodeW(path_out.c_str()); - mir_free(file[i]); - file[i] = tmp2; - transfers.push_back(path_out); - - } - } - } - return Proto_ChainSend(w, ccs); -} - -void HistoryLog(MCONTACT hContact, const char *msg, uint32_t _time, uint32_t flags) -{ - DBEVENTINFO dbei = {}; - dbei.szModule = MODULENAME; - dbei.flags = DBEF_UTF | flags; - dbei.timestamp = (_time) ? _time : (uint32_t)time(0); - dbei.cbBlob = (uint32_t)mir_strlen(msg) + 1; - dbei.pBlob = (uint8_t*)msg; - db_event_add(hContact, &dbei); -} - -static int ControlAddStringUtf(HWND ctrl, uint32_t msg, const wchar_t *szString) -{ - int item = -1; - item = SendMessage(ctrl, msg, 0, (LPARAM)szString); - return item; -} - -int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data) -{ - int item = ControlAddStringUtf(hCombo, CB_ADDSTRING, szString); - SendMessage(hCombo, CB_SETITEMDATA, item, data); - - return item; -} - -static JABBER_HANDLER_FUNC SendHandler(IJabberInterface *ji, TiXmlElement *node, void*) -{ - TiXmlDocument *pDoc = node->GetDocument(); - - for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) { - LPCSTR str = local_node->GetText(); - LPCSTR nodename = local_node->Name(); - LPCSTR attr = local_node->Attribute("to"); - if (attr) { - MCONTACT hContact = ji->ContactFromJID(attr); - if (hContact) - if (!isContactSecured(hContact)) - break; - } - - if (str == nullptr) - continue; - - // TODO: make following block more readable - if (g_plugin.bPresenceSigning && nodename && strstr(nodename, "status")) { - string path_out = ptrA(g_plugin.getUStringA("szHomePath", "")); - string file = get_random(10); - path_out += "\\tmp\\"; - path_out += file; - boost::filesystem::remove(path_out); - wfstream f(path_out.c_str(), std::ios::out); - f << str; - f.close(); - if (!boost::filesystem::exists(path_out)) { - if (globals.debuglog) - globals.debuglog << "info: Failed to write prescense in file"; - break; - } - - gpg_execution_params params; - { - char setting[64]; - mir_snprintf(setting, sizeof(setting) - 1, "%s_KeyID", ji->GetModuleName()); - CMStringA inkeyid = g_plugin.getMStringA(setting); - if (inkeyid.IsEmpty()) - inkeyid = g_plugin.getMStringA("KeyID"); - - CMStringW pass; - if (!inkeyid.IsEmpty()) { - string dbsetting = "szKey_"; - dbsetting += inkeyid; - dbsetting += "_Password"; - pass = g_plugin.getMStringW(dbsetting.c_str()); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to encrypt message from self with password"; - } - else { - pass = g_plugin.getMStringW("szKeyPassword"); - if (!pass.IsEmpty() && globals.debuglog) - globals.debuglog << "info: found password for all keys in database, trying to encrypt message from self with password"; - } - if (pass[0]) { - params.addParam(L"--passphrase"); - params.addParam(pass.c_str()); - } - else if (!globals.wszPassword.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "info: found password in memory, trying to encrypt message from self with password"; - params.addParam(L"--passphrase"); - params.addParam(globals.wszPassword.c_str()); - } - else if (globals.debuglog) - globals.debuglog << "info: passwords not found in database or memory, trying to encrypt message from self without password"; - } - - params.addParam(L"--local-user"); - params.addParam(g_plugin.getMStringW("KeyID").c_str()); - params.addParam(L"--default-key"); - params.addParam(g_plugin.getMStringW("KeyID").c_str()); - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"-abs"); - params.addParam(Utf2T(path_out.c_str()).get()); - gpg_launcher(params, boost::posix_time::seconds(15)); // TODO: handle errors - - boost::filesystem::remove(path_out); - path_out += ".asc"; - f.open(path_out.c_str(), std::ios::in | std::ios::ate | std::ios::binary); - wstring data; - if (f.is_open()) { - std::wifstream::pos_type size = f.tellg(); - wchar_t *tmp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1]; - f.seekg(0, std::ios::beg); - f.read(tmp, size); - tmp[size] = '\0'; - data.append(tmp); - delete[] tmp; - f.close(); - boost::filesystem::remove(path_out); - } - if (data.empty()) { - if (globals.debuglog) - globals.debuglog << "info: Failed to read prescense sign from file"; - break; - } - if (data.find(L"-----BEGIN PGP SIGNATURE-----") != wstring::npos && data.find(L"-----END PGP SIGNATURE-----") != wstring::npos) { - wstring::size_type p1 = data.find(L"-----BEGIN PGP SIGNATURE-----") + mir_wstrlen(L"-----BEGIN PGP SIGNATURE-----"); - if (data.find(L"Version: ", p1) != wstring::npos) { - p1 = data.find(L"Version: ", p1); - p1 = data.find(L"\n", p1); - if (data.find(L"Version: ", p1) != wstring::npos) { - p1 = data.find(L"Version: ", p1); - p1 = data.find(L"\n", p1) + 1; - } - else - p1 += 1; - } - if (data.find(L"Comment: ", p1) != wstring::npos) { - p1 = data.find(L"Comment: ", p1); - p1 = data.find(L"\n", p1); - if (data.find(L"Comment: ", p1) != wstring::npos) { - p1 = data.find(L"Comment: ", p1); - p1 = data.find(L"\n", p1) + 1; - } - else - p1 += 1; - } - else - p1 += 1; - p1++; - wstring::size_type p2 = data.find(L"-----END PGP SIGNATURE-----"); - - std::wstring tmp = data.substr(p1, p2 - p1); - strip_line_term(tmp); - TiXmlElement* encrypted_data = pDoc->NewElement("x"); node->InsertEndChild(encrypted_data); - encrypted_data->SetText(tmp.c_str()); - encrypted_data->SetAttribute("xmlns", "jabber:x:signed"); - } - break; - } - } - return FALSE; -} - -static JABBER_HANDLER_FUNC PresenceHandler(IJabberInterface *ji, TiXmlElement* node, void*) -{ - for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) { - LPCSTR nodename = local_node->Name(); - if (nodename == nullptr) - continue; - - if (!mir_strcmp(nodename, "x")) - continue; - - for (auto *pAttr = local_node->FirstAttribute(); pAttr; pAttr = pAttr->Next()) { - LPCSTR value = pAttr->Value(); - if (!mir_strcmp(value, "jabber:x:signed")) { - std::string status_str; - - for (auto *local_node2 = node->FirstChildElement(); local_node2; local_node2 = local_node2->NextSiblingElement()) { - LPCSTR nodename2 = local_node2->Name(); - if (!mir_strcmp(nodename2, "status")) { - LPCSTR status = local_node2->GetText(); - if (status) - status_str = status; - break; - } - } - - LPCSTR data = local_node->GetText(); - string sign = "-----BEGIN PGP SIGNATURE-----\n\n"; - wstring file = toUTF16(get_random(10)), status_file = toUTF16(get_random(10)); - sign += data; - sign += "\n-----END PGP SIGNATURE-----\n"; - - CMStringW path_out = g_plugin.getMStringW("szHomePath"), status_file_out = path_out; - path_out += L"\\tmp\\"; - path_out += file.c_str(); - path_out += L".sig"; - status_file_out += L"\\tmp\\"; - status_file_out += status_file.c_str(); - status_file_out += L".status"; - - boost::filesystem::remove(path_out.c_str()); - boost::filesystem::remove(status_file_out.c_str()); - wfstream f(path_out.c_str(), std::ios::out); - while (!f.is_open()) - f.open(path_out.c_str(), std::ios::out); - f << sign.c_str(); - f.close(); - f.open(status_file_out.c_str(), std::ios::out); - while (!f.is_open()) - f.open(status_file_out.c_str(), std::ios::out); - f << status_str.c_str(); - f.close(); - if (!boost::filesystem::exists(path_out.c_str())) { - if (globals.debuglog) - globals.debuglog << "info: Failed to write sign in file"; - return FALSE; - } - { - // gpg - gpg_execution_params params; - params.addParam(L"--verify"); - params.addParam(L"-a"); - params.addParam(path_out.c_str()); - params.addParam(status_file_out.c_str()); - if (!gpg_launcher(params, boost::posix_time::seconds(15))) - return FALSE; - if (params.result == pxNotFound) - return FALSE; - - boost::filesystem::remove(path_out.c_str()); - boost::filesystem::remove(status_file_out.c_str()); - - string out(params.out); - if (out.find("key ID ") != string::npos) { - //need to get hcontact here, i can get jid from hxml, and get handle from jid, maybe exists better way ? - string::size_type p1 = out.find("key ID ") + mir_strlen("key ID "); - string::size_type p2 = out.find("\n", p1); - if (p1 != string::npos && p2 != string::npos) { - MCONTACT hContact = ji->ContactFromJID(node->Attribute("from")); - if (hContact) - globals.hcontact_data[hContact].key_in_prescense = out.substr(p1, p2 - p1 - 1).c_str(); - } - } - } - return FALSE; - } - } - } - return FALSE; -} - -static JABBER_HANDLER_FUNC MessageHandler(IJabberInterface*, TiXmlElement*, void*) -{ - return FALSE; -} - -int GetJabberInterface(WPARAM, LPARAM) //get interface for all jabber accounts, options later -{ - list ::iterator p; - globals.Accounts.clear(); - - int accNum = 0; - for (auto &pa : Accounts()) { - IJabberInterface *pApi = getJabberApi(pa->szModuleName); - if (pApi == nullptr) - continue; - - auto *pAcc = new JabberAccount(); - pAcc->setJabberInterface(pApi); - if (pa->tszAccountName) - pAcc->setAccountName(mir_wstrdup(pa->tszAccountName)); - else - pAcc->setAccountName(mir_a2u(pa->szModuleName)); - - pAcc->setAccountNumber(accNum++); - pAcc->setSendHandler(pApi->AddSendHandler((JABBER_HANDLER_FUNC)SendHandler)); - pAcc->setPresenceHandler(pApi->AddPresenceHandler((JABBER_HANDLER_FUNC)PresenceHandler)); - - if (g_plugin.bAutoExchange) { - pApi->RegisterFeature("GPG_Key_Auto_Exchange:0", "Indicates that gpg installed and configured to public key auto exchange (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)"); - pApi->AddFeatures("GPG_Key_Auto_Exchange:0\0\0"); - } - if (g_plugin.bFileTransfers) { - pApi->RegisterFeature("GPG_Encrypted_FileTransfers:0", "Indicates that gpg installed and configured to encrypt files (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)"); - pApi->AddFeatures("GPG_Encrypted_FileTransfers:0\0\0"); - } - - globals.Accounts.push_back(pAcc); - } - - return 0; -} - -void RemoveHandlers() -{ - for (auto &it : globals.Accounts) { - auto *pApi = it->getJabberInterface(); - if (pApi == nullptr) - continue; - - if (it->getMessageHandler() != INVALID_HANDLE_VALUE) - pApi->RemoveHandler(it->getMessageHandler()); - if (it->getPresenceHandler() != INVALID_HANDLE_VALUE) - pApi->RemoveHandler(it->getPresenceHandler()); - pApi->RemoveFeatures("GPG_Encrypted_FileTransfers:0\0\0"); - pApi->RemoveFeatures("GPG_Key_Auto_Exchange:0\0\0"); - } -} - -bool isContactSecured(MCONTACT hContact) -{ - uint8_t gpg_enc = g_plugin.getByte(hContact, "GPGEncryption", 0); - if (!gpg_enc) { - if (globals.debuglog) - globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact)); - return false; - } - if (!db_mc_isMeta(hContact)) { - CMStringW key = g_plugin.getMStringW(hContact, "GPGPubKey"); - if (key.IsEmpty()) { - if (globals.debuglog) - globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact)); - return false; - } - } - if (globals.debuglog) - globals.debuglog << "encryption is turned on for " + toUTF8(Clist_GetContactDisplayName(hContact)); - return true; -} - -bool isContactHaveKey(MCONTACT hContact) -{ - ptrW key(g_plugin.getWStringA(hContact, "GPGPubKey")); - return (mir_wstrlen(key) > 0); -} - -bool isGPGKeyExist() -{ - CMStringW id(g_plugin.getMStringW("KeyID")); - CMStringA key(g_plugin.getMStringA("GPGPubKey")); - return (!id.IsEmpty() && !key.IsEmpty()); -} - -bool isGPGValid() -{ - ptrW tmp(g_plugin.getWStringA("szGpgBinPath", L"")); - bool gpg_exists = false, is_valid = true; - boost::filesystem::path p(tmp); - - if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) - gpg_exists = true; - else { - wchar_t path[MAX_PATH], mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - SetCurrentDirectoryW(mir_path); - - //mir_realloc(path, (mir_wstrlen(path)+64)*sizeof(wchar_t)); - wchar_t gpg_path[MAX_PATH]; - mir_wstrcpy(gpg_path, mir_path); - mir_wstrcat(gpg_path, L"\\GnuPG\\gpg.exe"); - - p = boost::filesystem::path(gpg_path); - if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) { - gpg_exists = true; - mir_wstrcpy(path, L"GnuPG\\gpg.exe"); - } - tmp = mir_wstrdup(path); - } - - if (gpg_exists) { - g_plugin.setWString("szGpgBinPath", tmp); - - gpg_execution_params params; - params.addParam(L"--version"); - bool _gpg_valid = globals.gpg_valid; - globals.gpg_valid = true; - gpg_launcher(params); - globals.gpg_valid = _gpg_valid; //TODO: check this - int p1 = params.out.Find("(GnuPG) "); - if (p1 == -1) - is_valid = false; - } - - return is_valid && gpg_exists; -} - -#define NEWTSTR_MALLOC(A) (A==NULL)?NULL:mir_strcpy((char*)mir_alloc(sizeof(char)*(mir_strlen(A)+1)),A) - -const bool StriStr(const char *str, const char *substr) -{ - bool i = false; - char *str_up = NEWTSTR_MALLOC(str); - char *substr_up = NEWTSTR_MALLOC(substr); - - CharUpperBuffA(str_up, (uint32_t)mir_strlen(str_up)); - CharUpperBuffA(substr_up, (uint32_t)mir_strlen(substr_up)); - - if (strstr(str_up, substr_up)) - i = true; - - mir_free(str_up); - mir_free(substr_up); - - return i; -} - -bool IsOnline(MCONTACT hContact) -{ - if (g_plugin.getByte(hContact, "Status", 0) == ID_STATUS_OFFLINE) - return false; - return true; -} - -string toUTF8(wstring str) -{ - string ustr; - try { - utf8::utf16to8(str.begin(), str.end(), back_inserter(ustr)); - } - catch (const utf8::exception& e) { - if (globals.debuglog) - globals.debuglog << std::string("utf8cpp encoding exception: ") + (char*)e.what(); - //TODO - } - return ustr; -} - -wstring toUTF16(string str) //convert as much as possible -{ - wstring ustr; - string tmpstr; - try { - utf8::replace_invalid(str.begin(), str.end(), back_inserter(tmpstr)); - utf8::utf8to16(tmpstr.begin(), tmpstr.end(), back_inserter(ustr)); - } - catch (const utf8::exception& e) { - if (globals.debuglog) - globals.debuglog << std::string("utf8cpp decoding exception: ") + (char*)e.what(); - //TODO - } - return ustr; -} - -string get_random(int length) -{ - string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); - string data; - boost::random_device rng; - boost::variate_generator> gen(rng, boost::uniform_int<>(0, (int)chars.length() - 1)); - for (int i = 0; i < length; ++i) - data += chars[gen()]; - return data; -} - -void send_encrypted_msgs_thread(void *param) -{ - MCONTACT hContact = (MCONTACT)(DWORD_PTR)param; - while (true) { - while (!isContactSecured(hContact)) - Sleep(1000); - - if (!globals.hcontact_data[hContact].msgs_to_send.empty()) { - Sleep(1000); - list::iterator end = globals.hcontact_data[hContact].msgs_to_send.end(); - extern std::list sent_msgs; - for (list::iterator p = globals.hcontact_data[hContact].msgs_to_send.begin(); p != end; ++p) { - sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)p->c_str())); - HistoryLog(hContact, p->c_str(), DBEF_SENT); - Sleep(1000); - } - globals.hcontact_data[hContact].msgs_to_send.clear(); - return; - } - else - return; - } -} - -void ExportGpGKeysFunc(int type) -{ - ptrW p(GetFilePath(L"Choose file to export keys", L"*", L"Any file", true)); - if (!p || !p[0]) - return; - - std::ofstream file; - file.open(p, std::ios::trunc | std::ios::out); - if (!file.is_open()) - return; //TODO: handle error - - int exported_keys = 0; - if (!type || type == 2) { - for (auto &hContact : Contacts()) { - CMStringA key = g_plugin.getMStringA(hContact, "GPGPubKey"); - if (key.IsEmpty()) - continue; - - ptrW wszLogin(Contact::GetInfo(CNF_UNIQUEID, 0, Proto_GetBaseAccountName(hContact))), wszContact(Contact::GetInfo(CNF_UNIQUEID, hContact)); - if (wszLogin == nullptr || wszContact == nullptr) - continue; - - std::string id = "Comment: login "; - id += T2Utf(wszLogin).get(); - id += " contact_id "; - id += T2Utf(wszContact).get(); - id += '\n'; - - int p1 = key.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----"); - if (p1 == -1) - continue; - p1 += mir_strlen("-----BEGIN PGP PUBLIC KEY BLOCK-----"); - p1++; - key.Insert(p1, id.c_str()); - file << key; - file << std::endl; - exported_keys++; - } - } - - if (type == 1 || type == 2) { - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--export-secret-keys"); - params.addParam(L"-a"); - gpg_launcher(params); //TODO: handle errors - - file << params.out.c_str(); - file << std::endl; - } - if (file.is_open()) - file.close(); - wchar_t msg[512]; - if (type == 2) - mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys and all private keys."), exported_keys); - else if (type == 1) - mir_snwprintf(msg, TranslateT("We have successfully exported all private keys.")); - else if (!type) - mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys."), exported_keys); - MessageBox(nullptr, msg, TranslateT("Keys export result"), MB_OK); -} - -INT_PTR ImportGpGKeys(WPARAM, LPARAM) -{ - ptrW p(GetFilePath(L"Choose file to import keys from", L"*", L"Any file")); - if (!p || !p[0]) - return 1; - - std::ifstream file; - file.open(p, std::ios::in); - if (!file.is_open()) - return 1; //TODO: handle error - - int processed_keys = 0, processed_private_keys = 0; - - char line[256]; - file.getline(line, 255); - if (!strstr(line, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && !strstr(line, "-----BEGIN PGP PRIVATE KEY BLOCK-----")) - return 1; //TODO: handle error - - std::string key, login, contact_id; - key += line; - key += '\n'; - while (file.is_open() && !file.eof()) { - file.getline(line, 255); - key += line; - key += '\n'; - if (strstr(line, "-----END PGP PUBLIC KEY BLOCK-----")) { - std::string::size_type p1 = key.rfind("Comment: login "), p2 = 0; - if (p1 == std::string::npos) - { - key.clear(); - continue; //TODO: warning about malformed file - } - p1 += mir_strlen("Comment: login "); - p2 = key.rfind(" contact_id "); - login = key.substr(p1, p2 - p1); - p2 += mir_strlen(" contact_id "); - p1 = key.find("\n", p2); - contact_id = key.substr(p2, p1 - p2); - p1 = key.rfind("Comment: login "); - p2 = key.find("\n", p1); - p2++; - key.erase(p1, p2 - p1); - - PROTOACCOUNT *pFoundAcc = nullptr; - for (auto &pa : Accounts()) { - ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, 0, pa->szModuleName)); - if (wszUniqueId == nullptr) - continue; - - if (login == T2Utf(wszUniqueId).get()) { - pFoundAcc = pa; - break; - } - } - - if (pFoundAcc == nullptr) - continue; - - for (auto &hContact : Contacts(pFoundAcc->szModuleName)) { - ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, hContact, pFoundAcc->szModuleName)); - if (wszUniqueId == nullptr) - continue; - - if (contact_id != T2Utf(wszUniqueId).get()) - continue; - - CMStringW path = g_plugin.getMStringW("szHomePath"); - - gpg_execution_params params; - { - wstring rand = toUTF16(get_random(10)); - path += L"\\"; - path += rand.c_str(); - boost::filesystem::remove(path.c_str()); - wfstream f(path, std::ios::out); - f << toUTF16(key).c_str(); - f.close(); - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(path.c_str()); - } - if (!gpg_launcher(params)) - break; - if (params.result == pxNotFound) - break; - if (params.result == pxSuccess) - processed_keys++; - - string output(params.out); - if (output.find("already in secret keyring") != string::npos) { - MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK); - boost::filesystem::remove(path.c_str()); - break; - } - char *tmp2; - string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); - string::size_type s2 = output.find(":", s); - tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp2, nullptr); - g_plugin.setString(hContact, "KeyID", tmp2); - mir_free(tmp2); - s = output.find("“", s2); - if (s == string::npos) { - s = output.find("\"", s2); - s += 1; - } - else - s += 3; - if ((s2 = output.find("(", s)) == string::npos) - s2 = output.find("<", s); - else if (s2 > output.find("<", s)) - s2 = output.find("<", s); - if (s2 != string::npos) { - tmp2 = (char *)mir_alloc((output.substr(s, s2 - s - 1).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s, s2 - s - 1).c_str()); - mir_utf8decode(tmp2, nullptr); - if (hContact) { - g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); - } - mir_free(tmp2); - if ((s = output.find(")", s2)) == string::npos) - s = output.find(">", s2); - else if (s > output.find(">", s2)) - s = output.find(">", s2); - s2++; - if (output[s] == ')') { - tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp2, nullptr); - if (hContact) - g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str()); - mir_free(tmp2); - s += 3; - s2 = output.find(">", s); - tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s, s2 - s).c_str()); - mir_utf8decode(tmp2, nullptr); - if (hContact) - g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str()); - mir_free(tmp2); - } - else { - tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); - mir_strcpy(tmp2, output.substr(s2, s - s2).c_str()); - mir_utf8decode(tmp2, nullptr); - if (hContact) - g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); - mir_free(tmp2); - } - } - g_plugin.setByte(hContact, "GPGEncryption", 1); - g_plugin.setWString(hContact, "GPGPubKey", toUTF16(key).c_str()); - - boost::filesystem::remove(path.c_str()); - break; - } - key.clear(); - } - else if (strstr(line, "-----END PGP PRIVATE KEY BLOCK-----")) { - CMStringW tmp2 = g_plugin.getMStringW("szHomePath"); - tmp2 += L"\\temporary_exported.asc"; - boost::filesystem::remove(tmp2.c_str()); - - wfstream f(tmp2, std::ios::out); - f << toUTF16(key).c_str(); - f.close(); - - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--import"); - params.addParam(tmp2.c_str()); - if (!gpg_launcher(params)) - break; - if (params.result == pxNotFound) - break; - if (params.result == pxSuccess) - processed_private_keys++; - key.clear(); - } - } - if (file.is_open()) - file.close(); - wchar_t msg[512]; - if (processed_private_keys) - mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys and some private keys."), processed_keys); - else - mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys."), processed_keys); - MessageBox(nullptr, msg, TranslateT("Keys import result"), MB_OK); - return 0; -} - -void SendErrorMessage(MCONTACT hContact) -{ - if (!g_plugin.bSendErrorMessages) - return; - - uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0); - g_plugin.setByte(hContact, "GPGEncryption", 0); - ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); - HistoryLog(hContact, "Error message sent", DBEF_SENT); - g_plugin.setByte(hContact, "GPGEncryption", enc); -} - -void fix_line_term(std::string &s) -{ - if (s.empty()) - return; - - boost::algorithm::erase_all(s, "\r\r"); - - // unified line endings for unix & windows port - boost::algorithm::replace_all(s, "\r\n", "\n"); - boost::algorithm::replace_all(s, "\n", "\r\n"); -} - -void strip_line_term(std::wstring &s) -{ - if (s.empty()) - return; - boost::algorithm::erase_all(s, L"\r"); - boost::algorithm::erase_all(s, L"\n"); -} - -void strip_line_term(std::string &s) -{ - if (s.empty()) - return; - boost::algorithm::erase_all(s, "\r"); - boost::algorithm::erase_all(s, "\n"); -} - -void strip_tags(std::string &str) -{ - if (str.empty()) - return; - boost::algorithm::erase_all(str, globals.wszInopentag.c_str()); - boost::algorithm::erase_all(str, globals.wszInclosetag.c_str()); - boost::algorithm::erase_all(str, globals.wszOutopentag.c_str()); - boost::algorithm::erase_all(str, globals.wszOutclosetag.c_str()); -} - - -void ShowEncryptedFileMsgBox() -{ - CDlgEncryptedFileMsgBox *d = new CDlgEncryptedFileMsgBox; - d->DoModal(); -} - -void clean_temp_dir() -{ - using namespace boost::filesystem; - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - SetCurrentDirectoryW(mir_path); - - CMStringW tmp = mir_path; - tmp += g_plugin.getMStringW("szHomePath"); - tmp += L"\\tmp"; - if (exists(tmp.c_str()) && is_directory(tmp.c_str())) { - boost::filesystem::path p(tmp); - for (directory_iterator i = directory_iterator(p), end = directory_iterator(); i != end; ++i) { - if (boost::filesystem::is_regular_file(i->path())) { - if ((i->path().filename().generic_string().length() == 10 && (i->path().filename().generic_string().find(".") == std::string::npos)) || - i->path().extension() == ".sig" || i->path().extension() == ".asc" || i->path().extension() == ".status") - boost::filesystem::remove(i->path()); - } - } - } -} - -bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path) -{ - wstring tmp = gpg_bin_path; - if (!tmp.empty()) { - wchar_t mir_path[MAX_PATH]; - PathToAbsoluteW(L"\\", mir_path); - SetCurrentDirectoryW(mir_path); - if (!boost::filesystem::exists(tmp)) { - MessageBox(nullptr, TranslateT("GPG binary does not exist.\nPlease choose another location"), TranslateT("Warning"), MB_OK); - return false; - } - } - else { - MessageBox(nullptr, TranslateT("Please choose GPG binary location"), TranslateT("Warning"), MB_OK); - return false; - } - { - bool bad_version = false; - g_plugin.setWString("szGpgBinPath", tmp.c_str()); - - gpg_execution_params params; - params.addParam(L"--version"); - bool _gpg_valid = globals.gpg_valid; - globals.gpg_valid = true; - gpg_launcher(params); - globals.gpg_valid = _gpg_valid; //TODO: check this - g_plugin.delSetting("szGpgBinPath"); - int p1 = params.out.Find("(GnuPG) "); - if (p1 != -1) { - p1 += mir_strlen("(GnuPG) "); - if (params.out[p1] != '1') - bad_version = true; - } - else { - bad_version = false; - MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); - return false; - } - if (bad_version) - MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); - } - tmp = gpg_home_path; - if (tmp[tmp.length()] == '\\') - tmp.erase(tmp.length()); - if (tmp.empty()) { - MessageBox(nullptr, TranslateT("Please set keyring's home directory"), TranslateT("Warning"), MB_OK); - return false; - } - { - CMStringW path = g_plugin.getMStringW("szHomePath"); - uint32_t dwFileAttr = GetFileAttributes(path); - if (dwFileAttr != INVALID_FILE_ATTRIBUTES) { - dwFileAttr &= ~FILE_ATTRIBUTE_READONLY; - SetFileAttributes(path, dwFileAttr); - } - } - return true; -} - -void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path) -{ - g_plugin.setWString("szGpgBinPath", gpg_bin_path); - g_plugin.setWString("szHomePath", gpg_home_path); -} - -bool gpg_use_new_random_key(const char *account_name) -{ - CMStringW path = g_plugin.getMStringW("szHomePath"); - path += L"\\new_key"; - - // generating key file - wfstream f(path.c_str(), std::ios::out); - if (!f.is_open()) { - MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); - return false; - } - f << "Key-Type: RSA"; - f << "\n"; - f << "Key-Length: 4096"; - f << "\n"; - f << "Subkey-Type: RSA"; - f << "\n"; - f << "Name-Real: "; - f << get_random(6).c_str(); - f << "\n"; - f << "Name-Email: "; - f << get_random(5).c_str(); - f << "@"; - f << get_random(5).c_str(); - f << "."; - f << get_random(3).c_str(); - f << "\n"; - f.close(); - - { - // gpg execution - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"--yes"); - params.addParam(L"--gen-key"); - params.addParam(path.c_str()); - params.bNoOutput = true; - if (!gpg_launcher(params, boost::posix_time::minutes(10))) - return false; - if (params.result == pxNotFound) - return false; - - boost::filesystem::remove(path.c_str()); - string out(params.out); - int p1 = params.out.Find("key "); - if (p1 != -1) - path = ptrW(mir_utf8decodeW(params.out.Mid(p1 + 4, 8).c_str())); - else - path.Empty(); - } - - if (!path.IsEmpty()) { - gpg_execution_params params; - params.addParam(L"--batch"); - params.addParam(L"-a"); - params.addParam(L"--export"); - params.addParam(path.c_str()); - if (!gpg_launcher(params)) - return false; - - if (params.result == pxNotFound) - return false; - - params.out.Remove('\r'); - - if (account_name == nullptr) { - g_plugin.setString("GPGPubKey", params.out.c_str()); - g_plugin.setWString("KeyID", path.c_str()); - } - else { - CMStringA acc_str = account_name; - g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str()); - g_plugin.setWString(acc_str + "_KeyID", path.c_str()); - } - } - return true; -} +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "stdafx.h" + +#include "utf8.h" + +void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc) +{ + wchar_t str[MAX_PATH + 2] = {}; + OPENFILENAME ofn = {}; + wchar_t filter[512], *pfilter; + ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName); + ofn.Flags = OFN_EXPLORER; + ofn.lpstrTitle = TranslateW(WindowTittle); + wcsncpy(filter, TranslateW(szExtDesc), _countof(filter) - 1); + pfilter = filter + mir_wstrlen(filter) + 1; + mir_wstrcpy(pfilter, szExt); + pfilter[mir_wstrlen(pfilter) + 1] = '\0'; + pfilter[mir_wstrlen(pfilter) + 2] = '\0'; + ofn.lpstrFilter = filter; + wcsncpy(str, g_plugin.getMStringW(szSetting), _countof(str) - 1); + if (mir_wstrlen(str) < 2) + str[0] = '\0'; + ofn.lpstrFile = str; + ofn.nMaxFile = _MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + if (GetOpenFileName(&ofn)) + g_plugin.setWString(szSetting, str); +} + +wchar_t* GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file) +{ + wchar_t str[MAX_PATH + 1]; + OPENFILENAME ofn = {}; + wchar_t filter[512], *pfilter; + ofn.lStructSize = CDSIZEOF_STRUCT(OPENFILENAME, lpTemplateName); + ofn.Flags = OFN_EXPLORER; + ofn.lpstrTitle = TranslateW(WindowTittle); + mir_wstrcpy(filter, TranslateW(szExtDesc)); + pfilter = filter + mir_wstrlen(filter) + 1; + mir_wstrcpy(pfilter, szExt); + pfilter[mir_wstrlen(pfilter) + 1] = '\0'; + pfilter[mir_wstrlen(pfilter) + 2] = '\0'; + ofn.lpstrFilter = filter; + mir_wstrcpy(str, L""); + if (mir_wstrlen(str) < 2) + str[0] = '\0'; + ofn.lpstrFile = str; + ofn.nMaxFile = _MAX_PATH; + ofn.nMaxFileTitle = MAX_PATH; + if (!save_file) { + if (!GetOpenFileName(&ofn)) + return nullptr; + } + else { + if (!GetSaveFileName(&ofn)) + return nullptr; + } + return mir_wstrdup(str); +} + +void GetFolderPath(wchar_t *WindowTittle) +{ + BROWSEINFO pbi = {}; + pbi.lpszTitle = WindowTittle; + pbi.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE; + LPITEMIDLIST pidl = SHBrowseForFolder(&pbi); + if (pidl != nullptr) { + wchar_t path[MAX_PATH]; + if (SHGetPathFromIDList(pidl, path)) { + g_plugin.setWString("szHomePath", path); + } + IMalloc * imalloc = nullptr; + if (SUCCEEDED(SHGetMalloc(&imalloc))) { + imalloc->Free(pidl); + imalloc->Release(); + } + } +} + +INT_PTR LoadKey(WPARAM w, LPARAM) +{ + ShowLoadPublicKeyDialog((MCONTACT)w, false); + return 0; +} + +INT_PTR SendKey(WPARAM w, LPARAM) +{ + MCONTACT hContact = db_mc_tryMeta(w); + std::string key_id_str; + + LPSTR proto = Proto_GetBaseAccountName(hContact); + PROTOACCOUNT *acc = Proto_GetAccount(proto); + std::string acc_str; + if (acc) { + acc_str = acc->szModuleName; + key_id_str = acc_str; + key_id_str += "_KeyID"; + acc_str += "_GPGPubKey"; + } + + CMStringA szMessage = g_plugin.getMStringA(acc_str.empty() ? "GPGPubKey" : acc_str.c_str()); + if (szMessage.IsEmpty()) + szMessage = g_plugin.getMStringA("GPGPubKey"); //try to get default key as fallback in any way + + if (!szMessage.IsEmpty()) { + uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0); + g_plugin.setByte(hContact, "GPGEncryption", 0); + ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)szMessage.c_str()); + std::string msg = "Public key "; + CMStringA keyid = g_plugin.getMStringA(key_id_str.c_str()); + if (keyid.IsEmpty()) + keyid = g_plugin.getMStringA("KeyID"); + msg += keyid; + msg += " sent"; + + HistoryLog(hContact, msg.c_str(), DBEF_SENT); + g_plugin.setByte(hContact, "GPGEncryption", enc); + } + + return 0; +} + +INT_PTR ToggleEncryption(WPARAM w, LPARAM) +{ + MCONTACT hContact = (MCONTACT)w; + uint8_t enc; + if (db_mc_isMeta(hContact)) { + enc = g_plugin.getByte(metaGetMostOnline(hContact), "GPGEncryption"); + if (MessageBox(nullptr, TranslateT("Do you want to toggle encryption for all subcontacts?"), TranslateT("Metacontact detected"), MB_YESNO) == IDYES) { + int count = db_mc_getSubCount(hContact); + for (int i = 0; i < count; i++) { + MCONTACT hcnt = db_mc_getSub(hContact, i); + if (hcnt) + g_plugin.getByte(hcnt, "GPGEncryption", enc ? 0 : 1); + } + g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1); + } + } + else { + enc = g_plugin.getByte(hContact, "GPGEncryption", 0); + g_plugin.setByte(hContact, "GPGEncryption", enc ? 0 : 1); + } + setSrmmIcon(hContact); + return 0; +} + +int OnPreBuildContactMenu(WPARAM w, LPARAM) +{ + MCONTACT hContact = db_mc_tryMeta(w); + { + CMenuItem mi2(&g_plugin); + LPSTR proto = Proto_GetBaseAccountName(hContact); + PROTOACCOUNT *acc = Proto_GetAccount(proto); + std::string setting; + if (acc) { + setting = acc->szModuleName; + setting += "_KeyID"; + } + + CMStringA keyid = g_plugin.getMStringA(setting.c_str()); + if (keyid.IsEmpty()) + keyid = g_plugin.getMStringA("KeyID"); + + wchar_t buf[128] = { 0 }; + mir_snwprintf(buf, L"%s: %S", TranslateT("Send public key"), keyid.c_str()); + Menu_ModifyItem(g_plugin.hSendKey, buf); + } + + int flags; + CMStringA tmp = g_plugin.getMStringW(hContact, "GPGPubKey"); + if (tmp.IsEmpty()) { + g_plugin.delSetting(hContact, "GPGEncryption"); + flags = CMIF_GRAYED; + } + else flags = 0; + + if (g_plugin.getByte(hContact, "GPGEncryption")) + Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn off GPG encryption"), g_plugin.getIconHandle(IDI_SECURED), flags); + else + Menu_ModifyItem(g_plugin.hToggleEncryption, LPGENW("Turn on GPG encryption"), g_plugin.getIconHandle(IDI_UNSECURED), flags); + return 0; +} + +list transfers; + +int onProtoAck(WPARAM, LPARAM l) +{ + ACKDATA *ack = (ACKDATA*)l; + if (ack->type == ACKTYPE_FILE) { + switch (ack->result) { + case ACKRESULT_DENIED: case ACKRESULT_FAILED: + break; + case ACKRESULT_SUCCESS: + if (ack->hProcess) { + PROTOFILETRANSFERSTATUS *f = (PROTOFILETRANSFERSTATUS*)ack->hProcess; + if (f == nullptr) + break; + + if ((f->flags & PFTS_SENDING) != PFTS_SENDING) { + wchar_t *filename = nullptr; + if (f->flags & PFTS_UNICODE) { + if (f->szCurrentFile.w && f->szCurrentFile.w[0]) + filename = mir_wstrdup(f->szCurrentFile.w); + if (!filename) + return 0; + } + else { + if (f->szCurrentFile.a && f->szCurrentFile.a[0]) + filename = mir_utf8decodeW(f->szCurrentFile.a); + if (!filename) + return 0; + } + if (wcsstr(filename, L".gpg")) { //decrypt it + //process encrypted file + if (!g_plugin.bFileTransfers && !g_plugin.bSameAction) { + void ShowEncryptedFileMsgBox(); + ShowEncryptedFileMsgBox(); + } + if (!g_plugin.bFileTransfers && g_plugin.bSameAction) + return 0; + if (!globals.bDecryptFiles) + return 0; + HistoryLog(ack->hContact, "Received encrypted file, trying to decrypt"); + if (!boost::filesystem::exists(f->szCurrentFile.w)) + return 0; + + gpg_execution_params params; + params.addParam(L"-o"); + wstring file = filename; + wstring::size_type p1 = file.rfind(L".gpg"); + file.erase(p1, mir_wstrlen(L".gpg")); + if (boost::filesystem::exists(file)) { + if (MessageBox(nullptr, TranslateT("Target file exists, do you want to replace it?"), TranslateT("Warning"), MB_YESNO) == IDNO) + return 0; + } + params.addParam(file); + boost::filesystem::remove(file); + { + // password + CMStringW pass; + CMStringA keyid = g_plugin.getMStringA(ack->hContact, "KeyID"); + if (!keyid.IsEmpty()) { + string dbsetting = "szKey_"; + dbsetting += keyid; + dbsetting += "_Password"; + pass = g_plugin.getMStringW(dbsetting.c_str()); + if (!pass.IsEmpty() && globals.debuglog) + globals.debuglog << "info: found password in database for key ID: " + string(keyid.c_str()) + ", trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; + } + else { + pass = g_plugin.getMStringW("szKeyPassword"); + if (!pass.IsEmpty() && globals.debuglog) + globals.debuglog << "info: found password for all keys in database, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; + } + if (!pass.IsEmpty()) { + params.addParam(L"--passphrase"); + params.addParam(pass.c_str()); + } + else if (!globals.wszPassword.IsEmpty()) { + if (globals.debuglog) + globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " with password"; + params.addParam(L"--passphrase"); + params.addParam(globals.wszPassword.c_str()); + } + else if (globals.debuglog) + globals.debuglog << "info: passwords not found in database or memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " without password"; + } + params.addParam(L"-d"); + params.addParam(filename); + if (!gpg_launcher(params, boost::posix_time::minutes(15))) + return 0; + + string out(params.out); + while (out.find("public key decryption failed: bad passphrase") != string::npos) { + if (globals.debuglog) + globals.debuglog << "info: failed to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)) + " password needed, trying to get one"; + if (globals._terminate) + break; + { //save inkey id + string::size_type s = out.find(" encrypted with "); + s = out.find(" ID ", s); + s += mir_strlen(" ID "); + string::size_type s2 = out.find(",", s); + if (db_mc_isMeta(ack->hContact)) + g_plugin.setString(metaGetMostOnline(ack->hContact), "InKeyID", out.substr(s, s2 - s).c_str()); + else + g_plugin.setString(ack->hContact, "InKeyID", out.substr(s, s2 - s).c_str()); + } + + CDlgKeyPasswordMsgBox(ack->hContact).DoModal(); + + if (!globals.wszPassword.IsEmpty()) { + if (globals.debuglog) + globals.debuglog << "info: found password in memory, trying to decrypt message from " + toUTF8(Clist_GetContactDisplayName(ack->hContact)); + + params.addParam(L"--passphrase"); + params.addParam(globals.wszPassword.c_str()); + } + + if (!gpg_launcher(params, boost::posix_time::seconds(15))) + return 0; + if (params.result == pxNotFound) + return 0; + } + if (params.result == pxSuccess) + boost::filesystem::remove(filename); + } + mir_free(filename); + } + } + break; + } + } + else if (ack->type == ACKTYPE_MESSAGE) { + extern std::list sent_msgs; + if (!sent_msgs.empty()) { + if (ack->result == ACKRESULT_FAILED) { + std::list::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess); + if (it != sent_msgs.end()) + HistoryLog(ack->hContact, "Failed to send encrypted message"); + } + else if (ack->result == ACKRESULT_SUCCESS) { + std::list::iterator it = std::find(sent_msgs.begin(), sent_msgs.end(), ack->hProcess); + if (it != sent_msgs.end()) + sent_msgs.erase(it); + } + } + } + return 0; +} + +std::wstring encrypt_file(MCONTACT hContact, wchar_t *filename) +{ + MCONTACT hcnt = db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact; + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--tes"); + params.addParam(L"-r"); + + CMStringW keyid = g_plugin.getMStringW(hcnt, "KeyID"); + wchar_t *name = wcsrchr(filename, '\\'); + if (!name) + name = filename; + else + name++; + wchar_t *file_out = new wchar_t[mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1]; + mir_snwprintf(file_out, mir_wstrlen(name) + mir_wstrlen(L".gpg") + 1, L"%s.gpg", name); + params.addParam(keyid.c_str()); + + if (g_plugin.getByte(hcnt, "bAlwaysTrust")) { + params.addParam(L"--trust-model"); + params.addParam(L"always"); + } + + params.addParam(L"-o"); + wchar_t *temp = _tgetenv(L"TEMP"); + params.addParam(wstring(temp) + L"\\" + file_out); + wstring path_out = temp; + path_out += L"\\"; + path_out += file_out; + boost::filesystem::remove(path_out); + params.addParam(L"-e"); + params.addParam(filename); + delete[] file_out; + + if (!gpg_launcher(params, boost::posix_time::minutes(3))) + return nullptr; + + if (params.out.Find("There is no assurance this key belongs to the named user") != -1) { + if (IDYES != MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO)) + return nullptr; + + g_plugin.setByte(hcnt, "bAlwaysTrust", 1); + + params.addParam(L"--trust-model"); + params.addParam(L"always"); + if (!gpg_launcher(params, boost::posix_time::minutes(3))) + return nullptr; + } + return path_out; +} + +//from secureim partially +INT_PTR onSendFile(WPARAM w, LPARAM l) +{ + CCSDATA *ccs = (CCSDATA*)l; + if (!g_plugin.bFileTransfers) + return Proto_ChainSend(w, ccs); + + if (isContactSecured(ccs->hContact)) { + char *proto = Proto_GetBaseAccountName(ccs->hContact); + bool cap_found = false, supported_proto = false; + ptrA jid(db_get_utfa(ccs->hContact, proto, "jid", "")); + if (jid[0]) { + for (auto p : globals.Accounts) { + ptrA caps(p->getJabberInterface()->GetResourceFeatures(jid)); + if (caps) { + supported_proto = true; + string str; + for (int i = 0;; i++) { + str.push_back(caps[i]); + if (caps[i] == '\0') + if (caps[i + 1] == '\0') + break; + } + + if (str.find("GPG_Encrypted_FileTransfers:0") != string::npos) + cap_found = true; + } + } + } + + if (supported_proto && !cap_found) { + if (MessageBox(nullptr, TranslateT("Capability to decrypt file not found on other side.\nRecipient may be unable to decrypt file(s).\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO) + return Proto_ChainSend(w, ccs); + } + if (!supported_proto) { + if (MessageBox(nullptr, TranslateT("Unable to check encryption support on other side.\nRecipient may be unable to decrypt file(s).\nCurrently capability check supported only for ICQ and Jabber protocols.\nIt will work for any other proto if Miranda with New_GPG is used on other side.\nDo you want to encrypt file(s) anyway?"), TranslateT("File transfer warning"), MB_YESNO) == IDNO) + return Proto_ChainSend(w, ccs); + } + HistoryLog(ccs->hContact, TranslateU("encrypting file for transfer"), DBEF_SENT); + if (StriStr(ccs->szProtoService, "/sendfilew")) { + wchar_t **file = (wchar_t **)ccs->lParam; + for (int i = 0; file[i]; i++) { + if (!boost::filesystem::exists(file[i])) + return 0; //we do not want to send file unencrypted (sometimes ack have wrong info) + if (wcsstr(file[i], L".gpg")) + continue; + std::wstring path_out = encrypt_file(ccs->hContact, file[i]); + mir_free(file[i]); + file[i] = mir_wstrdup(path_out.c_str()); + transfers.push_back(path_out); + } + } + else { + char **file = (char**)ccs->lParam; + for (int i = 0; file[i]; i++) { + if (!boost::filesystem::exists(file[i])) + return 0; //we do not want to send file unencrypted (sometimes ack have wrong info) + if (strstr(file[i], ".gpg")) + continue; + wchar_t *tmp = mir_utf8decodeW(file[i]); + std::wstring path_out = encrypt_file(ccs->hContact, tmp); + mir_free(tmp); + char* tmp2 = mir_utf8encodeW(path_out.c_str()); + mir_free(file[i]); + file[i] = tmp2; + transfers.push_back(path_out); + + } + } + } + return Proto_ChainSend(w, ccs); +} + +void HistoryLog(MCONTACT hContact, const char *msg, uint32_t _time, uint32_t flags) +{ + DBEVENTINFO dbei = {}; + dbei.szModule = MODULENAME; + dbei.flags = DBEF_UTF | flags; + dbei.timestamp = (_time) ? _time : (uint32_t)time(0); + dbei.cbBlob = (uint32_t)mir_strlen(msg) + 1; + dbei.pBlob = (uint8_t*)msg; + db_event_add(hContact, &dbei); +} + +static int ControlAddStringUtf(HWND ctrl, uint32_t msg, const wchar_t *szString) +{ + int item = -1; + item = SendMessage(ctrl, msg, 0, (LPARAM)szString); + return item; +} + +int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data) +{ + int item = ControlAddStringUtf(hCombo, CB_ADDSTRING, szString); + SendMessage(hCombo, CB_SETITEMDATA, item, data); + + return item; +} + +static JABBER_HANDLER_FUNC SendHandler(IJabberInterface *ji, TiXmlElement *node, void*) +{ + TiXmlDocument *pDoc = node->GetDocument(); + + for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) { + LPCSTR str = local_node->GetText(); + LPCSTR nodename = local_node->Name(); + LPCSTR attr = local_node->Attribute("to"); + if (attr) { + MCONTACT hContact = ji->ContactFromJID(attr); + if (hContact) + if (!isContactSecured(hContact)) + break; + } + + if (str == nullptr) + continue; + + // TODO: make following block more readable + if (g_plugin.bPresenceSigning && nodename && strstr(nodename, "status")) { + string path_out = ptrA(g_plugin.getUStringA("szHomePath", "")); + string file = get_random(10); + path_out += "\\tmp\\"; + path_out += file; + boost::filesystem::remove(path_out); + wfstream f(path_out.c_str(), std::ios::out); + f << str; + f.close(); + if (!boost::filesystem::exists(path_out)) { + if (globals.debuglog) + globals.debuglog << "info: Failed to write prescense in file"; + break; + } + + gpg_execution_params params; + { + char setting[64]; + mir_snprintf(setting, sizeof(setting) - 1, "%s_KeyID", ji->GetModuleName()); + CMStringA inkeyid = g_plugin.getMStringA(setting); + if (inkeyid.IsEmpty()) + inkeyid = g_plugin.getMStringA("KeyID"); + + CMStringW pass; + if (!inkeyid.IsEmpty()) { + string dbsetting = "szKey_"; + dbsetting += inkeyid; + dbsetting += "_Password"; + pass = g_plugin.getMStringW(dbsetting.c_str()); + if (!pass.IsEmpty() && globals.debuglog) + globals.debuglog << "info: found password in database for key ID: " + string(inkeyid.c_str()) + ", trying to encrypt message from self with password"; + } + else { + pass = g_plugin.getMStringW("szKeyPassword"); + if (!pass.IsEmpty() && globals.debuglog) + globals.debuglog << "info: found password for all keys in database, trying to encrypt message from self with password"; + } + if (pass[0]) { + params.addParam(L"--passphrase"); + params.addParam(pass.c_str()); + } + else if (!globals.wszPassword.IsEmpty()) { + if (globals.debuglog) + globals.debuglog << "info: found password in memory, trying to encrypt message from self with password"; + params.addParam(L"--passphrase"); + params.addParam(globals.wszPassword.c_str()); + } + else if (globals.debuglog) + globals.debuglog << "info: passwords not found in database or memory, trying to encrypt message from self without password"; + } + + params.addParam(L"--local-user"); + params.addParam(g_plugin.getMStringW("KeyID").c_str()); + params.addParam(L"--default-key"); + params.addParam(g_plugin.getMStringW("KeyID").c_str()); + params.addParam(L"--batch"); + params.addParam(L"--yes"); + params.addParam(L"-abs"); + params.addParam(Utf2T(path_out.c_str()).get()); + gpg_launcher(params, boost::posix_time::seconds(15)); // TODO: handle errors + + boost::filesystem::remove(path_out); + path_out += ".asc"; + f.open(path_out.c_str(), std::ios::in | std::ios::ate | std::ios::binary); + wstring data; + if (f.is_open()) { + std::wifstream::pos_type size = f.tellg(); + wchar_t *tmp = new wchar_t[(std::ifstream::pos_type)size + (std::ifstream::pos_type)1]; + f.seekg(0, std::ios::beg); + f.read(tmp, size); + tmp[size] = '\0'; + data.append(tmp); + delete[] tmp; + f.close(); + boost::filesystem::remove(path_out); + } + if (data.empty()) { + if (globals.debuglog) + globals.debuglog << "info: Failed to read prescense sign from file"; + break; + } + if (data.find(L"-----BEGIN PGP SIGNATURE-----") != wstring::npos && data.find(L"-----END PGP SIGNATURE-----") != wstring::npos) { + wstring::size_type p1 = data.find(L"-----BEGIN PGP SIGNATURE-----") + mir_wstrlen(L"-----BEGIN PGP SIGNATURE-----"); + if (data.find(L"Version: ", p1) != wstring::npos) { + p1 = data.find(L"Version: ", p1); + p1 = data.find(L"\n", p1); + if (data.find(L"Version: ", p1) != wstring::npos) { + p1 = data.find(L"Version: ", p1); + p1 = data.find(L"\n", p1) + 1; + } + else + p1 += 1; + } + if (data.find(L"Comment: ", p1) != wstring::npos) { + p1 = data.find(L"Comment: ", p1); + p1 = data.find(L"\n", p1); + if (data.find(L"Comment: ", p1) != wstring::npos) { + p1 = data.find(L"Comment: ", p1); + p1 = data.find(L"\n", p1) + 1; + } + else + p1 += 1; + } + else + p1 += 1; + p1++; + wstring::size_type p2 = data.find(L"-----END PGP SIGNATURE-----"); + + std::wstring tmp = data.substr(p1, p2 - p1); + strip_line_term(tmp); + TiXmlElement* encrypted_data = pDoc->NewElement("x"); node->InsertEndChild(encrypted_data); + encrypted_data->SetText(tmp.c_str()); + encrypted_data->SetAttribute("xmlns", "jabber:x:signed"); + } + break; + } + } + return FALSE; +} + +static JABBER_HANDLER_FUNC PresenceHandler(IJabberInterface *ji, TiXmlElement* node, void*) +{ + for (auto *local_node = node->FirstChildElement(); local_node; local_node = local_node->NextSiblingElement()) { + LPCSTR nodename = local_node->Name(); + if (nodename == nullptr) + continue; + + if (!mir_strcmp(nodename, "x")) + continue; + + for (auto *pAttr = local_node->FirstAttribute(); pAttr; pAttr = pAttr->Next()) { + LPCSTR value = pAttr->Value(); + if (!mir_strcmp(value, "jabber:x:signed")) { + std::string status_str; + + for (auto *local_node2 = node->FirstChildElement(); local_node2; local_node2 = local_node2->NextSiblingElement()) { + LPCSTR nodename2 = local_node2->Name(); + if (!mir_strcmp(nodename2, "status")) { + LPCSTR status = local_node2->GetText(); + if (status) + status_str = status; + break; + } + } + + LPCSTR data = local_node->GetText(); + string sign = "-----BEGIN PGP SIGNATURE-----\n\n"; + wstring file = toUTF16(get_random(10)), status_file = toUTF16(get_random(10)); + sign += data; + sign += "\n-----END PGP SIGNATURE-----\n"; + + CMStringW path_out = g_plugin.getMStringW("szHomePath"), status_file_out = path_out; + path_out += L"\\tmp\\"; + path_out += file.c_str(); + path_out += L".sig"; + status_file_out += L"\\tmp\\"; + status_file_out += status_file.c_str(); + status_file_out += L".status"; + + boost::filesystem::remove(path_out.c_str()); + boost::filesystem::remove(status_file_out.c_str()); + wfstream f(path_out.c_str(), std::ios::out); + while (!f.is_open()) + f.open(path_out.c_str(), std::ios::out); + f << sign.c_str(); + f.close(); + f.open(status_file_out.c_str(), std::ios::out); + while (!f.is_open()) + f.open(status_file_out.c_str(), std::ios::out); + f << status_str.c_str(); + f.close(); + if (!boost::filesystem::exists(path_out.c_str())) { + if (globals.debuglog) + globals.debuglog << "info: Failed to write sign in file"; + return FALSE; + } + { + // gpg + gpg_execution_params params; + params.addParam(L"--verify"); + params.addParam(L"-a"); + params.addParam(path_out.c_str()); + params.addParam(status_file_out.c_str()); + if (!gpg_launcher(params, boost::posix_time::seconds(15))) + return FALSE; + if (params.result == pxNotFound) + return FALSE; + + boost::filesystem::remove(path_out.c_str()); + boost::filesystem::remove(status_file_out.c_str()); + + string out(params.out); + if (out.find("key ID ") != string::npos) { + //need to get hcontact here, i can get jid from hxml, and get handle from jid, maybe exists better way ? + string::size_type p1 = out.find("key ID ") + mir_strlen("key ID "); + string::size_type p2 = out.find("\n", p1); + if (p1 != string::npos && p2 != string::npos) { + MCONTACT hContact = ji->ContactFromJID(node->Attribute("from")); + if (hContact) + globals.hcontact_data[hContact].key_in_prescense = out.substr(p1, p2 - p1 - 1).c_str(); + } + } + } + return FALSE; + } + } + } + return FALSE; +} + +static JABBER_HANDLER_FUNC MessageHandler(IJabberInterface*, TiXmlElement*, void*) +{ + return FALSE; +} + +int GetJabberInterface(WPARAM, LPARAM) //get interface for all jabber accounts, options later +{ + list ::iterator p; + globals.Accounts.clear(); + + int accNum = 0; + for (auto &pa : Accounts()) { + IJabberInterface *pApi = getJabberApi(pa->szModuleName); + if (pApi == nullptr) + continue; + + auto *pAcc = new JabberAccount(); + pAcc->setJabberInterface(pApi); + if (pa->tszAccountName) + pAcc->setAccountName(mir_wstrdup(pa->tszAccountName)); + else + pAcc->setAccountName(mir_a2u(pa->szModuleName)); + + pAcc->setAccountNumber(accNum++); + pAcc->setSendHandler(pApi->AddSendHandler((JABBER_HANDLER_FUNC)SendHandler)); + pAcc->setPresenceHandler(pApi->AddPresenceHandler((JABBER_HANDLER_FUNC)PresenceHandler)); + + if (g_plugin.bAutoExchange) { + pApi->RegisterFeature("GPG_Key_Auto_Exchange:0", "Indicates that gpg installed and configured to public key auto exchange (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)"); + pApi->AddFeatures("GPG_Key_Auto_Exchange:0\0\0"); + } + if (g_plugin.bFileTransfers) { + pApi->RegisterFeature("GPG_Encrypted_FileTransfers:0", "Indicates that gpg installed and configured to encrypt files (currently implemented in new_gpg plugin for Miranda IM and Miranda NG)"); + pApi->AddFeatures("GPG_Encrypted_FileTransfers:0\0\0"); + } + + globals.Accounts.push_back(pAcc); + } + + return 0; +} + +void RemoveHandlers() +{ + for (auto &it : globals.Accounts) { + auto *pApi = it->getJabberInterface(); + if (pApi == nullptr) + continue; + + if (it->getMessageHandler() != INVALID_HANDLE_VALUE) + pApi->RemoveHandler(it->getMessageHandler()); + if (it->getPresenceHandler() != INVALID_HANDLE_VALUE) + pApi->RemoveHandler(it->getPresenceHandler()); + pApi->RemoveFeatures("GPG_Encrypted_FileTransfers:0\0\0"); + pApi->RemoveFeatures("GPG_Key_Auto_Exchange:0\0\0"); + } +} + +bool isContactSecured(MCONTACT hContact) +{ + uint8_t gpg_enc = g_plugin.getByte(hContact, "GPGEncryption", 0); + if (!gpg_enc) { + if (globals.debuglog) + globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact)); + return false; + } + if (!db_mc_isMeta(hContact)) { + CMStringW key = g_plugin.getMStringW(hContact, "GPGPubKey"); + if (key.IsEmpty()) { + if (globals.debuglog) + globals.debuglog << "encryption is turned off for " + toUTF8(Clist_GetContactDisplayName(hContact)); + return false; + } + } + if (globals.debuglog) + globals.debuglog << "encryption is turned on for " + toUTF8(Clist_GetContactDisplayName(hContact)); + return true; +} + +bool isContactHaveKey(MCONTACT hContact) +{ + ptrW key(g_plugin.getWStringA(hContact, "GPGPubKey")); + return (mir_wstrlen(key) > 0); +} + +bool isGPGKeyExist() +{ + CMStringW id(g_plugin.getMStringW("KeyID")); + CMStringA key(g_plugin.getMStringA("GPGPubKey")); + return (!id.IsEmpty() && !key.IsEmpty()); +} + +bool isGPGValid() +{ + ptrW tmp(g_plugin.getWStringA("szGpgBinPath", L"")); + bool gpg_exists = false, is_valid = true; + boost::filesystem::path p(tmp); + + if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) + gpg_exists = true; + else { + wchar_t path[MAX_PATH], mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + SetCurrentDirectoryW(mir_path); + + //mir_realloc(path, (mir_wstrlen(path)+64)*sizeof(wchar_t)); + wchar_t gpg_path[MAX_PATH]; + mir_wstrcpy(gpg_path, mir_path); + mir_wstrcat(gpg_path, L"\\GnuPG\\gpg.exe"); + + p = boost::filesystem::path(gpg_path); + if (boost::filesystem::exists(p) && boost::filesystem::is_regular_file(p)) { + gpg_exists = true; + mir_wstrcpy(path, L"GnuPG\\gpg.exe"); + } + tmp = mir_wstrdup(path); + } + + if (gpg_exists) { + g_plugin.setWString("szGpgBinPath", tmp); + + gpg_execution_params params; + params.addParam(L"--version"); + bool _gpg_valid = globals.gpg_valid; + globals.gpg_valid = true; + gpg_launcher(params); + globals.gpg_valid = _gpg_valid; //TODO: check this + int p1 = params.out.Find("(GnuPG) "); + if (p1 == -1) + is_valid = false; + } + + return is_valid && gpg_exists; +} + +#define NEWTSTR_MALLOC(A) (A==NULL)?NULL:mir_strcpy((char*)mir_alloc(sizeof(char)*(mir_strlen(A)+1)),A) + +const bool StriStr(const char *str, const char *substr) +{ + bool i = false; + char *str_up = NEWTSTR_MALLOC(str); + char *substr_up = NEWTSTR_MALLOC(substr); + + CharUpperBuffA(str_up, (uint32_t)mir_strlen(str_up)); + CharUpperBuffA(substr_up, (uint32_t)mir_strlen(substr_up)); + + if (strstr(str_up, substr_up)) + i = true; + + mir_free(str_up); + mir_free(substr_up); + + return i; +} + +bool IsOnline(MCONTACT hContact) +{ + if (g_plugin.getByte(hContact, "Status", 0) == ID_STATUS_OFFLINE) + return false; + return true; +} + +string toUTF8(wstring str) +{ + string ustr; + try { + utf8::utf16to8(str.begin(), str.end(), back_inserter(ustr)); + } + catch (const utf8::exception& e) { + if (globals.debuglog) + globals.debuglog << std::string("utf8cpp encoding exception: ") + (char*)e.what(); + //TODO + } + return ustr; +} + +wstring toUTF16(string str) //convert as much as possible +{ + wstring ustr; + string tmpstr; + try { + utf8::replace_invalid(str.begin(), str.end(), back_inserter(tmpstr)); + utf8::utf8to16(tmpstr.begin(), tmpstr.end(), back_inserter(ustr)); + } + catch (const utf8::exception& e) { + if (globals.debuglog) + globals.debuglog << std::string("utf8cpp decoding exception: ") + (char*)e.what(); + //TODO + } + return ustr; +} + +string get_random(int length) +{ + string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); + string data; + boost::random_device rng; + boost::variate_generator> gen(rng, boost::uniform_int<>(0, (int)chars.length() - 1)); + for (int i = 0; i < length; ++i) + data += chars[gen()]; + return data; +} + +void send_encrypted_msgs_thread(void *param) +{ + MCONTACT hContact = (MCONTACT)(DWORD_PTR)param; + while (true) { + while (!isContactSecured(hContact)) + Sleep(1000); + + if (!globals.hcontact_data[hContact].msgs_to_send.empty()) { + Sleep(1000); + list::iterator end = globals.hcontact_data[hContact].msgs_to_send.end(); + extern std::list sent_msgs; + for (list::iterator p = globals.hcontact_data[hContact].msgs_to_send.begin(); p != end; ++p) { + sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)p->c_str())); + HistoryLog(hContact, p->c_str(), DBEF_SENT); + Sleep(1000); + } + globals.hcontact_data[hContact].msgs_to_send.clear(); + return; + } + else + return; + } +} + +void ExportGpGKeysFunc(int type) +{ + ptrW p(GetFilePath(L"Choose file to export keys", L"*", L"Any file", true)); + if (!p || !p[0]) + return; + + std::ofstream file; + file.open(p, std::ios::trunc | std::ios::out); + if (!file.is_open()) + return; //TODO: handle error + + int exported_keys = 0; + if (!type || type == 2) { + for (auto &hContact : Contacts()) { + CMStringA key = g_plugin.getMStringA(hContact, "GPGPubKey"); + if (key.IsEmpty()) + continue; + + ptrW wszLogin(Contact::GetInfo(CNF_UNIQUEID, 0, Proto_GetBaseAccountName(hContact))), wszContact(Contact::GetInfo(CNF_UNIQUEID, hContact)); + if (wszLogin == nullptr || wszContact == nullptr) + continue; + + std::string id = "Comment: login "; + id += T2Utf(wszLogin).get(); + id += " contact_id "; + id += T2Utf(wszContact).get(); + id += '\n'; + + int p1 = key.Find("-----BEGIN PGP PUBLIC KEY BLOCK-----"); + if (p1 == -1) + continue; + p1 += mir_strlen("-----BEGIN PGP PUBLIC KEY BLOCK-----"); + p1++; + key.Insert(p1, id.c_str()); + file << key; + file << std::endl; + exported_keys++; + } + } + + if (type == 1 || type == 2) { + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--export-secret-keys"); + params.addParam(L"-a"); + gpg_launcher(params); //TODO: handle errors + + file << params.out.c_str(); + file << std::endl; + } + if (file.is_open()) + file.close(); + wchar_t msg[512]; + if (type == 2) + mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys and all private keys."), exported_keys); + else if (type == 1) + mir_snwprintf(msg, TranslateT("We have successfully exported all private keys.")); + else if (!type) + mir_snwprintf(msg, TranslateT("We have successfully exported %d public keys."), exported_keys); + MessageBox(nullptr, msg, TranslateT("Keys export result"), MB_OK); +} + +INT_PTR ImportGpGKeys(WPARAM, LPARAM) +{ + ptrW p(GetFilePath(L"Choose file to import keys from", L"*", L"Any file")); + if (!p || !p[0]) + return 1; + + std::ifstream file; + file.open(p, std::ios::in); + if (!file.is_open()) + return 1; //TODO: handle error + + int processed_keys = 0, processed_private_keys = 0; + + char line[256]; + file.getline(line, 255); + if (!strstr(line, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && !strstr(line, "-----BEGIN PGP PRIVATE KEY BLOCK-----")) + return 1; //TODO: handle error + + std::string key, login, contact_id; + key += line; + key += '\n'; + while (file.is_open() && !file.eof()) { + file.getline(line, 255); + key += line; + key += '\n'; + if (strstr(line, "-----END PGP PUBLIC KEY BLOCK-----")) { + std::string::size_type p1 = key.rfind("Comment: login "), p2 = 0; + if (p1 == std::string::npos) + { + key.clear(); + continue; //TODO: warning about malformed file + } + p1 += mir_strlen("Comment: login "); + p2 = key.rfind(" contact_id "); + login = key.substr(p1, p2 - p1); + p2 += mir_strlen(" contact_id "); + p1 = key.find("\n", p2); + contact_id = key.substr(p2, p1 - p2); + p1 = key.rfind("Comment: login "); + p2 = key.find("\n", p1); + p2++; + key.erase(p1, p2 - p1); + + PROTOACCOUNT *pFoundAcc = nullptr; + for (auto &pa : Accounts()) { + ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, 0, pa->szModuleName)); + if (wszUniqueId == nullptr) + continue; + + if (login == T2Utf(wszUniqueId).get()) { + pFoundAcc = pa; + break; + } + } + + if (pFoundAcc == nullptr) + continue; + + for (auto &hContact : Contacts(pFoundAcc->szModuleName)) { + ptrW wszUniqueId(Contact::GetInfo(CNF_UNIQUEID, hContact, pFoundAcc->szModuleName)); + if (wszUniqueId == nullptr) + continue; + + if (contact_id != T2Utf(wszUniqueId).get()) + continue; + + CMStringW path = g_plugin.getMStringW("szHomePath"); + + gpg_execution_params params; + { + wstring rand = toUTF16(get_random(10)); + path += L"\\"; + path += rand.c_str(); + boost::filesystem::remove(path.c_str()); + wfstream f(path, std::ios::out); + f << toUTF16(key).c_str(); + f.close(); + params.addParam(L"--batch"); + params.addParam(L"--import"); + params.addParam(path.c_str()); + } + if (!gpg_launcher(params)) + break; + if (params.result == pxNotFound) + break; + if (params.result == pxSuccess) + processed_keys++; + + string output(params.out); + if (output.find("already in secret keyring") != string::npos) { + MessageBox(nullptr, TranslateT("Key already in secret keyring."), TranslateT("Info"), MB_OK); + boost::filesystem::remove(path.c_str()); + break; + } + char *tmp2; + string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); + string::size_type s2 = output.find(":", s); + tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); + mir_strcpy(tmp2, output.substr(s, s2 - s).c_str()); + mir_utf8decode(tmp2, nullptr); + g_plugin.setString(hContact, "KeyID", tmp2); + mir_free(tmp2); + s = output.find("“", s2); + if (s == string::npos) { + s = output.find("\"", s2); + s += 1; + } + else + s += 3; + if ((s2 = output.find("(", s)) == string::npos) + s2 = output.find("<", s); + else if (s2 > output.find("<", s)) + s2 = output.find("<", s); + if (s2 != string::npos) { + tmp2 = (char *)mir_alloc((output.substr(s, s2 - s - 1).length() + 1) * sizeof(char)); + mir_strcpy(tmp2, output.substr(s, s2 - s - 1).c_str()); + mir_utf8decode(tmp2, nullptr); + if (hContact) { + g_plugin.setString(hContact, "KeyMainName", output.substr(s, s2 - s - 1).c_str()); + } + mir_free(tmp2); + if ((s = output.find(")", s2)) == string::npos) + s = output.find(">", s2); + else if (s > output.find(">", s2)) + s = output.find(">", s2); + s2++; + if (output[s] == ')') { + tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); + mir_strcpy(tmp2, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp2, nullptr); + if (hContact) + g_plugin.setString(hContact, "KeyComment", output.substr(s2, s - s2).c_str()); + mir_free(tmp2); + s += 3; + s2 = output.find(">", s); + tmp2 = (char *)mir_alloc((output.substr(s, s2 - s).length() + 1) * sizeof(char)); + mir_strcpy(tmp2, output.substr(s, s2 - s).c_str()); + mir_utf8decode(tmp2, nullptr); + if (hContact) + g_plugin.setString(hContact, "KeyMainEmail", output.substr(s, s2 - s).c_str()); + mir_free(tmp2); + } + else { + tmp2 = (char *)mir_alloc((output.substr(s2, s - s2).length() + 1) * sizeof(char)); + mir_strcpy(tmp2, output.substr(s2, s - s2).c_str()); + mir_utf8decode(tmp2, nullptr); + if (hContact) + g_plugin.setString(hContact, "KeyMainEmail", output.substr(s2, s - s2).c_str()); + mir_free(tmp2); + } + } + g_plugin.setByte(hContact, "GPGEncryption", 1); + g_plugin.setWString(hContact, "GPGPubKey", toUTF16(key).c_str()); + + boost::filesystem::remove(path.c_str()); + break; + } + key.clear(); + } + else if (strstr(line, "-----END PGP PRIVATE KEY BLOCK-----")) { + CMStringW tmp2 = g_plugin.getMStringW("szHomePath"); + tmp2 += L"\\temporary_exported.asc"; + boost::filesystem::remove(tmp2.c_str()); + + wfstream f(tmp2, std::ios::out); + f << toUTF16(key).c_str(); + f.close(); + + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--import"); + params.addParam(tmp2.c_str()); + if (!gpg_launcher(params)) + break; + if (params.result == pxNotFound) + break; + if (params.result == pxSuccess) + processed_private_keys++; + key.clear(); + } + } + if (file.is_open()) + file.close(); + wchar_t msg[512]; + if (processed_private_keys) + mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys and some private keys."), processed_keys); + else + mir_snwprintf(msg, TranslateT("We have successfully processed %d public keys."), processed_keys); + MessageBox(nullptr, msg, TranslateT("Keys import result"), MB_OK); + return 0; +} + +void SendErrorMessage(MCONTACT hContact) +{ + if (!g_plugin.bSendErrorMessages) + return; + + uint8_t enc = g_plugin.getByte(hContact, "GPGEncryption", 0); + g_plugin.setByte(hContact, "GPGEncryption", 0); + ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); + HistoryLog(hContact, "Error message sent", DBEF_SENT); + g_plugin.setByte(hContact, "GPGEncryption", enc); +} + +void fix_line_term(std::string &s) +{ + if (s.empty()) + return; + + boost::algorithm::erase_all(s, "\r\r"); + + // unified line endings for unix & windows port + boost::algorithm::replace_all(s, "\r\n", "\n"); + boost::algorithm::replace_all(s, "\n", "\r\n"); +} + +void strip_line_term(std::wstring &s) +{ + if (s.empty()) + return; + boost::algorithm::erase_all(s, L"\r"); + boost::algorithm::erase_all(s, L"\n"); +} + +void strip_line_term(std::string &s) +{ + if (s.empty()) + return; + boost::algorithm::erase_all(s, "\r"); + boost::algorithm::erase_all(s, "\n"); +} + +void strip_tags(std::string &str) +{ + if (str.empty()) + return; + boost::algorithm::erase_all(str, globals.wszInopentag.c_str()); + boost::algorithm::erase_all(str, globals.wszInclosetag.c_str()); + boost::algorithm::erase_all(str, globals.wszOutopentag.c_str()); + boost::algorithm::erase_all(str, globals.wszOutclosetag.c_str()); +} + + +void ShowEncryptedFileMsgBox() +{ + CDlgEncryptedFileMsgBox *d = new CDlgEncryptedFileMsgBox; + d->DoModal(); +} + +void clean_temp_dir() +{ + using namespace boost::filesystem; + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + SetCurrentDirectoryW(mir_path); + + CMStringW tmp = mir_path; + tmp += g_plugin.getMStringW("szHomePath"); + tmp += L"\\tmp"; + if (exists(tmp.c_str()) && is_directory(tmp.c_str())) { + boost::filesystem::path p(tmp); + for (directory_iterator i = directory_iterator(p), end = directory_iterator(); i != end; ++i) { + if (boost::filesystem::is_regular_file(i->path())) { + if ((i->path().filename().generic_string().length() == 10 && (i->path().filename().generic_string().find(".") == std::string::npos)) || + i->path().extension() == ".sig" || i->path().extension() == ".asc" || i->path().extension() == ".status") + boost::filesystem::remove(i->path()); + } + } + } +} + +bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path) +{ + wstring tmp = gpg_bin_path; + if (!tmp.empty()) { + wchar_t mir_path[MAX_PATH]; + PathToAbsoluteW(L"\\", mir_path); + SetCurrentDirectoryW(mir_path); + if (!boost::filesystem::exists(tmp)) { + MessageBox(nullptr, TranslateT("GPG binary does not exist.\nPlease choose another location"), TranslateT("Warning"), MB_OK); + return false; + } + } + else { + MessageBox(nullptr, TranslateT("Please choose GPG binary location"), TranslateT("Warning"), MB_OK); + return false; + } + { + bool bad_version = false; + g_plugin.setWString("szGpgBinPath", tmp.c_str()); + + gpg_execution_params params; + params.addParam(L"--version"); + bool _gpg_valid = globals.gpg_valid; + globals.gpg_valid = true; + gpg_launcher(params); + globals.gpg_valid = _gpg_valid; //TODO: check this + g_plugin.delSetting("szGpgBinPath"); + int p1 = params.out.Find("(GnuPG) "); + if (p1 != -1) { + p1 += mir_strlen("(GnuPG) "); + if (params.out[p1] != '1') + bad_version = true; + } + else { + bad_version = false; + MessageBox(nullptr, TranslateT("This is not GnuPG binary!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); + return false; + } + if (bad_version) + MessageBox(nullptr, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), TranslateT("Warning"), MB_OK); + } + tmp = gpg_home_path; + if (tmp[tmp.length()] == '\\') + tmp.erase(tmp.length()); + if (tmp.empty()) { + MessageBox(nullptr, TranslateT("Please set keyring's home directory"), TranslateT("Warning"), MB_OK); + return false; + } + { + CMStringW path = g_plugin.getMStringW("szHomePath"); + uint32_t dwFileAttr = GetFileAttributes(path); + if (dwFileAttr != INVALID_FILE_ATTRIBUTES) { + dwFileAttr &= ~FILE_ATTRIBUTE_READONLY; + SetFileAttributes(path, dwFileAttr); + } + } + return true; +} + +void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path) +{ + g_plugin.setWString("szGpgBinPath", gpg_bin_path); + g_plugin.setWString("szHomePath", gpg_home_path); +} + +bool gpg_use_new_random_key(const char *account_name) +{ + CMStringW path = g_plugin.getMStringW("szHomePath"); + path += L"\\new_key"; + + // generating key file + wfstream f(path.c_str(), std::ios::out); + if (!f.is_open()) { + MessageBox(nullptr, TranslateT("Failed to open file"), TranslateT("Error"), MB_OK); + return false; + } + f << "Key-Type: RSA"; + f << "\n"; + f << "Key-Length: 4096"; + f << "\n"; + f << "Subkey-Type: RSA"; + f << "\n"; + f << "Name-Real: "; + f << get_random(6).c_str(); + f << "\n"; + f << "Name-Email: "; + f << get_random(5).c_str(); + f << "@"; + f << get_random(5).c_str(); + f << "."; + f << get_random(3).c_str(); + f << "\n"; + f.close(); + + { + // gpg execution + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"--yes"); + params.addParam(L"--gen-key"); + params.addParam(path.c_str()); + params.bNoOutput = true; + if (!gpg_launcher(params, boost::posix_time::minutes(10))) + return false; + if (params.result == pxNotFound) + return false; + + boost::filesystem::remove(path.c_str()); + string out(params.out); + int p1 = params.out.Find("key "); + if (p1 != -1) + path = ptrW(mir_utf8decodeW(params.out.Mid(p1 + 4, 8).c_str())); + else + path.Empty(); + } + + if (!path.IsEmpty()) { + gpg_execution_params params; + params.addParam(L"--batch"); + params.addParam(L"-a"); + params.addParam(L"--export"); + params.addParam(path.c_str()); + if (!gpg_launcher(params)) + return false; + + if (params.result == pxNotFound) + return false; + + params.out.Remove('\r'); + + if (account_name == nullptr) { + g_plugin.setString("GPGPubKey", params.out.c_str()); + g_plugin.setWString("KeyID", path.c_str()); + } + else { + CMStringA acc_str = account_name; + g_plugin.setString(acc_str + "_GPGPubKey", params.out.c_str()); + g_plugin.setWString(acc_str + "_KeyID", path.c_str()); + } + } + return true; +} diff --git a/plugins/New_GPG/src/utilities.h b/plugins/New_GPG/src/utilities.h index 1ac119660d..c6390b61a2 100644 --- a/plugins/New_GPG/src/utilities.h +++ b/plugins/New_GPG/src/utilities.h @@ -1,54 +1,54 @@ -// Copyright © 2010-22 sss -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program 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 General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef UTILITIES_H -#define UTILITIES_H - -void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc); -wchar_t *GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file = false); -void GetFolderPath(wchar_t *WindowTittle); - -void setSrmmIcon(MCONTACT); - -void send_encrypted_msgs_thread(void*); - -int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data); -bool isContactSecured(MCONTACT hContact); -bool isContactHaveKey(MCONTACT hContact); -bool isGPGKeyExist(); -bool isGPGValid(); - -void ExportGpGKeysFunc(int type); -void ImportKey(MCONTACT hContact, std::wstring new_key); - -void SendErrorMessage(MCONTACT hContact); - -const bool StriStr(const char *str, const char *substr); -string toUTF8(wstring str); -wstring toUTF16(string str); -string get_random(int length); - -void HistoryLog(MCONTACT, const char *msg, uint32_t _time = 0, uint32_t _flags = 0); -void fix_line_term(std::string &s); -void strip_line_term(std::wstring &s); -void strip_line_term(std::string &s); -void strip_tags(std::string &s); -void clean_temp_dir(); -bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path); -void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path); -bool gpg_use_new_random_key(const char *account_name); - -#endif +// Copyright © 2010-23 sss +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program 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 General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef UTILITIES_H +#define UTILITIES_H + +void GetFilePath(wchar_t *WindowTittle, char *szSetting, wchar_t *szExt, wchar_t *szExtDesc); +wchar_t *GetFilePath(wchar_t *WindowTittle, wchar_t *szExt, wchar_t *szExtDesc, bool save_file = false); +void GetFolderPath(wchar_t *WindowTittle); + +void setSrmmIcon(MCONTACT); + +void send_encrypted_msgs_thread(void*); + +int ComboBoxAddStringUtf(HWND hCombo, const wchar_t *szString, uint32_t data); +bool isContactSecured(MCONTACT hContact); +bool isContactHaveKey(MCONTACT hContact); +bool isGPGKeyExist(); +bool isGPGValid(); + +void ExportGpGKeysFunc(int type); +void ImportKey(MCONTACT hContact, std::wstring new_key); + +void SendErrorMessage(MCONTACT hContact); + +const bool StriStr(const char *str, const char *substr); +string toUTF8(wstring str); +wstring toUTF16(string str); +string get_random(int length); + +void HistoryLog(MCONTACT, const char *msg, uint32_t _time = 0, uint32_t _flags = 0); +void fix_line_term(std::string &s); +void strip_line_term(std::wstring &s); +void strip_line_term(std::string &s); +void strip_tags(std::string &s); +void clean_temp_dir(); +bool gpg_validate_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path); +void gpg_save_paths(wchar_t *gpg_bin_path, wchar_t *gpg_home_path); +bool gpg_use_new_random_key(const char *account_name); + +#endif diff --git a/plugins/New_GPG/src/version.h b/plugins/New_GPG/src/version.h index b0abab9a96..7dff493628 100644 --- a/plugins/New_GPG/src/version.h +++ b/plugins/New_GPG/src/version.h @@ -10,4 +10,4 @@ #define __DESCRIPTION "New GPG encryption support plugin, based on code from old GPG plugin and SecureIM." #define __AUTHOR "sss, Miranda NG Team" #define __AUTHORWEB "https://miranda-ng.org/p/New_GPG" -#define __COPYRIGHT "© 2010-22 sss, Miranda NG Team" +#define __COPYRIGHT "© 2010-23 sss, Miranda NG Team" -- cgit v1.2.3