// Copyright © 2010-2012 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" wstring new_key; MCONTACT new_key_hcnt = NULL; boost::mutex new_key_hcnt_mutex; bool _terminate = false; int returnNoError(MCONTACT hContact); std::list sent_msgs; void RecvMsgSvc_func(MCONTACT hContact, std::wstring str, char *msg, DWORD, DWORD timestamp) { DWORD dbflags = DBEF_UTF; { // check for gpg related data wstring::size_type s1 = str.find(L"-----BEGIN PGP MESSAGE-----"); wstring::size_type s2 = str.find(L"-----END PGP MESSAGE-----"); if (s2 != wstring::npos && s1 != wstring::npos) { //this is generic encrypted data block if (!isContactSecured(hContact)) { if (bDebugLog) debuglog << std::string(time_str() + ": info: received encrypted message from: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0)) + " 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)) { void ShowLoadPublicKeyDialog(bool = false); item_num = 0; //black magic here user_data[1] = hContact; ShowLoadPublicKeyDialog(true); } else { db_set_b(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, szGPGModuleName, "GPGEncryption", 1); setSrmmIcon(hContact); setClistIcon(hContact); } if (isContactHaveKey(hContact)) { db_set_b(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, szGPGModuleName, "GPGEncryption", 1); setSrmmIcon(hContact); setClistIcon(hContact); } } else if (MessageBox(nullptr, TranslateT("Do you want to try to decrypt encrypted message?"), TranslateT("Warning"), MB_YESNO) == IDNO) { HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); return; } } else if (bDebugLog) debuglog << std::string(time_str() + ": info: received encrypted message from: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); boost::algorithm::erase_all(str, "\r"); s2 += mir_wstrlen(L"-----END PGP MESSAGE-----"); ptrW ptszHomePath(UniGetContactSettingUtf(NULL, szGPGModuleName, "szHomePath", L"")); wstring encfile = toUTF16(get_random(10)); wstring decfile = toUTF16(get_random(10)); { wstring path = wstring(ptszHomePath) + L"\\tmp\\" + encfile; if(!bDebugLog) { 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()) { boost::this_thread::sleep(boost::posix_time::milliseconds(step)); count += step; if(count >= timeout) { db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); setSrmmIcon(hContact); setClistIcon(hContact); debuglog< cmd; cmd.push_back(L"--batch"); { char *inkeyid = UniGetContactSettingUtf(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, szGPGModuleName, "InKeyID", ""); wchar_t *pass = nullptr; if (inkeyid[0]) { string dbsetting = "szKey_"; dbsetting += inkeyid; dbsetting += "_Password"; pass = UniGetContactSettingUtf(NULL, szGPGModuleName, dbsetting.c_str(), L""); if (pass[0] && bDebugLog) debuglog << std::string(time_str() + ": info: found password in database for key ID: " + inkeyid + ", trying to decrypt message from " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0)) + " with password"); } else { pass = UniGetContactSettingUtf(NULL, szGPGModuleName, "szKeyPassword", L""); if (pass[0] && bDebugLog) debuglog << std::string(time_str() + ": info: found password for all keys in database, trying to decrypt message from " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0)) + " with password"); } if (pass && pass[0]) { cmd.push_back(L"--passphrase"); cmd.push_back(pass); } else if (password && password[0]) { if (bDebugLog) debuglog << std::string(time_str() + ": info: found password in memory, trying to decrypt message from " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0)) + " with password"); cmd.push_back(L"--passphrase"); cmd.push_back(password); } else if (bDebugLog) debuglog << std::string(time_str() + ": info: passwords not found in database or memory, trying to decrypt message from " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0)) + " with out password"); mir_free(pass); mir_free(inkeyid); } if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + decfile, e); } cmd.push_back(L"--output"); cmd.push_back(std::wstring(ptszHomePath) + L"\\tmp\\" + decfile); cmd.push_back(L"-d"); cmd.push_back(L"-a"); cmd.push_back(path); gpg_execution_params params(cmd); pxResult result; params.out = &out; params.code = &code; params.result = &result; if (!gpg_launcher(params)) { if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); BYTE enc = db_get_b(hContact, szGPGModuleName, "GPGEncryption", 0); db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); HistoryLog(hContact, db_event("Error message sent", 0, 0, DBEF_SENT)); db_set_b(hContact, szGPGModuleName, "GPGEncryption", enc); return; } if (result == pxNotFound) { if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); return; } /* if (result == pxSuccessExitCodeInvalid) //sometime we have invalid return code after succesful decryption, this should be non-fatal at least { if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); HistoryLog(hContact, db_event(Translate("failed to decrypt message, GPG returned error, turn on debug log for more details"), timestamp, 0, 0)); return; } */ //TODO: check gpg output for errors _terminate = false; while (out.find("public key decryption failed: bad passphrase") != string::npos) { if (bDebugLog) debuglog << std::string(time_str() + ": info: failed to decrypt messaage from " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0)) + " password needed, trying to get one"); if (_terminate) { BYTE enc = db_get_b(hContact, szGPGModuleName, "GPGEncryption", 0); db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); HistoryLog(hContact, db_event("Error message sent", 0, 0, DBEF_SENT)); db_set_b(hContact, szGPGModuleName, "GPGEncryption", enc); break; } { //save inkey id string::size_type s = out.find(" encrypted with "); s = out.find(" ID ", s); s += mir_strlen(" ID "); db_set_s(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, szGPGModuleName, "InKeyID", out.substr(s, out.find(",", s) - s).c_str()); } void ShowLoadKeyPasswordWindow(); new_key_hcnt_mutex.lock(); new_key_hcnt = hContact; ShowLoadKeyPasswordWindow(); std::vector cmd2 = cmd; if (password) { if (bDebugLog) debuglog << std::string(time_str() + ": info: found password in memory, trying to decrypt message from " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); std::vector tmp3; tmp3.push_back(L"--passphrase"); tmp3.push_back(password); cmd2.insert(cmd2.begin(), tmp3.begin(), tmp3.end()); } out.clear(); gpg_execution_params params2(cmd2); pxResult result2; params2.out = &out; params2.code = &code; params2.result = &result2; if (!gpg_launcher(params2)) { if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); BYTE enc = db_get_b(hContact, szGPGModuleName, "GPGEncryption", 0); db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); HistoryLog(hContact, db_event("Error message sent", 0, 0, DBEF_SENT)); db_set_b(hContact, szGPGModuleName, "GPGEncryption", enc); return; } if (result2 == pxNotFound) { if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); return; } } out.clear(); if (!gpg_launcher(params)) { if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); BYTE enc = db_get_b(hContact, szGPGModuleName, "GPGEncryption", 0); db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); HistoryLog(hContact, db_event("Error message sent", 0, 0, DBEF_SENT)); db_set_b(hContact, szGPGModuleName, "GPGEncryption", enc); return; } if (result == pxNotFound) { if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); } if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(wstring(ptszHomePath) + L"\\tmp\\" + encfile, e); } if (!boost::filesystem::exists(wstring(ptszHomePath) + L"\\tmp\\" + decfile)) { string str1 = msg; str1.insert(0, "Received unencrypted message:\n"); if (bDebugLog) debuglog << std::string(time_str() + ": info: Failed to decrypt GPG encrypted message."); ptrA tmp4((char*)mir_alloc(sizeof(char)*(str1.length() + 1))); mir_strcpy(tmp4, str1.c_str()); HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); BYTE enc = db_get_b(hContact, szGPGModuleName, "GPGEncryption", 0); db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); HistoryLog(hContact, db_event("Error message sent", 0, 0, DBEF_SENT)); db_set_b(hContact, szGPGModuleName, "GPGEncryption", enc); return; } str.clear(); 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'; toUTF16(tmp); str.append(toUTF16(tmp)); delete[] tmp; f.close(); if(!bDebugLog) { boost::system::error_code ec; boost::filesystem::remove(tszDecPath, ec); if(ec) { //TODO: handle error } } } } if (str.empty()) { string szMsg = msg; szMsg.insert(0, "Failed to decrypt GPG encrypted message.\nMessage body for manual decryption:\n"); if (bDebugLog) debuglog << std::string(time_str() + ": info: Failed to decrypt GPG encrypted message."); HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); BYTE enc = db_get_b(hContact, szGPGModuleName, "GPGEncryption", 0); db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"Unable to decrypt PGP encrypted message"); HistoryLog(hContact, db_event("Error message sent", 0, 0, DBEF_SENT)); db_set_b(hContact, szGPGModuleName, "GPGEncryption", enc); return; } fix_line_term(str); if (bAppendTags) { str.insert(0, inopentag); str.append(inclosetag); } char *tmp = mir_strdup(toUTF8(str).c_str()); HistoryLog(hContact, db_event(tmp, timestamp, 0, dbflags)); mir_free(tmp); return; } } } if (db_get_b(db_mc_isMeta(hContact) ? metaGetMostOnline(hContact) : hContact, szGPGModuleName, "GPGEncryption", 0)) { HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags | DBEF_READ)); return; } HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); return; } 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); DWORD dbflags = DBEF_UTF; if (db_mc_isMeta(ccs->hContact)) { if (!strstr(msg, "-----BEGIN PGP MESSAGE-----")) return Proto_ChainRecv(w, ccs); else { if (bDebugLog) debuglog << std::string(time_str() + ": info: blocked pgp message to metacontact:" + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); return 0; } } wstring str = toUTF16(msg); size_t s1, s2; if (bAutoExchange && (str.find(L"-----PGP KEY RESPONSE-----") != wstring::npos)) { if (bDebugLog) debuglog << std::string(time_str() + ": info(autoexchange): parsing key response:" + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); 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 (bDebugLog) debuglog << std::string(time_str() + ": info(autoexchange): found pubkey block:" + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); s2 += mir_wstrlen(L"-----END PGP PUBLIC KEY BLOCK-----"); db_set_ws(ccs->hContact, szGPGModuleName, "GPGPubKey", str.substr(s1, s2 - s1).c_str()); { //gpg execute block std::vector cmd; wchar_t tmp2[MAX_PATH] = { 0 }; string output; DWORD exitcode; { ptrW ptmp(UniGetContactSettingUtf(NULL, szGPGModuleName, "szHomePath", L"")); mir_wstrcpy(tmp2, ptmp); mir_free(ptmp); mir_wstrcat(tmp2, L"\\"); wchar_t *tmp3 = mir_a2u(get_random(5).c_str()); mir_wstrcat(tmp2, tmp3); mir_wstrcat(tmp2, L".asc"); mir_free(tmp3); //mir_wstrcat(tmp2, L"temporary_exported.asc"); if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(tmp2, e); } wfstream f(tmp2, std::ios::out); { const int timeout = 5000, step = 100; int count = 0; while (!f.is_open()) { boost::this_thread::sleep(boost::posix_time::milliseconds(step)); count += step; if(count >= timeout) { db_set_b(ccs->hContact, szGPGModuleName, "GPGEncryption", 0); setSrmmIcon(ccs->hContact); setClistIcon(ccs->hContact); debuglog<hContact, szGPGModuleName, "GPGPubKey", L""); f << (wchar_t*)ptmp; 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 1; if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(tmp2, e); } if (result == pxNotFound) return 1; /* if (result == pxSuccessExitCodeInvalid) //sometime we have invalid return code after succesful decryption, this should be non-fatal at least { HistoryLog(ccs->hContact, db_event(Translate("failed to decrypt message, GPG returned error, turn on debug log for more details"))); return 1; } */ { char *tmp = nullptr; s1 = output.find("gpg: key ") + mir_strlen("gpg: key "); s2 = output.find(":", s1); db_set_s(ccs->hContact, szGPGModuleName, "KeyID", output.substr(s1, s2 - s1).c_str()); s2 += 2; s1 = output.find("“", s2); if (s1 == string::npos) { s1 = output.find("\"", s2); s1 += 1; } else s1 += 3; if ((s2 = output.find("(", s1)) == string::npos) s2 = output.find("<", s1); else if (s2 > output.find("<", s1)) s2 = output.find("<", s1); 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); db_set_s(ccs->hContact, szGPGModuleName, "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); db_set_s(ccs->hContact, szGPGModuleName, "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); db_set_s(ccs->hContact, szGPGModuleName, "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); db_set_s(ccs->hContact, szGPGModuleName, "KeyMainEmail", output.substr(s2, s1 - s2).c_str()); mir_free(tmp); } db_set_b(ccs->hContact, szGPGModuleName, "GPGEncryption", 1); db_set_b(ccs->hContact, szGPGModuleName, "bAlwatsTrust", 1); setSrmmIcon(ccs->hContact); setClistIcon(ccs->hContact); if (db_mc_isSub(ccs->hContact)) { setSrmmIcon(db_mc_getMeta(ccs->hContact)); setClistIcon(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 (bDebugLog) debuglog << std::string(time_str() + ": info: received key from: " + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); s1 = 0; while ((s1 = str.find(L"\r", s1)) != wstring::npos) str.erase(s1, 1); void ShowNewKeyDialog(); 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-----"); new_key.append(str.substr(s1, s2 - s1)); //new_key_hcnt_mutex.lock(); new_key_hcnt = ccs->hContact; ShowNewKeyDialog(); HistoryLog(ccs->hContact, db_event(msg, 0, 0, dbflags)); return 0; } if (bAutoExchange && strstr(msg, "-----PGP KEY REQUEST-----") && gpg_valid && gpg_keyexist) { if (bDebugLog) debuglog << std::string(time_str() + ": info(autoexchange): received key request from: " + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); ptrA tmp(UniGetContactSettingUtf(NULL, szGPGModuleName, "GPGPubKey", "")); if (tmp[0]) { int enc_state = db_get_b(ccs->hContact, szGPGModuleName, "GPGEncryption", 0); if (enc_state) db_set_b(ccs->hContact, szGPGModuleName, "GPGEncryption", 0); string str1 = "-----PGP KEY RESPONSE-----"; str1.append(tmp); ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)str1.c_str()); if (enc_state) db_set_b(ccs->hContact, szGPGModuleName, "GPGEncryption", 1); } return 0; } else if (!isContactHaveKey(ccs->hContact) && bAutoExchange && gpg_valid && gpg_keyexist) { char *proto = GetContactProto(ccs->hContact); DWORD uin = db_get_dw(ccs->hContact, proto, "UIN", 0); if (uin) { if (ProtoServiceExists(proto, PS_ICQ_CHECKCAPABILITY)) { ICQ_CUSTOMCAP cap = { 0 }; strncpy(cap.caps, "GPGAutoExchange", sizeof(cap.caps)); if (CallProtoService(proto, PS_ICQ_CHECKCAPABILITY, (WPARAM)ccs->hContact, (LPARAM)&cap)) { ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); return 0; } } } else { wchar_t *jid = UniGetContactSettingUtf(ccs->hContact, proto, "jid", L""); if (jid[0]) { extern list Accounts; list::iterator end = Accounts.end(); for (list::iterator p = Accounts.begin(); p != end; p++) { wchar_t *caps = (*p)->getJabberInterface()->GetResourceFeatures(jid); if (caps) { wstring str1; for (int i = 0;; i++) { str1.push_back(caps[i]); if (caps[i] == '\0') if (caps[i + 1] == '\0') break; } mir_free(caps); if (str1.find(L"GPG_Key_Auto_Exchange:0") != string::npos) { ProtoChainSend(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); return 0; } } } } mir_free(jid); } } if (!strstr(msg, "-----BEGIN PGP MESSAGE-----")) return Proto_ChainRecv(w, ccs); new boost::thread(boost::bind(RecvMsgSvc_func, ccs->hContact, str, msg, (DWORD)ccs->wParam, pre->timestamp)); return 0; } void SendMsgSvc_func(MCONTACT hContact, char *msg, DWORD flags) { wstring str = toUTF16(msg); if (bStripTags && bAppendTags) { if (bDebugLog) debuglog << std::string(time_str() + ": info: stripping tags in outgoing message, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); strip_tags(str); } /* for(std::wstring::size_type i = str.find(L"\r\n"); i != std::wstring::npos; i = str.find(L"\r\n", i+1)) str.replace(i, 2, L"\n"); */ string out; DWORD code; wstring file = toUTF16(get_random(10)), path; std::vector cmd; extern bool bJabberAPI; { wchar_t *tmp2; { char *tmp = UniGetContactSettingUtf(hContact, szGPGModuleName, "KeyID", ""); if (!tmp[0]) { mir_free(tmp); HistoryLog(hContact, db_event("Failed to encrypt message with GPG (not found key for encryption in db)", 0, 0, DBEF_SENT)); ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } if (!bJabberAPI) //force jabber to handle encrypted message by itself { cmd.push_back(L"--comment"); cmd.push_back(L"\"\""); cmd.push_back(L"--no-version"); } if (db_get_b(hContact, szGPGModuleName, "bAlwaysTrust", 0)) { cmd.push_back(L"--trust-model"); cmd.push_back(L"always"); } cmd.push_back(L"--batch"); cmd.push_back(L"--yes"); cmd.push_back(L"-eatr"); tmp2 = mir_a2u(tmp); mir_free(tmp); } cmd.push_back(tmp2); mir_free(tmp2); } { wchar_t *tmp2 = UniGetContactSettingUtf(NULL, szGPGModuleName, "szHomePath", L""); path = tmp2; cmd.push_back(std::wstring(tmp2) + L"\\tmp\\" + file); mir_free(tmp2); } path += L"\\tmp\\"; path += file; const int timeout = 5000, step = 100; int count = 0; { fstream f(path.c_str(), std::ios::out); while (!f.is_open()) { boost::this_thread::sleep(boost::posix_time::milliseconds(step)); count += step; if(count >= timeout) { db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); //disable encryption setSrmmIcon(hContact); setClistIcon(hContact); debuglog << std::string(time_str() + ": 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) { std::string tmp = toUTF8(str); f.write(tmp.c_str(), tmp.size()); f.close(); } } pxResult result; { gpg_execution_params params(cmd); params.out = &out; params.code = &code; params.result = &result; if (!gpg_launcher(params)) { //mir_free(msg); ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } if (result == pxNotFound) { //mir_free(msg); ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } } if (out.find("There is no assurance this key belongs to the named user") != string::npos) { out.clear(); if (MessageBox(nullptr, TranslateT("We're trying to encrypt with untrusted key. Do you want to trust this key permanently?"), TranslateT("Warning"), MB_YESNO) == IDYES) { db_set_b(hContact, szGPGModuleName, "bAlwaysTrust", 1); std::vector tmp; tmp.push_back(L"--trust-model"); tmp.push_back(L"always"); cmd.insert(cmd.begin(), tmp.begin(), tmp.end()); gpg_execution_params params(cmd); params.out = &out; params.code = &code; params.result = &result; if (!gpg_launcher(params)) { ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } if (result == pxNotFound) { ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } //TODO: check gpg output for errors } else return; } // if (result == pxSuccessExitCodeInvalid) { //sometims gpg return error after succesful operation, this should be non-fatal at least //mir_free(msg); // HistoryLog(hContact, db_event(Translate("failed to encrypt message, GPG returned error, turn on debug log for more details"), 0, 0, DBEF_SENT)); // if(!bDebugLog) // { // boost::system::error_code e; // boost::filesystem::remove(path, e); // } // return; // } if (out.find("usage: ") != string::npos) { 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(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } return; } if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } path.append(L".asc"); wfstream f(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); count = 0; while (!f.is_open()) { boost::this_thread::sleep(boost::posix_time::milliseconds(step)); f.open(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); count += step; if(count >= timeout) { db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); //disable encryption setSrmmIcon(hContact); setClistIcon(hContact); debuglog << std::string(time_str() + ": info: gpg failed to encrypt message, disabling encryption to avoid deadlock"); break; } } str.clear(); 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'; str.append(tmp); delete[] tmp; f.close(); if(!bDebugLog) { boost::system::error_code e; boost::filesystem::remove(path, e); } } if (str.empty()) { HistoryLog(hContact, db_event("Failed to encrypt message with GPG", 0, 0, DBEF_SENT)); if (bDebugLog) debuglog << std::string(time_str() + ": info: Failed to encrypt message with GPG"); ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } string str_event = msg; if (bAppendTags) { str_event.insert(0, toUTF8(outopentag)); str_event.append(toUTF8(outclosetag)); } if (bDebugLog) debuglog << std::string(time_str() + ": adding event to contact: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0)) + " on send message."); fix_line_term(str); sent_msgs.push_back((HANDLE)ProtoChainSend(hContact, PSS_MESSAGE, flags, (LPARAM)toUTF8(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); char *msg = (char*)ccs->lParam; if (!msg) { if (bDebugLog) debuglog << std::string(time_str() + ": info: failed to get message data, name: " + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); return Proto_ChainSend(w, ccs); } if (strstr(msg, "-----BEGIN PGP MESSAGE-----")) { if (bDebugLog) debuglog << std::string(time_str() + ": info: encrypted messge, let it go, name: " + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); return Proto_ChainSend(w, ccs); } if (bDebugLog) debuglog << std::string(time_str() + ": info: contact have key, name: " + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); if (bDebugLog && db_mc_isMeta(ccs->hContact)) debuglog << std::string(time_str() + ": info: protocol is metacontacts, name: " + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); if (!isContactSecured(ccs->hContact) || db_mc_isMeta(ccs->hContact)) { if (bDebugLog) debuglog << std::string(time_str() + ": info: contact not secured, name: " + toUTF8(pcli->pfnGetContactDisplayName(ccs->hContact, 0))); return Proto_ChainSend(w, ccs); } return returnNoError(ccs->hContact); } boost::mutex event_processing_mutex; int HookSendMsg(WPARAM w, LPARAM l) { if (!l) return 0; DBEVENTINFO * dbei = (DBEVENTINFO*)l; if (dbei->eventType != EVENTTYPE_MESSAGE) return 0; MCONTACT hContact = (MCONTACT)w; 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 (bDebugLog) debuglog << std::string(time_str() + ": info(send handler): block pgp message event, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); return 1; } if (bAutoExchange && (strstr((char*)dbei->pBlob, "-----PGP KEY RESPONSE-----") || strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----"))) ///do not show service data in history { if (bDebugLog) debuglog << std::string(time_str() + ": info(send handler): block pgp key request/response event, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); return 1; } } if (db_mc_isMeta(hContact)) return 0; if (!isContactHaveKey(hContact)) { if (bDebugLog) debuglog << std::string(time_str() + ": info: contact have not key, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); if (bAutoExchange && !strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----") && !strstr((char*)dbei->pBlob, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && gpg_valid) { if (bDebugLog) debuglog << std::string(time_str() + ": info: checking for autoexchange possibility, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); LPSTR proto = GetContactProto(hContact); DWORD uin = db_get_dw(hContact, proto, "UIN", 0); if (uin) { if (bDebugLog) debuglog << std::string(time_str() + ": info(autoexchange): protocol looks like icq, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); char *proto2 = GetContactProto(hContact); if (ProtoServiceExists(proto2, PS_ICQ_CHECKCAPABILITY)) { if (bDebugLog) debuglog << std::string(time_str() + ": info(autoexchange, icq): checking for autoexchange icq capability, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); ICQ_CUSTOMCAP cap = { 0 }; strncpy(cap.caps, "GPGAutoExchange", sizeof(cap.caps)); if (CallProtoService(proto2, PS_ICQ_CHECKCAPABILITY, hContact, (LPARAM)&cap)) { if (bDebugLog) debuglog << std::string(time_str() + ": info(autoexchange, icq): sending key requiest, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); hcontact_data[hContact].msgs_to_send.push_back((char*)dbei->pBlob); new boost::thread(boost::bind(send_encrypted_msgs_thread, (void*)hContact)); return 0; } } } else { wchar_t *jid = UniGetContactSettingUtf(hContact, proto, "jid", L""); if (jid[0]) { if (bDebugLog) debuglog << std::string(time_str() + ": info(autoexchange): protocol looks like jabber, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); extern list Accounts; list::iterator end = Accounts.end(); for (list::iterator p = Accounts.begin(); p != end; p++) { wchar_t *caps = (*p)->getJabberInterface()->GetResourceFeatures(jid); if (caps) { wstring str; for (int i = 0;; i++) { str.push_back(caps[i]); if (caps[i] == '\0') if (caps[i + 1] == '\0') break; } mir_free(caps); if (str.find(L"GPG_Key_Auto_Exchange:0") != string::npos) { if (bDebugLog) debuglog << std::string(time_str() + ": info(autoexchange, jabber): autoexchange capability found, sending key request, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); ProtoChainSend(hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); hcontact_data[hContact].msgs_to_send.push_back((char*)dbei->pBlob); new boost::thread(boost::bind(send_encrypted_msgs_thread, (void*)hContact)); return 0; } } } } mir_free(jid); } } 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 (bAppendTags) { string str_event = (char*)dbei->pBlob; //mir_free(dbei->pBlob); str_event.insert(0, toUTF8(outopentag)); str_event.append(toUTF8(outclosetag)); dbei->pBlob = (PBYTE)mir_strdup(str_event.c_str()); dbei->cbBlob = (DWORD)str_event.length() + 1; } return 0; } if (!isContactSecured(hContact)) { if (bDebugLog) debuglog << std::string(time_str() + ": event message: \"" + (char*)dbei->pBlob + "\" passed event filter, contact " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0)) + " 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 (bDebugLog) debuglog << std::string(time_str() + ": info(send handler): block pgp message event, name: " + toUTF8(pcli->pfnGetContactDisplayName(hContact, 0))); return 1; } } return 0; } extern HINSTANCE hInst; class CDlgKeyPasswordMsgBox : public CDlgBase //always modal { public: CDlgKeyPasswordMsgBox() : CDlgBase(hInst, 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), btn_OK(this, IDOK), btn_CANCEL(this, IDCANCEL) { btn_OK.OnClick = Callback(this, &CDlgKeyPasswordMsgBox::onClick_OK); btn_CANCEL.OnClick = Callback(this, &CDlgKeyPasswordMsgBox::onClick_CANCEL); } virtual void OnInitDialog() override { inkeyid = UniGetContactSettingUtf(new_key_hcnt, szGPGModuleName, "InKeyID", ""); new_key_hcnt_mutex.unlock(); SetWindowPos(m_hwnd, nullptr, key_password_rect.left, key_password_rect.top, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); { string questionstr = "Please enter password for key with ID: "; questionstr += inkeyid; mir_free(inkeyid); lbl_KEYID.SetTextA(questionstr.c_str()); chk_DEFAULT_PASSWORD.Disable(); } } virtual void OnClose() override { DestroyWindow(m_hwnd); } virtual void OnDestroy() override { mir_free(inkeyid); GetWindowRect(m_hwnd, &key_password_rect); db_set_dw(NULL, szGPGModuleName, "PasswordWindowX", key_password_rect.left); db_set_dw(NULL, szGPGModuleName, "PasswordWindowY", key_password_rect.top); delete this; } void onClick_OK(CCtrlButton*) { wchar_t *tmp = mir_wstrdup(edit_KEY_PASSWORD.GetText()); if (tmp && tmp[0]) { extern wchar_t *password; if (chk_SAVE_PASSWORD.GetState()) { inkeyid = UniGetContactSettingUtf(new_key_hcnt, szGPGModuleName, "InKeyID", ""); if (inkeyid && inkeyid[0] && !chk_DEFAULT_PASSWORD.GetState()) { string dbsetting = "szKey_"; dbsetting += inkeyid; dbsetting += "_Password"; db_set_ws(NULL, szGPGModuleName, dbsetting.c_str(), tmp); } else db_set_ws(NULL, szGPGModuleName, "szKeyPassword", tmp); } if (password) mir_free(password); password = (wchar_t*)mir_alloc(sizeof(wchar_t)*(mir_wstrlen(tmp) + 1)); mir_wstrcpy(password, tmp); } mir_free(inkeyid); DestroyWindow(m_hwnd); } void onClick_CANCEL(CCtrlButton*) { _terminate = true; DestroyWindow(m_hwnd); } private: char *inkeyid = nullptr; CCtrlData lbl_KEYID; CCtrlEdit edit_KEY_PASSWORD; CCtrlCheck chk_DEFAULT_PASSWORD, chk_SAVE_PASSWORD; CCtrlButton btn_OK, btn_CANCEL; }; void ShowLoadKeyPasswordWindow() { CDlgKeyPasswordMsgBox *d = new CDlgKeyPasswordMsgBox; d->DoModal(); }