// Copyright © 2010-24 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(LPGENW("Choose gpg.exe"), "szGpgBinPath", L"*.exe", LPGENW("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()); }