summaryrefslogtreecommitdiff
path: root/protocols/WhatsApp/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2022-10-23 20:46:16 +0300
committerGeorge Hazan <ghazan@miranda.im>2022-10-23 20:46:16 +0300
commit71e5d084b56798711a93e8018b925dafaa724ef9 (patch)
treecbbb98732fbcc5114b78d15bf3032bd981a7c3bd /protocols/WhatsApp/src
parente44efd52b62e375eb76bec62b71e83d6b8b23972 (diff)
WhatsApp: prepare to send messages + code reordering
Diffstat (limited to 'protocols/WhatsApp/src')
-rw-r--r--protocols/WhatsApp/src/iq.cpp256
-rw-r--r--protocols/WhatsApp/src/message.cpp64
-rw-r--r--protocols/WhatsApp/src/proto.cpp31
-rw-r--r--protocols/WhatsApp/src/proto.h15
-rw-r--r--protocols/WhatsApp/src/signal.cpp32
-rw-r--r--protocols/WhatsApp/src/wanode.cpp3
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;