// 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 "commonheaders.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 flags, DWORD timestamp) { DWORD dbflags = DBEF_UTF; { //check for gpg related data wstring::size_type s1 = wstring::npos, s2 = wstring::npos; s1 = str.find(_T("-----BEGIN PGP MESSAGE-----")); s2 = str.find(_T("-----END PGP MESSAGE-----")); if((s2 != wstring::npos) && (s1 != wstring::npos)) { //this is generic encrypted data block if(!isContactSecured(hContact)) { if(bDebugLog) debuglog< cmd; cmd.push_back(L"--batch"); { char *inkeyid = UniGetContactSettingUtf(db_mc_isMeta(hContact)?metaGetMostOnline(hContact):hContact, szGPGModuleName, "InKeyID", ""); TCHAR *pass = NULL; if(inkeyid[0]) { string dbsetting = "szKey_"; dbsetting += inkeyid; dbsetting += "_Password"; pass = UniGetContactSettingUtf(NULL, szGPGModuleName, dbsetting.c_str(), _T("")); if(pass[0] && bDebugLog) debuglog< cmd2 = cmd; if(password) { if(bDebugLog) debuglog< tmp; tmp.push_back(L"--passphrase"); tmp.push_back(password); cmd2.insert(cmd2.begin(), tmp.begin(), tmp.end()); } out.clear(); gpg_execution_params params(cmd2); pxResult result; params.out = &out; params.code = &code; params.result = &result; if(!gpg_launcher(params)) { boost::filesystem::remove(path); HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); BYTE enc = db_get_b(hContact, szGPGModuleName, "GPGEncryption", 0); db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); CallContactService(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) { boost::filesystem::remove(path); HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); return; } } out.clear(); if(!gpg_launcher(params)) { boost::filesystem::remove(path); HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); BYTE enc = db_get_b(hContact, szGPGModuleName, "GPGEncryption", 0); db_set_b(hContact, szGPGModuleName, "GPGEncryption", 0); CallContactService(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) { boost::filesystem::remove(path); HistoryLog(hContact, db_event(msg, timestamp, 0, dbflags)); } { wstring tmp = tmp2; tmp += _T("\\tmp\\"); tmp += encfile; boost::filesystem::remove(tmp); } { wstring tmp = tmp2; tmp += _T("\\tmp\\"); tmp += decfile; if(!boost::filesystem::exists(tmp)) { string str = msg; str.insert(0, "Received unencrypted message:\n"); if(bDebugLog) debuglog<lParam); if (!pre) return CallService(MS_PROTO_CHAINRECV, w, l); char *msg = pre->szMessage; if (!msg) return CallService(MS_PROTO_CHAINRECV, w, l); DWORD dbflags = DBEF_UTF; if(db_mc_isMeta(ccs->hContact)) { if(!strstr(msg, "-----BEGIN PGP MESSAGE-----")) return CallService(MS_PROTO_CHAINRECV, w, l); else { if(bDebugLog) debuglog<hContact, GCDNF_TCHAR))); return 0; } } wstring str = toUTF16(msg); wstring::size_type s1 = wstring::npos, s2 = wstring::npos; if(bAutoExchange && (str.find(_T("-----PGP KEY RESPONSE-----")) != wstring::npos)) { if(bDebugLog) debuglog<hContact, GCDNF_TCHAR))); s2 = str.find(_T("-----END PGP PUBLIC KEY BLOCK-----")); s1 = str.find(_T("-----BEGIN PGP PUBLIC KEY BLOCK-----")); if(s1 != wstring::npos && s2 != wstring::npos) { if(bDebugLog) debuglog<hContact, GCDNF_TCHAR))); s2 += mir_tstrlen(_T("-----END PGP PUBLIC KEY BLOCK-----")); db_set_ts(ccs->hContact, szGPGModuleName, "GPGPubKey", str.substr(s1,s2-s1).c_str()); { //gpg execute block std::vector cmd; TCHAR tmp2[MAX_PATH] = {0}; TCHAR *ptmp; string output; DWORD exitcode; { ptmp = UniGetContactSettingUtf(NULL, szGPGModuleName, "szHomePath", _T("")); mir_tstrcpy(tmp2, ptmp); mir_free(ptmp); mir_tstrcat(tmp2, _T("\\")); TCHAR *tmp3 = mir_a2t(get_random(5).c_str()); mir_tstrcat(tmp2, tmp3); mir_tstrcat(tmp2, _T(".asc")); mir_free(tmp3); //mir_tstrcat(tmp2, _T("temporary_exported.asc")); boost::filesystem::remove(tmp2); wfstream f(tmp2, std::ios::out); while(!f.is_open()) f.open(tmp2, std::ios::out); ptmp = UniGetContactSettingUtf(ccs->hContact, szGPGModuleName, "GPGPubKey", _T("")); wstring new_key = ptmp; mir_free(ptmp); f<hContact, db_event(Translate("failed to decrypt message, GPG returned error, turn on debug log for more details"))); return 1; } { char *tmp = NULL; string::size_type s = output.find("gpg: key ") + mir_strlen("gpg: key "); string::size_type s2 = output.find(":", s); db_set_s(ccs->hContact, szGPGModuleName, "KeyID", output.substr(s,s2-s).c_str()); s2+=2; 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); tmp = (char*)mir_alloc(output.substr(s,s2-s-1).length()+1); mir_strcpy(tmp, output.substr(s,s2-s-1).c_str()); mir_utf8decode(tmp, 0); db_set_s(ccs->hContact, szGPGModuleName, "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(output[s] == ')') { tmp = (char*)mir_alloc(output.substr(s2,s-s2).length()+1); mir_strcpy(tmp, output.substr(s2,s-s2).c_str()); mir_utf8decode(tmp, 0); db_set_s(ccs->hContact, szGPGModuleName, "KeyComment", tmp); mir_free(tmp); s+=3; s2 = output.find(">", s); tmp = (char*)mir_alloc(output.substr(s,s2-s).length()+1); mir_strcpy(tmp, output.substr(s,s2-s).c_str()); mir_utf8decode(tmp, 0); db_set_s(ccs->hContact, szGPGModuleName, "KeyMainEmail", tmp); mir_free(tmp); } else { tmp = (char*)mir_alloc(output.substr(s2,s-s2).length()+1); mir_strcpy(tmp, output.substr(s2,s-s2).c_str()); mir_utf8decode(tmp, 0); db_set_s(ccs->hContact, szGPGModuleName, "KeyMainEmail", output.substr(s2,s-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(_T("-----END PGP PUBLIC KEY BLOCK-----"))) == wstring::npos) || ((s1 = str.find(_T("-----BEGIN PGP PUBLIC KEY BLOCK-----"))) == wstring::npos)) { s2 = str.find(_T("-----END PGP PRIVATE KEY BLOCK-----")); s1 = str.find(_T("-----BEGIN PGP PRIVATE KEY BLOCK-----")); } if((s2 != wstring::npos) && (s1 != wstring::npos)) { //this is public key if(bDebugLog) debuglog<hContact, GCDNF_TCHAR))); s1 = 0; while((s1 = str.find(_T("\r"), s1)) != wstring::npos) str.erase(s1, 1); void ShowNewKeyDialog(); if(((s2 = str.find(_T("-----END PGP PUBLIC KEY BLOCK-----"))) != wstring::npos) && ((s1 = str.find(_T("-----BEGIN PGP PUBLIC KEY BLOCK-----"))) != wstring::npos)) { s2 += mir_tstrlen(_T("-----END PGP PUBLIC KEY BLOCK-----")); } else if(((s2 = str.find(_T("-----BEGIN PGP PRIVATE KEY BLOCK-----"))) != wstring::npos) && ((s1 = str.find(_T("-----END PGP PRIVATE KEY BLOCK-----"))) != wstring::npos)) { s2 += mir_tstrlen(_T("-----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<hContact, GCDNF_TCHAR))); char *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 str = "-----PGP KEY RESPONSE-----"; str.append(tmp); CallContactService(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)str.c_str()); if(enc_state) db_set_b(ccs->hContact, szGPGModuleName, "GPGEncryption", 1); } mir_free(tmp); 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(ProtoCallService(proto, PS_ICQ_CHECKCAPABILITY, (WPARAM)ccs->hContact, (LPARAM)&cap)) { CallContactService(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); return 0; } } } else { TCHAR *jid = UniGetContactSettingUtf(ccs->hContact, proto, "jid", _T("")); if(jid[0]) { extern list Accounts; list::iterator end = Accounts.end(); for(list::iterator p = Accounts.begin(); p != end; p++) { TCHAR *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(_T("GPG_Key_Auto_Exchange:0")) != string::npos) { CallContactService(ccs->hContact, PSS_MESSAGE, 0, (LPARAM)"-----PGP KEY REQUEST-----"); return 0; } } } } mir_free(jid); } } if(!strstr(msg, "-----BEGIN PGP MESSAGE-----")) return CallService(MS_PROTO_CHAINRECV, w, l); boost::thread *thr = 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) { bool isansi = false; DWORD dbflags = DBEF_UTF; wstring str = toUTF16(msg); if(bStripTags && bAppendTags) { if(bDebugLog) debuglog< cmd; extern bool bJabberAPI, bIsMiranda09; 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)); CallContactService(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } if(!bJabberAPI || !bIsMiranda09) //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"); TCHAR *tmp2 = mir_a2t(tmp); mir_free(tmp); cmd.push_back(tmp2); mir_free(tmp2); tmp2 = UniGetContactSettingUtf(NULL, szGPGModuleName, "szHomePath", _T("")); path = tmp2; cmd.push_back(std::wstring(tmp2) + L"\\tmp\\" + file); mir_free(tmp2); path += L"\\tmp\\"; path += file; { fstream f(path.c_str(), std::ios::out); while(!f.is_open()) { boost::this_thread::sleep(boost::posix_time::milliseconds(50)); f.open(path.c_str(), std::ios::out); } std::string tmp = toUTF8(str); f.write(tmp.c_str(), tmp.size()); f.close(); } gpg_execution_params params(cmd); pxResult result; params.out = &out; params.code = &code; params.result = &result; if(!gpg_launcher(params)) { //mir_free(msg); CallContactService(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } if(result == pxNotFound) { //mir_free(msg); CallContactService(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(0, 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); pxResult result; params.out = &out; params.code = &code; params.result = &result; if(!gpg_launcher(params)) { //mir_free(msg); CallContactService(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } if(result == pxNotFound) { //mir_free(msg); CallContactService(hContact, PSS_MESSAGE, flags, (LPARAM)msg); return; } //TODO: check gpg output for errors } else { //mir_free(msg); return; } } if(result == pxSuccessExitCodeInvalid) { //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)); boost::filesystem::remove(path); return; } if(out.find("usage: ") != string::npos) { MessageBox(0, TranslateT("Something is wrong, GPG does not understand us, aborting encryption."), TranslateT("Warning"), MB_OK); //mir_free(msg); CallContactService(hContact, PSS_MESSAGE, flags, (LPARAM)msg); boost::filesystem::remove(path); return; } boost::filesystem::remove(path); path.append(_T(".asc")); wfstream f(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); while(!f.is_open()) { boost::this_thread::sleep(boost::posix_time::milliseconds(100)); f.open(path.c_str(), std::ios::in | std::ios::ate | std::ios::binary); } str.clear(); if(f.is_open()) { std::wifstream::pos_type size = f.tellg(); TCHAR *tmp = new TCHAR [(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(); boost::filesystem::remove(path); } if(str.empty()) { HistoryLog(hContact, db_event("Failed to encrypt message with GPG", 0,0, DBEF_SENT)); if(bDebugLog) debuglog<lParam) return CallService(MS_PROTO_CHAINSEND, w, l); char *msg = (char*)ccs->lParam; if (!msg) { if(bDebugLog) debuglog<hContact, GCDNF_TCHAR))); return CallService(MS_PROTO_CHAINSEND, w, l); } if(strstr(msg,"-----BEGIN PGP MESSAGE-----")) { if(bDebugLog) debuglog<hContact, GCDNF_TCHAR))); return CallService(MS_PROTO_CHAINSEND, w, l); } if(bDebugLog) debuglog<hContact, GCDNF_TCHAR))); if(bDebugLog && db_mc_isMeta(ccs->hContact)) debuglog<hContact, GCDNF_TCHAR))); if(!isContactSecured(ccs->hContact) || db_mc_isMeta(ccs->hContact)) { if(bDebugLog) debuglog<hContact, GCDNF_TCHAR))); return CallService(MS_PROTO_CHAINSEND, w, l); } 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.t.c. { if(bDebugLog) debuglog<pBlob, "-----PGP KEY RESPONSE-----") || strstr((char*)dbei->pBlob, "-----PGP KEY REQUEST-----"))) ///do not show service data in history { if(bDebugLog) debuglog<pBlob, "-----PGP KEY REQUEST-----") && !strstr((char*)dbei->pBlob, "-----BEGIN PGP PUBLIC KEY BLOCK-----") && gpg_valid) { if(bDebugLog) debuglog<pBlob); boost::thread *thr = new boost::thread(boost::bind(send_encrypted_msgs_thread, (void*)hContact)); //TODO: wait for message return 0; } } } else { TCHAR *jid = UniGetContactSettingUtf(hContact, proto, "jid", _T("")); if(jid[0]) { if(bDebugLog) debuglog< Accounts; list::iterator end = Accounts.end(); for(list::iterator p = Accounts.begin(); p != end; p++) { TCHAR *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(_T("GPG_Key_Auto_Exchange:0")) != string::npos) { if(bDebugLog) debuglog<pBlob); boost::thread *thr = new boost::thread(boost::bind(send_encrypted_msgs_thread, (void*)hContact)); //mir_free((char*)dbei->pBlob); //TODO: wait for message 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<pBlob+"\" passed event filter, contact "+toUTF8((TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, hContact, GCDNF_TCHAR))+" 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<