summaryrefslogtreecommitdiff
path: root/protocols/WhatsAppWeb/src/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/WhatsAppWeb/src/server.cpp')
-rw-r--r--protocols/WhatsAppWeb/src/server.cpp329
1 files changed, 0 insertions, 329 deletions
diff --git a/protocols/WhatsAppWeb/src/server.cpp b/protocols/WhatsAppWeb/src/server.cpp
deleted file mode 100644
index 91f36b7df7..0000000000
--- a/protocols/WhatsAppWeb/src/server.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
-
-WhatsAppWeb plugin for Miranda NG
-Copyright © 2019-22 George Hazan
-
-*/
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// gateway worker thread
-
-void WhatsAppProto::ServerThread(void *)
-{
- do {
- m_bRespawn = false;
- ServerThreadWorker();
- }
- while (m_bRespawn);
-
- OnLoggedOut();
-}
-
-void WhatsAppProto::ServerThreadWorker()
-{
- // connect websocket
- NETLIBHTTPHEADER hdrs[] =
- {
- { "Origin", "https://web.whatsapp.com" },
- { 0, 0 }
- };
-
- NLHR_PTR pReply(WebSocket_Connect(m_hNetlibUser, "web.whatsapp.com/ws/chat", hdrs));
- if (pReply == nullptr) {
- debugLogA("Server connection failed, exiting");
- return;
- }
-
- if (pReply->resultCode != 101)
- return;
-
- delete m_noise;
- m_noise = new WANoise(this);
- m_noise->init();
-
- debugLogA("Server connection succeeded");
- m_hServerConn = pReply->nlc;
- m_lastRecvTime = time(0);
- m_iPacketId = 1;
-
- Utils_GetRandom(m_wMsgPrefix, sizeof(m_wMsgPrefix));
-
- auto &pubKey = m_noise->ephemeral.pub;
- auto *client = new proto::HandshakeMessage::ClientHello(); client->set_ephemeral(pubKey.data(), pubKey.length());
- proto::HandshakeMessage msg; msg.set_allocated_clienthello(client);
- WSSend(msg);
-
- MBinBuffer netbuf;
-
- for (m_bTerminated = false; !m_bTerminated;) {
- unsigned char buf[2048];
- int bufSize = Netlib_Recv(m_hServerConn, (char *)buf, _countof(buf), MSG_NODUMP);
- if (bufSize == 0) {
- debugLogA("Gateway connection gracefully closed");
- break;
- }
- if (bufSize < 0) {
- debugLogA("Gateway connection error, exiting");
- break;
- }
-
- netbuf.append(buf, bufSize);
-
- WSHeader hdr;
- if (!WebSocket_InitHeader(hdr, netbuf.data(), netbuf.length()))
- continue;
-
- // we lack some data, let's read them
- if (netbuf.length() < hdr.headerSize + hdr.payloadSize)
- if (!WSReadPacket(hdr, netbuf))
- break;
-
- // debugLogA("Got packet: buffer = %d, opcode = %d, headerSize = %d, payloadSize = %d, final = %d, masked = %d",
- // netbuf.length(), hdr.opCode, hdr.headerSize, hdr.payloadSize, hdr.bIsFinal, hdr.bIsMasked);
- // Netlib_Dump(m_hServerConn, netbuf.data(), netbuf.length(), false, 0);
-
- m_lastRecvTime = time(0);
-
- // read all payloads from the current buffer, one by one
- while (true) {
- MBinBuffer currPacket;
- currPacket.assign(netbuf.data() + hdr.headerSize, hdr.payloadSize);
-
- switch (hdr.opCode) {
- case 1: // json packet
- debugLogA("Text packet, skipping");
- /*
- currPacket.append("", 1); // add 0 to use strchr safely
- CMStringA szJson(pos, (int)dataSize);
-
- JSONNode root = JSONNode::parse(szJson);
- if (root) {
- debugLogA("JSON received:\n%s", start);
-
- CMStringA szPrefix(start, int(pos - start - 1));
- auto *pReq = m_arPacketQueue.find((WARequest *)&szPrefix);
- if (pReq != nullptr) {
- root << CHAR_PARAM("$id$", szPrefix);
- }
- }
- }
- */
- break;
-
- case 2: // binary packet
- if (hdr.payloadSize > 32)
- ProcessBinaryPacket(currPacket.data(), hdr.payloadSize);
- break;
-
- case 8: // close
- debugLogA("server required to exit");
- m_bRespawn = m_bTerminated = true; // simply reconnect, don't exit
- break;
-
- default:
- Netlib_Dump(m_hServerConn, currPacket.data(), hdr.payloadSize, false, 0);
- }
-
- netbuf.remove(hdr.headerSize + hdr.payloadSize);
- // debugLogA("%d bytes removed from network buffer, %d bytes remain", hdr.headerSize + hdr.payloadSize, netbuf.length());
- if (netbuf.length() == 0)
- break;
-
- // if we have not enough data for header, continue reading
- if (!WebSocket_InitHeader(hdr, netbuf.data(), netbuf.length())) {
- debugLogA("not enough data for header, continue reading");
- break;
- }
-
- // if we have not enough data for data, continue reading
- if (hdr.headerSize + hdr.payloadSize > netbuf.length()) {
- debugLogA("not enough place for data (%d+%d > %d), continue reading", hdr.headerSize, hdr.payloadSize, netbuf.length());
- break;
- }
-
- debugLogA("Got inner packet: buffer = %d, opcode = %d, headerSize = %d, payloadSize = %d, final = %d, masked = %d",
- netbuf.length(), hdr.opCode, hdr.headerSize, hdr.payloadSize, hdr.bIsFinal, hdr.bIsMasked);
- }
- }
-
- debugLogA("Server connection dropped");
- Netlib_CloseHandle(m_hServerConn);
- m_hServerConn = nullptr;
-}
-
-bool WhatsAppProto::WSReadPacket(const WSHeader &hdr, MBinBuffer &res)
-{
- size_t currPacketSize = res.length() - hdr.headerSize;
-
- char buf[1024];
- while (currPacketSize < hdr.payloadSize) {
- int result = Netlib_Recv(m_hServerConn, buf, _countof(buf), MSG_NODUMP);
- if (result == 0) {
- debugLogA("Gateway connection gracefully closed");
- return false;
- }
- if (result < 0) {
- debugLogA("Gateway connection error, exiting");
- return false;
- }
-
- currPacketSize += result;
- res.append(buf, result);
- }
- return true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Binary data processing
-
-void WhatsAppProto::ProcessBinaryPacket(const void *pData, size_t cbDataLen)
-{
- while (size_t payloadLen = m_noise->decodeFrame(pData, cbDataLen)) {
- if (m_noise->bInitFinished) {
- MBinBuffer buf = m_noise->decrypt(pData, payloadLen);
-
- WAReader rdr(buf.data(), buf.length());
- auto b = rdr.readInt8();
- if (b & 2) {
- buf.remove(1);
- buf = unzip(buf);
- rdr = WAReader(buf.data(), buf.length());
- }
-
- if (WANode *pNode = rdr.readNode()) {
- CMStringA szText;
- pNode->print(szText);
- debugLogA("Got binary node:\n%s", szText.c_str());
-
- auto pHandler = FindPersistentHandler(*pNode);
- if (pHandler)
- (this->*pHandler)(*pNode);
- else
- debugLogA("cannot handle incoming message");
-
- delete pNode;
- }
- else {
- debugLogA("wrong or broken payload");
- Netlib_Dump(m_hServerConn, pData, cbDataLen, false, 0);
- }
- }
- else OnProcessHandshake(pData, (int)payloadLen);
-
- pData = (BYTE*)pData + payloadLen;
- cbDataLen -= payloadLen;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void WhatsAppProto::OnLoggedIn()
-{
- debugLogA("WhatsAppProto::OnLoggedIn");
-
- SetServerStatus(m_iDesiredStatus);
-
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iDesiredStatus);
- m_iStatus = m_iDesiredStatus;
-
- m_impl.m_keepAlive.Start(1000);
-
- // retrieve loaded prekeys count
- WSSendNode(
- WANodeIq(IQ::GET, "encrypt") << XCHILD("count"),
- &WhatsAppProto::OnIqCountPrekeys);
-
- // retrieve initial info
- WANodeIq abt(IQ::GET, "abt");
- abt.addChild("props")->addAttr("protocol", "1");
- WSSendNode(abt, &WhatsAppProto::OnIqDoNothing);
-
- WSSendNode(
- WANodeIq(IQ::GET, "w") << XCHILD("props"),
- &WhatsAppProto::OnIqDoNothing);
-
- WSSendNode(
- WANodeIq(IQ::GET, "blocklist"),
- &WhatsAppProto::OnIqBlockList);
-
- WSSendNode(
- WANodeIq(IQ::GET, "privacy") << XCHILD("privacy"),
- &WhatsAppProto::OnIqDoNothing);
-
- /*
- OBJLIST<WACollection> task(1);
- task.insert(new WACollection("regular_high", 9));
- task.insert(new WACollection("regular_low", 11));
- ResyncServer(task);
- */
-}
-
-void WhatsAppProto::OnLoggedOut(void)
-{
- m_impl.m_keepAlive.Stop();
-
- debugLogA("WhatsAppProto::OnLoggedOut");
- m_bTerminated = true;
-
- ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, ID_STATUS_OFFLINE);
- m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
-
- setAllContactStatuses(ID_STATUS_OFFLINE, false);
-}
-
-void WhatsAppProto::SendKeepAlive()
-{
- time_t now = time(0);
- if (now - m_lastRecvTime > 20) {
- WSSendNode(WANodeIq(IQ::GET, "w:p") << XCHILD("ping"), &WhatsAppProto::OnIqDoNothing);
-
- m_lastRecvTime = now;
- }
-}
-
-void WhatsAppProto::SendReceipt(const char *pszTo, const char *pszParticipant, const char *pszId, const char *pszType)
-{
- WANode receipt("receipt");
- receipt << CHAR_PARAM("id", pszId);
-
- if (!mir_strcmp(pszType, "read") || !mir_strcmp(pszType, "read-self"))
- receipt << INT_PARAM("t", time(0));
-
- if (!mir_strcmp(pszType, "sender") && WAJid(pszTo).isUser())
- receipt << CHAR_PARAM("to", pszParticipant) << CHAR_PARAM("recipient", pszTo);
- else {
- receipt << CHAR_PARAM("to", pszTo);
- if (pszParticipant)
- receipt << CHAR_PARAM("participant", pszParticipant);
- }
-
- if (pszType)
- receipt << CHAR_PARAM("type", pszType);
- WSSendNode(receipt, &WhatsAppProto::OnIqDoNothing);
-}
-
-void WhatsAppProto::SetServerStatus(int iStatus)
-{
- if (mir_wstrlen(m_wszNick))
- WSSendNode(
- WANode("presence") << CHAR_PARAM("name", T2Utf(m_wszNick)) << CHAR_PARAM("type", (iStatus == ID_STATUS_ONLINE) ? "available" : "unavailable"),
- &WhatsAppProto::OnIqDoNothing);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void WhatsAppProto::ShutdownSession()
-{
- if (m_bTerminated)
- return;
-
- debugLogA("WhatsAppProto::ShutdownSession");
-
- // shutdown all resources
- if (m_hServerConn)
- Netlib_Shutdown(m_hServerConn);
-
- OnLoggedOut();
-}