summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--protocols/Steam/Steam.vcxproj1
-rw-r--r--protocols/Steam/Steam.vcxproj.filters3
-rw-r--r--protocols/Steam/src/stdafx.h2
-rw-r--r--protocols/Steam/src/steam_login.cpp76
-rw-r--r--protocols/Steam/src/steam_proto.cpp2
-rw-r--r--protocols/Steam/src/steam_proto.h25
-rw-r--r--protocols/Steam/src/steam_server.cpp153
-rw-r--r--protocols/Steam/src/steam_utils.cpp57
-rw-r--r--protocols/Steam/src/steam_ws.cpp237
9 files changed, 321 insertions, 235 deletions
diff --git a/protocols/Steam/Steam.vcxproj b/protocols/Steam/Steam.vcxproj
index ce3c8d4cc9..e1a2f61bfa 100644
--- a/protocols/Steam/Steam.vcxproj
+++ b/protocols/Steam/Steam.vcxproj
@@ -68,6 +68,7 @@
<ClCompile Include="src\steam_request.cpp" />
<ClCompile Include="src\steam_server.cpp" />
<ClCompile Include="src\steam_utils.cpp" />
+ <ClCompile Include="src\steam_ws.cpp" />
<ClCompile Include="src\steam_xstatus.cpp" />
<ClInclude Include="src\api\app_info.h" />
<ClInclude Include="src\api\avatar.h" />
diff --git a/protocols/Steam/Steam.vcxproj.filters b/protocols/Steam/Steam.vcxproj.filters
index 13f6be78d9..e11da99ae2 100644
--- a/protocols/Steam/Steam.vcxproj.filters
+++ b/protocols/Steam/Steam.vcxproj.filters
@@ -84,6 +84,9 @@
<ClCompile Include="src\protobuf-c\steammessages_clientserver_friends.pb-c.cpp">
<Filter>Source Files\protobuf</Filter>
</ClCompile>
+ <ClCompile Include="src\steam_ws.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\resource.h">
diff --git a/protocols/Steam/src/stdafx.h b/protocols/Steam/src/stdafx.h
index 368e3e5bf6..2beb51b714 100644
--- a/protocols/Steam/src/stdafx.h
+++ b/protocols/Steam/src/stdafx.h
@@ -66,6 +66,8 @@ extern HANDLE hExtraXStatus;
#define now() time(0)
+int64_t getRandomInt();
+
#include "steam_dialogs.h"
#include "api/enums.h"
diff --git a/protocols/Steam/src/steam_login.cpp b/protocols/Steam/src/steam_login.cpp
index c852f2e6f7..d607657485 100644
--- a/protocols/Steam/src/steam_login.cpp
+++ b/protocols/Steam/src/steam_login.cpp
@@ -20,18 +20,11 @@ bool CSteamProto::IsMe(const char *steamId)
return m_iSteamId == _atoi64(steamId);
}
-void CSteamProto::LoginFailed()
-{
- m_bTerminated = true;
-
- m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
- ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iStatus);
-}
-
void CSteamProto::Logout()
{
m_bTerminated = true;
+ m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iStatus);
}
@@ -55,13 +48,15 @@ void CSteamProto::OnGotHosts(const JSONNode &root, void*)
void CSteamProto::Login()
{
+ m_iSessionId = 0;
+
CMsgClientHello hello;
hello.protocol_version = STEAM_PROTOCOL_VERSION; hello.has_protocol_version = true;
WSSend(EMsg::ClientHello, hello);
ptrA username(getUStringA("Username"));
if (username == NULL) {
- LoginFailed();
+ Logout();
return;
}
@@ -74,7 +69,7 @@ void CSteamProto::OnGotRsaKey(const uint8_t *buf, size_t cbLen)
{
proto::AuthenticationGetPasswordRSAPublicKeyResponse reply(buf, cbLen);
if (reply == nullptr || !reply->publickey_exp || !reply->publickey_mod) {
- LoginFailed();
+ Logout();
return;
}
@@ -125,7 +120,7 @@ INT_PTR CALLBACK CSteamProto::EnterEmailCode(void *param)
ppro->SendConfirmationCode(true, T2Utf(es.ptszResult));
mir_free(es.ptszResult);
}
- else ppro->LoginFailed();
+ else ppro->Logout();
return 0;
}
@@ -140,7 +135,7 @@ INT_PTR CALLBACK CSteamProto::EnterTotpCode(void *param)
ppro->SendConfirmationCode(false, T2Utf(es.ptszResult));
mir_free(es.ptszResult);
}
- else ppro->LoginFailed();
+ else ppro->Logout();
return 0;
}
@@ -148,7 +143,7 @@ void CSteamProto::OnGotConfirmationCode(const uint8_t *buf, size_t cbLen)
{
proto::AuthenticationUpdateAuthSessionWithSteamGuardCodeResponse reply(buf, cbLen);
if (reply == nullptr)
- LoginFailed();
+ Logout();
else
SendPollRequest();
}
@@ -173,7 +168,7 @@ void CSteamProto::OnBeginSession(const uint8_t *buf, size_t cbLen)
{
proto::AuthenticationBeginAuthSessionViaCredentialsResponse reply(buf, cbLen);
if (reply == nullptr) {
- LoginFailed();
+ Logout();
return;
}
@@ -207,7 +202,7 @@ void CSteamProto::OnBeginSession(const uint8_t *buf, size_t cbLen)
}
debugLogA("Unsupported confirmation code: %i", conf->confirmation_type);
- LoginFailed();
+ Logout();
}
// no confirmation needed - we've done
@@ -215,7 +210,7 @@ void CSteamProto::OnBeginSession(const uint8_t *buf, size_t cbLen)
}
else {
debugLogA("Something went wrong: %s", reply->extended_error_message);
- LoginFailed();
+ Logout();
}
}
@@ -250,7 +245,7 @@ void CSteamProto::OnPollSession(const uint8_t *buf, size_t cbLen)
{
proto::AuthenticationPollAuthSessionStatusResponse reply(buf, cbLen);
if (reply == nullptr) {
- LoginFailed();
+ Logout();
return;
}
@@ -272,11 +267,54 @@ void CSteamProto::OnPollSession(const uint8_t *buf, size_t cbLen)
m_szAccessToken = reply->access_token;
m_szRefreshToken = reply->refresh_token;
- OnLoggedIn();
+ // sending logon packet
+ ptrA szAccountName(getUStringA(DBKEY_ACCOUNT_NAME)), szPassword(getUStringA("Password"));
+ T2Utf szMachineName(m_wszDeviceName);
+
+ MBinBuffer machineId(getBlob(DBKEY_MACHINE_ID));
+ if (!machineId.length()) {
+ uint8_t random[100], hashOut[20];
+ Utils_GetRandom(random, sizeof(random));
+ mir_sha1_hash(random, sizeof(random), hashOut);
+
+ db_set_blob(0, m_szModuleName, DBKEY_MACHINE_ID, hashOut, sizeof(hashOut));
+ machineId.append(hashOut, sizeof(hashOut));
+ }
+
+ CMsgIPAddress privateIp;
+ privateIp.ip_case = CMSG_IPADDRESS__IP_V4;
+ privateIp.v4 = 0;
+
+ CMsgClientLogon request;
+ request.access_token = reply->access_token;
+ request.account_name = szAccountName;
+ request.password = szPassword;
+ request.machine_name = szMachineName;
+ request.client_language = "english";
+ request.client_os_type = 16; request.has_client_os_type = true;
+ request.should_remember_password = false; request.has_should_remember_password = true;
+ request.obfuscated_private_ip = &privateIp;
+ request.protocol_version = STEAM_PROTOCOL_VERSION; request.has_protocol_version = true;
+ request.client_package_version = 1771; request.has_client_package_version = true;
+ request.supports_rate_limit_response = request.has_supports_rate_limit_response = true;
+ request.steamguard_dont_remember_computer = false; request.has_steamguard_dont_remember_computer = true;
+ request.chat_mode = 2; request.has_chat_mode = true;
+ request.cell_id = 7; request.has_cell_id = true;
+ request.machine_id.data = machineId.data();
+ request.machine_id.len = machineId.length();
+ WSSend(EMsg::ClientLogon, request);
}
-void CSteamProto::OnLoggedIn()
+void CSteamProto::OnClientLogon(const uint8_t *buf, size_t cbLen)
{
+ proto::MsgClientLogonResponse reply(buf, cbLen);
+ if (reply == nullptr) {
+ Logout();
+ return;
+ }
+
+ m_impl.m_heartBeat.Start(1000);
+
// go to online now
ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_CONNECTING, m_iStatus = m_iDesiredStatus);
diff --git a/protocols/Steam/src/steam_proto.cpp b/protocols/Steam/src/steam_proto.cpp
index 27c65aee33..158b5e215b 100644
--- a/protocols/Steam/src/steam_proto.cpp
+++ b/protocols/Steam/src/steam_proto.cpp
@@ -263,8 +263,6 @@ int CSteamProto::SetStatus(int new_status)
m_iDesiredStatus = new_status;
if (new_status == ID_STATUS_OFFLINE) {
- m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
-
if (!Miranda_IsTerminated())
SetAllContactStatuses(ID_STATUS_OFFLINE);
diff --git a/protocols/Steam/src/steam_proto.h b/protocols/Steam/src/steam_proto.h
index 1c042c0916..49befe2800 100644
--- a/protocols/Steam/src/steam_proto.h
+++ b/protocols/Steam/src/steam_proto.h
@@ -15,7 +15,7 @@
#define DBKEY_CLIENT_ID "ClientID"
#define DBKEY_STEAM_ID "SteamID"
#define DBKEY_ACCOUNT_NAME "Username"
-#define DBKEY_MACHINE_ID "MachineId"
+#define DBKEY_MACHINE_ID "DeviceId"
struct SendAuthParam
{
@@ -73,17 +73,24 @@ class CSteamProto : public PROTO<CSteamProto>
friend class CSteamProto;
CSteamProto &m_proto;
- CTimer m_poll;
+ CTimer m_poll, m_heartBeat;
void OnPoll(CTimer *)
{
m_proto.SendPollRequest();
}
+ void OnHeartBeat(CTimer *)
+ {
+ m_proto.SendHeartBeat();
+ }
+
CProtoImpl(CSteamProto &pro) :
m_proto(pro),
- m_poll(Miranda_GetSystemWindow(), UINT_PTR(this))
+ m_poll(Miranda_GetSystemWindow(), UINT_PTR(this)),
+ m_heartBeat(Miranda_GetSystemWindow(), UINT_PTR(this)+1)
{
m_poll.OnEvent = Callback(this, &CProtoImpl::OnPoll);
+ m_heartBeat.OnEvent = Callback(this, &CProtoImpl::OnHeartBeat);
}
}
m_impl;
@@ -91,7 +98,7 @@ class CSteamProto : public PROTO<CSteamProto>
ptrW m_password;
bool m_bTerminated, m_bPollCanceled;
time_t m_idleTS;
- int64_t m_iSteamId, m_iClientId;
+ int64_t m_iSteamId, m_iClientId, m_iSessionId;
MBinBuffer m_requestId;
int64_t GetId(const char *pszSetting);
@@ -101,7 +108,7 @@ class CSteamProto : public PROTO<CSteamProto>
void SetId(MCONTACT, const char *pszSetting, int64_t id);
// polling
- CMStringA m_szRefreshToken, m_szAccessToken, m_szUmqId, m_szChatToken;
+ CMStringA m_szRefreshToken, m_szAccessToken, m_szUmqId;
ULONG hAuthProcess = 1;
ULONG hMessageProcess = 1;
int m_iPollingInterval;
@@ -131,20 +138,20 @@ class CSteamProto : public PROTO<CSteamProto>
bool SendRequest(HttpRequest *request, HttpCallback callback, void *param = nullptr);
bool SendRequest(HttpRequest *request, JsonCallback callback, void *param = nullptr);
+ void SendHeartBeat();
+
// login
bool IsOnline();
bool IsMe(const char *steamId);
void Login();
- void LoginFailed();
void Logout();
- void OnLoggedIn();
-
static INT_PTR CALLBACK EnterTotpCode(void *param);
static INT_PTR CALLBACK EnterEmailCode(void *param);
- void OnBeginSession(const uint8_t *buf, size_t cbLen);
+ void OnBeginSession(const uint8_t *buf, size_t cbLen);
+ void OnClientLogon(const uint8_t *buf, size_t cbLen);
void OnGotRsaKey(const uint8_t *buf, size_t cbLen);
void OnGotConfirmationCode(const uint8_t *buf, size_t cbLen);
void OnPollSession(const uint8_t *buf, size_t cbLen);
diff --git a/protocols/Steam/src/steam_server.cpp b/protocols/Steam/src/steam_server.cpp
index c2b43e5b88..d940e42ddd 100644
--- a/protocols/Steam/src/steam_server.cpp
+++ b/protocols/Steam/src/steam_server.cpp
@@ -17,154 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
-void __cdecl CSteamProto::ServerThread(void *)
+void CSteamProto::SendHeartBeat()
{
- // load web socket servers first if needed
- int iTimeDiff = db_get_dw(0, STEAM_MODULE, DBKEY_HOSTS_DATE);
- int iHostCount = db_get_dw(0, STEAM_MODULE, DBKEY_HOSTS_COUNT);
- if (!iHostCount || time(0) - iTimeDiff > 3600 * 24 * 7) { // once a week
- if (!SendRequest(new GetHostsRequest(), &CSteamProto::OnGotHosts)) {
- LoginFailed();
- return;
- }
- }
-
- srand(time(0));
- m_ws = nullptr;
-
- CMStringA szHost;
- do {
- szHost.Format("Host%d", rand() % iHostCount);
- szHost = db_get_sm(0, STEAM_MODULE, szHost);
- szHost.Insert(0, "wss://");
- szHost += "/cmsocket/";
- }
- while (ServerThreadStub(szHost));
-}
-
-bool CSteamProto::ServerThreadStub(const char *szHost)
-{
- WebSocket<CSteamProto> ws(this);
-
- NLHR_PTR pReply(ws.connect(m_hNetlibUser, szHost));
- if (pReply == nullptr) {
- debugLogA("websocket connection failed");
- return false;
- }
-
- if (pReply->resultCode != 101) {
- debugLogA("websocket connection failed: %d", pReply->resultCode);
- return false;
- }
-
- m_ws = &ws;
-
- debugLogA("Websocket connection succeeded");
-
- // Send init packets
- Login();
-
- ws.run();
- m_ws = nullptr;
- return false;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void WebSocket<CSteamProto>::process(const uint8_t *buf, size_t cbLen)
-{
- uint32_t dwSign = *(uint32_t *)buf;
- EMsg msgType = (EMsg)(dwSign & ~STEAM_PROTOCOL_MASK);
-
- // now process the body
- if (msgType == EMsg::Multi) {
- buf += 8; cbLen -= 8;
- p->ProcessMulti(buf, cbLen);
- }
- else p->ProcessMessage(buf, cbLen);
-}
-
-void CSteamProto::ProcessMulti(const uint8_t *buf, size_t cbLen)
-{
- proto::MsgMulti pMulti(buf, cbLen);
- if (pMulti == nullptr) {
- debugLogA("Unable to decode multi message, exiting");
- return;
- }
-
- debugLogA("processing %s multi message of size %d", (pMulti->size_unzipped) ? "zipped" : "normal", pMulti->message_body.len);
-
- ptrA tmp;
- if (pMulti->size_unzipped) {
- tmp = (char *)mir_alloc(pMulti->size_unzipped + 1);
- cbLen = FreeImage_ZLibGUnzip((uint8_t*)tmp.get(), pMulti->size_unzipped, pMulti->message_body.data, (unsigned)pMulti->message_body.len);
- if (!cbLen) {
- debugLogA("Unable to unzip multi message, exiting");
- return;
- }
-
- buf = (const uint8_t *)tmp.get();
- }
- else {
- buf = pMulti->message_body.data;
- cbLen = pMulti->message_body.len;
- }
-
- while ((int)cbLen > 0) {
- uint32_t cbPacketLen = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t);
- Netlib_Dump(HNETLIBCONN(m_ws->getConn()), buf, cbLen, false, 0);
- ProcessMessage(buf, cbPacketLen);
- buf += cbPacketLen; cbLen -= cbPacketLen;
- }
-}
-
-void CSteamProto::ProcessMessage(const uint8_t *buf, size_t cbLen)
-{
- uint32_t dwSign = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t);
- EMsg msgType = (EMsg)(dwSign & ~STEAM_PROTOCOL_MASK);
- bool bIsProto = (dwSign & STEAM_PROTOCOL_MASK) != 0;
-
- CMsgProtoBufHeader hdr;
-
- if (msgType == EMsg::ChannelEncryptRequest || msgType == EMsg::ChannelEncryptResult) {
- hdr.has_jobid_source = hdr.has_jobid_target = true;
- hdr.jobid_source = *(int64_t *)buf; buf += sizeof(int64_t);
- hdr.jobid_target = *(int64_t *)buf; buf += sizeof(int64_t);
- }
- else if (bIsProto) {
- uint32_t hdrLen = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t);
- proto::MsgProtoBufHeader tmpHeader(buf, hdrLen);
- if (tmpHeader == nullptr) {
- debugLogA("Unable to decode message header, exiting");
- return;
- }
-
- memcpy(&hdr, tmpHeader, sizeof(hdr));
- buf += hdrLen; cbLen -= hdrLen;
- }
- else {
- debugLogA("Got unknown header, exiting");
- return;
- }
-
- MsgCallback pCallback = 0;
- {
- mir_cslock lck(m_csRequests);
- if (auto *pReq = m_arRequests.find((ProtoRequest *)&hdr.jobid_target)) {
- pCallback = pReq->pCallback;
- m_arRequests.remove(pReq);
- }
- }
-
- if (pCallback) {
- (this->*pCallback)(buf, cbLen);
- return;
- }
-
- // persistent callbacks
- switch (msgType) {
- case EMsg::ClientLogOnResponse:
- OnLoggedIn();
- break;
- }
+ CMsgClientHeartBeat packet;
+ packet.has_send_reply = packet.send_reply = true;
+ WSSend(EMsg::ClientHeartBeat, packet);
}
diff --git a/protocols/Steam/src/steam_utils.cpp b/protocols/Steam/src/steam_utils.cpp
index ed90bc5178..800abe47e8 100644
--- a/protocols/Steam/src/steam_utils.cpp
+++ b/protocols/Steam/src/steam_utils.cpp
@@ -1,67 +1,12 @@
#include "stdafx.h"
-static int64_t getRandomInt()
+int64_t getRandomInt()
{
int64_t ret;
Utils_GetRandom(&ret, sizeof(ret));
return (ret >= 0) ? ret : -ret;
}
-void CSteamProto::WSSend(EMsg msgType, const ProtobufCppMessage &msg)
-{
- CMsgProtoBufHeader hdr;
- hdr.has_client_sessionid = hdr.has_steamid = hdr.has_jobid_source = hdr.has_jobid_target = true;
-
- switch (msgType) {
- case EMsg::ClientHello:
- hdr.jobid_source = -1;
- break;
-
- default:
- hdr.jobid_source = getRandomInt();
- break;
- }
-
- hdr.jobid_target = -1;
-
- WSSendHeader(msgType, hdr, msg);
-}
-
-void CSteamProto::WSSendHeader(EMsg msgType, const CMsgProtoBufHeader &hdr, const ProtobufCppMessage &msg)
-{
- uint32_t hdrLen = (uint32_t)protobuf_c_message_get_packed_size(&hdr);
- MBinBuffer hdrbuf(hdrLen);
- protobuf_c_message_pack(&hdr, (uint8_t *)hdrbuf.data());
- hdrbuf.appendBefore(&hdrLen, sizeof(hdrLen));
-
- uint32_t type = (uint32_t)msgType;
- type |= STEAM_PROTOCOL_MASK;
- hdrbuf.appendBefore(&type, sizeof(type));
-
- MBinBuffer body(protobuf_c_message_get_packed_size(&msg));
- protobuf_c_message_pack(&msg, body.data());
-
- hdrbuf.append(body);
- m_ws->sendBinary(hdrbuf.data(), hdrbuf.length());
-}
-
-void CSteamProto::WSSendService(const char *pszServiceName, const ProtobufCppMessage &msg, MsgCallback pCallback)
-{
- CMsgProtoBufHeader hdr;
- hdr.has_client_sessionid = hdr.has_steamid = hdr.has_jobid_source = hdr.has_jobid_target = true;
- hdr.jobid_source = getRandomInt();
- hdr.jobid_target = -1;
- hdr.target_job_name = (char*)pszServiceName;
- hdr.realm = 1; hdr.has_realm = true;
-
- if (pCallback) {
- mir_cslock lck(m_csRequests);
- m_arRequests.insert(new ProtoRequest(hdr.jobid_source, pCallback));
- }
-
- WSSendHeader(EMsg::ServiceMethodCallFromClientNonAuthed, hdr, msg);
-}
-
/////////////////////////////////////////////////////////////////////////////////////////
int64_t CSteamProto::GetId(MCONTACT hContact, const char *pszSetting)
diff --git a/protocols/Steam/src/steam_ws.cpp b/protocols/Steam/src/steam_ws.cpp
new file mode 100644
index 0000000000..e6ee7f7d7a
--- /dev/null
+++ b/protocols/Steam/src/steam_ws.cpp
@@ -0,0 +1,237 @@
+/*
+Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+void __cdecl CSteamProto::ServerThread(void *)
+{
+ // load web socket servers first if needed
+ int iTimeDiff = db_get_dw(0, STEAM_MODULE, DBKEY_HOSTS_DATE);
+ int iHostCount = db_get_dw(0, STEAM_MODULE, DBKEY_HOSTS_COUNT);
+ if (!iHostCount || time(0) - iTimeDiff > 3600 * 24 * 7) { // once a week
+ if (!SendRequest(new GetHostsRequest(), &CSteamProto::OnGotHosts)) {
+ Logout();
+ return;
+ }
+ }
+
+ srand(time(0));
+ m_ws = nullptr;
+
+ CMStringA szHost;
+ do {
+ szHost.Format("Host%d", rand() % iHostCount);
+ szHost = db_get_sm(0, STEAM_MODULE, szHost);
+ szHost.Insert(0, "wss://");
+ szHost += "/cmsocket/";
+ }
+ while (ServerThreadStub(szHost));
+}
+
+bool CSteamProto::ServerThreadStub(const char *szHost)
+{
+ WebSocket<CSteamProto> ws(this);
+
+ NLHR_PTR pReply(ws.connect(m_hNetlibUser, szHost));
+ if (pReply == nullptr) {
+ debugLogA("websocket connection failed");
+ return false;
+ }
+
+ if (pReply->resultCode != 101) {
+ debugLogA("websocket connection failed: %d", pReply->resultCode);
+ return false;
+ }
+
+ m_ws = &ws;
+
+ debugLogA("Websocket connection succeeded");
+
+ // Send init packets
+ Login();
+
+ ws.run();
+
+ Logout();
+ m_impl.m_heartBeat.Stop();
+ m_ws = nullptr;
+ return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void WebSocket<CSteamProto>::process(const uint8_t *buf, size_t cbLen)
+{
+ uint32_t dwSign = *(uint32_t *)buf;
+ EMsg msgType = (EMsg)(dwSign & ~STEAM_PROTOCOL_MASK);
+
+ // now process the body
+ if (msgType == EMsg::Multi) {
+ buf += 8; cbLen -= 8;
+ p->ProcessMulti(buf, cbLen);
+ }
+ else p->ProcessMessage(buf, cbLen);
+}
+
+void CSteamProto::ProcessMulti(const uint8_t *buf, size_t cbLen)
+{
+ proto::MsgMulti pMulti(buf, cbLen);
+ if (pMulti == nullptr) {
+ debugLogA("Unable to decode multi message, exiting");
+ return;
+ }
+
+ debugLogA("processing %s multi message of size %d", (pMulti->size_unzipped) ? "zipped" : "normal", pMulti->message_body.len);
+
+ ptrA tmp;
+ if (pMulti->size_unzipped) {
+ tmp = (char *)mir_alloc(pMulti->size_unzipped + 1);
+ cbLen = FreeImage_ZLibGUnzip((uint8_t*)tmp.get(), pMulti->size_unzipped, pMulti->message_body.data, (unsigned)pMulti->message_body.len);
+ if (!cbLen) {
+ debugLogA("Unable to unzip multi message, exiting");
+ return;
+ }
+
+ buf = (const uint8_t *)tmp.get();
+ }
+ else {
+ buf = pMulti->message_body.data;
+ cbLen = pMulti->message_body.len;
+ }
+
+ while ((int)cbLen > 0) {
+ uint32_t cbPacketLen = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t);
+ ProcessMessage(buf, cbPacketLen);
+ buf += cbPacketLen; cbLen -= cbPacketLen;
+ }
+}
+
+void CSteamProto::ProcessMessage(const uint8_t *buf, size_t cbLen)
+{
+ Netlib_Dump(HNETLIBCONN(m_ws->getConn()), buf, cbLen, false, 0);
+
+ uint32_t dwSign = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t);
+ EMsg msgType = (EMsg)(dwSign & ~STEAM_PROTOCOL_MASK);
+ bool bIsProto = (dwSign & STEAM_PROTOCOL_MASK) != 0;
+
+ CMsgProtoBufHeader hdr;
+
+ if (msgType == EMsg::ChannelEncryptRequest || msgType == EMsg::ChannelEncryptResult) {
+ hdr.has_jobid_source = hdr.has_jobid_target = true;
+ hdr.jobid_source = *(int64_t *)buf; buf += sizeof(int64_t);
+ hdr.jobid_target = *(int64_t *)buf; buf += sizeof(int64_t);
+ }
+ else if (bIsProto) {
+ uint32_t hdrLen = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t);
+ proto::MsgProtoBufHeader tmpHeader(buf, hdrLen);
+ if (tmpHeader == nullptr) {
+ debugLogA("Unable to decode message header, exiting");
+ return;
+ }
+
+ memcpy(&hdr, tmpHeader, sizeof(hdr));
+ buf += hdrLen; cbLen -= hdrLen;
+
+ if (hdr.has_client_sessionid)
+ m_iSessionId = hdr.client_sessionid;
+ }
+ else {
+ debugLogA("Got unknown header, exiting");
+ return;
+ }
+
+ MsgCallback pCallback = 0;
+ {
+ mir_cslock lck(m_csRequests);
+ if (auto *pReq = m_arRequests.find((ProtoRequest *)&hdr.jobid_target)) {
+ pCallback = pReq->pCallback;
+ m_arRequests.remove(pReq);
+ }
+ }
+
+ if (pCallback) {
+ (this->*pCallback)(buf, cbLen);
+ return;
+ }
+
+ // persistent callbacks
+ switch (msgType) {
+ case EMsg::ClientLogOnResponse:
+ OnClientLogon(buf, cbLen);
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CSteamProto::WSSend(EMsg msgType, const ProtobufCppMessage &msg)
+{
+ CMsgProtoBufHeader hdr;
+ hdr.has_client_sessionid = hdr.has_steamid = hdr.has_jobid_source = hdr.has_jobid_target = true;
+ hdr.steamid = m_iSteamId;
+ hdr.client_sessionid = m_iSessionId;
+
+ switch (msgType) {
+ case EMsg::ClientHello:
+ hdr.jobid_source = -1;
+ break;
+
+ default:
+ hdr.jobid_source = getRandomInt();
+ break;
+ }
+
+ hdr.jobid_target = -1;
+
+ WSSendHeader(msgType, hdr, msg);
+}
+
+void CSteamProto::WSSendHeader(EMsg msgType, const CMsgProtoBufHeader &hdr, const ProtobufCppMessage &msg)
+{
+ uint32_t hdrLen = (uint32_t)protobuf_c_message_get_packed_size(&hdr);
+ MBinBuffer hdrbuf(hdrLen);
+ protobuf_c_message_pack(&hdr, (uint8_t *)hdrbuf.data());
+ hdrbuf.appendBefore(&hdrLen, sizeof(hdrLen));
+
+ uint32_t type = (uint32_t)msgType;
+ type |= STEAM_PROTOCOL_MASK;
+ hdrbuf.appendBefore(&type, sizeof(type));
+
+ MBinBuffer body(protobuf_c_message_get_packed_size(&msg));
+ protobuf_c_message_pack(&msg, body.data());
+
+ hdrbuf.append(body);
+ Netlib_Dump(HNETLIBCONN(m_ws->getConn()), hdrbuf.data(), hdrbuf.length(), true, 0);
+ m_ws->sendBinary(hdrbuf.data(), hdrbuf.length());
+}
+
+void CSteamProto::WSSendService(const char *pszServiceName, const ProtobufCppMessage &msg, MsgCallback pCallback)
+{
+ CMsgProtoBufHeader hdr;
+ hdr.has_client_sessionid = hdr.has_steamid = hdr.has_jobid_source = hdr.has_jobid_target = true;
+ hdr.jobid_source = getRandomInt();
+ hdr.jobid_target = -1;
+ hdr.target_job_name = (char *)pszServiceName;
+ hdr.realm = 1; hdr.has_realm = true;
+
+ if (pCallback) {
+ mir_cslock lck(m_csRequests);
+ m_arRequests.insert(new ProtoRequest(hdr.jobid_source, pCallback));
+ }
+
+ WSSendHeader(EMsg::ServiceMethodCallFromClientNonAuthed, hdr, msg);
+}