diff options
author | George Hazan <ghazan@miranda.im> | 2020-02-15 17:21:20 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2020-02-15 17:21:20 +0300 |
commit | 1513340956911f3e3efc252bc1a0bae046dc8d3e (patch) | |
tree | 440259d88935aad26ea8245d3e29fe22cbbb950a /protocols/JabberG | |
parent | 87cd5608a92fb2dda0694076ec99704c6ca7fd67 (diff) |
Jabber: partial support for XEP-0231: Bits of Binary
Diffstat (limited to 'protocols/JabberG')
-rwxr-xr-x | protocols/JabberG/src/jabber_caps.h | 5 | ||||
-rw-r--r-- | protocols/JabberG/src/jabber_console.cpp | 12 | ||||
-rwxr-xr-x | protocols/JabberG/src/jabber_misc.cpp | 136 | ||||
-rwxr-xr-x | protocols/JabberG/src/jabber_proto.h | 2 | ||||
-rwxr-xr-x | protocols/JabberG/src/jabber_thread.cpp | 60 | ||||
-rw-r--r-- | protocols/JabberG/src/jabber_xml.h | 4 |
6 files changed, 123 insertions, 96 deletions
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<CJabberProto>, 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
|