From 1513340956911f3e3efc252bc1a0bae046dc8d3e Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sat, 15 Feb 2020 17:21:20 +0300 Subject: Jabber: partial support for XEP-0231: Bits of Binary --- protocols/JabberG/src/jabber_caps.h | 5 +- protocols/JabberG/src/jabber_console.cpp | 12 +-- protocols/JabberG/src/jabber_misc.cpp | 136 ++++++++++++++++++++++--------- protocols/JabberG/src/jabber_proto.h | 2 + protocols/JabberG/src/jabber_thread.cpp | 60 +++----------- protocols/JabberG/src/jabber_xml.h | 4 + 6 files changed, 123 insertions(+), 96 deletions(-) (limited to 'protocols/JabberG') diff --git a/protocols/JabberG/src/jabber_caps.h b/protocols/JabberG/src/jabber_caps.h index 2db7ba7fe4..aca0eb5bdc 100755 --- a/protocols/JabberG/src/jabber_caps.h +++ b/protocols/JabberG/src/jabber_caps.h @@ -189,6 +189,9 @@ typedef unsigned __int64 JabberCapsBits; #define JABBER_FEAT_CARBONS "urn:xmpp:carbons:2" #define JABBER_CAPS_CARBONS ((JabberCapsBits)1<<49) +#define JABBER_FEAT_BITS "urn:xmpp:bob" +#define JABBER_CAPS_BITS ((JabberCapsBits)1<<50) + #define JABBER_FEAT_ARCHIVE "urn:xmpp:archive" #define JABBER_FEAT_BIND "urn:ietf:params:xml:ns:xmpp-bind" #define JABBER_FEAT_CAPTCHA "urn:xmpp:captcha" @@ -209,7 +212,7 @@ typedef unsigned __int64 JabberCapsBits; JABBER_CAPS_BYTESTREAMS | JABBER_CAPS_IBB | JABBER_CAPS_OOB | JABBER_CAPS_CHATSTATES | JABBER_CAPS_AGENTS | JABBER_CAPS_BROWSE | \ JABBER_CAPS_VERSION | JABBER_CAPS_LAST_ACTIVITY | JABBER_CAPS_DATA_FORMS | JABBER_CAPS_VCARD_TEMP | \ JABBER_CAPS_ENTITY_TIME | JABBER_CAPS_PING | JABBER_CAPS_PRIVACY_LISTS | JABBER_CAPS_MESSAGE_RECEIPTS | JABBER_CAPS_PRIVATE_STORAGE | \ - JABBER_CAPS_ROSTER_EXCHANGE | JABBER_CAPS_DIRECT_MUC_INVITE | JABBER_CAPS_CHAT_MARKERS) + JABBER_CAPS_ROSTER_EXCHANGE | JABBER_CAPS_DIRECT_MUC_INVITE | JABBER_CAPS_CHAT_MARKERS | JABBER_CAPS_BITS) #define JABBER_CAPS_MIRANDA_ALL (JABBER_CAPS_MIRANDA_PARTIAL | JABBER_CAPS_COMMANDS | \ JABBER_CAPS_USER_MOOD_NOTIFY | JABBER_CAPS_USER_TUNE_NOTIFY | JABBER_CAPS_USER_ACTIVITY_NOTIFY \ diff --git a/protocols/JabberG/src/jabber_console.cpp b/protocols/JabberG/src/jabber_console.cpp index dd0048bd50..0a612dfacb 100644 --- a/protocols/JabberG/src/jabber_console.cpp +++ b/protocols/JabberG/src/jabber_console.cpp @@ -26,12 +26,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "stdafx.h" -#define JCPF_IN 0x01UL -#define JCPF_OUT 0x02UL -#define JCPF_ERROR 0x04UL - -#define JCPF_TCHAR 0x00UL - #define WM_CREATECONSOLE WM_USER+1000 #ifndef SES_EXTENDBACKCOLOR @@ -196,8 +190,10 @@ static void sttRtfAppendXml(StringBuf *buf, const TiXmlElement *node, DWORD flag sttAppendBufRaw(buf, RTF_BEGINTAG); sttAppendBufRaw(buf, indentLevel); - if (flags&JCPF_IN) sttAppendBufRaw(buf, "\\highlight3 "); - if (flags&JCPF_OUT) sttAppendBufRaw(buf, "\\highlight4 "); + if (flags & JCPF_IN) + sttAppendBufRaw(buf, "\\highlight3 "); + if (flags & JCPF_OUT) + sttAppendBufRaw(buf, "\\highlight4 "); sttAppendBufRaw(buf, "<"); sttAppendBufRaw(buf, RTF_BEGINTAGNAME); sttAppendBufRaw(buf, node->Name()); diff --git a/protocols/JabberG/src/jabber_misc.cpp b/protocols/JabberG/src/jabber_misc.cpp index b83bd2b87a..4d96430685 100755 --- a/protocols/JabberG/src/jabber_misc.cpp +++ b/protocols/JabberG/src/jabber_misc.cpp @@ -137,7 +137,7 @@ void CJabberProto::GetAvatarFileName(MCONTACT hContact, wchar_t* pszDest, size_t { int tPathLen = mir_snwprintf(pszDest, cbLen, L"%s\\%S", VARSW(L"%miranda_avatarcache%").get(), m_szModuleName); - DWORD dwAttributes = GetFileAttributes(pszDest); + DWORD dwAttributes = GetFileAttributesW(pszDest); if (dwAttributes == 0xffffffff || (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) CreateDirectoryTreeW(pszDest); @@ -440,48 +440,106 @@ void CJabberProto::MsgPopup(MCONTACT hContact, const wchar_t *szMsg, const wchar Popup_AddClass(&ppd); } +///////////////////////////////////////////////////////////////////////////////////////// + +static const TiXmlElement* XmlFindChildRecursively(const TiXmlElement *p, const char *elem) +{ + if (auto *res = XmlFirstChild(p, elem)) + return res; + + for (auto *n : TiXmlEnum(p)) + if (auto *res = XmlFindChildRecursively(n, elem)) + return res; + + return nullptr; +} + +static bool SaveBlobToFile(const wchar_t *pwszFileName, const CMStringA &body) +{ + HANDLE h = CreateFile(pwszFileName, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + if (h == INVALID_HANDLE_VALUE) + return false; + + DWORD n; + size_t bufferLen; + ptrA buffer((char *)mir_base64_decode(body, &bufferLen)); + WriteFile(h, buffer, (DWORD)bufferLen, &n, nullptr); + CloseHandle(h); + return true; +} + +void CJabberProto::OnGetBob(const TiXmlElement *node, CJabberIqInfo *pReq) +{ + if (auto *data = XmlFirstChild(node, "data")) { + if (auto *cid = XmlGetAttr(data, "cid")) { + if (auto *src = data->GetText()) { + VARSW wszTempPath(L"%miranda_userdata%\\JabberTmp"); + DWORD dwAttributes = GetFileAttributesW(wszTempPath); + if (dwAttributes == 0xffffffff || (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + CreateDirectoryTreeW(wszTempPath); + + const wchar_t *pwszExt = L".bin"; + if (auto *pszType = XmlGetAttr(data, "type")) + pwszExt = ProtoGetAvatarExtension(ProtoGetAvatarFormatByMimeType(pszType)); + + CMStringA szHash = CMStringA(cid).Mid(5, 40); + CMStringW wszFileName(FORMAT, L"%s\\%S%s", wszTempPath.get(), szHash.c_str(), pwszExt); + SaveBlobToFile(wszFileName, src); + + wszFileName.Insert(0, L"[img]"); wszFileName.Append(L"[/img]"); + T2Utf szMsg(wszFileName); + PROTORECVEVENT pre = {}; + pre.timestamp = time(0); + pre.szMessage = szMsg; + ProtoChainRecvMsg(pReq->GetHContact(), &pre); + } + } + } +} + CMStringA CJabberProto::ExtractImage(const TiXmlElement *node) { - const TiXmlElement *nHtml, *nBody, *nImg; - const char *src; CMStringA link; - if ((nHtml = XmlFirstChild(node, "html")) != nullptr && - (nBody = XmlFirstChild(nHtml, "body")) != nullptr && - (nImg = XmlFirstChild(nBody, "img")) != nullptr && - (src = XmlGetAttr(nImg, "src")) != nullptr) { - - CMStringA strSrc(src); - if (strSrc.Left(11).Compare("data:image/") == 0) { - int end = strSrc.Find(';'); - if (end != -1) { - CMStringW ext(strSrc.c_str() + 11, end - 11); - int comma = strSrc.Find(L',', end); - if (comma != -1) { - CMStringA image(strSrc.c_str() + comma + 1, strSrc.GetLength() - comma - 1); - image.Replace("%2B", "+"); - image.Replace("%2F", "/"); - image.Replace("%3D", "="); - - wchar_t tszTempPath[MAX_PATH], tszTempFile[MAX_PATH]; - GetTempPath(_countof(tszTempPath), tszTempPath); - GetTempFileName(tszTempPath, L"jab", InterlockedIncrement(&g_nTempFileId), tszTempFile); - wcsncat_s(tszTempFile, L".", 1); - wcsncat_s(tszTempFile, ext, ext.GetLength()); - - HANDLE h = CreateFile(tszTempFile, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, nullptr); - - if (h != INVALID_HANDLE_VALUE) { - DWORD n; - size_t bufferLen; - ptrA buffer((char*)mir_base64_decode(image, &bufferLen)); - WriteFile(h, buffer, (DWORD)bufferLen, &n, nullptr); - CloseHandle(h); - - link = " file:///"; - link += T2Utf(tszTempFile); + if (auto *nHtml = XmlFirstChild(node, "html")) { + if (auto *nBody = XmlFirstChild(nHtml, "body")) { + if (auto *nImg = XmlFindChildRecursively(nBody, "img")) { + if (auto *src = XmlGetAttr(nImg, "src")) { + CMStringA strSrc(src); + + // direct inline + if (strSrc.Left(11).Compare("data:image/") == 0) { + int end = strSrc.Find(';'); + if (end != -1) { + CMStringW ext(strSrc.c_str() + 11, end - 11); + int comma = strSrc.Find(L',', end); + if (comma != -1) { + CMStringA image(strSrc.c_str() + comma + 1, strSrc.GetLength() - comma - 1); + image.Replace("%2B", "+"); + image.Replace("%2F", "/"); + image.Replace("%3D", "="); + + wchar_t tszTempPath[MAX_PATH], tszTempFile[MAX_PATH]; + GetTempPath(_countof(tszTempPath), tszTempPath); + GetTempFileName(tszTempPath, L"jab", InterlockedIncrement(&g_nTempFileId), tszTempFile); + wcsncat_s(tszTempFile, L".", 1); + wcsncat_s(tszTempFile, ext, ext.GetLength()); + + if (SaveBlobToFile(tszTempFile, image)) + link.AppendFormat(" file://%s", T2Utf(tszTempFile).get()); + } + } + } + + // XEP-0231: Bits Of Bytes + else if (strSrc.Left(9) == "cid:sha1+" && strSrc.Right(13) == "@bob.xmpp.org") { + auto *pIQ = AddIQ(&CJabberProto::OnGetBob, JABBER_IQ_TYPE_GET, XmlGetAttr(node, "from")); + pIQ->SetParamsToParse(JABBER_IQ_PARSE_HCONTACT); + + strSrc.Delete(0, 4); + m_ThreadInfo->send(XmlNodeIq(pIQ) << XCHILDNS("data", JABBER_FEAT_BITS) << XATTR("cid", strSrc)); } } } diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h index e30d434c58..5ae2686229 100755 --- a/protocols/JabberG/src/jabber_proto.h +++ b/protocols/JabberG/src/jabber_proto.h @@ -832,6 +832,8 @@ struct CJabberProto : public PROTO, public IJabberInterface //---- jabber_util.c ----------------------------------------------------------------- pResourceStatus ResourceInfoFromJID(const char *jid); + void OnGetBob(const TiXmlElement *node, CJabberIqInfo *pInfo); + MCONTACT HContactFromJID(const char *jid, bool bStripResource = true); MCONTACT ChatRoomHContactFromJID(const char *jid); void SendVisibleInvisiblePresence(bool invisible); diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp index 5d65e472e6..dda7597d6a 100755 --- a/protocols/JabberG/src/jabber_thread.cpp +++ b/protocols/JabberG/src/jabber_thread.cpp @@ -46,16 +46,6 @@ static ThreadData *g_pRegInfo; // pointer to registration thread int iqIdRegGetReg; int iqIdRegSetReg; -// XML Console -#define JCPF_IN 0x01UL -#define JCPF_OUT 0x02UL -#define JCPF_ERROR 0x04UL - -static VOID CALLBACK JabberDummyApcFunc(DWORD_PTR) -{ - return; -} - struct JabberPasswordDlgParam { CJabberProto *pro; @@ -103,7 +93,8 @@ static INT_PTR CALLBACK JabberPasswordDlgProc(HWND hwndDlg, UINT msg, WPARAM wPa param->saveOnlinePassword = TRUE; } } - // Fall through + __fallthrough; + case IDCANCEL: param->dlgResult = LOWORD(wParam); SetEvent(param->hEventPasswdDlg); @@ -116,7 +107,7 @@ static INT_PTR CALLBACK JabberPasswordDlgProc(HWND hwndDlg, UINT msg, WPARAM wPa return FALSE; } -static VOID CALLBACK JabberPasswordCreateDialogApcProc(void* param) +static VOID CALLBACK JabberPasswordCreateDialogApcProc(void *param) { CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_PASSWORD), nullptr, JabberPasswordDlgProc, (LPARAM)param); } @@ -1180,11 +1171,6 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info) item = ListGetItemPtr(LIST_VCARD_TEMP, from); time_t msgTime = 0; - bool isChatRoomInvitation = false; - const char *inviteRoomJid = nullptr; - const char *inviteFromJid = nullptr; - const char *inviteReason = nullptr; - const char *invitePassword = nullptr; // check chatstates availability if (pFromResource && XmlGetChildByTag(node, "active", "xmlns", JABBER_FEAT_CHATSTATES)) @@ -1330,13 +1316,12 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info) else if (!mir_strcmp(pszXmlns, JABBER_FEAT_MUC_USER)) { auto *inviteNode = XmlFirstChild(xNode, "invite"); if (inviteNode != nullptr) { - inviteFromJid = XmlGetAttr(inviteNode, "from"); - inviteReason = XmlGetChildText(inviteNode, "reason"); - inviteRoomJid = from; + auto *inviteReason = XmlGetChildText(inviteNode, "reason"); if (inviteReason == nullptr) inviteReason = szMessage; - isChatRoomInvitation = true; - invitePassword = XmlGetChildText(xNode, "password"); + if (!m_bIgnoreMUCInvites) + GroupchatProcessInvite(XmlGetAttr(inviteNode, "from"), from, inviteReason, XmlGetChildText(xNode, "password")); + return; } } else if (!mir_strcmp(pszXmlns, JABBER_FEAT_ROSTER_EXCHANGE) && item != nullptr && (item->subscription == SUB_BOTH || item->subscription == SUB_TO)) { @@ -1361,35 +1346,14 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info) } } } - else if (!isChatRoomInvitation && !mir_strcmp(pszXmlns, JABBER_FEAT_DIRECT_MUC_INVITE)) { - inviteRoomJid = XmlGetAttr(xNode, "jid"); - inviteFromJid = from; - if (inviteReason == nullptr) - inviteReason = xNode->GetText(); + else if (!mir_strcmp(pszXmlns, JABBER_FEAT_DIRECT_MUC_INVITE)) { + auto *inviteReason = xNode->GetText(); if (!inviteReason) inviteReason = szMessage; - isChatRoomInvitation = true; - } - } - - if (isChatRoomInvitation) { - if (inviteRoomJid != nullptr) { - if (m_bIgnoreMUCInvites) { - // FIXME: temporary disabled due to MUC inconsistence on server side - /* - XmlNode m("message"); XmlAddAttr(m, "to", from); - XmlNode xNode = XmlAddChild(m, "x"); - XmlAddAttr(xNode, "xmlns", JABBER_FEAT_MUC_USER); - XmlNode declineNode = XmlAddChild(xNode, "decline"); - XmlAddAttr(declineNode, "from", inviteRoomJid); - XmlNode reasonNode = XmlAddChild(declineNode, "reason", "The user has chosen to not accept chat invites"); - info->send(m); - */ - } - else GroupchatProcessInvite(inviteRoomJid, inviteFromJid, inviteReason, invitePassword); + if (!m_bIgnoreMUCInvites) + GroupchatProcessInvite(XmlGetAttr(xNode, "jid"), from, inviteReason, nullptr); + return; } - debugLogA("chat room invitation processed, returning"); - return; } // all service info was already processed diff --git a/protocols/JabberG/src/jabber_xml.h b/protocols/JabberG/src/jabber_xml.h index 2a46f43db4..c809860a81 100644 --- a/protocols/JabberG/src/jabber_xml.h +++ b/protocols/JabberG/src/jabber_xml.h @@ -26,6 +26,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef _JABBER_XML_H_ #define _JABBER_XML_H_ +#define JCPF_IN 0x01UL +#define JCPF_OUT 0x02UL +#define JCPF_ERROR 0x04UL + void XmlAddAttrID(TiXmlElement*, int id); class XmlNodeHash : public tinyxml2::XMLVisitor -- cgit v1.2.3