From 6c0bc679241489afbe3a9ae55bd41da5ed6056b6 Mon Sep 17 00:00:00 2001 From: Alexander Lantsev Date: Tue, 3 Jun 2014 12:39:16 +0000 Subject: Steam: work commit - added requests queue - code refactoring Note: this commit may contain regress of functionality and bugs git-svn-id: http://svn.miranda-ng.org/main/trunk@9401 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Steam/src/Steam/authorization.h | 41 ++++ protocols/Steam/src/Steam/avatar.h | 4 +- protocols/Steam/src/Steam/crypto.h | 14 +- protocols/Steam/src/Steam/friend.h | 11 + protocols/Steam/src/Steam/friend_list.h | 58 +++++ protocols/Steam/src/Steam/login.h | 26 +++ protocols/Steam/src/Steam/message.h | 18 ++ protocols/Steam/src/Steam/pending.h | 48 ++++ protocols/Steam/src/Steam/poll.h | 19 +- protocols/Steam/src/Steam/rsa_key.h | 13 +- protocols/Steam/src/Steam/search.h | 13 ++ protocols/Steam/src/Steam/session.h | 19 ++ protocols/Steam/src/Steam/steam.h | 102 +++++++++ protocols/Steam/src/steam_account.cpp | 202 ++++++++++++++++- protocols/Steam/src/steam_contacts.cpp | 356 ++++++++++++++++++++++++++++++ protocols/Steam/src/steam_messages.cpp | 15 ++ protocols/Steam/src/steam_pooling.cpp | 338 ++++++++++++++++++++++++++++ protocols/Steam/src/steam_proto.cpp | 115 ++++++++-- protocols/Steam/src/steam_proto.h | 135 ++++++++--- protocols/Steam/src/steam_queue.cpp | 146 ++++++++++++ protocols/Steam/src/steam_utils.cpp | 5 +- 21 files changed, 1627 insertions(+), 71 deletions(-) create mode 100644 protocols/Steam/src/steam_pooling.cpp create mode 100644 protocols/Steam/src/steam_queue.cpp (limited to 'protocols/Steam/src') diff --git a/protocols/Steam/src/Steam/authorization.h b/protocols/Steam/src/Steam/authorization.h index a8911034a0..0fcc5c9923 100644 --- a/protocols/Steam/src/Steam/authorization.h +++ b/protocols/Steam/src/Steam/authorization.h @@ -173,6 +173,47 @@ namespace SteamWebApi } } }; + + class AuthorizationRequest : public HttpsPostRequest + { + void InitData(const char *username, const char *password, const char *timestamp, const char *guardId = "-1", const char *guardCode = "") + { + char data[1024]; + mir_snprintf(data, SIZEOF(data), + "username=%s&password=%s&emailsteamid=%s&emailauth=%s&captchagid=%s&captcha_text=%s&rsatimestamp=%s&donotcache=%ld&remember_login=true&oauth_client_id=DE45CD61&oauth_scope=read_profile write_profile read_client write_client", + username, + ptrA(mir_urlEncode(password)), + guardId, + guardCode, + "-1", + "", + timestamp, + time(NULL)); + + SetData(data, strlen(data)); + } + + public: + AuthorizationRequest(const char *username, const char *password, const char *timestamp) : + HttpsPostRequest(STEAM_COM_URL "/mobilelogin/dologin") + { + flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP; + + InitData(username, password, timestamp); + } + + AuthorizationRequest(const char *username, const char *password, const char *timestamp, const char *guardId, const char *guardCode) : + HttpsPostRequest(STEAM_COM_URL "/mobilelogin/dologin") + { + flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP; + + InitData(username, password, timestamp, guardId, guardCode); + } + + /*const wchar_t *GetUsername() { return username; } + const char *GetPassword() { return password; } + const char *GetTimestamp() { return timestamp; }*/ + }; } diff --git a/protocols/Steam/src/Steam/avatar.h b/protocols/Steam/src/Steam/avatar.h index 253e4a3299..1ad2637def 100644 --- a/protocols/Steam/src/Steam/avatar.h +++ b/protocols/Steam/src/Steam/avatar.h @@ -31,7 +31,7 @@ namespace SteamWebApi { avatar->success = false; - HttpGetRequest request(hConnection, avatarUrl); + /*HttpGetRequest request(hConnection, avatarUrl); request.ResetFlags(NLHRF_HTTP11 | NLHRF_NODUMP); mir_ptr response(request.Send()); @@ -43,7 +43,7 @@ namespace SteamWebApi avatar->size = response->dataLength; avatar->data = (BYTE*)mir_alloc(avatar->size); - memcpy(avatar->data, response->pData, avatar->size); + memcpy(avatar->data, response->pData, avatar->size);*/ avatar->success = true; } diff --git a/protocols/Steam/src/Steam/crypto.h b/protocols/Steam/src/Steam/crypto.h index 6cd761ecde..553e311cf7 100644 --- a/protocols/Steam/src/Steam/crypto.h +++ b/protocols/Steam/src/Steam/crypto.h @@ -1,13 +1,13 @@ #ifndef _STEAM_CRYPTO_H_ #define _STEAM_CRYPTO_H_ - #include - #include - #include - #include - #include - #include - #include +#include +#include +#include +#include +#include +#include +#include namespace SteamWebApi { diff --git a/protocols/Steam/src/Steam/friend.h b/protocols/Steam/src/Steam/friend.h index 3fa559b2ec..d3ed0cc8ee 100644 --- a/protocols/Steam/src/Steam/friend.h +++ b/protocols/Steam/src/Steam/friend.h @@ -149,6 +149,17 @@ namespace SteamWebApi summaries->success = true; } }; + + class GetUserSummariesRequest : public HttpsGetRequest + { + public: + GetUserSummariesRequest(const char *token, const char *steamIds) : + HttpsGetRequest(STEAM_API_URL "/ISteamUserOAuth/GetUserSummaries/v0001") + { + AddParameter("access_token", token); + AddParameter("steamids", steamIds); + } + }; } #endif //_STEAM_FRIEND_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/friend_list.h b/protocols/Steam/src/Steam/friend_list.h index d5c1c5dc34..94f25d4427 100644 --- a/protocols/Steam/src/Steam/friend_list.h +++ b/protocols/Steam/src/Steam/friend_list.h @@ -165,6 +165,64 @@ namespace SteamWebApi result->success = true; } }; + + class GetFriendListRequest : public HttpsGetRequest + { + public: + GetFriendListRequest(const char *token, const char *steamId) : + HttpsGetRequest(STEAM_API_URL "/ISteamUserOAuth/GetFriendList/v0001") + { + AddParameter("access_token", token); + AddParameter("steamid", steamId); + AddParameter("relationship=friend,ignoredfriend,requestrecipient"); + } + }; + + class AddFriendRequest : public HttpsPostRequest + { + public: + AddFriendRequest(const char *token, const char *sessionId, const char *steamId, const char *who) : + HttpsPostRequest(STEAM_COM_URL "/actions/AddFriendAjax") + { + char login[MAX_PATH]; + mir_snprintf(login, SIZEOF(login), "%s||oauth:%s", steamId, token); + + char cookie[MAX_PATH]; + mir_snprintf(cookie, SIZEOF(cookie), "steamLogin=%s;sessionid=%s;forceMobile=1", login, sessionId); + + char data[128]; + mir_snprintf(data, SIZEOF(data), + "sessionID=%s&steamid=%s", + sessionId, + who); + + SetData(data, strlen(data)); + AddHeader("Cookie", cookie); + } + }; + + class RemoveFriendRequest : public HttpsPostRequest + { + public: + RemoveFriendRequest(const char *token, const char *sessionId, const char *steamId, const char *who) : + HttpsPostRequest(STEAM_COM_URL "/actions/RemoveFriendAjax") + { + char login[MAX_PATH]; + mir_snprintf(login, SIZEOF(login), "%s||oauth:%s", steamId, token); + + char cookie[MAX_PATH]; + mir_snprintf(cookie, SIZEOF(cookie), "steamLogin=%s;sessionid=%s;forceMobile=1", login, sessionId); + + char data[128]; + mir_snprintf(data, SIZEOF(data), + "sessionID=%s&steamid=%s", + sessionId, + who); + + SetData(data, strlen(data)); + AddHeader("Cookie", cookie); + } + }; } #endif //_STEAM_FRIEND_LIST_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/login.h b/protocols/Steam/src/Steam/login.h index 84d29ef2f2..c3ee1c5b17 100644 --- a/protocols/Steam/src/Steam/login.h +++ b/protocols/Steam/src/Steam/login.h @@ -71,6 +71,32 @@ namespace SteamWebApi mir_ptr response(request.Send()); } }; + + class LogonRequest : public HttpsPostRequest + { + public: + LogonRequest(const char *token) : + HttpsPostRequest(STEAM_API_URL "/ISteamWebUserPresenceOAuth/Logon/v0001") + { + char data[256]; + mir_snprintf(data, SIZEOF(data), "access_token=%s", token); + + SetData(data, strlen(data)); + } + }; + + class LogoffRequest : public HttpsPostRequest + { + public: + LogoffRequest(const char *token, const char *umqId) : + HttpsPostRequest(STEAM_API_URL "/ISteamWebUserPresenceOAuth/Logoff/v0001") + { + char data[256]; + mir_snprintf(data, SIZEOF(data), "access_token=%s&umqid=%s", token, umqId); + + SetData(data, strlen(data)); + } + }; } #endif //_STEAM_LOGIN_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/message.h b/protocols/Steam/src/Steam/message.h index bb9fec3bc1..12284315bc 100644 --- a/protocols/Steam/src/Steam/message.h +++ b/protocols/Steam/src/Steam/message.h @@ -103,6 +103,24 @@ namespace SteamWebApi sendResult->success = true; } }; + + class SendMessageRequest : public HttpsPostRequest + { + public: + SendMessageRequest(const char *token, const char *umqId, const char *steamId, const char *text) : + HttpsPostRequest(STEAM_API_URL "/ISteamWebUserPresenceOAuth/Message/v0001") + { + char data[1024]; + mir_snprintf(data, SIZEOF(data), + "access_token=%s&umqid=%s&steamid_dst=%s&type=saytext&text=%s", + token, + umqId, + steamId, + ptrA(mir_urlEncode(text))); + + SetData(data, strlen(data)); + } + }; } diff --git a/protocols/Steam/src/Steam/pending.h b/protocols/Steam/src/Steam/pending.h index ea65627b32..639804fb44 100644 --- a/protocols/Steam/src/Steam/pending.h +++ b/protocols/Steam/src/Steam/pending.h @@ -96,6 +96,54 @@ namespace SteamWebApi result->success = true; } }; + + class ApprovePendingRequest : public HttpsPostRequest + { + public: + ApprovePendingRequest(const char *token, const char *sessionId, const char *steamId, const char *who) : + HttpsPostRequest(STEAM_COM_URL "/profiles/%s/home_process") + { + char login[MAX_PATH]; + mir_snprintf(login, SIZEOF(login), "%s||oauth:%s", steamId, token); + + char cookie[MAX_PATH]; + mir_snprintf(cookie, SIZEOF(cookie), "steamLogin=%s;sessionid=%s;forceMobile=1", login, sessionId); + + char url[MAX_PATH]; + mir_snprintf(url, SIZEOF(url), STEAM_COM_URL "/profiles/%s/home_process", steamId); + this->url = url; + + char data[MAX_PATH]; + mir_snprintf(data, SIZEOF(data), "sessionID=%s&id=%s&perform=accept&action=approvePending&itype=friend&json=1&xml=0", sessionId, who); + + SetData(data, strlen(data)); + AddHeader("Cookie", cookie); + } + }; + + class IgnorePendingRequest : public HttpsPostRequest + { + public: + IgnorePendingRequest(const char *token, const char *sessionId, const char *steamId, const char *who) : + HttpsPostRequest(STEAM_COM_URL "/profiles/%s/home_process") + { + char login[MAX_PATH]; + mir_snprintf(login, SIZEOF(login), "%s||oauth:%s", steamId, token); + + char cookie[MAX_PATH]; + mir_snprintf(cookie, SIZEOF(cookie), "steamLogin=%s;sessionid=%s;forceMobile=1", login, sessionId); + + char url[MAX_PATH]; + mir_snprintf(url, SIZEOF(url), STEAM_COM_URL "/profiles/%s/home_process", steamId); + this->url = url; + + char data[MAX_PATH]; + mir_snprintf(data, SIZEOF(data), "sessionID=%s&id=%s&perform=ignore&action=approvePending&itype=friend&json=1&xml=0", sessionId, who); + + SetData(data, strlen(data)); + AddHeader("Cookie", cookie); + } + }; } #endif //_STEAM_PENDING_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/poll.h b/protocols/Steam/src/Steam/poll.h index 218787e9ac..73597e81c4 100644 --- a/protocols/Steam/src/Steam/poll.h +++ b/protocols/Steam/src/Steam/poll.h @@ -94,7 +94,7 @@ namespace SteamWebApi SecureHttpPostRequest request(hConnection, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Poll/v0001"); request.SetData(data, strlen(data)); - request.SetTimeout(90000); // may need to encrease timeout + request.SetTimeout(30000); // may need to encrease timeout mir_ptr response(request.Send()); if (!response) @@ -226,6 +226,23 @@ namespace SteamWebApi pollResult->success = true; } }; + + class PollRequest : public HttpsPostRequest + { + public: + PollRequest(const char *token, const char *umqId, UINT32 messageId) : + HttpsPostRequest(STEAM_API_URL "/ISteamWebUserPresenceOAuth/Poll/v0001") + { + timeout = 30000; + flags |= NLHRF_PERSISTENT; + + char data[256]; + mir_snprintf(data, SIZEOF(data), "access_token=%s&umqid=%s&message=%u", token, umqId, messageId); + + SetData(data, strlen(data)); + AddHeader("Connection", "keep-alive"); + } + }; } diff --git a/protocols/Steam/src/Steam/rsa_key.h b/protocols/Steam/src/Steam/rsa_key.h index 35b2eb6f28..2ea9a20558 100644 --- a/protocols/Steam/src/Steam/rsa_key.h +++ b/protocols/Steam/src/Steam/rsa_key.h @@ -56,7 +56,18 @@ namespace SteamWebApi rsaKey->success = true; } }; -} + class RsaKeyRequest : public HttpsGetRequest + { + public: + RsaKeyRequest(const char *username) : + HttpsGetRequest(STEAM_COM_URL "/mobilelogin/getrsakey") + { + flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP; + + AddParameter("username", (char*)username); + } + }; +} #endif //_STEAM_RSA_KEY_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/search.h b/protocols/Steam/src/Steam/search.h index 1efe0e807b..ff85f5960f 100644 --- a/protocols/Steam/src/Steam/search.h +++ b/protocols/Steam/src/Steam/search.h @@ -91,6 +91,19 @@ namespace SteamWebApi searchResult->success = true; } }; + + class SearchRequest : public HttpsGetRequest + { + public: + SearchRequest(const char *token, const char *text) : + HttpsGetRequest(STEAM_API_URL "/ISteamUserOAuth/Search/v0001") + { + AddParameter("access_token", token); + AddParameter("keywords", ptrA(mir_urlEncode(text))); + // todo: may need to load all results (15 first at now) + AddParameter("offset=0&count=15&targets=users&fields=all"); + } + }; } #endif //_STEAM_SEARCH_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/session.h b/protocols/Steam/src/Steam/session.h index cad2f96dbf..3ddcc219cd 100644 --- a/protocols/Steam/src/Steam/session.h +++ b/protocols/Steam/src/Steam/session.h @@ -50,6 +50,25 @@ namespace SteamWebApi sessionId->success = true; } }; + + class GetSessionRequest : public HttpsPostRequest + { + public: + GetSessionRequest(const char *token, const char *steamId, const char *cookie) : + HttpsPostRequest(STEAM_COM_URL "/mobileloginsucceeded") + { + flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP; + + char data[512]; + mir_snprintf(data, SIZEOF(data), + "oauth_token=%s&steamid=%s&webcookie=%s", + token, + steamId, + cookie); + + SetData(data, strlen(data)); + } + }; } #endif //_STEAM_SESSION_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/steam.h b/protocols/Steam/src/Steam/steam.h index 66d9068100..df039288a5 100644 --- a/protocols/Steam/src/Steam/steam.h +++ b/protocols/Steam/src/Steam/steam.h @@ -28,6 +28,108 @@ namespace SteamWebApi HTTP_STATUS GetStatus() const { return status; } }; }; + + class HttpRequest : public NETLIBHTTPREQUEST, public MZeroedObject + { + public: + std::string url; + + HttpRequest(int type, LPCSTR url) + { + cbSize = sizeof(NETLIBHTTPREQUEST); + + requestType = type; + this->url = url; + szUrl = (char*)this->url.c_str(); + timeout = 0; + flags = NLHRF_HTTP11 | NLHRF_NODUMPSEND | NLHRF_DUMPASTEXT; + } + + ~HttpRequest() + { + for (int i = 0; i < headersCount; i++) + { + mir_free(headers[i].szName); + mir_free(headers[i].szValue); + } + mir_free(headers); + mir_free(pData); + } + + void AddHeader(LPCSTR szName, LPCSTR szValue) + { + headers = (NETLIBHTTPHEADER*)mir_realloc(headers, sizeof(NETLIBHTTPHEADER)*(headersCount + 1)); + headers[headersCount].szName = mir_strdup(szName); + headers[headersCount].szValue = mir_strdup(szValue); + headersCount++; + } + + void AddParameter(LPCSTR szName, LPCSTR szValue) + { + if (url.find('?') == -1) + url.append("?").append(szName).append("=").append(szValue); + else + url.append("&").append(szName).append("=").append(szValue); + } + + void AddParameter(LPCSTR szValue) + { + if (url.find('?') == -1) + url.append("?").append(szValue); + else + url.append("&").append(szValue); + } + + void SetData(const char *data, size_t size) + { + if (pData != NULL) + mir_free(pData); + + dataLength = (int)size; + pData = (char*)mir_alloc(size + 1); + memcpy(pData, data, size); + pData[size] = 0; + } + }; + + class HttpGetRequest : public HttpRequest + { + public: + HttpGetRequest(LPCSTR url) : HttpRequest(REQUEST_GET, url) { } + }; + + /*class HttpPostRequest : public HttpRequest + { + public: + HttpPostRequest(LPCSTR url) : HttpRequest(REQUEST_POST, url) + { + AddHeader("Content-Type", "application/x-www-form-urlencoded"); + } + };*/ + + class HttpsRequest : public HttpRequest + { + public: + HttpsRequest(int type, LPCSTR url) : HttpRequest(type, url) + { + flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMPSEND | NLHRF_DUMPASTEXT; + } + }; + + class HttpsGetRequest : public HttpsRequest + { + public: + HttpsGetRequest(LPCSTR url) : HttpsRequest(REQUEST_GET, url) { } + }; + + class HttpsPostRequest : public HttpsRequest + { + public: + HttpsPostRequest(LPCSTR url) : HttpsRequest(REQUEST_POST, url) + { + AddHeader("Content-Type", "application/x-www-form-urlencoded"); + } + }; } #include "Steam\rsa_key.h" diff --git a/protocols/Steam/src/steam_account.cpp b/protocols/Steam/src/steam_account.cpp index 812962db81..59915ddeac 100644 --- a/protocols/Steam/src/steam_account.cpp +++ b/protocols/Steam/src/steam_account.cpp @@ -14,6 +14,196 @@ bool CSteamProto::IsMe(const char *steamId) return false; } +void CSteamProto::OnGotRsaKey(const NETLIBHTTPREQUEST *response, void *arg) +{ + // load rsa key parts + JSONNODE *root = json_parse(response->pData), *node; + if (!root) return; + + node = json_get(root, "success"); + if (!json_as_bool(node)) return; + + node = json_get(root, "publickey_mod"); + ptrA modulus(mir_u2a(json_as_string(node))); + + // exponent "010001" is used as constant in CSteamProto::RsaEncrypt + /*node = json_get(root, "publickey_exp"); + ptrA exponent(mir_u2a(json_as_string(node)));*/ + + node = json_get(root, "timestamp"); + ptrA timestamp(mir_u2a(json_as_string(node))); + setString("Timestamp", timestamp); + + // encrcrypt password + ptrA base64RsaEncryptedPassword; + ptrA password(getStringA("Password")); + + DWORD error = 0; + DWORD encryptedSize = 0; + DWORD passwordSize = (DWORD)strlen(password); + if ((error = RsaEncrypt(modulus, password, NULL, encryptedSize)) != 0) + { + debugLogA("CSteamProto::OnGotRsaKey: encryption error (%lu)", error); + return; + } + + BYTE *encryptedPassword = (BYTE*)mir_calloc(encryptedSize); + if ((error = RsaEncrypt(modulus, password, encryptedPassword, encryptedSize)) != 0) + { + debugLogA("CSteamProto::OnGotRsaKey: encryption error (%lu)", error); + return; + } + + base64RsaEncryptedPassword = mir_base64_encode(encryptedPassword, encryptedSize); + mir_free(encryptedPassword); + + setString("EncryptedPassword", base64RsaEncryptedPassword); + + // run authorization request + ptrA username(mir_urlEncode(ptrA(mir_utf8encodeW(getWStringA("Username"))))); + + PushRequest( + new SteamWebApi::AuthorizationRequest(username, base64RsaEncryptedPassword, timestamp), + &CSteamProto::OnAuthorization); +} + +void CSteamProto::OnAuthorization(const NETLIBHTTPREQUEST *response, void *arg) +{ + JSONNODE *root = json_parse(response->pData), *node; + + node = json_get(root, "success"); + if (json_as_bool(node) == 0) + { + node = json_get(root, "emailauth_needed"); + if (json_as_bool(node) > 0) + { + node = json_get(root, "emailsteamid"); + ptrA guardId(mir_u2a(json_as_string(node))); + + node = json_get(root, "emaildomain"); + ptrA emailDomain(mir_utf8encodeW(json_as_string(node))); + + GuardParam guard; + lstrcpyA(guard.domain, emailDomain); + + if (DialogBoxParam( + g_hInstance, + MAKEINTRESOURCE(IDD_GUARD), + NULL, + CSteamProto::GuardProc, + (LPARAM)&guard) != 1) + return; + + ptrA username(mir_urlEncode(ptrA(mir_utf8encodeW(getWStringA("Username"))))); + ptrA base64RsaEncryptedPassword(getStringA("EncryptedPassword")); + ptrA timestamp(getStringA("Timestamp")); + + PushRequest( + new SteamWebApi::AuthorizationRequest(username, base64RsaEncryptedPassword, timestamp, guardId, guard.code), + &CSteamProto::OnAuthorization); + } + + // todo: show captcha dialog + /*node = json_get(root, "captcha_needed"); + if (json_as_bool(node) > 0) + { + node = json_get(root, "captcha_gid"); + authResult->captchagid = ptrA(mir_u2a(json_as_string(node))); + } + + if (!authResult->emailauth_needed && !authResult->captcha_needed) + { + node = json_get(root, "message"); + authResult->message = json_as_string(node); + }*/ + + return; + } + + node = json_get(root, "login_complete"); + if (!json_as_bool(node)) + return; + + node = json_get(root, "oauth"); + root = json_parse(ptrA(mir_u2a(json_as_string(node)))); + + node = json_get(root, "steamid"); + ptrA steamId(mir_u2a(json_as_string(node))); + setString("SteamID", steamId); + + node = json_get(root, "oauth_token"); + ptrA token(mir_u2a(json_as_string(node))); + setString("TokenSecret", token); + + node = json_get(root, "webcookie"); + ptrA cookie(mir_u2a(json_as_string(node))); + + delSetting("Timestamp"); + delSetting("EncryptedPassword"); + + PushRequest( + new SteamWebApi::GetSessionRequest(token, steamId, cookie), + &CSteamProto::OnGotSession); + + PushRequest( + new SteamWebApi::LogonRequest(token), + &CSteamProto::OnLoggedOn); +} + +void CSteamProto::OnGotSession(const NETLIBHTTPREQUEST *response, void *arg) +{ + for (int i = 0; i < response->headersCount; i++) + { + if (lstrcmpiA(response->headers[i].szName, "Set-Cookie")) + continue; + + std::string cookies = response->headers[i].szValue; + size_t start = cookies.find("sessionid=") + 10; + size_t end = cookies.substr(start).find(';'); + std::string sessionId = cookies.substr(start, end - start + 10); + setString("SessionID", sessionId.c_str()); + break; + } +} + +void CSteamProto::OnLoggedOn(const NETLIBHTTPREQUEST *response, void *arg) +{ + JSONNODE *root = json_parse(response->pData), *node; + + node = json_get(root, "error"); + ptrW error(json_as_string(node)); + if (lstrcmpi(error, L"OK")/* || response->resultCode == HTTP_STATUS_UNAUTHORIZED*/) + { + //delSetting("TokenSecret"); + //delSetting("Cookie"); + + // set status to offline + m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; + ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_CONNECTING, ID_STATUS_OFFLINE); + return; + } + + node = json_get(root, "umqid"); + setString("UMQID", ptrA(mir_u2a(json_as_string(node)))); + + node = json_get(root, "message"); + setDword("MessageID", json_as_int(node)); + + // load contact list + ptrA token(getStringA("TokenSecret")); + ptrA steamId(getStringA("SteamID")); + + PushRequest( + new SteamWebApi::GetFriendListRequest(token, steamId), + &CSteamProto::OnGotFriendList); + + // start polling thread + m_hPollingThread = ForkThreadEx(&CSteamProto::PollingThread, 0, NULL); + + // go to online now + ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_CONNECTING, m_iStatus = m_iDesiredStatus); +} + void CSteamProto::SetServerStatusThread(void *arg) { WORD status = *((WORD*)&arg); @@ -66,7 +256,7 @@ void CSteamProto::Authorize(SteamWebApi::AuthorizationApi::AuthResult *authResul ptrA password(getStringA("Password")); - DWORD error = 0; + /*DWORD error = 0; DWORD encryptedSize = 0; DWORD passwordSize = (DWORD)strlen(password); if ((error = RsaEncrypt(rsaKey, password, passwordSize, NULL, encryptedSize)) != 0) @@ -83,7 +273,7 @@ void CSteamProto::Authorize(SteamWebApi::AuthorizationApi::AuthResult *authResul } base64RsaEncryptedPassword = mir_base64_encode(encryptedPassword, encryptedSize); - mir_free(encryptedPassword); + mir_free(encryptedPassword);*/ // try to authorize debugLogA("CSteamProto::Authorize: call SteamWebApi::AuthorizationApi::Authorize"); @@ -123,7 +313,7 @@ void CSteamProto::Authorize(SteamWebApi::AuthorizationApi::AuthResult *authResul void CSteamProto::LogInThread(void* param) { - while (m_bTerminated || m_hPollingThread != NULL) + while (isTerminated || m_hPollingThread != NULL) Sleep(500); ptrA token(getStringA("TokenSecret")); @@ -195,7 +385,7 @@ void CSteamProto::LogInThread(void* param) // start pooling thread if (m_hPollingThread == NULL) { - m_bTerminated = false; + isTerminated = false; m_hPollingThread = ForkThreadEx(&CSteamProto::PollingThread, NULL, NULL); } } @@ -205,12 +395,12 @@ void CSteamProto::LogOutThread(void*) ptrA token(getStringA("TokenSecret")); ptrA umqId(getStringA("UMQID")); - while (!Miranda_Terminated() && m_bTerminated && m_hPollingThread != NULL) + while (!Miranda_Terminated() && isTerminated && m_hPollingThread != NULL) Sleep(200); debugLogA("CSteamProto::LogOutThread: call SteamWebApi::LoginApi::Logoff"); SteamWebApi::LoginApi::Logoff(m_hNetlibUser, token, umqId); delSetting("UMQID"); - m_bTerminated = false; + isTerminated = false; } \ No newline at end of file diff --git a/protocols/Steam/src/steam_contacts.cpp b/protocols/Steam/src/steam_contacts.cpp index 75c85812ce..f0bf2454ac 100644 --- a/protocols/Steam/src/steam_contacts.cpp +++ b/protocols/Steam/src/steam_contacts.cpp @@ -181,6 +181,126 @@ void CSteamProto::UpdateContactsThread(void *arg) } } +void CSteamProto::UpdateContact(MCONTACT hContact, JSONNODE *data) +{ + JSONNODE *node = NULL; + + // set common data + node = json_get(data, "personaname"); + setWString(hContact, "Nick", json_as_string(node)); + + node = json_get(data, "profileurl"); + setString(hContact, "Homepage", ptrA(mir_u2a(json_as_string(node)))); + + // set name + node = json_get(data, "realname"); + if (node != NULL) + { + std::wstring realname = json_as_string(node); + if (!realname.empty()) + { + size_t pos = realname.find(' ', 1); + if (pos != std::string::npos) + { + const wchar_t *firstName = realname.substr(0, pos).c_str(); + const wchar_t *lastName = realname.substr(pos + 1).c_str(); + + setWString(hContact, "FirstName", firstName); + setWString(hContact, "LastName", lastName); + } + else + { + setWString(hContact, "FirstName", realname.c_str()); + delSetting(hContact, "LastName"); + } + } + } + else + { + delSetting(hContact, "FirstName"); + delSetting(hContact, "LastName"); + } + + //// avatar + + //node = json_get(data, "avatarfull"); + //item->avatarUrl = ptrA(mir_u2a(json_as_string(node))); + + //ptrA oldAvatar(getStringA("AvatarUrl")); + //if (lstrcmpiA(oldAvatar, summary->GetAvatarUrl())) + //{ + // // todo: need to place in thread + // SteamWebApi::AvatarApi::Avatar avatar; + // debugLogA("CSteamProto::UpdateContact: SteamWebApi::AvatarApi::GetAvatar"); + // SteamWebApi::AvatarApi::GetAvatar(m_hNetlibUser, summary->GetAvatarUrl(), &avatar); + + // if (avatar.IsSuccess() && avatar.GetDataSize() > 0) + // { + // ptrW avatarPath(GetAvatarFilePath(hContact)); + // FILE *fp = _wfopen(avatarPath, L"wb"); + // if (fp) + // { + // fwrite(avatar.GetData(), sizeof(char), avatar.GetDataSize(), fp); + // fclose(fp); + + // PROTO_AVATAR_INFORMATIONW pai = { sizeof(pai) }; + // pai.format = PA_FORMAT_JPEG; + // pai.hContact = hContact; + // wcscpy(pai.filename, avatarPath); + + // ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, (HANDLE)&pai, 0); + + // setString("AvatarUrl", summary->GetAvatarUrl()); + // } + // } + //} + + // set country + node = json_get(data, "loccountrycode"); + if (node != NULL) + { + const char *iso = ptrA(mir_u2a(json_as_string(node))); + char *country = (char *)CallService(MS_UTILS_GETCOUNTRYBYISOCODE, (WPARAM)iso, 0); + setString(hContact, "Country", country); + } + else + this->delSetting(hContact, "Country"); + + // only for contacts + if (hContact) + { + node = json_get(data, "lastlogoff"); + setDword(hContact, "LastEventDateTS", json_as_int(node)); + + node = json_get(data, "gameid"); + DWORD gameId = atol(ptrA(mir_u2a(json_as_string(node)))); + if (gameId > 0) + { + node = json_get(data, "gameextrainfo"); + const wchar_t *gameInfo = json_as_string(node); + + db_set_ws(hContact, "CList", "StatusMsg", gameInfo); + setWord(hContact, "Status", ID_STATUS_OUTTOLUNCH); + + setWString(hContact, "GameInfo", gameInfo); + setDword(hContact, "GameID", gameId); + } + else + { + node = json_get(data, "personastate"); + WORD status = SteamToMirandaStatus(json_as_int(node)); + setWord(hContact, "Status", status); + + db_unset(hContact, "CList", "StatusMsg"); + delSetting(hContact, "GameID"); + } + } + + + /*node = json_get(data, "timecreated"); + time_t created = json_as_int(node);*/ +} + MCONTACT CSteamProto::AddContact(const char *steamId, bool isTemporary) { MCONTACT hContact = this->FindContact(steamId); @@ -214,6 +334,242 @@ MCONTACT CSteamProto::AddContact(const char *steamId, bool isTemporary) return hContact; } +void CSteamProto::OnGotFriendList(const NETLIBHTTPREQUEST *response, void *arg) +{ + JSONNODE *root = json_parse(response->pData), *node, *child; + + if (root == NULL) + return; + + std::string steamIds; + + node = json_get(root, "friends"); + root = json_as_array(node); + if (root != NULL) + { + for (size_t i = 0; i < json_size(root); i++) + { + child = json_at(root, i); + if (child == NULL) + break; + + node = json_get(child, "steamid"); + ptrA steamId(mir_u2a(json_as_string(node))); + + node = json_get(child, "relationship"); + ptrA relationship(mir_u2a(json_as_string(node))); + if (!lstrcmpiA(relationship, "friend")) + { + if (!FindContact(steamId)) + { + AddContact(steamId); + steamIds.append(steamId).append(","); + } + } + else if (!lstrcmpiA(relationship, "ignoredfriend")) + { + // todo + } + else if (!lstrcmpiA(relationship, "requestrecipient")) + { + MCONTACT hContact = FindContact(steamId); + if (!hContact) + hContact = AddContact(steamId, true); + + RaiseAuthRequestThread((void*)hContact); + } + else continue; + } + } + + if (!steamIds.empty()) + { + steamIds.pop_back(); + ptrA token(getStringA("TokenSecret")); + + PushRequest( + new SteamWebApi::GetUserSummariesRequest(token, steamIds.c_str()), + &CSteamProto::OnGotUserSummaries); + } +} + +void CSteamProto::OnGotUserSummaries(const NETLIBHTTPREQUEST *response, void *arg) +{ + JSONNODE *root = json_parse(response->pData), *node, *item; + + node = json_get(root, "players"); + root = json_as_array(node); + if (root != NULL) + { + for (size_t i = 0; i < json_size(root); i++) + { + item = json_at(root, i); + if (item == NULL) + break; + + node = json_get(item, "steamid"); + ptrA steamId(mir_u2a(json_as_string(node))); + + MCONTACT hContact = FindContact(steamId); + if (!hContact) + hContact = AddContact(steamId); + + UpdateContact(hContact, item); + } + } +} + +void CSteamProto::OnFriendAdded(const NETLIBHTTPREQUEST *response, void *arg) +{ + SendAuthParam *param = (SendAuthParam*)arg; + + if (response->resultCode != HTTP_STATUS_OK || lstrcmpiA(response->pData, "true")) + { + ptrA steamId(getStringA(param->hContact, "SteamID")); + debugLogA("CSteamProto::OnFriendAdded: failed to add friend %s", steamId); + + ProtoBroadcastAck(param->hContact, ACKTYPE_AUTHREQ, ACKRESULT_FAILED, param->hAuth, 0); + + return; + } + + delSetting(param->hContact, "Auth"); + delSetting(param->hContact, "Grant"); + db_unset(param->hContact, "CList", "NotOnList"); + + ProtoBroadcastAck(param->hContact, ACKTYPE_AUTHREQ, ACKRESULT_SUCCESS, param->hAuth, 0); +} + +void CSteamProto::OnFriendRemoved(const NETLIBHTTPREQUEST *response, void *arg) +{ + if (response->resultCode != HTTP_STATUS_OK || lstrcmpiA(response->pData, "true")) + { + debugLogA("CSteamProto::OnFriendRemoved: failed to remove friend %s", ptrA((char*)arg)); + } +} + +void CSteamProto::OnAuthRequested(const NETLIBHTTPREQUEST *response, void *arg) +{ + if (response == NULL || response->resultCode != HTTP_STATUS_OK) + { + debugLogA("CSteamProto::OnAuthRequested: failed to request info for %s", ptrA((char*)arg)); + return; + } + + JSONNODE *root = json_parse(response->pData), *node; + + node = json_get(root, "players"); + root = json_at(json_as_array(node), 0); + if (root != NULL) + { + node = json_get(root, "steamid"); + ptrA steamId(mir_u2a(json_as_string(node))); + + MCONTACT hContact = FindContact(steamId); + if (!hContact) + hContact = AddContact(steamId); + + UpdateContact(hContact, root); + + char *nickName = getStringA(hContact, "Nick"); + char *firstName = getStringA(hContact, "FirstName"); + char *lastName = getStringA(hContact, "LastName"); + + char reason[MAX_PATH]; + mir_snprintf(reason, SIZEOF(reason), Translate("%s has added you to his or her Friend List"), nickName); + + // blob is: 0(DWORD), hContact(DWORD), nick(ASCIIZ), firstName(ASCIIZ), lastName(ASCIIZ), sid(ASCIIZ), reason(ASCIIZ) + DWORD cbBlob = (DWORD)(sizeof(DWORD)* 2 + strlen(nickName) + strlen(firstName) + strlen(lastName) + strlen(steamId) + strlen(reason) + 5); + + PBYTE pBlob, pCurBlob; + pCurBlob = pBlob = (PBYTE)mir_alloc(cbBlob); + + *((PDWORD)pCurBlob) = 0; + pCurBlob += sizeof(DWORD); + *((PDWORD)pCurBlob) = (DWORD)hContact; + pCurBlob += sizeof(DWORD); + strcpy((char*)pCurBlob, nickName); + pCurBlob += strlen(nickName) + 1; + strcpy((char*)pCurBlob, firstName); + pCurBlob += strlen(firstName) + 1; + strcpy((char*)pCurBlob, lastName); + pCurBlob += strlen(lastName) + 1; + strcpy((char*)pCurBlob, steamId); + pCurBlob += strlen(steamId) + 1; + strcpy((char*)pCurBlob, mir_strdup(reason)); + + AddDBEvent(hContact, EVENTTYPE_AUTHREQUEST, time(NULL), DBEF_UTF, cbBlob, pBlob); + } +} + +void CSteamProto::OnPendingApproved(const NETLIBHTTPREQUEST *response, void *arg) +{ + if (response->resultCode != HTTP_STATUS_OK || lstrcmpiA(response->pData, "true")) + { + debugLogA("CSteamProto::OnPendingApproved: failed to approve pending from %s", ptrA((char*)arg)); + } +} + +void CSteamProto::OnPendingIgnoreded(const NETLIBHTTPREQUEST *response, void *arg) +{ + if (response->resultCode != HTTP_STATUS_OK || lstrcmpiA(response->pData, "true")) + { + debugLogA("CSteamProto::OnPendingIgnoreded: failed to ignore pending from %s", ptrA((char*)arg)); + } +} + +void CSteamProto::OnSearchByIdEnded(const NETLIBHTTPREQUEST *response, void *arg) +{ + if (response == NULL || response->resultCode != HTTP_STATUS_OK) + { + ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)STEAM_SEARCH_BYID, 0); + return; + } + + JSONNODE *root = json_parse(response->pData), *node; + + node = json_get(root, "players"); + root = json_at(json_as_array(node), 0); + if (root != NULL) + { + STEAM_SEARCH_RESULT ssr = { 0 }; + ssr.hdr.cbSize = sizeof(STEAM_SEARCH_RESULT); + ssr.hdr.flags = PSR_TCHAR; + + ssr.hdr.id = (wchar_t*)arg; + + node = json_get(root, "personaname"); + ssr.hdr.nick = mir_wstrdup(json_as_string(node)); + + node = json_get(root, "realname"); + if (node != NULL) + { + std::wstring realname = json_as_string(node); + if (!realname.empty()) + { + size_t pos = realname.find(' ', 1); + if (pos != std::string::npos) + { + ssr.hdr.firstName = mir_wstrdup(realname.substr(0, pos).c_str()); + ssr.hdr.lastName = mir_wstrdup(realname.substr(pos + 1).c_str()); + } + else + ssr.hdr.firstName = mir_wstrdup(realname.c_str()); + } + } + + //ssr.contact = contact; + ssr.data = json_copy(root); + + ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)STEAM_SEARCH_BYID, (LPARAM)&ssr); + ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)STEAM_SEARCH_BYID, 0); + } +} + +void CSteamProto::OnSearchByNameStarted(const NETLIBHTTPREQUEST *response, void *arg) +{ +} + void CSteamProto::RaiseAuthRequestThread(void *arg) { MCONTACT hContact = (MCONTACT)arg; diff --git a/protocols/Steam/src/steam_messages.cpp b/protocols/Steam/src/steam_messages.cpp index ec94d20907..80e68ecdc0 100644 --- a/protocols/Steam/src/steam_messages.cpp +++ b/protocols/Steam/src/steam_messages.cpp @@ -33,4 +33,19 @@ void CSteamProto::SendTypingThread(void *arg) SteamWebApi::MessageApi::SendResult sendResult; debugLogA("CSteamProto::SendTypingThread: call SteamWebApi::PollApi::SteamWebApi::MessageApi::SendMessage"); SteamWebApi::MessageApi::SendTyping(m_hNetlibUser, token, umqId, steamId, &sendResult); +} + +void CSteamProto::OnMessageSent(const NETLIBHTTPREQUEST *response, void *arg) +{ + SendMessageParam *param = (SendMessageParam*)arg; + + int status = response->resultCode == HTTP_STATUS_OK ? ACKRESULT_SUCCESS : ACKRESULT_FAILED; + + ProtoBroadcastAck( + param->hContact, + ACKTYPE_MESSAGE, + status, + param->hMessage, 0); + + mir_free(param); } \ No newline at end of file diff --git a/protocols/Steam/src/steam_pooling.cpp b/protocols/Steam/src/steam_pooling.cpp new file mode 100644 index 0000000000..826187c9a4 --- /dev/null +++ b/protocols/Steam/src/steam_pooling.cpp @@ -0,0 +1,338 @@ +#include "common.h" + +void CSteamProto::PollServer(const char *token, const char *umqId, UINT32 messageId, SteamWebApi::PollApi::PollResult *pollResult) +{ + debugLogA("CSteamProto::PollServer: call SteamWebApi::PollApi::Poll"); + SteamWebApi::PollApi::Poll(m_hNetlibUser, token, umqId, messageId, pollResult); + + if (!pollResult->IsSuccess()) + return; + + CMStringA updatedIds; + for (size_t i = 0; i < pollResult->GetItemCount(); i++) + { + const SteamWebApi::PollApi::PoolItem *item = pollResult->GetAt(i); + switch (item->GetType()) + { + case SteamWebApi::PollApi::POOL_TYPE_TYPING: + break; + + case SteamWebApi::PollApi::POOL_TYPE_MESSAGE: + { + SteamWebApi::PollApi::Message *message = (SteamWebApi::PollApi::Message*)item; + + MCONTACT hContact = FindContact(message->GetSteamId()); + if (hContact) + { + const wchar_t *text = message->GetText(); + + PROTORECVEVENT recv = { 0 }; + recv.flags = PREF_UTF; + recv.timestamp = message->GetTimestamp(); + recv.szMessage = mir_utf8encodeW(text); + + ProtoChainRecvMsg(hContact, &recv); + } + } + break; + + case SteamWebApi::PollApi::POOL_TYPE_MYMESSAGE: + { + SteamWebApi::PollApi::Message *message = (SteamWebApi::PollApi::Message*)item; + + MCONTACT hContact = FindContact(message->GetSteamId()); + if (hContact) + { + const wchar_t *text = message->GetText(); + + AddDBEvent(hContact, EVENTTYPE_MESSAGE, time(NULL), DBEF_UTF | DBEF_SENT, lstrlen(text), (BYTE*)mir_utf8encodeW(text)); + } + } + break; + + case SteamWebApi::PollApi::POOL_TYPE_STATE: + { + SteamWebApi::PollApi::State *state = (SteamWebApi::PollApi::State*)item; + + WORD status = CSteamProto::SteamToMirandaStatus(state->GetStatus()); + const char *steamId = state->GetSteamId(); + const wchar_t *nickname = state->GetNickname(); + + if (IsMe(steamId)) + { + if (status == ID_STATUS_OFFLINE) + continue; + + if (status != m_iStatus) + { + debugLogA("Change own status to %i", status); + WORD oldStatus = m_iStatus; + m_iStatus = m_iDesiredStatus = status; + ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus); + } + } + /*else + { + MCONTACT hContact = FindContact(steamId); + if (hContact) + SetContactStatus(hContact, status); + }*/ + + if (updatedIds.IsEmpty()) + updatedIds.Append(steamId); + else + updatedIds.AppendFormat(",%s", steamId); + } + break; + + case SteamWebApi::PollApi::POOL_TYPE_CONTACT_ADD: + { + SteamWebApi::PollApi::Relationship *crs = (SteamWebApi::PollApi::Relationship*)item; + + const char *steamId = crs->GetSteamId(); + if (updatedIds.IsEmpty()) + updatedIds.Append(steamId); + else + updatedIds.AppendFormat(",%s", steamId); + } + break; + + case SteamWebApi::PollApi::POOL_TYPE_CONTACT_REMOVE: + { + SteamWebApi::PollApi::Relationship *crs = (SteamWebApi::PollApi::Relationship*)item; + + const char *steamId = crs->GetSteamId(); + MCONTACT hContact = FindContact(steamId); + if (hContact) + CallService(MS_DB_CONTACT_DELETE, hContact, 0); + } + break; + + case SteamWebApi::PollApi::POOL_TYPE_CONTACT_REQUEST: + { + SteamWebApi::PollApi::Relationship *crs = (SteamWebApi::PollApi::Relationship*)item; + + const char *steamId = crs->GetSteamId(); + + MCONTACT hContact = FindContact(steamId); + if (!hContact) + hContact = AddContact(steamId, true); + + RaiseAuthRequestThread((void*)hContact); + } + break; + } + } + + if (!updatedIds.IsEmpty()) + UpdateContactsThread(mir_strdup(updatedIds)); +} + +void CSteamProto::ParsePollData(JSONNODE *data) +{ + JSONNODE *node, *item = NULL; + + for (size_t i = 0; i < json_size(data); i++) + { + item = json_at(data, i); + if (item == NULL) + break; + + node = json_get(item, "steamid_from"); + ptrA steamId(mir_u2a(json_as_string(node))); + + node = json_get(item, "utc_timestamp"); + time_t timestamp = atol(ptrA(mir_u2a(json_as_string(node)))); + + node = json_get(item, "type"); + ptrW type(json_as_string(node)); + if (!lstrcmpi(type, L"saytext") || !lstrcmpi(type, L"emote") || + !lstrcmpi(type, L"my_saytext") || !lstrcmpi(type, L"my_emote")) + { + node = json_get(item, "text"); + const wchar_t *text = json_as_string(node); + + if (_tcsstr(type, L"my_") == NULL) + { + MCONTACT hContact = FindContact(steamId); + if (hContact) + { + PROTORECVEVENT recv = { 0 }; + recv.flags = PREF_UTF; + recv.timestamp = timestamp; + recv.szMessage = mir_utf8encodeW(text); + + ProtoChainRecvMsg(hContact, &recv); + } + } + else + { + MCONTACT hContact = FindContact(steamId); + if (hContact) + AddDBEvent(hContact, EVENTTYPE_MESSAGE, timestamp, DBEF_UTF | DBEF_SENT, lstrlen(text), (BYTE*)mir_utf8encodeW(text)); + } + + /*node = json_get(item, "text"); + if (node != NULL) message->text = json_as_string(node); + + node = json_get(item, "utc_timestamp"); + message->timestamp = atol(ptrA(mir_u2a(json_as_string(node))));*/ + } + /*else if (!lstrcmpi(type, L"typing")) + { + }*/ + else if (!lstrcmpi(type, L"personastate")) + { + node = json_get(item, "persona_state"); + int status = SteamToMirandaStatus(json_as_int(node)); + + if (IsMe(steamId)) + { + if (status == ID_STATUS_OFFLINE) + continue; + + if (status != m_iStatus) + { + debugLogA("CSteamProto::ParsePollData: Change own status to %i", status); + WORD oldStatus = m_iStatus; + m_iStatus = m_iDesiredStatus = status; + ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus); + } + } + else if(!FindContact(steamId)) + { + MCONTACT hContact = AddContact(steamId); + + setWord(hContact, "Status", status); + + node = json_get(item, "persona_name"); + setWString(hContact, "Nick", json_as_string(node)); + } + + // todo: find difference between state changing and info changing + } + else if (!lstrcmpi(type, L"personarelationship")) + { + node = json_get(item, "persona_state"); + int state = json_as_int(node); + + switch (state) + { + case 0: + // removed + break; + + case 1: + // ignored + break; + + case 2: + { // auth request + /*MCONTACT hContact = FindContact(steamId); + if (!hContact) + hContact = AddContact(steamId, true);*/ + + //RaiseAuthRequestThread((void*)hContact); + + ptrA token(getStringA("TokenSecret")); + + PushRequest( + new SteamWebApi::GetUserSummariesRequest(token, steamId), + &CSteamProto::OnAuthRequested, + mir_strdup(steamId)); + } + break; + + case 3: + // add to list + break; + + default: continue; + } + } + /*else if (!lstrcmpi(type, L"leftconversation")) + { + }*/ + else + { + continue; + } + } +} + +void CSteamProto::PollingThread(void*) +{ + debugLogA("CSteamProto::PollingThread: entering"); + + ptrA token(getStringA("TokenSecret")); + ptrA umqId(getStringA("UMQID")); + UINT32 messageId = getDword("MessageID", 0); + + //SteamWebApi::PollApi::PollResult pollResult; + bool breaked = false; + while (!isTerminated && !breaked) + { + SteamWebApi::PollRequest *request = new SteamWebApi::PollRequest(token, umqId, messageId); + debugLogA("CSteamProto::PollingThread: %s", request->szUrl); + request->szUrl = (char*)request->url.c_str(); + request->nlc = m_pollingConnection; + NETLIBHTTPREQUEST *response = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)m_hNetlibUser, (LPARAM)request); + delete request; + + if (response == NULL || response->resultCode != HTTP_STATUS_OK) + return; + + JSONNODE *root = json_parse(response->pData), *node; + node = json_get(root, "error"); + ptrW error(json_as_string(node)); + + if (!lstrcmpi(error, L"OK")) + { + + node = json_get(root, "messagelast"); + messageId = json_as_int(node); + + node = json_get(root, "messages"); + root = json_as_array(node); + + if (root != NULL) + ParsePollData(root); + + m_pollingConnection = response->nlc; + } + else if (!lstrcmpi(error, L"Timeout")) + { + continue; + } + else if (!lstrcmpi(error, L"Not Logged On")) + { + if (!IsOnline()) + { + // need to relogin + debugLogA("CSteamProto::PollingThread: not logged on"); + + SetStatus(ID_STATUS_OFFLINE); + } + + breaked = true; + } + else if (lstrcmpi(error, L"Timeout")) + { + // something wrong + debugLogA("CSteamProto::PollingThread: error (%d)", response->resultCode); + + // token has expired + if (response->resultCode == HTTP_STATUS_UNAUTHORIZED) + delSetting("TokenSecret"); + + breaked = true; + } + + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)response); + } + + setDword("MessageID", messageId); + + m_hPollingThread = NULL; + debugLogA("CSteamProto::PollingThread: leaving"); +} \ No newline at end of file diff --git a/protocols/Steam/src/steam_proto.cpp b/protocols/Steam/src/steam_proto.cpp index cc589ec3bc..3705916e46 100644 --- a/protocols/Steam/src/steam_proto.cpp +++ b/protocols/Steam/src/steam_proto.cpp @@ -3,12 +3,15 @@ CSteamProto::CSteamProto(const char* protoName, const TCHAR* userName) : PROTO(protoName, userName), hAuthProcess(1), - hMessageProcess(1) + hMessageProcess(1), + requestsQueue(1) { CreateProtoService(PS_CREATEACCMGRUI, &CSteamProto::OnAccountManagerInit); InitializeCriticalSection(&this->contact_search_lock); + InitQueue(); + // icons wchar_t filePath[MAX_PATH]; GetModuleFileName(g_hInstance, filePath, MAX_PATH); @@ -41,6 +44,8 @@ CSteamProto::CSteamProto(const char* protoName, const TCHAR* userName) : CSteamProto::~CSteamProto() { + UninitQueue(); + DeleteCriticalSection(&this->contact_search_lock); } @@ -53,15 +58,21 @@ MCONTACT __cdecl CSteamProto::AddToList(int flags, PROTOSEARCHRESULT* psr) ptrA steamId(mir_u2a(psr->id)); if (!FindContact(steamId)) { - hContact = AddContact(steamId, true); - ForkThread(&CSteamProto::UpdateContactsThread, (void*)mir_strdup(steamId)); + //hContact = AddContact(steamId, true); + //ForkThread(&CSteamProto::UpdateContactsThread, (void*)mir_strdup(steamId)); + + ptrA token(getStringA("TokenSecret")); + + PushRequest( + new SteamWebApi::GetUserSummariesRequest(token, steamId), + &CSteamProto::OnGotUserSummaries); } } else if (psr->cbSize == sizeof(STEAM_SEARCH_RESULT)) { STEAM_SEARCH_RESULT *ssr = (STEAM_SEARCH_RESULT*)psr; hContact = AddContact(ssr->contact->GetSteamId(), true); - UpdateContact(hContact, ssr->contact); + UpdateContact(hContact, ssr->data); } return hContact; @@ -80,7 +91,17 @@ int __cdecl CSteamProto::Authorize(HANDLE hDbEvent) if (hContact == INVALID_CONTACT_ID) return 1; - ForkThread(&CSteamProto::AuthAllowThread, (void*)hContact); + //ForkThread(&CSteamProto::AuthAllowThread, (void*)hContact); + + ptrA token(getStringA("TokenSecret")); + ptrA sessionId(getStringA("SessionID")); + ptrA steamId(getStringA("SteamID")); + char *who = getStringA(hContact, "SteamID"); + + PushRequest( + new SteamWebApi::ApprovePendingRequest(token, sessionId, steamId, who), + &CSteamProto::OnPendingApproved, + who); return 0; } @@ -96,7 +117,17 @@ int __cdecl CSteamProto::AuthDeny(HANDLE hDbEvent, const TCHAR* szReason) if (hContact == INVALID_CONTACT_ID) return 1; - ForkThread(&CSteamProto::AuthDenyThread, (void*)hContact); + //ForkThread(&CSteamProto::AuthDenyThread, (void*)hContact); + + ptrA token(getStringA("TokenSecret")); + ptrA sessionId(getStringA("SessionID")); + ptrA steamId(getStringA("SteamID")); + char *who = getStringA(hContact, "SteamID"); + + PushRequest( + new SteamWebApi::IgnorePendingRequest(token, sessionId, steamId, who), + &CSteamProto::OnPendingIgnoreded, + who); return 0; } @@ -119,7 +150,17 @@ int __cdecl CSteamProto::AuthRequest(MCONTACT hContact, const TCHAR* szMessage) param->hContact = hContact; param->hAuth = (HANDLE)hAuth; - ForkThread(&CSteamProto::AddContactThread, param); + //ForkThread(&CSteamProto::AddContactThread, param); + + ptrA token(getStringA("TokenSecret")); + ptrA sessionId(getStringA("SessionID")); + ptrA steamId(getStringA("SteamID")); + ptrA who(getStringA(hContact, "SteamID")); + + PushRequest( + new SteamWebApi::AddFriendRequest(token, sessionId, steamId, who), + &CSteamProto::OnFriendAdded, + param); return hAuth; } @@ -175,7 +216,15 @@ HANDLE __cdecl CSteamProto::SearchBasic(const TCHAR* id) if (!this->IsOnline()) return 0; - ForkThread(&CSteamProto::SearchByIdThread, mir_wstrdup(id)); + //ForkThread(&CSteamProto::SearchByIdThread, mir_wstrdup(id)); + + ptrA token(getStringA("TokenSecret")); + ptrA steamId(mir_u2a(id)); + + PushRequest( + new SteamWebApi::GetUserSummariesRequest(token, steamId), + &CSteamProto::OnSearchByIdEnded, + mir_wstrdup(id)); return (HANDLE)STEAM_SEARCH_BYID; } @@ -187,16 +236,21 @@ HANDLE __cdecl CSteamProto::SearchByEmail(const TCHAR* email) HANDLE __cdecl CSteamProto::SearchByName(const TCHAR* nick, const TCHAR* firstName, const TCHAR* lastName) { - if (!this->IsOnline()) + //if (!this->IsOnline()) return 0; + ptrA token(getStringA("TokenSecret")); + CMString keywords; keywords.AppendFormat(L" %s", nick); keywords.AppendFormat(L" %s", firstName); keywords.AppendFormat(L" %s", lastName); keywords.Trim(); - ForkThread(&CSteamProto::SearchByNameThread, mir_wstrdup(keywords)); + //ForkThread(&CSteamProto::SearchByNameThread, mir_wstrdup(keywords)); + PushRequest( + new SteamWebApi::SearchRequest(token, mir_utf8encodeW(keywords)), + &CSteamProto::OnSearchByNameStarted); return (HANDLE)STEAM_SEARCH_BYNAME; } @@ -238,10 +292,19 @@ int __cdecl CSteamProto::SendMsg(MCONTACT hContact, int flags, const char *msg) SendMessageParam *param = (SendMessageParam*)mir_calloc(sizeof(SendMessageParam)); param->hContact = hContact; - param->text = mir_utf8encode(msg); + //param->text = mir_utf8encode(msg); param->hMessage = (HANDLE)hMessage; - ForkThread(&CSteamProto::SendMessageThread, param); + //ForkThread(&CSteamProto::SendMessageThread, param); + + ptrA token(getStringA("TokenSecret")); + ptrA umqid(getStringA("UMQID")); + ptrA steamId(getStringA(hContact, "SteamId")); + + PushRequest( + new SteamWebApi::SendMessageRequest(token, umqid, steamId, ptrA(mir_utf8encode(msg))), + &CSteamProto::OnMessageSent, + param); return hMessage; } @@ -252,22 +315,24 @@ int __cdecl CSteamProto::SetApparentMode(MCONTACT hContact, int mode) { return 0 int CSteamProto::SetStatus(int new_status) { - debugLogA("CSteamProto::SetStatus: from %i to %i", m_iStatus, new_status); - if (new_status == m_iDesiredStatus) return 0; + debugLogA("CSteamProto::SetStatus: changing status from %i to %i", m_iStatus, new_status); + int old_status = m_iStatus; m_iDesiredStatus = new_status; if (new_status == ID_STATUS_OFFLINE) { - m_bTerminated = true; - ForkThread(&CSteamProto::LogOutThread, NULL); + //isTerminated = true; + //ForkThread(&CSteamProto::LogOutThread, NULL); m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); + StopQueue(); + if (!Miranda_Terminated()) SetAllContactsStatus(ID_STATUS_OFFLINE); } @@ -276,7 +341,8 @@ int CSteamProto::SetStatus(int new_status) m_iStatus = ID_STATUS_CONNECTING; ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); - ForkThread(&CSteamProto::LogInThread, NULL); + //ForkThread(&CSteamProto::LogInThread, NULL); + StartQueue(); } return 0; @@ -305,7 +371,20 @@ int __cdecl CSteamProto::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM return this->OnOptionsInit(wParam, lParam);*/ case EV_PROTO_ONCONTACTDELETED: - ForkThread(&CSteamProto::RemoveContactThread, (void*)getStringA(wParam, "SteamID")); + if (IsOnline()) + { + //ForkThread(&CSteamProto::RemoveContactThread, (void*)getStringA(wParam, "SteamID")); + + ptrA token(getStringA("TokenSecret")); + ptrA sessionId(getStringA("SessionID")); + ptrA steamId(getStringA("SteamID")); + char *who = getStringA(wParam, "SteamID"); + + PushRequest( + new SteamWebApi::RemoveFriendRequest(token, sessionId, steamId, who), + &CSteamProto::OnFriendRemoved, + who); + } return 0; /*case EV_PROTO_ONMENU: diff --git a/protocols/Steam/src/steam_proto.h b/protocols/Steam/src/steam_proto.h index 88a18a74f1..8e930d287a 100644 --- a/protocols/Steam/src/steam_proto.h +++ b/protocols/Steam/src/steam_proto.h @@ -34,6 +34,7 @@ struct STEAM_SEARCH_RESULT { PROTOSEARCHRESULT hdr; const SteamWebApi::FriendApi::Summary *contact; + JSONNODE *data; }; enum @@ -46,6 +47,26 @@ enum CMI_MAX // this item shall be the last one }; +typedef void (CSteamProto::*RESPONSE)(const NETLIBHTTPREQUEST *response, void *arg); + +struct QueueItem +{ + SteamWebApi::HttpRequest *request; + void *arg; + RESPONSE responseCallback; + RESPONSE responseFailedCallback; + + QueueItem(SteamWebApi::HttpRequest *request) : + request(request), arg(NULL), responseCallback(NULL), responseFailedCallback(NULL) { } + + QueueItem(SteamWebApi::HttpRequest *request, RESPONSE response) : + request(request), arg(NULL), responseCallback(response), responseFailedCallback(NULL) { } + + QueueItem(SteamWebApi::HttpRequest *request, RESPONSE response, RESPONSE responseFailedCallback) : + request(request), arg(NULL), responseCallback(response), responseFailedCallback(responseFailedCallback) { } + + ~QueueItem() { delete request; responseCallback = NULL; } +}; class CSteamProto : public PROTO { @@ -55,48 +76,48 @@ public: ~CSteamProto(); // PROTO_INTERFACE - virtual MCONTACT __cdecl AddToList( int flags, PROTOSEARCHRESULT* psr ); - virtual MCONTACT __cdecl AddToListByEvent( int flags, int iContact, HANDLE hDbEvent ); + virtual MCONTACT __cdecl AddToList(int flags, PROTOSEARCHRESULT *psr); + virtual MCONTACT __cdecl AddToListByEvent(int flags, int iContact, HANDLE hDbEvent); - virtual int __cdecl Authorize( HANDLE hDbEvent ); - virtual int __cdecl AuthDeny( HANDLE hDbEvent, const TCHAR* szReason ); - virtual int __cdecl AuthRecv(MCONTACT hContact, PROTORECVEVENT* ); - virtual int __cdecl AuthRequest(MCONTACT hContact, const TCHAR* szMessage ); + virtual int __cdecl Authorize(HANDLE hDbEvent); + virtual int __cdecl AuthDeny(HANDLE hDbEvent, const TCHAR *szReason); + virtual int __cdecl AuthRecv(MCONTACT hContact, PROTORECVEVENT *); + virtual int __cdecl AuthRequest(MCONTACT hContact, const TCHAR * szMessage); - virtual HANDLE __cdecl FileAllow(MCONTACT hContact, HANDLE hTransfer, const TCHAR* szPath ); - virtual int __cdecl FileCancel(MCONTACT hContact, HANDLE hTransfer ); - virtual int __cdecl FileDeny(MCONTACT hContact, HANDLE hTransfer, const TCHAR* szReason ); - virtual int __cdecl FileResume( HANDLE hTransfer, int* action, const TCHAR** szFilename ); + virtual HANDLE __cdecl FileAllow(MCONTACT hContact, HANDLE hTransfer, const TCHAR* szPath); + virtual int __cdecl FileCancel(MCONTACT hContact, HANDLE hTransfer); + virtual int __cdecl FileDeny(MCONTACT hContact, HANDLE hTransfer, const TCHAR* szReason); + virtual int __cdecl FileResume(HANDLE hTransfer, int *action, const TCHAR** szFilename); - virtual DWORD_PTR __cdecl GetCaps( int type, MCONTACT hContact = NULL ); - virtual int __cdecl GetInfo(MCONTACT hContact, int infoType ); + virtual DWORD_PTR __cdecl GetCaps(int type, MCONTACT hContact = NULL); + virtual int __cdecl GetInfo(MCONTACT hContact, int infoType); - virtual HANDLE __cdecl SearchBasic( const TCHAR* id ); - virtual HANDLE __cdecl SearchByEmail( const TCHAR* email ); - virtual HANDLE __cdecl SearchByName( const TCHAR* nick, const TCHAR* firstName, const TCHAR* lastName ); - virtual HWND __cdecl SearchAdvanced( HWND owner ); - virtual HWND __cdecl CreateExtendedSearchUI( HWND owner ); + virtual HANDLE __cdecl SearchBasic(const TCHAR *id); + virtual HANDLE __cdecl SearchByEmail(const TCHAR *email); + virtual HANDLE __cdecl SearchByName(const TCHAR *nick, const TCHAR *firstName, const TCHAR *lastName); + virtual HWND __cdecl SearchAdvanced(HWND owner); + virtual HWND __cdecl CreateExtendedSearchUI(HWND owner); - virtual int __cdecl RecvContacts(MCONTACT hContact, PROTORECVEVENT* ); - virtual int __cdecl RecvFile(MCONTACT hContact, PROTORECVFILET* ); - virtual int __cdecl RecvMsg(MCONTACT hContact, PROTORECVEVENT* ); - virtual int __cdecl RecvUrl(MCONTACT hContact, PROTORECVEVENT* ); + virtual int __cdecl RecvContacts(MCONTACT hContact, PROTORECVEVENT*); + virtual int __cdecl RecvFile(MCONTACT hContact, PROTORECVFILET*); + virtual int __cdecl RecvMsg(MCONTACT hContact, PROTORECVEVENT*); + virtual int __cdecl RecvUrl(MCONTACT hContact, PROTORECVEVENT*); virtual int __cdecl SendContacts(MCONTACT hContact, int flags, int nContacts, MCONTACT *hContactsList); - virtual HANDLE __cdecl SendFile(MCONTACT hContact, const TCHAR* szDescription, TCHAR** ppszFiles ); - virtual int __cdecl SendMsg(MCONTACT hContact, int flags, const char* msg ); - virtual int __cdecl SendUrl(MCONTACT hContact, int flags, const char* url ); + virtual HANDLE __cdecl SendFile(MCONTACT hContact, const TCHAR* szDescription, TCHAR** ppszFiles); + virtual int __cdecl SendMsg(MCONTACT hContact, int flags, const char* msg); + virtual int __cdecl SendUrl(MCONTACT hContact, int flags, const char* url); - virtual int __cdecl SetApparentMode(MCONTACT hContact, int mode ); - virtual int __cdecl SetStatus( int iNewStatus ); + virtual int __cdecl SetApparentMode(MCONTACT hContact, int mode); + virtual int __cdecl SetStatus(int iNewStatus); - virtual HANDLE __cdecl GetAwayMsg(MCONTACT hContact ); - virtual int __cdecl RecvAwayMsg(MCONTACT hContact, int mode, PROTORECVEVENT* evt ); - virtual int __cdecl SetAwayMsg( int m_iStatus, const TCHAR* msg ); + virtual HANDLE __cdecl GetAwayMsg(MCONTACT hContact); + virtual int __cdecl RecvAwayMsg(MCONTACT hContact, int mode, PROTORECVEVENT* evt); + virtual int __cdecl SetAwayMsg(int m_iStatus, const TCHAR* msg); - virtual int __cdecl UserIsTyping(MCONTACT hContact, int type ); + virtual int __cdecl UserIsTyping(MCONTACT hContact, int type); - virtual int __cdecl OnEvent( PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam ); + virtual int __cdecl OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam); // instances static CSteamProto* InitProtoInstance(const char* protoName, const wchar_t* userName); @@ -110,18 +131,37 @@ public: static void UninitMenus(); protected: - bool m_bTerminated; - HANDLE m_hPollingThread; + bool isTerminated; + HANDLE m_evRequestsQueue, m_hQueueThread; + HANDLE m_pollingConnection, m_hPollingThread; ULONG hAuthProcess; ULONG hMessageProcess; CRITICAL_SECTION contact_search_lock; + CRITICAL_SECTION requests_queue_lock; + LIST requestsQueue; // instances static LIST InstanceList; static int CompareProtos(const CSteamProto *p1, const CSteamProto *p2); + // queue + void InitQueue(); + void UninitQueue(); + + void StartQueue(); + void StopQueue(); + + void PushRequest(SteamWebApi::HttpRequest *request); + void PushRequest(SteamWebApi::HttpRequest *request, RESPONSE response); + void PushRequest(SteamWebApi::HttpRequest *request, RESPONSE response, void *arg); + + void ExecuteRequest(QueueItem *requestItem); + + void __cdecl QueueThread(void*); + // pooling thread void PollServer(const char *sessionId, const char *steamId, UINT32 messageId, SteamWebApi::PollApi::PollResult *pollResult); + void ParsePollData(JSONNODE *data); void __cdecl PollingThread(void*); // account @@ -133,6 +173,13 @@ protected: void __cdecl LogOutThread(void*); void __cdecl SetServerStatusThread(void*); + void OnGotRsaKey(const NETLIBHTTPREQUEST *response, void *arg); + + void OnAuthorization(const NETLIBHTTPREQUEST *response, void *arg); + void OnGotSession(const NETLIBHTTPREQUEST *response, void *arg); + + void OnLoggedOn(const NETLIBHTTPREQUEST *response, void *arg); + // contacts void SetContactStatus(MCONTACT hContact, WORD status); void SetAllContactsStatus(WORD status); @@ -142,9 +189,27 @@ protected: void UpdateContact(MCONTACT hContact, const SteamWebApi::FriendApi::Summary *summary); void __cdecl UpdateContactsThread(void*); + void UpdateContact(MCONTACT hContact, JSONNODE *data); + MCONTACT FindContact(const char *steamId); MCONTACT AddContact(const char *steamId, bool isTemporary = false); + void OnGotFriendList(const NETLIBHTTPREQUEST *response, void *arg); + void OnGotUserSummaries(const NETLIBHTTPREQUEST *response, void *arg); + + void OnFriendAdded(const NETLIBHTTPREQUEST *response, void *arg); + void OnFriendRemoved(const NETLIBHTTPREQUEST *response, void *arg); + + void OnAuthRequested(const NETLIBHTTPREQUEST *response, void *arg); + + void OnPendingApproved(const NETLIBHTTPREQUEST *response, void *arg); + void OnPendingIgnoreded(const NETLIBHTTPREQUEST *response, void *arg); + + void OnSearchByIdEnded(const NETLIBHTTPREQUEST *response, void *arg); + + void OnSearchByNameStarted(const NETLIBHTTPREQUEST *response, void *arg); + void OnSearchByNameFinished(const NETLIBHTTPREQUEST *response, void *arg); + void __cdecl RaiseAuthRequestThread(void*); void __cdecl AuthAllowThread(void*); void __cdecl AuthDenyThread(void*); @@ -161,6 +226,8 @@ protected: void __cdecl SendMessageThread(void*); void __cdecl SendTypingThread(void*); + void OnMessageSent(const NETLIBHTTPREQUEST *response, void *arg); + // menus HGENMENU m_hMenuRoot; static HANDLE hChooserMenu; @@ -191,7 +258,7 @@ protected: static WORD SteamToMirandaStatus(int state); static int MirandaToSteamState(int status); - static int RsaEncrypt(const SteamWebApi::RsaKeyApi::RsaKey &rsaKey, const char *data, DWORD dataSize, BYTE *encrypted, DWORD &encryptedSize); + static int RsaEncrypt(const char *pszModulus, const char *data, BYTE *encrypted, DWORD &encryptedSize); HANDLE AddDBEvent(MCONTACT hContact, WORD type, DWORD timestamp, DWORD flags, DWORD cbBlob, PBYTE pBlob); diff --git a/protocols/Steam/src/steam_queue.cpp b/protocols/Steam/src/steam_queue.cpp new file mode 100644 index 0000000000..22096f70be --- /dev/null +++ b/protocols/Steam/src/steam_queue.cpp @@ -0,0 +1,146 @@ +#include "common.h" + +void CSteamProto::InitQueue() +{ + InitializeCriticalSection(&requests_queue_lock); + m_evRequestsQueue = CreateEvent(NULL, FALSE, FALSE, NULL); +} + +void CSteamProto::UninitQueue() +{ + requestsQueue.destroy(); + CloseHandle(m_evRequestsQueue); + DeleteCriticalSection(&requests_queue_lock); +} + +void CSteamProto::StartQueue() +{ + isTerminated = false; + + if (m_hQueueThread == NULL) + { + ptrA token(getStringA("TokenSecret")); + if (token && lstrlenA(token) > 0) + { + PushRequest( + new SteamWebApi::LogonRequest(token), + &CSteamProto::OnLoggedOn); + } + else + { + ptrA username(mir_urlEncode(ptrA(mir_utf8encodeW(getWStringA("Username"))))); + if (username == NULL || strlen(username) == 0) + return; + PushRequest(new SteamWebApi::RsaKeyRequest(username), (RESPONSE)&CSteamProto::OnGotRsaKey); + } + + m_hQueueThread = ForkThreadEx(&CSteamProto::QueueThread, 0, NULL); + } +} + +void CSteamProto::StopQueue() +{ + isTerminated = true; + + { + mir_cslock lock(requests_queue_lock); + + int count = requestsQueue.getCount(); + while (count > 0) + requestsQueue.remove(count - 1); + } + + // logoff + ptrA token(getStringA("TokenSecret")); + ptrA umqid(getStringA("UMQID")); + + SteamWebApi::HttpRequest *request = new SteamWebApi::LogoffRequest(token, umqid); + debugLogA("CSteamProto::StopQueue: %s", request->szUrl); + request->szUrl = (char*)request->url.c_str(); + NETLIBHTTPREQUEST *response = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)m_hNetlibUser, (LPARAM)request); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)response); + delete request; + + m_hQueueThread = NULL; +} + +void CSteamProto::PushRequest(SteamWebApi::HttpRequest *request) +{ + PushRequest(request, NULL, NULL); +} + +void CSteamProto::PushRequest(SteamWebApi::HttpRequest *request, RESPONSE response) +{ + PushRequest(request, response, NULL); +} + +void CSteamProto::PushRequest(SteamWebApi::HttpRequest *request, RESPONSE response, void *arg) +{ + if (isTerminated) + return; + + { + mir_cslock lock(requests_queue_lock); + QueueItem *item = new QueueItem(request, response); + item->arg = arg; + requestsQueue.insert(item); + } + + SetEvent(m_evRequestsQueue); +} + +void CSteamProto::ExecuteRequest(QueueItem *item) +{ + if (isTerminated) + return; + + debugLogA("CSteamProto::ExecuteRequest: %s", item->request->szUrl); + + item->request->szUrl = (char*)item->request->url.c_str(); + NETLIBHTTPREQUEST *response = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)m_hNetlibUser, (LPARAM)item->request); + + if (item->responseCallback != NULL) + (this->*(item->responseCallback))(response, item->arg); + + // todo: add succeed and failed handlers if need + if (response != NULL/* && response->resultCode != 200*/) + { + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)response); + } + /*else if (requestItem->responseFailedCallback != NULL) + { + (this->*(requestItem->responseFailedCallback))(response); + }*/ + + delete item; +} + +void CSteamProto::QueueThread(void*) +{ + debugLogA("CSteamProto::QueueThread: entering"); + + while (!isTerminated) + { + WaitForSingleObject(m_evRequestsQueue, 1000); + + while (true) + { + QueueItem *item = NULL; + + { + mir_cslock lock(requests_queue_lock); + + if (requestsQueue.getCount() == 0) + break; + + item = requestsQueue[0]; + requestsQueue.remove(0); + } + + if (item != NULL) + ExecuteRequest(item); + } + } + + debugLogA("CSteamProto::QueueThread: leaving"); +} \ No newline at end of file diff --git a/protocols/Steam/src/steam_utils.cpp b/protocols/Steam/src/steam_utils.cpp index bc5d624a75..1ac29f593c 100644 --- a/protocols/Steam/src/steam_utils.cpp +++ b/protocols/Steam/src/steam_utils.cpp @@ -44,9 +44,8 @@ int CSteamProto::MirandaToSteamState(int status) } } -int CSteamProto::RsaEncrypt(const SteamWebApi::RsaKeyApi::RsaKey &rsaKey, const char *data, DWORD dataSize, BYTE *encryptedData, DWORD &encryptedSize) +int CSteamProto::RsaEncrypt(const char *pszModulus, const char *data, BYTE *encryptedData, DWORD &encryptedSize) { - const char *pszModulus = rsaKey.GetModulus(); DWORD cchModulus = (DWORD)strlen(pszModulus); // convert hex string to byte array @@ -100,6 +99,8 @@ int CSteamProto::RsaEncrypt(const SteamWebApi::RsaKeyApi::RsaKey &rsaKey, const if (!CryptImportKey(hCSP, pKeyBlob, cbKeyBlob, 0, 0, &phKey)) return GetLastError(); + DWORD dataSize = strlen(data); + // if data is not allocated just renurn size if (encryptedData == NULL) { -- cgit v1.2.3