diff options
author | George Hazan <ghazan@miranda.im> | 2022-10-23 20:46:16 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2022-10-23 20:46:16 +0300 |
commit | 71e5d084b56798711a93e8018b925dafaa724ef9 (patch) | |
tree | cbbb98732fbcc5114b78d15bf3032bd981a7c3bd /protocols/WhatsApp/src | |
parent | e44efd52b62e375eb76bec62b71e83d6b8b23972 (diff) |
WhatsApp: prepare to send messages + code reordering
Diffstat (limited to 'protocols/WhatsApp/src')
-rw-r--r-- | protocols/WhatsApp/src/iq.cpp | 256 | ||||
-rw-r--r-- | protocols/WhatsApp/src/message.cpp | 64 | ||||
-rw-r--r-- | protocols/WhatsApp/src/proto.cpp | 31 | ||||
-rw-r--r-- | protocols/WhatsApp/src/proto.h | 15 | ||||
-rw-r--r-- | protocols/WhatsApp/src/signal.cpp | 32 | ||||
-rw-r--r-- | protocols/WhatsApp/src/wanode.cpp | 3 |
6 files changed, 258 insertions, 143 deletions
diff --git a/protocols/WhatsApp/src/iq.cpp b/protocols/WhatsApp/src/iq.cpp index 063a71264c..8abf959895 100644 --- a/protocols/WhatsApp/src/iq.cpp +++ b/protocols/WhatsApp/src/iq.cpp @@ -76,129 +76,27 @@ void WhatsAppProto::UploadMorePrekeys() ///////////////////////////////////////////////////////////////////////////////////////// -void WhatsAppProto::OnIqDoNothing(const WANode &) +void WhatsAppProto::OnIqDoNothing(const WANode&) { } ///////////////////////////////////////////////////////////////////////////////////////// -void WhatsAppProto::OnNotifyDevices(const WANode &node) -{ - if (!mir_strcmp(node.getAttr("jid"), m_szJid)) - debugLogA("received list of my own devices"); - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnNotifyEncrypt(const WANode &node) -{ - if (!mir_strcmp(node.getAttr("from"), S_WHATSAPP_NET)) - OnIqCountPrekeys(node); - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnReceiveInfo(const WANode &node) +void WhatsAppProto::OnIqGetUsync(const WANode &node) { - if (auto *pChild = node.getFirstChild()) { - if (pChild->title == "offline") { - debugLogA("Processed %d offline events", pChild->getAttrInt("count")); - - // retrieve loaded prekeys count - if (!m_bUpdatedPrekeys) - WSSendNode(WANodeIq(IQ::GET, "encrypt") << XCHILD("count"), &WhatsAppProto::OnIqCountPrekeys); - - for (auto &it: m_arCollections) { - if (it->version == 0) { - m_impl.m_resyncApp.Stop(); - m_impl.m_resyncApp.Start(1000); - break; - } - } - } - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::ProcessReceipt(MCONTACT hContact, const char *msgId, bool bRead) -{ - MEVENT hEvent = db_event_getById(m_szModuleName, msgId); - if (hEvent == 0) - return; - - if (g_plugin.bHasMessageState) - CallService(MS_MESSAGESTATE_UPDATE, hContact, bRead ? MRD_TYPE_READ : MRD_TYPE_DELIVERED); - - if (bRead) - db_event_markRead(hContact, hEvent); -} - -void WhatsAppProto::OnReceiveReceipt(const WANode &node) -{ - if (auto *pUser = FindUser(node.getAttr("from"))) { - bool bRead = mir_strcmp(node.getAttr("type"), "read") == 0; - ProcessReceipt(pUser->hContact, node.getAttr("id"), bRead); + m_arDevices.destroy(); - if (auto *pList = node.getChild("list")) + if (auto *nUser = node.getChild("usync")->getChild("list")->getChild("user")) { + auto *pszJid = nUser->getAttr("jid"); + if (auto *pList = nUser->getChild("devices")->getChild("device-list")) for (auto &it : pList->getChildren()) - if (it->title == "item") - ProcessReceipt(pUser->hContact, it->getAttr("id"), bRead); - } - - SendAck(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WhatsAppProto::OnStreamError(const WANode &node) -{ - m_bTerminated = true; - - if (auto *pszCode = node.getAttr("code")) { - switch (atoi(pszCode)) { - case 401: - debugLogA("Connection logged out from another device, exiting"); - break; - - case 408: - debugLogA("Connection lost, exiting"); - break; - - case 411: - debugLogA("Conflict between two devices, exiting"); - break; - - case 428: - debugLogA("Connection forcibly closed by the server, exiting"); - break; - - case 440: - debugLogA("Connection replaced from another device, exiting"); - break; - - case 515: - debugLogA("Server required to restart immediately, leaving thread"); - m_bRespawn = true; - break; - } + if (it->title == "device") + m_arDevices.insert(new WADevice(pszJid, it->getAttrInt("id"))); } } ///////////////////////////////////////////////////////////////////////////////////////// -void WhatsAppProto::OnIqResult(const WANode &node) -{ - if (auto *pszId = node.getAttr("id")) - for (auto &it: m_arPacketQueue) - if (it->szPacketId == pszId) - (this->*it->pHandler)(node); -} - -///////////////////////////////////////////////////////////////////////////////////////// - void WhatsAppProto::OnIqPairDevice(const WANode &node) { WSSendNode(WANodeIq(IQ::RESULT) << CHAR_PARAM("id", node.getAttr("id"))); @@ -325,6 +223,41 @@ void WhatsAppProto::OnIqPairSuccess(const WANode &node) ///////////////////////////////////////////////////////////////////////////////////////// +void WhatsAppProto::OnIqResult(const WANode &node) +{ + if (auto *pszId = node.getAttr("id")) + for (auto &it : m_arPacketQueue) + if (it->szPacketId == pszId) + (this->*it->pHandler)(node); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void WhatsAppProto::OnNotifyAny(const WANode &node) +{ + SendAck(node); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void WhatsAppProto::OnNotifyDevices(const WANode &node) +{ + if (!mir_strcmp(node.getAttr("jid"), m_szJid)) + debugLogA("received list of my own devices"); + SendAck(node); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void WhatsAppProto::OnNotifyEncrypt(const WANode &node) +{ + if (!mir_strcmp(node.getAttr("from"), S_WHATSAPP_NET)) + OnIqCountPrekeys(node); + SendAck(node); +} + +///////////////////////////////////////////////////////////////////////////////////////// + void WhatsAppProto::OnProcessHandshake(const void *pData, int cbLen) { proto::HandshakeMessage msg; @@ -449,6 +382,108 @@ LBL_Error: ///////////////////////////////////////////////////////////////////////////////////////// +void WhatsAppProto::OnReceiveInfo(const WANode &node) +{ + if (auto *pChild = node.getFirstChild()) { + if (pChild->title == "offline") { + debugLogA("Processed %d offline events", pChild->getAttrInt("count")); + + // retrieve loaded prekeys count + if (!m_bUpdatedPrekeys) + WSSendNode(WANodeIq(IQ::GET, "encrypt") << XCHILD("count"), &WhatsAppProto::OnIqCountPrekeys); + + if (m_arDevices.getCount() == 0) { + WANodeIq iq(IQ::GET, "usync"); + + auto *pNode1 = iq.addChild("usync"); + *pNode1 << CHAR_PARAM("sid", GenerateMessageId()) << CHAR_PARAM("mode", "query") << CHAR_PARAM("last", "true") + << CHAR_PARAM("index", "0") << CHAR_PARAM("context", "message"); + + pNode1->addChild("query")->addChild("devices")->addAttr("version", "2"); + pNode1->addChild("list")->addChild("user")->addAttr("jid", m_szJid); + + WSSendNode(iq, &WhatsAppProto::OnIqGetUsync); + } + + for (auto &it : m_arCollections) { + if (it->version == 0) { + m_impl.m_resyncApp.Stop(); + m_impl.m_resyncApp.Start(1000); + break; + } + } + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void WhatsAppProto::ProcessReceipt(MCONTACT hContact, const char *msgId, bool bRead) +{ + MEVENT hEvent = db_event_getById(m_szModuleName, msgId); + if (hEvent == 0) + return; + + if (g_plugin.bHasMessageState) + CallService(MS_MESSAGESTATE_UPDATE, hContact, bRead ? MRD_TYPE_READ : MRD_TYPE_DELIVERED); + + if (bRead) + db_event_markRead(hContact, hEvent); +} + +void WhatsAppProto::OnReceiveReceipt(const WANode &node) +{ + if (auto *pUser = FindUser(node.getAttr("from"))) { + bool bRead = mir_strcmp(node.getAttr("type"), "read") == 0; + ProcessReceipt(pUser->hContact, node.getAttr("id"), bRead); + + if (auto *pList = node.getChild("list")) + for (auto &it : pList->getChildren()) + if (it->title == "item") + ProcessReceipt(pUser->hContact, it->getAttr("id"), bRead); + } + + SendAck(node); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void WhatsAppProto::OnStreamError(const WANode &node) +{ + m_bTerminated = true; + + if (auto *pszCode = node.getAttr("code")) { + switch (atoi(pszCode)) { + case 401: + debugLogA("Connection logged out from another device, exiting"); + break; + + case 408: + debugLogA("Connection lost, exiting"); + break; + + case 411: + debugLogA("Conflict between two devices, exiting"); + break; + + case 428: + debugLogA("Connection forcibly closed by the server, exiting"); + break; + + case 440: + debugLogA("Connection replaced from another device, exiting"); + break; + + case 515: + debugLogA("Server required to restart immediately, leaving thread"); + m_bRespawn = true; + break; + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + void WhatsAppProto::OnSuccess(const WANode &) { OnLoggedIn(); @@ -467,6 +502,7 @@ void WhatsAppProto::InitPersistentHandlers() m_arPersistent.insert(new WAPersistentHandler("notification", "encrypt", 0, 0, &WhatsAppProto::OnNotifyEncrypt)); m_arPersistent.insert(new WAPersistentHandler("notification", "account_sync", 0, 0, &WhatsAppProto::OnAccountSync)); m_arPersistent.insert(new WAPersistentHandler("notification", "server_sync", 0, 0, &WhatsAppProto::OnServerSync)); + m_arPersistent.insert(new WAPersistentHandler("notification", 0, 0, 0, &WhatsAppProto::OnNotifyAny)); m_arPersistent.insert(new WAPersistentHandler("ib", 0, 0, 0, &WhatsAppProto::OnReceiveInfo)); m_arPersistent.insert(new WAPersistentHandler("message", 0, 0, 0, &WhatsAppProto::OnReceiveMessage)); diff --git a/protocols/WhatsApp/src/message.cpp b/protocols/WhatsApp/src/message.cpp index abaaa89faf..5c0504d4b5 100644 --- a/protocols/WhatsApp/src/message.cpp +++ b/protocols/WhatsApp/src/message.cpp @@ -303,3 +303,67 @@ void WhatsAppProto::ProcessMessage(WAMSG type, const proto::WebMessageInfo &msg) } } } + +///////////////////////////////////////////////////////////////////////////////////////// + +bool WhatsAppProto::CreateMsgParticipant(WANode *pParticipants, const WAJid &jid, const MBinBuffer &orig) +{ + int type = 0; + + try { + SignalBuffer pBuffer(m_signalStore.encryptSignalProto(jid, orig, type)); + + auto *pNode = pParticipants->addChild("to"); + pNode->addAttr("jid", jid.toString()); + + auto *pEnc = pNode->addChild("enc"); + *pEnc << CHAR_PARAM("v", "2") << CHAR_PARAM("type", (type == 3) ? "pkmsg" : "msg"); + pEnc->content.assign(pBuffer.data(), pBuffer.len()); + } + catch (const char *) { + } + + return type == 3; +} + +int WhatsAppProto::SendTextMessage(const char *jid, const char *pszMsg) +{ + char msgId[16], szMsgId[33]; + Utils_GetRandom(msgId, sizeof(msgId)); + bin2hex(msgId, sizeof(msgId), szMsgId); + strupr(szMsgId); + + auto *pBody = new proto::Message(); + pBody->set_conversation(pszMsg); + + auto *pSentBody = new proto::Message_DeviceSentMessage(); + pSentBody->set_allocated_message(pBody); + pSentBody->set_destinationjid(jid); + + proto::Message msg; + msg.set_allocated_devicesentmessage(pSentBody); + + MBinBuffer encMsg(msg.ByteSizeLong()); + msg.SerializeToArray(encMsg.data(), (int)encMsg.length()); + + WANode payLoad("message"); + payLoad << CHAR_PARAM("id", szMsgId) << CHAR_PARAM("type", "text") << CHAR_PARAM("to", jid); + + auto *pParticipants = payLoad.addChild("participants"); + bool shouldIncludeIdentity = CreateMsgParticipant(pParticipants, WAJid(jid), false); + for (auto &it : m_arDevices) + shouldIncludeIdentity |= CreateMsgParticipant(pParticipants, it->jid, true); + + if (shouldIncludeIdentity) { + MBinBuffer encIdentity(m_signalStore.encodeSignedIdentity(true)); + auto *pNode = payLoad.addChild("device-identity"); + pNode->content.assign(encIdentity.data(), encIdentity.length()); + } + + WSSendNode(payLoad); + + mir_cslock lck(m_csOwnMessages); + int pktId = m_iPacketId++; + m_arOwnMsgs.insert(new WAOwnMessage(pktId, jid, szMsgId)); + return pktId; +} diff --git a/protocols/WhatsApp/src/proto.cpp b/protocols/WhatsApp/src/proto.cpp index 701bf8f3a7..863d12df1b 100644 --- a/protocols/WhatsApp/src/proto.cpp +++ b/protocols/WhatsApp/src/proto.cpp @@ -203,6 +203,7 @@ int WhatsAppProto::SetStatus(int new_status) ///////////////////////////////////////////////////////////////////////////////////////// +/* void WhatsAppProto::OnSendMessage(const JSONNode &node, void*) { CMStringA szPrefix= node["$id$"].as_mstring(); @@ -227,6 +228,7 @@ void WhatsAppProto::OnSendMessage(const JSONNode &node, void*) ProtoBroadcastAck(tmp.hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)tmp.pktId, LPARAM(wszError.c_str())); } } +*/ int WhatsAppProto::SendMsg(MCONTACT hContact, int, const char *pszMsg) { @@ -239,34 +241,7 @@ int WhatsAppProto::SendMsg(MCONTACT hContact, int, const char *pszMsg) return 0; } - char msgId[16], szMsgId[33]; - Utils_GetRandom(msgId, sizeof(msgId)); - bin2hex(msgId, sizeof(msgId), szMsgId); - - auto *key = new proto::MessageKey(); - key->set_remotejid(jid); - key->set_fromme(true); - key->set_id(szMsgId); - - proto::WebMessageInfo msg; - msg.set_allocated_key(key); - msg.mutable_message()->set_conversation(pszMsg); - msg.set_messagetimestamp(_time64(0)); - - size_t cbBinaryLen = msg.ByteSizeLong(); - mir_ptr<BYTE> pBuf((BYTE *)mir_alloc(cbBinaryLen)); - msg.SerializeToArray(pBuf, (int)cbBinaryLen); - - WANode payLoad; - payLoad.title = "action"; - payLoad.addAttr("type", "relay"); - payLoad.content.assign(pBuf, cbBinaryLen); - - int pktId = WSSendNode(payLoad); - - mir_cslock lck(m_csOwnMessages); - m_arOwnMsgs.insert(new WAOwnMessage(pktId, hContact, szMsgId)); - return pktId; + return SendTextMessage(jid, pszMsg); } int WhatsAppProto::UserIsTyping(MCONTACT hContact, int) diff --git a/protocols/WhatsApp/src/proto.h b/protocols/WhatsApp/src/proto.h index 35200ad61e..085a53116b 100644 --- a/protocols/WhatsApp/src/proto.h +++ b/protocols/WhatsApp/src/proto.h @@ -97,15 +97,14 @@ struct WAUser struct WAOwnMessage { - WAOwnMessage(int _1, MCONTACT _2, const char *_3) : + WAOwnMessage(int _1, const char *_2, const char *_3) : pktId(_1), - hContact(_2), + szJid(_2), szPrefix(_3) {} int pktId; - MCONTACT hContact; - CMStringA szPrefix; + CMStringA szPrefix, szJid; }; struct WACollection @@ -207,6 +206,9 @@ public: signal_buffer* decryptSignalProto(const CMStringA &from, const char *pszType, const MBinBuffer &encrypted); signal_buffer* decryptGroupSignalProto(const CMStringA &from, const CMStringA &author, const MBinBuffer &encrypted); + signal_buffer* encryptSignalProto(const WAJid &to, const MBinBuffer &buf, int &type); + + MBinBuffer encodeSignedIdentity(bool); void generatePrekeys(int count); void logError(int code, const char *szMessage); @@ -304,6 +306,7 @@ class WhatsAppProto : public PROTO<WhatsAppProto> uint16_t m_wMsgPrefix[2]; CMStringA GenerateMessageId(); void ProcessMessage(WAMSG type, const proto::WebMessageInfo &msg); + bool CreateMsgParticipant(WANode *pParticipants, const WAJid &jid, const MBinBuffer &orig); void ProcessReceipt(MCONTACT hContact, const char *msgId, bool bRead); @@ -322,6 +325,7 @@ class WhatsAppProto : public PROTO<WhatsAppProto> void SendAck(const WANode &node); void SendReceipt(const char *pszTo, const char *pszParticipant, const char *pszId, const char *pszType); void SendKeepAlive(); + int SendTextMessage(const char *jid, const char *pszMsg); void SetServerStatus(int iStatus); /// Popups ///////////////////////////////////////////////////////////////////////////// @@ -336,7 +340,6 @@ class WhatsAppProto : public PROTO<WhatsAppProto> void OnGetAvatarInfo(const JSONNode &node, void*); void OnGetChatInfo(const JSONNode &node, void*); - void OnSendMessage(const JSONNode &node, void*); void OnProcessHandshake(const void *pData, int cbLen); @@ -345,10 +348,12 @@ class WhatsAppProto : public PROTO<WhatsAppProto> void OnIqBlockList(const WANode &node); void OnIqCountPrekeys(const WANode &node); void OnIqDoNothing(const WANode &node); + void OnIqGetUsync(const WANode &node); void OnIqPairDevice(const WANode &node); void OnIqPairSuccess(const WANode &node); void OnIqResult(const WANode &node); void OnIqServerSync(const WANode &node); + void OnNotifyAny(const WANode &node); void OnNotifyDevices(const WANode &node); void OnNotifyEncrypt(const WANode &node); void OnReceiveInfo(const WANode &node); diff --git a/protocols/WhatsApp/src/signal.cpp b/protocols/WhatsApp/src/signal.cpp index 5710392e43..39b0347eae 100644 --- a/protocols/WhatsApp/src/signal.cpp +++ b/protocols/WhatsApp/src/signal.cpp @@ -517,6 +517,38 @@ signal_buffer* MSignalStore::decryptGroupSignalProto(const CMStringA &group, con } ///////////////////////////////////////////////////////////////////////////////////////// +// encryption + +signal_buffer* MSignalStore::encryptSignalProto(const WAJid &to, const MBinBuffer &buf, int &type) +{ + auto *pSession = createSession(to.user, to.device); + + ciphertext_message *pEncrypted; + logError( + session_cipher_encrypt(pSession->getCipher(), buf.data(), buf.length(), &pEncrypted), + "unable to encrypt signal message"); + + type = ciphertext_message_get_type(pEncrypted); + + auto *res = ciphertext_message_get_serialized(pEncrypted); + signal_message_destroy((signal_type_base *)pEncrypted); + return res; +} + +MBinBuffer MSignalStore::encodeSignedIdentity(bool bIncludeSignatureKey) +{ + proto::ADVSignedDeviceIdentity identity; + identity << pProto->getBlob("WAAccount"); + + if (!bIncludeSignatureKey) + identity.clear_accountsignaturekey(); + + MBinBuffer res(identity.ByteSize()); + identity.SerializeToArray(res.data(), (int)res.length()); + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////// // generate and save pre keys set void MSignalStore::generatePrekeys(int count) diff --git a/protocols/WhatsApp/src/wanode.cpp b/protocols/WhatsApp/src/wanode.cpp index cbe9408f20..8d5a2ef7f0 100644 --- a/protocols/WhatsApp/src/wanode.cpp +++ b/protocols/WhatsApp/src/wanode.cpp @@ -104,6 +104,9 @@ WANode *WANode::addChild(const char *pszName) WANode* WANode::getChild(const char *pszName) const { + if (this == nullptr) + return nullptr; + for (auto &it : children) if (it->title == pszName) return it; |