summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2021-04-19 20:24:23 +0300
committerGeorge Hazan <ghazan@miranda.im>2021-04-19 20:24:28 +0300
commit5c92a5a3d0464d98f2b4b9627c4c99c9f7137897 (patch)
tree5e63c8cbfc031b66485fe169f983c42ee0ee23b7
parent3078435372e4b8b139b76cd4ab361856345a6d2a (diff)
WhatsApp: basic message decoding
-rw-r--r--protocols/WhatsAppWeb/src/proto.cpp2
-rw-r--r--protocols/WhatsAppWeb/src/proto.h12
-rw-r--r--protocols/WhatsAppWeb/src/server.cpp89
-rw-r--r--protocols/WhatsAppWeb/src/utils.cpp73
-rw-r--r--protocols/WhatsAppWeb/src/utils.h29
5 files changed, 190 insertions, 15 deletions
diff --git a/protocols/WhatsAppWeb/src/proto.cpp b/protocols/WhatsAppWeb/src/proto.cpp
index 447496d090..c1761eaff3 100644
--- a/protocols/WhatsAppWeb/src/proto.cpp
+++ b/protocols/WhatsAppWeb/src/proto.cpp
@@ -125,7 +125,7 @@ INT_PTR WhatsAppProto::GetCaps(int type, MCONTACT)
case PFLAGNUM_3:
return 0;
case PFLAGNUM_4:
- return PF4_NOCUSTOMAUTH | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE | PF4_OFFLINEFILES | PF4_SUPPORTTYPING | PF4_AVATARS;
+ return PF4_NOCUSTOMAUTH | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE | PF4_OFFLINEFILES | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_SERVERMSGID;
case PFLAGNUM_5:
return 0;
case PFLAG_UNIQUEIDTEXT:
diff --git a/protocols/WhatsAppWeb/src/proto.h b/protocols/WhatsAppWeb/src/proto.h
index d165b43361..a2517d13e7 100644
--- a/protocols/WhatsAppWeb/src/proto.h
+++ b/protocols/WhatsAppWeb/src/proto.h
@@ -37,6 +37,17 @@ struct WAUser
SESSION_INFO *si = 0;
};
+struct WAMessage
+{
+ bool bFromTo;
+ BYTE iMsgType;
+ ptrA szShit;
+ ptrA szJid;
+ ptrA szMsgId;
+ ptrA szBody;
+ __int64 timestamp;
+};
+
class WhatsAppProto : public PROTO<WhatsAppProto>
{
bool m_bTerminated, m_bOnline;
@@ -96,6 +107,7 @@ class WhatsAppProto : public PROTO<WhatsAppProto>
// binary packets
void ProcessBinaryPacket(const MBinBuffer &buf);
+ void ProcessAdd(const JSONNode &node);
void ProcessChats(const JSONNode &node);
void ProcessContacts(const JSONNode &node);
diff --git a/protocols/WhatsAppWeb/src/server.cpp b/protocols/WhatsAppWeb/src/server.cpp
index 2668a9955a..d452c8bd1b 100644
--- a/protocols/WhatsAppWeb/src/server.cpp
+++ b/protocols/WhatsAppWeb/src/server.cpp
@@ -7,17 +7,6 @@ Copyright © 2019-21 George Hazan
#include "stdafx.h"
-bool WhatsAppProto::getBlob(const char *szSetting, MBinBuffer &buf)
-{
- DBVARIANT dbv = { DBVT_BLOB };
- if (db_get(0, m_szModuleName, szSetting, &dbv))
- return false;
-
- buf.assign(dbv.pbVal, dbv.cpbVal);
- db_free(&dbv);
- return true;
-}
-
/////////////////////////////////////////////////////////////////////////////////////////
// sends a piece of JSON to a server via a websocket, masked
@@ -328,17 +317,18 @@ bool WhatsAppProto::ServerThreadWorker()
}
offset = 0;
- debugLogA("Got packet: buffer = %d, opcode = %d, headerSize = %d, final = %d, masked = %d", bufSize, hdr.opCode, hdr.headerSize, hdr.bIsFinal, hdr.bIsMasked);
+ debugLogA("Got packet: buffer = %d, opcode = %d, payloadSize = %d, final = %d, masked = %d", netbuf.length(), hdr.opCode, hdr.payloadSize, hdr.bIsFinal, hdr.bIsMasked);
// read all payloads from the current buffer, one by one
size_t prevSize = 0;
while (true) {
+ const char *start = netbuf.data() + hdr.headerSize;
+
switch (hdr.opCode) {
case 1: // json packet
case 2: // binary packet
if (hdr.bIsFinal) {
// process a packet here
- const char *start = netbuf.data() + hdr.headerSize;
const char *pos = strchr(start, ',');
if (pos == nullptr) {
debugLogA("invalid packet received, no comma");
@@ -385,8 +375,11 @@ bool WhatsAppProto::ServerThreadWorker()
case 9: // ping
debugLogA("ping received");
- Netlib_Send(m_hServerConn, (char *)buf + hdr.headerSize, bufSize - int(hdr.headerSize), 0);
+ Netlib_Send(m_hServerConn, start, (int)hdr.payloadSize, 0);
break;
+
+ default:
+ Netlib_Dump(m_hServerConn, start, hdr.payloadSize, false, 0);
}
if (hdr.bIsFinal)
@@ -437,6 +430,74 @@ void WhatsAppProto::ProcessBinaryPacket(const MBinBuffer &buf)
ProcessContacts(root["$list$"]);
else if (szType == "chat")
ProcessChats(root["$list$"]);
+ else {
+ CMStringW szAdd = root["add"].as_mstring();
+ if (!szAdd.IsEmpty())
+ ProcessAdd(root["$list$"]);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static WAS_Field msgFields[] =
+{
+ { WAS_STRING, 1, FIELD_OFFSET(WAMessage, szShit) },
+ { WAS_STRING, 0, FIELD_OFFSET(WAMessage, szJid) },
+ { WAS_BOOL, 0, FIELD_OFFSET(WAMessage, bFromTo) },
+ { WAS_BINARY, 0, FIELD_OFFSET(WAMessage, szMsgId) },
+ { WAS_INT8, 0, FIELD_OFFSET(WAMessage, iMsgType) },
+ { WAS_STRING, 0, FIELD_OFFSET(WAMessage, szBody) },
+ { WAS_INT64, 0, FIELD_OFFSET(WAMessage, timestamp) },
+};
+
+void WhatsAppProto::ProcessAdd(const JSONNode &list)
+{
+ for (auto &it : list) {
+ std::string buf = it["$bin$"].as_string();
+
+ size_t resLen;
+ void *pRes = mir_base64_decode(buf.c_str(), &resLen);
+ if (pRes == nullptr)
+ continue;
+
+ WAMessage msg;
+ WAS_Decoder rdr(pRes, resLen);
+ if (rdr.read(&msg, msgFields, _countof(msgFields))) {
+ CMStringA jid(msg.szJid);
+ jid.Replace("@s.whatsapp.net", "@c.us");
+ jid.Replace("@g.whatsapp.net", "@g.us");
+
+ auto *pUser = AddUser(jid, false);
+ if (db_event_getById(m_szModuleName, msg.szMsgId))
+ continue;
+
+ if (pUser->si) {
+ CMStringA szText(msg.szBody);
+ szText.Replace("%", "%%");
+
+ GCEVENT gce = { m_szModuleName, 0, GC_EVENT_MESSAGE };
+ gce.pszID.a = msg.szJid;
+ gce.dwFlags = GCEF_ADDTOLOG | GCEF_UTF8;
+ gce.pszUID.a = "";
+ gce.pszText.a = szText;
+ gce.time = int(msg.timestamp / 1000000ll);
+ gce.bIsMe = true;
+ Chat_Event(&gce);
+ }
+ else {
+ PROTORECVEVENT pre = { 0 };
+ pre.timestamp = int(msg.timestamp / 1000000ll);
+ pre.szMessage = msg.szBody;
+ pre.flags = PREF_CREATEREAD;
+ pre.szMsgId = msg.szMsgId;
+ if (msg.bFromTo)
+ pre.flags |= PREF_SENT;
+ ProtoChainRecvMsg(pUser->hContact, &pre);
+ }
+ }
+
+ mir_free(pRes);
+ }
}
void WhatsAppProto::ProcessChats(const JSONNode &list)
diff --git a/protocols/WhatsAppWeb/src/utils.cpp b/protocols/WhatsAppWeb/src/utils.cpp
index b6877f529a..70f2f5892f 100644
--- a/protocols/WhatsAppWeb/src/utils.cpp
+++ b/protocols/WhatsAppWeb/src/utils.cpp
@@ -37,6 +37,19 @@ WAUser* WhatsAppProto::AddUser(const char *szId, bool bTemporary)
/////////////////////////////////////////////////////////////////////////////////////////
+bool WhatsAppProto::getBlob(const char *szSetting, MBinBuffer &buf)
+{
+ DBVARIANT dbv = { DBVT_BLOB };
+ if (db_get(0, m_szModuleName, szSetting, &dbv))
+ return false;
+
+ buf.assign(dbv.pbVal, dbv.cpbVal);
+ db_free(&dbv);
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
bool WhatsAppProto::decryptBinaryMessage(size_t cbSize, const void *buf, MBinBuffer &res)
{
if (cbSize <= 32)
@@ -331,3 +344,63 @@ CMStringA WAReader::readStringFromChars(int size)
m_buf += size;
return ret;
}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Object serialization
+
+bool WAS_Decoder::read(void *pObj, const WAS_Field *fields, size_t iFieldCount)
+{
+ BYTE *pDest = (BYTE *)pObj;
+
+ for (int i = 0; i < iFieldCount; i++) {
+ auto &F = fields[i];
+ switch (F.type) {
+ case WAS_STRING:
+ if (*m_buf++ != 0x0A) return false;
+ {
+ char **d = (char **)&pDest[F.offset];
+ int len = (F.len == 0) ? *m_buf++ : F.len;
+ *d = (char *)mir_strndup((char*)m_buf, len);
+ m_buf += len;
+ }
+ break;
+
+ case WAS_BINARY:
+ if (*m_buf++ != 0x1A) return false;
+ {
+ char **d = (char **)&pDest[F.offset];
+ int len = (F.len == 0) ? *m_buf++ : F.len;
+ *d = (char *)mir_strndup((char*)m_buf, len);
+ m_buf += len;
+ }
+ break;
+
+ case WAS_BOOL:
+ if (*m_buf++ != 0x10) return false;
+ {
+ bool *d = (bool *)&pDest[F.offset];
+ *d = *m_buf++;
+ }
+ break;
+
+ case WAS_INT8:
+ if (*m_buf++ != 0x12) return false;
+ {
+ BYTE *d = (BYTE*)&pDest[F.offset];
+ *d = *m_buf++;
+ }
+ break;
+
+ case WAS_INT64:
+ if (*m_buf++ != 0x18) return false;
+ {
+ __int64 *d = (__int64 *)&pDest[F.offset];
+ *d = *(__int64 *)m_buf;
+ m_buf += sizeof(__int64);
+ }
+ break;
+ }
+ }
+
+ return true;
+}
diff --git a/protocols/WhatsAppWeb/src/utils.h b/protocols/WhatsAppWeb/src/utils.h
index d37c8fb187..aa74f865f2 100644
--- a/protocols/WhatsAppWeb/src/utils.h
+++ b/protocols/WhatsAppWeb/src/utils.h
@@ -45,3 +45,32 @@ public:
CMStringA readString(int tag);
bool readNode(JSONNode&);
};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// object serialization
+
+#define WAS_STRING 1
+#define WAS_BINARY 2
+#define WAS_BOOL 3
+#define WAS_INT8 4
+#define WAS_INT64 5
+
+struct WAS_Field
+{
+ uint8_t type;
+ uint8_t len;
+ uint16_t offset;
+};
+
+class WAS_Decoder
+{
+ const BYTE *m_buf, *m_limit;
+
+public:
+ WAS_Decoder(const void *pData, size_t cbLen) :
+ m_buf((BYTE*)pData),
+ m_limit((BYTE*)pData + cbLen)
+ {}
+
+ bool read(void *pObj, const WAS_Field *fields, size_t iFieldCount);
+};