// Copyright © 2010-20 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; int item_num = 0; HWND hwndList_p = nullptr; HWND hwndCurKey_p = nullptr; void ShowLoadPublicKeyDialog(bool = false); class COptGpgMainDlg : public CDlgBase { 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); } bool OnInitDialog() override { 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("Protocol"), 60); list_USERLIST.SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_SINGLEROW); int i = 1; for (auto &hContact : Contacts()) { if (isContactHaveKey(hContact)) { wchar_t *name = Clist_GetContactDisplayName(hContact); int row = list_USERLIST.AddItem(L"", 0); list_USERLIST.SetItemText(row, 0, name); wchar_t *tmp = mir_a2u(Proto_GetBaseAccountName(hContact)); list_USERLIST.SetItemText(row, 4, tmp); mir_free(tmp); char *tmp2 = db_get_sa(hContact, MODULENAME, "KeyID", ""); tmp = mir_a2u(tmp2); mir_free(tmp2); list_USERLIST.SetItemText(row, 1, (mir_wstrlen(tmp) > 1) ? tmp : L"not set"); mir_free(tmp); tmp2 = db_get_sa(hContact, MODULENAME, "KeyMainName", ""); if (!toUTF16(tmp2).empty()) tmp = mir_wstrdup(toUTF16(tmp2).c_str()); else tmp = db_get_wsa(hContact, MODULENAME, "KeyMainName", L""); mir_free(tmp2); list_USERLIST.SetItemText(row, 2, (mir_wstrlen(tmp) > 1) ? tmp : L"not set"); mir_free(tmp); tmp2 = db_get_sa(hContact, MODULENAME, "KeyMainEmail", ""); if (!toUTF16(tmp2).empty()) tmp = mir_wstrdup(toUTF16(tmp2).c_str()); else tmp = db_get_wsa(hContact, MODULENAME, "KeyMainEmail", L""); mir_free(tmp2); list_USERLIST.SetItemText(row, 3, (mir_wstrlen(tmp) > 1) ? tmp : L"not set"); mir_free(tmp); if (g_plugin.getByte(hContact, "GPGEncryption", 0)) list_USERLIST.SetCheckState(row, 1); globals.user_data[(int)i] = hContact; 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); i++; } } edit_LOG_FILE_EDIT.SetText(ptrW(db_get_wsa(0, MODULENAME, "szLogFilePath", L""))); check_DEBUG_LOG.SetState(g_plugin.getByte("bDebugLog", 0)); check_JABBER_API.Enable(); check_AUTO_EXCHANGE.Enable(globals.bJabberAPI); { string keyinfo = Translate("Default private key ID"); keyinfo += ": "; char *keyid = db_get_sa(0, MODULENAME, "KeyID", ""); keyinfo += (mir_strlen(keyid) > 0) ? keyid : Translate("not set"); mir_free(keyid); lbl_CURRENT_KEY.SetTextA(keyinfo.c_str()); } 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 //////////////// hwndList_p = list_USERLIST.GetHwnd(); hwndCurKey_p = lbl_CURRENT_KEY.GetHwnd(); //////////////// list_USERLIST.OnItemChanged = Callback(this, &COptGpgMainDlg::onItemChanged_USERLIST); return true; } bool OnApply() override { g_plugin.setByte("bDebugLog", globals.bDebugLog = check_DEBUG_LOG.GetState()); if (globals.bDebugLog) globals.debuglog.init(); g_plugin.setByte("bJabberAPI", globals.bJabberAPI = check_JABBER_API.GetState()); bool old_bFileTransfers = g_plugin.getByte("bFileTransfers", 0) != 0; g_plugin.setByte("bFileTransfers", globals.bFileTransfers = check_FILE_TRANSFERS.GetState()); if (globals.bFileTransfers != old_bFileTransfers) { g_plugin.setByte("bSameAction", 0); globals.bSameAction = false; } g_plugin.setByte("bAutoExchange", globals.bAutoExchange = check_AUTO_EXCHANGE.GetState()); g_plugin.setWString("szLogFilePath", ptrW(edit_LOG_FILE_EDIT.GetText())); return true; } void onClick_DELETE_KEY_BUTTON(CCtrlButton*) { void setClistIcon(MCONTACT hContact); void setSrmmIcon(MCONTACT hContact); { //gpg execute block wchar_t *ptmp; char *tmp; bool keep = false; bool ismetacontact = false; MCONTACT meta = NULL; MCONTACT hContact = globals.user_data[item_num + 1]; if (db_mc_isMeta(hContact)) { meta = hContact; hContact = metaGetMostOnline(hContact); ismetacontact = true; } else if ((meta = db_mc_getMeta(globals.user_data[item_num + 1])) != NULL) { hContact = metaGetMostOnline(meta); ismetacontact = true; } tmp = db_get_sa(hContact, MODULENAME, "KeyID", ""); for (auto &hcnttmp : Contacts()) { if (hcnttmp != hContact) { char *tmp2 = db_get_sa(hcnttmp, MODULENAME, "KeyID", ""); if (!mir_strcmp(tmp, tmp2)) { mir_free(tmp2); keep = true; break; } mir_free(tmp2); } } 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) { std::vector cmd; string output; DWORD exitcode; cmd.push_back(L"--batch"); cmd.push_back(L"--yes"); cmd.push_back(L"--delete-key"); ptmp = mir_a2u(tmp); cmd.push_back(ptmp); mir_free(ptmp); gpg_execution_params params(cmd); pxResult result; params.out = &output; params.code = &exitcode; params.result = &result; if (!gpg_launcher(params)) { mir_free(tmp); return; } if (result == pxNotFound) { mir_free(tmp); return; } if (output.find("--delete-secret-keys") != string::npos) 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); } mir_free(tmp); 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) { db_unset(hcnt, MODULENAME, "KeyID"); db_unset(hcnt, MODULENAME, "GPGPubKey"); db_unset(hcnt, MODULENAME, "KeyMainName"); db_unset(hcnt, MODULENAME, "KeyType"); db_unset(hcnt, MODULENAME, "KeyMainEmail"); db_unset(hcnt, MODULENAME, "KeyComment"); setClistIcon(hcnt); 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"); setClistIcon(hContact); setSrmmIcon(hContact); } } else { db_unset(globals.user_data[item_num + 1], MODULENAME, "KeyID"); db_unset(globals.user_data[item_num + 1], MODULENAME, "GPGPubKey"); db_unset(globals.user_data[item_num + 1], MODULENAME, "KeyMainName"); db_unset(globals.user_data[item_num + 1], MODULENAME, "KeyType"); db_unset(globals.user_data[item_num + 1], MODULENAME, "KeyMainEmail"); db_unset(globals.user_data[item_num + 1], MODULENAME, "KeyComment"); setClistIcon(globals.user_data[item_num + 1]); setSrmmIcon(globals.user_data[item_num + 1]); } } list_USERLIST.SetItemText(item_num, 3, TranslateT("not set")); list_USERLIST.SetItemText(item_num, 2, TranslateT("not set")); list_USERLIST.SetItemText(item_num, 1, TranslateT("not set")); } void onClick_SELECT_KEY(CCtrlButton*) { CDlgFirstRun *d = new CDlgFirstRun; d->Show(); } void onClick_SAVE_KEY_BUTTON(CCtrlButton*) { wchar_t *tmp = GetFilePath(TranslateT("Export public key"), L"*", TranslateT(".asc pubkey file"), true); if (tmp) { wstring str(ptrW(db_get_wsa(globals.user_data[item_num + 1], MODULENAME, "GPGPubKey", L""))); wstring::size_type s = 0; while ((s = str.find(L"\r", s)) != wstring::npos) str.erase(s, 1); wfstream f(tmp, std::ios::out); delete[] tmp; f << str.c_str(); f.close(); } } void onClick_COPY_KEY(CCtrlButton*) { if (OpenClipboard(m_hwnd)) { char *szKey = db_get_sa(0, MODULENAME, "GPGPubKey", ""); std::string str = szKey; mir_free(szKey); boost::algorithm::replace_all(str, "\n", "\r\n"); HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, str.size() + 1); if (!hMem) { MessageBox(nullptr, TranslateT("Failed to allocate memory"), TranslateT("Error"), MB_OK); return; } szKey = (char*)GlobalLock(hMem); if (!szKey) { wchar_t msg[64]; mir_snwprintf(msg, TranslateT("Failed to lock memory with error %d"), GetLastError()); MessageBox(nullptr, msg, TranslateT("Error"), MB_OK); GlobalFree(hMem); } else { memcpy(szKey, str.c_str(), str.size()); szKey[str.size()] = '\0'; str.clear(); EmptyClipboard(); GlobalUnlock(hMem); if (!SetClipboardData(CF_OEMTEXT, hMem)) { GlobalFree(hMem); wchar_t msg[64]; mir_snwprintf(msg, TranslateT("Failed write to clipboard with error %d"), GetLastError()); MessageBox(nullptr, msg, TranslateT("Error"), MB_OK); } CloseClipboard(); } } else { wchar_t msg[64]; mir_snwprintf(msg, TranslateT("Failed to open clipboard with error %d"), GetLastError()); MessageBox(nullptr, msg, TranslateT("Error"), MB_OK); } } 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) { //TODO: get rid of "item_num" if (ev->nmlv) { NMLISTVIEW *hdr = ev->nmlv; if (hdr->iItem == -1) return; void setClistIcon(MCONTACT hContact); void setSrmmIcon(MCONTACT hContact); item_num = hdr->iItem; if (list_USERLIST.GetCheckState(hdr->iItem)) db_set_b(globals.user_data[item_num + 1], MODULENAME, "GPGEncryption", 1); else db_set_b(globals.user_data[item_num + 1], MODULENAME, "GPGEncryption", 0); setClistIcon(globals.user_data[item_num + 1]); setSrmmIcon(globals.user_data[item_num + 1]); } } void onClick_USERLIST(CCtrlListView::TEventInfo *ev) { //TODO: get rid of "item_num" if (ev->nmlv) { NMLISTVIEW *hdr = ev->nmlv; if (hdr->iItem == -1) return; item_num = hdr->iItem; } } }; 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(ptrW(db_get_wsa(0, MODULENAME, "szGpgBinPath", L"gpg.exe"))); edit_HOME_DIR.SetText(ptrW(db_get_wsa(0, MODULENAME, "szHomePath", L"gpg"))); return true; } bool OnApply() override { wchar_t tmp[8192]; g_plugin.setWString("szGpgBinPath", edit_BIN_PATH.GetText()); mir_wstrncpy(tmp, edit_HOME_DIR.GetText(), 8191); while (tmp[mir_wstrlen(tmp) - 1] == '\\') tmp[mir_wstrlen(tmp) - 1] = '\0'; g_plugin.setWString("szHomePath", tmp); return true; } void onClick_SET_BIN_PATH(CCtrlButton*) { GetFilePath(TranslateT("Choose gpg.exe"), "szGpgBinPath", L"*.exe", TranslateT("EXE Executables")); CMStringW tmp(ptrW(db_get_wsa(0, MODULENAME, "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; wchar_t *tmp_path = db_get_wsa(0, MODULENAME, "szGpgBinPath", L""); g_plugin.setWString("szGpgBinPath", tmp); string out; DWORD code; std::vector cmd; cmd.push_back(L"--version"); gpg_execution_params params(cmd); pxResult result; params.out = &out; params.code = &code; params.result = &result; 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); mir_free(tmp_path); string::size_type p1 = out.find("(GnuPG) "); if (p1 != string::npos) { p1 += mir_strlen("(GnuPG) "); if (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); } /* if(bad_version) //looks like working fine with gpg2 MessageBox(0, TranslateT("Unsupported GnuPG version found, use at you own risk!\nIt is recommended that you use GnuPG v1.x.x with this plugin."), L"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(ptrW(db_get_wsa(0, MODULENAME, "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) {} bool OnInitDialog() override { check_APPEND_TAGS.SetState(g_plugin.getByte("bAppendTags", 0)); check_STRIP_TAGS.SetState(g_plugin.getByte("bStripTags", 0)); edit_IN_OPEN_TAG.SetText(ptrW(db_get_wsa(0, MODULENAME, "szInOpenTag", L""))); edit_IN_CLOSE_TAG.SetText(ptrW(db_get_wsa(0, MODULENAME, "szInCloseTag", L""))); edit_OUT_OPEN_TAG.SetText(ptrW(db_get_wsa(0, MODULENAME, "szOutOpenTag", L""))); edit_OUT_CLOSE_TAG.SetText(ptrW(db_get_wsa(0, MODULENAME, "szOutCloseTag", L""))); return true; } bool OnApply() override { g_plugin.setByte("bAppendTags", globals.bAppendTags = check_APPEND_TAGS.GetState()); g_plugin.setByte("bStripTags", globals.bStripTags = check_STRIP_TAGS.GetState()); { wchar_t *tmp = mir_wstrdup(edit_IN_OPEN_TAG.GetText()); g_plugin.setWString("szInOpenTag", tmp); mir_free(globals.inopentag); globals.inopentag = tmp; tmp = mir_wstrdup(edit_IN_CLOSE_TAG.GetText()); g_plugin.setWString("szInCloseTag", tmp); mir_free(globals.inclosetag); globals.inclosetag = tmp; tmp = mir_wstrdup(edit_OUT_OPEN_TAG.GetText()); g_plugin.setWString("szOutOpenTag", tmp); mir_free(globals.outopentag); globals.outopentag = tmp; tmp = mir_wstrdup(edit_OUT_CLOSE_TAG.GetText()); g_plugin.setWString("szOutCloseTag", tmp); mir_free(globals.outclosetag); globals.outclosetag = tmp; } return true; } }; class COptGpgAdvDlg : public CDlgBase { CCtrlButton btn_EXPORT, btn_IMPORT; CCtrlCheck check_PRESCENSE_SUBSCRIPTION; public: COptGpgAdvDlg() : CDlgBase(g_plugin, IDD_OPT_GPG_ADVANCED), btn_EXPORT(this, IDC_EXPORT), btn_IMPORT(this, IDC_IMPORT), check_PRESCENSE_SUBSCRIPTION(this, IDC_PRESCENSE_SUBSCRIPTION) { btn_EXPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_EXPORT); btn_IMPORT.OnClick = Callback(this, &COptGpgAdvDlg::onClick_IMPORT); } bool OnInitDialog() override { check_PRESCENSE_SUBSCRIPTION.SetState(g_plugin.getByte("bPresenceSigning", 0)); check_PRESCENSE_SUBSCRIPTION.Enable(globals.bJabberAPI); return true; } bool OnApply() override { g_plugin.setByte("bPresenceSigning", globals.bPresenceSigning = check_PRESCENSE_SUBSCRIPTION.GetState()); 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() : CDlgBase(g_plugin, IDD_LOAD_PUBLIC_KEY), 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 { hContact = globals.user_data[1]; SetWindowPos(m_hwnd, nullptr, globals.load_key_rect.left, globals.load_key_rect.top, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); 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) { wchar_t *tmp = db_get_wsa(hcnt, MODULENAME, "GPGPubKey", L""); wstring str = tmp; mir_free(tmp); tmp = nullptr; 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; } } } // char *tmp = db_get_wsa(hcnt, MODULENAME, "KeyID_Prescense", ""); if (!globals.hcontact_data[hcnt].key_in_prescense.empty()) { char *tmp2 = db_get_sa(hcnt, MODULENAME, "KeyID", ""); if (!tmp2[0]) { string out; DWORD code; std::vector cmd; cmd.push_back(L"--export"); cmd.push_back(L"-a"); cmd.push_back(toUTF16(globals.hcontact_data[hcnt].key_in_prescense)); gpg_execution_params params(cmd); pxResult result; params.out = &out; params.code = &code; params.result = &result; gpg_launcher(params); //TODO: handle errors if ((out.find("-----BEGIN PGP PUBLIC KEY BLOCK-----") != string::npos) && (out.find("-----END PGP PUBLIC KEY BLOCK-----") != string::npos)) { boost::algorithm::replace_all(out, "\n", "\r\n"); wchar_t *tmp3 = mir_a2u(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(); } } mir_free(tmp2); } if (tmp) mir_free(tmp); edit_PUBLIC_KEY_EDIT.SetText(!str.empty() ? str.c_str() : L""); } edit_p_PubKeyEdit = &edit_PUBLIC_KEY_EDIT; return true; } virtual void OnDestroy() override { GetWindowRect(m_hwnd, &globals.load_key_rect); g_plugin.setDword("LoadKeyWindowX", globals.load_key_rect.left); g_plugin.setDword("LoadKeyWindowY", globals.load_key_rect.top); edit_p_PubKeyEdit = nullptr; } void onClick_SELECT_EXISTING(CCtrlButton*) { CDlgLoadExistingKey *d = new CDlgLoadExistingKey; d->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) db_set_ws(hcnt, MODULENAME, "GPGPubKey", key_buf.substr(ws1, ws2 - ws1).c_str()); } } else db_set_ws(metaGetMostOnline(hContact), MODULENAME, "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; wchar_t tmp2[MAX_PATH] = { 0 }; wchar_t *ptmp; string output; DWORD exitcode; { MCONTACT hcnt = db_mc_tryMeta(hContact); ptmp = db_get_wsa(0, MODULENAME, "szHomePath", L""); wcsncpy(tmp2, ptmp, MAX_PATH - 1); mir_free(ptmp); mir_wstrncat(tmp2, L"\\", _countof(tmp2) - mir_wstrlen(tmp2)); mir_wstrncat(tmp2, L"temporary_exported.asc", _countof(tmp2) - mir_wstrlen(tmp2)); boost::filesystem::remove(tmp2); wfstream f(tmp2, std::ios::out); ptmp = db_get_wsa(hcnt, MODULENAME, "GPGPubKey", L""); wstring str = ptmp; mir_free(ptmp); wstring::size_type s = 0; while ((s = str.find(L"\r", s)) != wstring::npos) { str.erase(s, 1); } f << str.c_str(); f.close(); cmd.push_back(L"--batch"); cmd.push_back(L"--import"); cmd.push_back(tmp2); } gpg_execution_params params(cmd); pxResult result; params.out = &output; params.code = &exitcode; params.result = &result; if (!gpg_launcher(params)) return; if (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) db_unset(hcnt, MODULENAME, "bAlwatsTrust"); } } else db_unset(metaGetMostOnline(hContact), MODULENAME, "bAlwatsTrust"); } else g_plugin.delSetting(hContact, "bAlwatsTrust"); } { 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); 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) db_set_s(hcnt, MODULENAME, "KeyID", tmp3); } } else db_set_s(metaGetMostOnline(hContact), MODULENAME, "KeyID", tmp3); } else g_plugin.setString(hContact, "KeyID", tmp3); } mir_free(tmp3); } tmp = mir_wstrdup(toUTF16(output.substr(s, s2 - s)).c_str()); if (hContact && hwndList_p) ListView_SetItemText(hwndList_p, item_num, 1, tmp); mir_free(tmp); 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); } tmp = mir_wstrdup(toUTF16(output.substr(s, s2 - s - 1)).c_str()); if (hContact && hwndList_p) ListView_SetItemText(hwndList_p, item_num, 2, tmp); //TODO: do something with this 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] == ')') { 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); tmp = mir_wstrdup(toUTF16(output.substr(s, s2 - s)).c_str()); if (hContact && hwndList_p) ListView_SetItemText(hwndList_p, item_num, 3, tmp); mir_free(tmp); } 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); tmp = mir_wstrdup(toUTF16(output.substr(s2, s - s2)).c_str()); if (hContact && hwndList_p) ListView_SetItemText(hwndList_p, item_num, 3, tmp); //TODO: do something with this mir_free(tmp); } } } if (hContact && hwndList_p) //TODO: do something with this { ListView_SetColumnWidth(hwndList_p, 0, LVSCW_AUTOSIZE); ListView_SetColumnWidth(hwndList_p, 1, LVSCW_AUTOSIZE); ListView_SetColumnWidth(hwndList_p, 2, LVSCW_AUTOSIZE); ListView_SetColumnWidth(hwndList_p, 3, LVSCW_AUTOSIZE); } } if (!hContact) { wchar_t *fp = db_get_wsa(hContact, MODULENAME, "KeyID", L""); { string out; DWORD code; std::vector cmds; cmds.push_back(L"--batch"); cmds.push_back(L"-a"); cmds.push_back(L"--export"); cmds.push_back(fp); mir_free(fp); gpg_execution_params params2(cmds); pxResult result2; params2.out = &out; params2.code = &code; params2.result = &result2; if (!gpg_launcher(params2)) return; if (result2 == pxNotFound) return; string::size_type s = 0; while ((s = out.find("\r", s)) != string::npos) { out.erase(s, 1); } g_plugin.setString(hContact, "GPGPubKey", out.c_str()); } } tmp = mir_wstrdup(toUTF16(output).c_str()); MessageBox(nullptr, tmp, L"", MB_OK); mir_free(tmp); boost::filesystem::remove(tmp2); } 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) { if (!isContactSecured(hcnt)) db_set_b(hcnt, MODULENAME, "GPGEncryption", 1); else db_set_b(hcnt, MODULENAME, "GPGEncryption", 0); setSrmmIcon(hContact); setClistIcon(hContact); } } } else if (!isContactSecured(hContact)) db_set_b(metaGetMostOnline(hContact), MODULENAME, "GPGEncryption", 1); else db_set_b(metaGetMostOnline(hContact), MODULENAME, "GPGEncryption", 0); } else if (!isContactSecured(hContact)) g_plugin.setByte(hContact, "GPGEncryption", 1); else g_plugin.setByte(hContact, "GPGEncryption", 0); } } this->Close(); } void onClick_LOAD_FROM_FILE(CCtrlButton*) { wchar_t *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); delete[] tmp; 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.bDebugLog) globals.debuglog << std::string(time_str() + ": 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(bool modal) { CDlgLoadPubKeyDlg *d = new CDlgLoadPubKeyDlg(); if (modal) 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; }