diff options
author | George Hazan <ghazan@miranda.im> | 2017-01-06 23:09:58 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2017-01-06 23:10:07 +0300 |
commit | 3d4f907c1237b1210ae75a9530a82fabda2af484 (patch) | |
tree | 6dd0ed3b2f81e28ccf27707539e37b9cd752dd30 | |
parent | 85e72f5e8a819daeb333d38f4ef312b5f49ead6c (diff) |
gateway commands
-rw-r--r-- | protocols/Discord/src/gateway.cpp | 94 | ||||
-rw-r--r-- | protocols/Discord/src/proto.h | 23 | ||||
-rw-r--r-- | protocols/Discord/src/utils.cpp | 6 |
3 files changed, 103 insertions, 20 deletions
diff --git a/protocols/Discord/src/gateway.cpp b/protocols/Discord/src/gateway.cpp index adf07eff4a..c8ed0121ce 100644 --- a/protocols/Discord/src/gateway.cpp +++ b/protocols/Discord/src/gateway.cpp @@ -34,13 +34,15 @@ void CDiscordProto::OnReceiveGateway(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest ForkThread(&CDiscordProto::GatewayThread, NULL); } -void CDiscordProto::GatewaySend(int opCode, const char *szBuf) +void CDiscordProto::GatewaySend(int opCode, const JSONNode &pRoot) { if (m_hGatewayConnection == NULL) return; + json_string szText = pRoot.write(); + BYTE header[20]; - size_t datalen, strLen = mir_strlen(szBuf); + size_t datalen, strLen = szText.length(); header[0] = 0x80 + (opCode & 0x7F); if (strLen < 126) { header[1] = (strLen & 0xFF); @@ -68,7 +70,7 @@ void CDiscordProto::GatewaySend(int opCode, const char *szBuf) ptrA sendBuf((char*)mir_alloc(strLen + datalen)); memcpy(sendBuf, header, datalen); if (strLen) - memcpy(sendBuf.get() + datalen, szBuf, strLen); + memcpy(sendBuf.get() + datalen, szText.c_str(), strLen); Netlib_Send(m_hGatewayConnection, sendBuf, int(strLen + datalen), 0); } @@ -100,7 +102,7 @@ void CDiscordProto::GatewayThread(void*) } { CMStringA szBuf; - szBuf.AppendFormat("GET https://%s/?encoding=etf&v=6 HTTP/1.1\r\n", m_szGateway.c_str()); + szBuf.AppendFormat("GET https://%s/?v=6 HTTP/1.1\r\n", m_szGateway.c_str()); szBuf.AppendFormat("Host: %s\r\n", m_szGateway.c_str()); szBuf.AppendFormat("Upgrade: websocket\r\n"); szBuf.AppendFormat("Pragma: no-cache\r\n"); @@ -134,8 +136,9 @@ void CDiscordProto::GatewayThread(void*) bool bExit = false; int offset = 0; - unsigned char *dataBuf = NULL; + char *dataBuf = NULL; size_t dataBufSize = 0; + bool bDataBufAllocated = false; while (!bExit) { if (m_bTerminated) @@ -145,8 +148,12 @@ void CDiscordProto::GatewayThread(void*) sel.cbSize = sizeof(sel); sel.dwTimeout = 1000; sel.hReadConns[0] = m_hGatewayConnection; - if (CallService(MS_NETLIB_SELECT, 0, (LPARAM)&sel) == 0) // timeout, send a hartbeat packet - GatewaySend(0, "{ \"op\":1, \"d\":(null) }"); + CallService(MS_NETLIB_SELECT, 0, (LPARAM)&sel); + { + int iInterval = GetTickCount() - m_dwLastHeartbeat; + if (m_iHartbeatInterval && iInterval > m_iHartbeatInterval) + GatewaySendHeartbeat(); + } unsigned char buf[2048]; int bufSize = Netlib_Recv(m_hGatewayConnection, (char*)buf+offset, _countof(buf) - offset, 0); @@ -201,12 +208,21 @@ void CDiscordProto::GatewayThread(void*) debugLogA("Got packet: buffer = %d, opcode = %d, headerSize = %d, final = %d, masked = %d", bufSize, opCode, headerSize, bIsFinal, bIsMasked); + // we have some additional data, not only opcode if (bufSize > headerSize) { - size_t newSize = dataBufSize + bufSize - headerSize; - dataBuf = (unsigned char*)mir_realloc(dataBuf, newSize); - memcpy(dataBuf + dataBufSize, buf + headerSize, bufSize - headerSize); - dataBufSize = newSize; - debugLogA("data buffer reallocated to %d bytes", dataBufSize); + if (bIsFinal && dataBuf == NULL) { // it fits, no need to reallocate a buffer + bDataBufAllocated = false; + dataBuf = (char*)buf + headerSize; + dataBufSize = bufSize - headerSize; + } + else { + size_t newSize = dataBufSize + bufSize - headerSize; + dataBuf = (char*)mir_realloc(dataBuf, newSize+1); + memcpy(dataBuf + dataBufSize, buf + headerSize, bufSize - headerSize); + dataBufSize = newSize; + debugLogA("data buffer reallocated to %d bytes", dataBufSize); + } + dataBuf[dataBufSize] = 0; } if (dataBufSize < payloadSize) @@ -218,12 +234,12 @@ void CDiscordProto::GatewayThread(void*) case 2: // continuation if (bIsFinal) { // process a packet here - z_stream stream = {}; - stream.next_in = dataBuf + headerSize; - stream.avail_in = bufSize - headerSize; - deflate(&stream, true); - - mir_free(dataBuf); dataBuf = NULL; + JSONNode root = JSONNode::parse(dataBuf); + if (root) + GatewayProcess(root); + if (bDataBufAllocated) + mir_free(dataBuf); + dataBuf = NULL; dataBufSize = 0; } break; @@ -244,3 +260,45 @@ void CDiscordProto::GatewayThread(void*) m_hGatewayConnection = NULL; ShutdownSession(); } + +void CDiscordProto::GatewayProcess(const JSONNode &pRoot) +{ + int opCode = pRoot["op"].as_int(); + switch (opCode) { + case 10: // hello + m_iHartbeatInterval = pRoot["d"]["heartbeat_interval"].as_int(); + m_dwLastHeartbeat = GetTickCount(); + m_iGatewaySeq = 1; + + GatewaySendIdentify(); + break; + } +} + +void CDiscordProto::GatewaySendHeartbeat() +{ + JSONNode root; + root << INT_PARAM("op", 1) << INT_PARAM("d", m_iGatewaySeq); + GatewaySend(1, root); +} + +void CDiscordProto::GatewaySendIdentify() +{ + wchar_t wszOs[256]; + GetOSDisplayString(wszOs, _countof(wszOs)); + + char szVersion[256]; + Miranda_GetVersionText(szVersion, _countof(szVersion)); + + JSONNode props; props.set_name("properties"); + props << WCHAR_PARAM("$os", L"Windows") << CHAR_PARAM("$browser", "Miranda NG") << CHAR_PARAM("$device", "Miranda NG") + << CHAR_PARAM("$referrer", "") << CHAR_PARAM("$referring_domain", ""); + + JSONNode shards(JSON_ARRAY); shards.set_name("shard"); + shards << INT_PARAM("", 1) << INT_PARAM("", 10); + + JSONNode root; + root << CHAR_PARAM("token", m_szAccessToken) << BOOL_PARAM("compress", false) << INT_PARAM("large_threshold", 250); + root << props; // << shards; + GatewaySend(1, root); +} diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h index b822bbf323..aa4e36ef1c 100644 --- a/protocols/Discord/src/proto.h +++ b/protocols/Discord/src/proto.h @@ -26,6 +26,15 @@ struct PARAM {} }; +struct BOOL_PARAM : public PARAM +{ + bool bValue; + __forceinline BOOL_PARAM(LPCSTR _name, bool _value) : + PARAM(_name), bValue(_value) + {} +}; +AsyncHttpRequest* operator<<(AsyncHttpRequest*, const BOOL_PARAM&); + struct INT_PARAM : public PARAM { int iValue; @@ -54,6 +63,7 @@ struct WCHAR_PARAM : public PARAM AsyncHttpRequest* operator<<(AsyncHttpRequest*, const WCHAR_PARAM&); JSONNode& operator<<(JSONNode &json, const INT_PARAM ¶m); +JSONNode& operator<<(JSONNode &json, const BOOL_PARAM ¶m); JSONNode& operator<<(JSONNode &json, const CHAR_PARAM ¶m); JSONNode& operator<<(JSONNode &json, const WCHAR_PARAM ¶m); @@ -123,10 +133,19 @@ class CDiscordProto : public PROTO<CDiscordProto> HANDLE m_hGatewayNetlibUser, // the separate netlib user handle for gateways m_hGatewayConnection; // gateway connection + void __cdecl GatewayThread(void*); - void GatewaySend(int opCode, const char*); + void GatewaySend(int opCode, const JSONNode&); + void GatewayProcess(const JSONNode&); + + void GatewaySendHeartbeat(void); + void GatewaySendIdentify(void); + + void OnReceiveGateway(NETLIBHTTPREQUEST*, AsyncHttpRequest*); - void OnReceiveGateway(NETLIBHTTPREQUEST*, AsyncHttpRequest*); + int m_iHartbeatInterval; + int m_iGatewaySeq; // gateway sequence number + DWORD m_dwLastHeartbeat; ////////////////////////////////////////////////////////////////////////////////////// // options diff --git a/protocols/Discord/src/utils.cpp b/protocols/Discord/src/utils.cpp index 0513e49aac..ce000c0989 100644 --- a/protocols/Discord/src/utils.cpp +++ b/protocols/Discord/src/utils.cpp @@ -32,6 +32,12 @@ JSONNode& operator<<(JSONNode &json, const INT_PARAM ¶m) return json; } +JSONNode& operator<<(JSONNode &json, const BOOL_PARAM ¶m) +{ + json.push_back(JSONNode(param.szName, param.bValue)); + return json; +} + JSONNode& operator<<(JSONNode &json, const CHAR_PARAM ¶m) { json.push_back(JSONNode(param.szName, param.szValue)); |