summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2022-10-31 20:20:16 +0300
committerGeorge Hazan <ghazan@miranda.im>2022-10-31 20:20:16 +0300
commit476aa34eeec80dc9b1b15b48ef5db84501dcae42 (patch)
tree84dbc5fd64e397c48666c476f60c10572728578f
parent61cb1c1445b8ebfef08c864f0062baa68390dd1a (diff)
WhatsApp: history sync processing
-rw-r--r--protocols/WhatsApp/src/appsync.cpp56
-rw-r--r--protocols/WhatsApp/src/message.cpp27
-rw-r--r--protocols/WhatsApp/src/pmsg.proto.h1
-rw-r--r--protocols/WhatsApp/src/proto.h11
-rw-r--r--protocols/WhatsApp/src/utils.cpp41
-rw-r--r--protocols/WhatsApp/src/utils.h4
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(