diff options
author | George Hazan <ghazan@miranda.im> | 2021-04-19 20:24:23 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2021-04-19 20:24:28 +0300 |
commit | 5c92a5a3d0464d98f2b4b9627c4c99c9f7137897 (patch) | |
tree | 5e63c8cbfc031b66485fe169f983c42ee0ee23b7 | |
parent | 3078435372e4b8b139b76cd4ab361856345a6d2a (diff) |
WhatsApp: basic message decoding
-rw-r--r-- | protocols/WhatsAppWeb/src/proto.cpp | 2 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/proto.h | 12 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/server.cpp | 89 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/utils.cpp | 73 | ||||
-rw-r--r-- | protocols/WhatsAppWeb/src/utils.h | 29 |
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); +}; |