diff options
author | George Hazan <ghazan@miranda.im> | 2022-10-31 20:20:16 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2022-10-31 20:20:16 +0300 |
commit | 476aa34eeec80dc9b1b15b48ef5db84501dcae42 (patch) | |
tree | 84dbc5fd64e397c48666c476f60c10572728578f | |
parent | 61cb1c1445b8ebfef08c864f0062baa68390dd1a (diff) |
WhatsApp: history sync processing
-rw-r--r-- | protocols/WhatsApp/src/appsync.cpp | 56 | ||||
-rw-r--r-- | protocols/WhatsApp/src/message.cpp | 27 | ||||
-rw-r--r-- | protocols/WhatsApp/src/pmsg.proto.h | 1 | ||||
-rw-r--r-- | protocols/WhatsApp/src/proto.h | 11 | ||||
-rw-r--r-- | protocols/WhatsApp/src/utils.cpp | 41 | ||||
-rw-r--r-- | protocols/WhatsApp/src/utils.h | 4 |
6 files changed, 116 insertions, 24 deletions
diff --git a/protocols/WhatsApp/src/appsync.cpp b/protocols/WhatsApp/src/appsync.cpp index f77fff2d4b..9289cd57b1 100644 --- a/protocols/WhatsApp/src/appsync.cpp +++ b/protocols/WhatsApp/src/appsync.cpp @@ -249,3 +249,59 @@ void WhatsAppProto::ApplyPatch(const JSONNode &index, const Wa__SyncActionValue } } } + +///////////////////////////////////////////////////////////////////////////////////////// + +void WhatsAppProto::ProcessHistorySync(const Wa__HistorySync *pSync) +{ + debugLogA("Got history sync: %s", protobuf_c_text_to_string(pSync).c_str()); + + switch (pSync->synctype) { + case WA__HISTORY_SYNC__HISTORY_SYNC_TYPE__INITIAL_BOOTSTRAP: + case WA__HISTORY_SYNC__HISTORY_SYNC_TYPE__RECENT: + for (int i = 0; i < pSync->n_conversations; i++) { + auto *pChat = pSync->conversations[i]; + + auto *pUser = AddUser(pChat->id, false); + for (int j = 0; j < pChat->n_messages; j++) { + auto *pMessage = pChat->messages[j]; + if (!pMessage->message) + continue; + + MEVENT hEvent = db_event_getById(m_szModuleName, pMessage->message->key->id); + if (hEvent) { + debugLogA("Event %s is already processed", pMessage->message->key->id); + continue; + } + + CMStringA szMessageText(getMessageText(pMessage->message->message)); + if (!szMessageText.IsEmpty()) { + PROTORECVEVENT pre = {}; + pre.timestamp = pMessage->message->messagetimestamp; + pre.szMessage = szMessageText.GetBuffer(); + pre.szMsgId = pMessage->message->key->id; + pre.flags = PREF_CREATEREAD; + if (pMessage->message->key->fromme) + pre.flags |= PREF_SENT; + ProtoChainRecvMsg(pUser->hContact, &pre); + } + } + } + break; + + case WA__HISTORY_SYNC__HISTORY_SYNC_TYPE__PUSH_NAME: + for (int i = 0; i < pSync->n_pushnames; i++) { + auto *pName = pSync->pushnames[i]; + if (auto *pUser = FindUser(pName->id)) + setUString(pUser->hContact, "Nick", pName->pushname); + } + break; + + case WA__HISTORY_SYNC__HISTORY_SYNC_TYPE__INITIAL_STATUS_V3: + for (int i = 0; i < pSync->n_statusv3messages; i++) { + auto *pStatus = pSync->statusv3messages[i]; + // TODO + } + break; + } +} diff --git a/protocols/WhatsApp/src/message.cpp b/protocols/WhatsApp/src/message.cpp index 84d863830a..74bead6695 100644 --- a/protocols/WhatsApp/src/message.cpp +++ b/protocols/WhatsApp/src/message.cpp @@ -210,17 +210,7 @@ void WhatsAppProto::ProcessMessage(WAMSG type, const Wa__WebMessageInfo &msg) // try to extract some text if (pUser) { - CMStringA szMessageText; - if (auto *pExt = body->extendedtextmessage) { - if (pExt->contextinfo && pExt->contextinfo->quotedmessage) - szMessageText.AppendFormat("> %s\n\n", pExt->contextinfo->quotedmessage->conversation); - - if (pExt->text) - szMessageText.Append(pExt->text); - } - else if (mir_strlen(body->conversation)) - szMessageText = body->conversation; - + CMStringA szMessageText(getMessageText(body)); if (!szMessageText.IsEmpty()) { PROTORECVEVENT pre = {}; pre.timestamp = timestamp; @@ -249,11 +239,24 @@ void WhatsAppProto::ProcessMessage(WAMSG type, const Wa__WebMessageInfo &msg) break; case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__APP_STATE_FATAL_EXCEPTION_NOTIFICATION: - debugLogA("History sync notification"); m_impl.m_resyncApp.Stop(); m_impl.m_resyncApp.Start(10000); break; + case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__HISTORY_SYNC_NOTIFICATION: + debugLogA("History sync notification"); + if (auto *pHist = protoMsg->historysyncnotification) { + MBinBuffer buf(DownloadEncryptedFile(directPath2url(pHist->directpath), pHist->mediakey, "History")); + if (buf.data()) { + MBinBuffer inflate(unzip(unpadBuffer16(buf))); + + proto::HistorySync sync(inflate); + if (sync) + ProcessHistorySync(sync); + } + } + break; + case WA__MESSAGE__PROTOCOL_MESSAGE__TYPE__REVOKE: break; diff --git a/protocols/WhatsApp/src/pmsg.proto.h b/protocols/WhatsApp/src/pmsg.proto.h index 1b5a5ccf4c..41ed4a3e03 100644 --- a/protocols/WhatsApp/src/pmsg.proto.h +++ b/protocols/WhatsApp/src/pmsg.proto.h @@ -38,6 +38,7 @@ namespace proto PROTOBUF_PTR(ClientPayload, wa__client_payload__descriptor); PROTOBUF_PTR(ExternalBlobReference, wa__external_blob_reference__descriptor); PROTOBUF_PTR(HandshakeMessage, wa__handshake_message__descriptor); + PROTOBUF_PTR(HistorySync, wa__history_sync__descriptor); PROTOBUF_PTR(Message, wa__message__descriptor); PROTOBUF_PTR(SyncActionData, wa__sync_action_data__descriptor); PROTOBUF_PTR(SyncdSnapshot, wa__syncd_snapshot__descriptor); diff --git a/protocols/WhatsApp/src/proto.h b/protocols/WhatsApp/src/proto.h index a11c992768..bddf804014 100644 --- a/protocols/WhatsApp/src/proto.h +++ b/protocols/WhatsApp/src/proto.h @@ -31,6 +31,15 @@ struct WAMSG }; }; +struct WAMediaKeys +{ + WAMediaKeys(const uint8_t *pKey, size_t keyLen, const char *pszMediaType); + + uint8_t iv[16]; + uint8_t cipherKey[32]; + uint8_t macKey[64]; +}; + struct WARequest { WARequest(const CMStringA &_1, WA_PKT_HANDLER _2, void *_3 = nullptr) : @@ -256,6 +265,7 @@ class WhatsAppProto : public PROTO<WhatsAppProto> void InitSync(void); void ApplyPatch(const JSONNode &index, const Wa__SyncActionValue *data); void ParsePatch(WACollection *pColl, const Wa__SyncdRecord *rec, bool bSet); + void ProcessHistorySync(const Wa__HistorySync *pSync); void ResyncServer(const OBJLIST<WACollection> &task); void ResyncAll(void); @@ -418,6 +428,7 @@ public: // Options ///////////////////////////////////////////////////////////////////////////// CMOption<wchar_t*> m_wszNick; // your nick name in presence + CMOption<wchar_t*> m_wszDeviceName; // how do you see Miranda in mobile phone CMOption<wchar_t*> m_wszDefaultGroup; // clist group to store contacts CMOption<bool> m_bHideGroupchats; // do not open chat windows on creation diff --git a/protocols/WhatsApp/src/utils.cpp b/protocols/WhatsApp/src/utils.cpp index 8f0f766651..5680c7ab1d 100644 --- a/protocols/WhatsApp/src/utils.cpp +++ b/protocols/WhatsApp/src/utils.cpp @@ -372,6 +372,8 @@ MBinBuffer WhatsAppProto::unzip(const MBinBuffer &src) } res.append(buf, sizeof(buf) - strm.avail_out); + if (ret == Z_STREAM_END) + break; } inflateEnd(&strm); @@ -411,6 +413,25 @@ CMStringA file2string(const wchar_t *pwszFileName) return res; } +CMStringA getMessageText(const Wa__Message *pMessage) +{ + CMStringA szMessageText; + + if (pMessage) { + if (auto *pExt = pMessage->extendedtextmessage) { + if (pExt->contextinfo && pExt->contextinfo->quotedmessage) + szMessageText.AppendFormat("> %s\n\n", pExt->contextinfo->quotedmessage->conversation); + + if (pExt->text) + szMessageText.Append(pExt->text); + } + else if (mir_strlen(pMessage->conversation)) + szMessageText = pMessage->conversation; + } + + return szMessageText; +} + ///////////////////////////////////////////////////////////////////////////////////////// void proto::CleanBinary(ProtobufCBinaryData &field) @@ -444,6 +465,13 @@ CMStringA directPath2url(const char *pszDirectPath) return CMStringA("https://mmg.whatsapp.net") + pszDirectPath; } +WAMediaKeys::WAMediaKeys(const uint8_t *pKey, size_t keyLen, const char *pszMediaType) +{ + CMStringA pszHkdfString(FORMAT, "WhatsApp %s Keys", pszMediaType); + + HKDF(EVP_sha256(), (BYTE *)"", 0, pKey, (int)keyLen, (BYTE *)pszHkdfString.c_str(), pszHkdfString.GetLength(), (BYTE *)this, sizeof(*this)); +} + MBinBuffer WhatsAppProto::DownloadEncryptedFile(const char *url, const ProtobufCBinaryData &mediaKeys, const char *pszMediaType) { NETLIBHTTPHEADER headers[1] = {{"Origin", "https://web.whatsapp.com"}}; @@ -459,17 +487,8 @@ MBinBuffer WhatsAppProto::DownloadEncryptedFile(const char *url, const ProtobufC auto *pResp = Netlib_HttpTransaction(m_hNetlibUser, &req); if (pResp) { if (pResp->resultCode == 200) { - CMStringA pszHkdfString = pszMediaType; - pszHkdfString.SetAt(_toupper(pszHkdfString[0]), 0); - pszHkdfString = "WhatsApp " + pszHkdfString + " Keys"; - - // 0 - 15: iv - // 16 - 47: cipherKey - // 48 - 111: macKey - uint8_t out[112]; - HKDF(EVP_sha256(), (BYTE *)"", 0, mediaKeys.data, (int)mediaKeys.len, (BYTE *)pszHkdfString.c_str(), pszHkdfString.GetLength(), out, sizeof(out)); - - ret = aesDecrypt(EVP_aes_256_cbc(), out + 16, out, pResp->pData, pResp->dataLength); + WAMediaKeys out(mediaKeys.data, mediaKeys.len, pszMediaType); + ret = aesDecrypt(EVP_aes_256_cbc(), out.cipherKey, out.iv, pResp->pData, pResp->dataLength); } } diff --git a/protocols/WhatsApp/src/utils.h b/protocols/WhatsApp/src/utils.h index 616e1de002..bda92a061e 100644 --- a/protocols/WhatsApp/src/utils.h +++ b/protocols/WhatsApp/src/utils.h @@ -105,7 +105,7 @@ class WAReader { const BYTE *m_buf, *m_limit; - uint32_t readIntN(int i); + uint32_t readIntN(int i); CMStringA readStringFromChars(int size); bool readAttributes(WANode *node, int count); @@ -208,6 +208,8 @@ MBinBuffer decodeBufStr(const std::string &buf); void padBuffer16(MBinBuffer &buf); MBinBuffer unpadBuffer16(const MBinBuffer &buf); +CMStringA getMessageText(const Wa__Message *pMessage); + CMStringA protobuf_c_text_to_string(const ProtobufCMessage *m); MBinBuffer aesDecrypt( |