From 0a41e7f5bd2d3cd705e5806b3190ff7afadde131 Mon Sep 17 00:00:00 2001 From: Alexander Lantsev Date: Tue, 8 Apr 2014 14:23:04 +0000 Subject: Steam: - added searching by steamid - attempt to replace OpenSSL on CryptoAPI git-svn-id: http://svn.miranda-ng.org/main/trunk@8890 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Steam/Steam_10.vcxproj | 9 +- protocols/Steam/Steam_10.vcxproj.filters | 15 +- protocols/Steam/src/Steam/authorization.h | 12 +- protocols/Steam/src/Steam/friend.h | 29 +++- protocols/Steam/src/Steam/login.h | 4 - protocols/Steam/src/Steam/poll.h | 33 ++--- protocols/Steam/src/Steam/rsa_key.h | 73 ++++++++++ protocols/Steam/src/Steam/search.h | 78 +++++++++++ protocols/Steam/src/Steam/steam.h | 8 +- protocols/Steam/src/common.h | 5 + protocols/Steam/src/http_request.h | 14 +- protocols/Steam/src/steam_account.cpp | 222 ++++++++++++++++++++++++++++-- protocols/Steam/src/steam_contacts.cpp | 42 ++++++ protocols/Steam/src/steam_proto.cpp | 14 +- protocols/Steam/src/steam_proto.h | 8 +- protocols/Steam/src/steam_thread.cpp | 78 ++++++----- 16 files changed, 543 insertions(+), 101 deletions(-) create mode 100644 protocols/Steam/src/Steam/rsa_key.h create mode 100644 protocols/Steam/src/Steam/search.h (limited to 'protocols/Steam') diff --git a/protocols/Steam/Steam_10.vcxproj b/protocols/Steam/Steam_10.vcxproj index 8ebad36d8c..4e0d8649f9 100644 --- a/protocols/Steam/Steam_10.vcxproj +++ b/protocols/Steam/Steam_10.vcxproj @@ -78,7 +78,7 @@ Level3 Disabled _DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - ..\..\include;..\..\plugins\ExternalAPI;..\..\..\OpenSSL\Win32\include;%(AdditionalIncludeDirectories) + ..\..\include;..\..\plugins\ExternalAPI;%(AdditionalIncludeDirectories) false true 4996;%(DisableSpecificWarnings) @@ -92,7 +92,7 @@ true $(ProfileDir)..\..\bin10\lib;$(ProfileDir)..\..\..\OpenSSL\Win32\lib\VC $(IntDir)$(TargetName).lib - comctl32.lib;libeay32MDd.lib;%(AdditionalDependencies) + crypt32.lib;comctl32.lib;%(AdditionalDependencies) false /PDBALTPATH:%_PDB% %(AdditionalOptions) @@ -191,11 +191,13 @@ - + + + @@ -207,6 +209,7 @@ + Create diff --git a/protocols/Steam/Steam_10.vcxproj.filters b/protocols/Steam/Steam_10.vcxproj.filters index 627e44cd78..4513b5e30e 100644 --- a/protocols/Steam/Steam_10.vcxproj.filters +++ b/protocols/Steam/Steam_10.vcxproj.filters @@ -42,6 +42,9 @@ Source Files + + Source Files + @@ -71,15 +74,21 @@ Header Files\Steam - - Header Files\Steam - Header Files\Steam Header Files\Steam + + Header Files\Steam + + + Header Files\Steam + + + Header Files\Steam + diff --git a/protocols/Steam/src/Steam/authorization.h b/protocols/Steam/src/Steam/authorization.h index c0015cdebc..17aa3410d6 100644 --- a/protocols/Steam/src/Steam/authorization.h +++ b/protocols/Steam/src/Steam/authorization.h @@ -56,7 +56,7 @@ namespace SteamWebApi } }; - static void Authorize(HANDLE hConnection, const wchar_t *username, const char *password, AuthResult *authResult) + static void Authorize(HANDLE hConnection, const wchar_t *username, const char *password, const char *timestamp, AuthResult *authResult) { authResult->success = false; authResult->captcha_needed = false; @@ -64,7 +64,7 @@ namespace SteamWebApi ptrA base64Username(mir_urlEncode(ptrA(mir_utf8encodeW(username)))); - CryptoApi::RsaKey rsaKey; + /*CryptoApi::RsaKey rsaKey; CryptoApi::GetRsaKey(hConnection, base64Username, &rsaKey); if (!rsaKey.IsSuccess()) return; @@ -72,16 +72,16 @@ namespace SteamWebApi BYTE *rsaEncryptedPassword = (BYTE*)mir_alloc(size); rsaKey.Encrypt((unsigned char*)password, strlen(password), rsaEncryptedPassword); ptrA base64RsaEncryptedPassword(mir_base64_encode(rsaEncryptedPassword, size)); - mir_free(rsaEncryptedPassword); + mir_free(rsaEncryptedPassword);*/ CMStringA data; data.AppendFormat("username=%s", base64Username); - data.AppendFormat("&password=%s", ptrA(mir_urlEncode(base64RsaEncryptedPassword))); + data.AppendFormat("&password=%s", ptrA(mir_urlEncode(password))); data.AppendFormat("&emailauth=%s", ptrA(mir_urlEncode(authResult->emailauth.c_str()))); data.AppendFormat("&emailsteamid=%s", authResult->emailsteamid.c_str()); - data.AppendFormat("&captchagid=%s", authResult->captchagid); + data.AppendFormat("&captchagid=%s", authResult->captchagid.c_str()); data.AppendFormat("&captcha_text=%s", ptrA(mir_urlEncode(authResult->captcha_text.c_str()))); - data.AppendFormat("&rsatimestamp=%llu", rsaKey.GetTimestamp()); + data.AppendFormat("&rsatimestamp=%s", timestamp); data.AppendFormat("&oauth_scope=%s", "read_profile write_profile read_client write_client"); data.Append("&oauth_client_id=DE45CD61"); diff --git a/protocols/Steam/src/Steam/friend.h b/protocols/Steam/src/Steam/friend.h index b5f544523f..2c89dd718a 100644 --- a/protocols/Steam/src/Steam/friend.h +++ b/protocols/Steam/src/Steam/friend.h @@ -9,25 +9,30 @@ namespace SteamWebApi struct Friend : public Result { friend FriendApi; - //LIST friendIds; private: std::string steamId; std::wstring nickname; + std::wstring realname; + std::string countryCode; std::string homepage; std::string avatarUrl; - int status; + int state; + DWORD created; DWORD lastEvent; public: const char *GetSteamId() const { return steamId.c_str(); } const wchar_t *GetNickname() const { return nickname.c_str(); } + const wchar_t *GetRealname() const { return realname.c_str(); } + const char *GetCountryCode() const { return countryCode.c_str(); } const char *GetHomepage() const { return homepage.c_str(); } const char *GetAvatarUrl() const { return avatarUrl.c_str(); } - int GetStatus() const { return status; } + int GetState() const { return state; } + const DWORD GetCreated() const { return created; } const DWORD GetLastEvent() const { return lastEvent; } }; @@ -47,8 +52,6 @@ namespace SteamWebApi JSONNODE *root = json_parse(response->pData), *node, *child; - /*node = json_get(root, "response"); - root = json_as_node(node);*/ node = json_get(root, "players"); root = json_as_array(node); if (root != NULL) @@ -68,20 +71,32 @@ namespace SteamWebApi node = json_get(child, "personaname"); result->nickname = json_as_string(node); + node = json_get(child, "realname"); + if (node != NULL) + result->realname = json_as_string(node); + + node = json_get(child, "loccountrycode"); + if (node != NULL) + result->countryCode = ptrA(mir_u2a(json_as_string(node))); + node = json_get(child, "personastate"); - result->status = json_as_int(node); + result->state = json_as_int(node); node = json_get(child, "profileurl"); result->homepage = ptrA(mir_u2a(json_as_string(node))); + node = json_get(child, "timecreated"); + result->created = json_as_int(node); + node = json_get(child, "lastlogoff"); - //LastEventDateTS result->lastEvent = json_as_int(node); node = json_get(child, "avatarfull"); result->avatarUrl = ptrA(mir_u2a(json_as_string(node))); } } + else + return; result->success = true; } diff --git a/protocols/Steam/src/Steam/login.h b/protocols/Steam/src/Steam/login.h index aab7225a4d..a6f743c45c 100644 --- a/protocols/Steam/src/Steam/login.h +++ b/protocols/Steam/src/Steam/login.h @@ -57,10 +57,6 @@ namespace SteamWebApi loginResult->success = true; } - static void Relogon(HANDLE hConnection, const char *token, LoginResult *loginResult) - { - } - static void Logoff(HANDLE hConnection, const char *token, const char *sessionId) { CMStringA data; diff --git a/protocols/Steam/src/Steam/poll.h b/protocols/Steam/src/Steam/poll.h index a8e7c76306..f5b0c602ea 100644 --- a/protocols/Steam/src/Steam/poll.h +++ b/protocols/Steam/src/Steam/poll.h @@ -8,12 +8,12 @@ namespace SteamWebApi public: enum POOL_TYPE { - UNKNOWN, - MESSAGE, - MYMESSAGE, - TYPING, - STATE, - //POOL_TYPE_RELATIONSHIP = 4 + POOL_TYPE_UNKNOWN, + POOL_TYPE_MESSAGE, + POOL_TYPE_MYMESSAGE, + POOL_TYPE_TYPING, + POOL_TYPE_STATE, + //POOL_TYPE_RELATIONSHIP }; class PoolItem : public Result @@ -26,7 +26,7 @@ namespace SteamWebApi POOL_TYPE type; public: - PoolItem() : timestamp(0), type(POOL_TYPE::UNKNOWN) { } + PoolItem() : timestamp(0), type(POOL_TYPE_UNKNOWN) { } const char *GetSteamId() const { return steamId.c_str(); } const DWORD GetTimestamp() const { return timestamp; } @@ -80,17 +80,18 @@ namespace SteamWebApi static void PollStatus(HANDLE hConnection, const char *token, const char *sessionId, UINT32 messageId, PollResult *pollResult) { pollResult->success = false; + pollResult->need_relogin = false; + pollResult->items.clear(); CMStringA data; data.AppendFormat("access_token=%s", token); data.AppendFormat("&umqid=%s", sessionId); - data.AppendFormat("&message=%i", messageId); - //data.Append("§imeout=90"); + data.AppendFormat("&message=%iu", messageId); HttpRequest request(hConnection, REQUEST_POST, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Poll/v0001"); request.AddHeader("Content-Type", "application/x-www-form-urlencoded"); request.SetData(data.GetBuffer(), data.GetLength()); - request.timeout = 90000; + request.timeout = 90000; // may need to encrease timeout mir_ptr response(request.Send()); if (!response || response->resultCode != HTTP_STATUS_OK) @@ -103,7 +104,7 @@ namespace SteamWebApi if (!lstrcmpi(error, L"Not Logged On")) { pollResult->need_relogin = true; - pollResult->success = true; + //pollResult->success = true; return; } else @@ -139,9 +140,9 @@ namespace SteamWebApi Message *message = new Message(); if (_tcsstr(type, L"my_") == NULL) - message->type = POOL_TYPE::MESSAGE; + message->type = POOL_TYPE_MESSAGE; else - message->type = POOL_TYPE::MYMESSAGE; + message->type = POOL_TYPE_MYMESSAGE; node = json_get(child, "text"); if (node != NULL) message->text = json_as_string(node); @@ -154,12 +155,12 @@ namespace SteamWebApi else if(!lstrcmpi(type, L"typing")) { item = new Typing(); - item->type = POOL_TYPE::TYPING; + item->type = POOL_TYPE_TYPING; } else if (!lstrcmpi(type, L"personastate")) { State *state = new State(); - state->type = POOL_TYPE::STATE; + state->type = POOL_TYPE_STATE; node = json_get(child, "persona_state"); if (node != NULL) state->status = json_as_int(node); @@ -171,7 +172,7 @@ namespace SteamWebApi } /*else if (!lstrcmpi(type, L"personarelationship")) { - type = (int)POOL_TYPE::RELATIONSHIP; + type = POOL_TYPE_RELATIONSHIP; }*/ /*else if (!lstrcmpi(type, L"leftconversation")) { diff --git a/protocols/Steam/src/Steam/rsa_key.h b/protocols/Steam/src/Steam/rsa_key.h new file mode 100644 index 0000000000..a5322d3523 --- /dev/null +++ b/protocols/Steam/src/Steam/rsa_key.h @@ -0,0 +1,73 @@ +#ifndef _STEAM_CRYPTO_H_ +#define _STEAM_CRYPTO_H_ + + //#include + //#include + //#include + //#include + //#include + //#include + //#include + +//#include "../RSA/RSA.h"; +//#include "../RSA/Key.h"; +//#include "../RSA/BigInt.h"; +// +//#include ; + +namespace SteamWebApi +{ + class RsaKeyApi : public BaseApi + { + public: + + class RsaKey : public Result + { + friend RsaKeyApi; + + private: + std::string modulus; + std::string exponent; + std::string timestamp; + + public: + const char * GetModulus() const { return modulus.c_str(); } + const char * GetExponent() const { return exponent.c_str(); } + const char * GetTimestamp() const { return timestamp.c_str(); } + }; + + static void GetRsaKey(HANDLE hConnection, const wchar_t *username, RsaKey *rsaKey) + { + rsaKey->success = false; + + ptrA base64Username(mir_urlEncode(ptrA(mir_utf8encodeW(username)))); + + HttpRequest request(hConnection, REQUEST_GET, STEAM_COMMUNITY_URL "/mobilelogin/getrsakey"); + request.AddParameter("username", (char*)base64Username); + + mir_ptr response(request.Send()); + if (!response || response->resultCode != HTTP_STATUS_OK) + return; + + 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"); + rsaKey->modulus = ptrA(mir_u2a(json_as_string(node))); + + node = json_get(root, "publickey_exp"); + rsaKey->exponent = ptrA(mir_u2a(json_as_string(node))); + + node = json_get(root, "timestamp"); + rsaKey->timestamp = ptrA(mir_u2a(json_as_string(node))); + + rsaKey->success = true; + } + }; +} + + +#endif //_STEAM_CRYPTO_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/search.h b/protocols/Steam/src/Steam/search.h new file mode 100644 index 0000000000..ad4bd9c5d9 --- /dev/null +++ b/protocols/Steam/src/Steam/search.h @@ -0,0 +1,78 @@ +#ifndef _STEAM_SEARCH_H_ +#define _STEAM_SEARCH_H_ + +namespace SteamWebApi +{ + class SearchApi : public BaseApi + { + public: + + class SearchItem : public Result + { + }; + + class SearchResult : public Result + { + friend SearchApi; + + private: + int count; + std::vector items; + + public: + SearchResult() : count(0) { } + + int GetCount() { return count; } + }; + + static void Search(HANDLE hConnection, const char *token, const char *text, SearchResult *searchResult) + { + searchResult->success = false; + searchResult->count = 0; + searchResult->items.clear(); + + HttpRequest request(hConnection, REQUEST_GET, STEAM_API_URL "/ISteamUserOAuth/Search/v0001"); + request.AddParameter("access_token=%s", token); + request.AddParameter("&keywords=%s", text); + request.AddParameter("&offset=0&count=50&targets=users&fields=all"); + + mir_ptr response(request.Send()); + if (!response || response->resultCode != HTTP_STATUS_OK) + return; + + JSONNODE *root = json_parse(response->pData), *node, *child;; + + node = json_get(root, "success"); + searchResult->success = json_as_bool(node) > 0; + if (!searchResult->success) + return; + + node = json_get(root, "count"); + searchResult->count = json_as_int(node); + + //node = json_get(root, "total"); + //searchResult->total = json_as_int(node); + + if (searchResult->count == 0) + return; + + node = json_get(root, "results"); + root = json_as_array(node); + if (root != NULL) + { + for (int i = 0;; i++) + { + child = json_at(root, i); + if (child == NULL) + break; + + SearchItem *item = NULL; + } + } + + searchResult->success = true; + } + }; +} + +#endif //_STEAM_SEARCH_H_ \ No newline at end of file diff --git a/protocols/Steam/src/Steam/steam.h b/protocols/Steam/src/Steam/steam.h index 54acf01ab8..acdb86393d 100644 --- a/protocols/Steam/src/Steam/steam.h +++ b/protocols/Steam/src/Steam/steam.h @@ -3,7 +3,7 @@ namespace SteamWebApi { - #define STEAM_API_URL "https://api.steampowered.com:443" + #define STEAM_API_URL "https://api.steampowered.com" #define STEAM_COMMUNITY_URL "https://steamcommunity.com" struct Result @@ -26,15 +26,13 @@ namespace SteamWebApi }; } -#include -#include - -#include "Steam\crypto.h" +#include "Steam\rsa_key.h" #include "Steam\authorization.h" #include "Steam\login.h" #include "Steam\friend_list.h" #include "Steam\friend.h" #include "Steam\poll.h" #include "Steam\message.h" +#include "Steam\search.h" #endif //_STEAM_H_ \ No newline at end of file diff --git a/protocols/Steam/src/common.h b/protocols/Steam/src/common.h index 34e155daa3..7c0a9a9abe 100644 --- a/protocols/Steam/src/common.h +++ b/protocols/Steam/src/common.h @@ -22,6 +22,11 @@ #include #include +#include +#include + +//#include + #include "resource.h" #include "version.h" diff --git a/protocols/Steam/src/http_request.h b/protocols/Steam/src/http_request.h index a065fec60e..f69b524c8a 100644 --- a/protocols/Steam/src/http_request.h +++ b/protocols/Steam/src/http_request.h @@ -129,20 +129,28 @@ public: m_szUrl.AppendFormat("&%s=%s", szName, szValue); } - void AddParameter(LPCSTR szName, int value) + /*void AddParameter(LPCSTR szName, int value) { if (m_szUrl.Find('?') == -1) m_szUrl.AppendFormat("?%s=%i", szName, value); else m_szUrl.AppendFormat("&%s=%i", szName, value); - } + }*/ - void AddParameter(LPCSTR szName, UINT64 value) + /*void AddParameter(LPCSTR szName, UINT64 value) { if (m_szUrl.Find('?') == -1) m_szUrl.AppendFormat("?%s=%llu", szName, value); else m_szUrl.AppendFormat("&%s=%llu", szName, value); + }*/ + + void AddParameter(LPCSTR szValue) + { + if (m_szUrl.Find('?') == -1) + m_szUrl.AppendFormat("?%s", szValue); + else + m_szUrl.AppendFormat("&%s", szValue); } NETLIBHTTPREQUEST *Send() diff --git a/protocols/Steam/src/steam_account.cpp b/protocols/Steam/src/steam_account.cpp index 768bfc4914..61469a02a1 100644 --- a/protocols/Steam/src/steam_account.cpp +++ b/protocols/Steam/src/steam_account.cpp @@ -27,8 +27,8 @@ int CSteamProto::MirandaToSteamState(int status) { switch (status) { - case ID_STATUS_ONLINE: - return 1; //Online + case ID_STATUS_OFFLINE: + return 0; //Offline case ID_STATUS_DND: return 2; //Busy case ID_STATUS_AWAY: @@ -42,13 +42,22 @@ int CSteamProto::MirandaToSteamState(int status) return "play";*/ //case 1: //Online default: - return ID_STATUS_OFFLINE; + return ID_STATUS_ONLINE; } } bool CSteamProto::IsOnline() { - return m_iStatus > ID_STATUS_OFFLINE; + return m_iStatus > ID_STATUS_OFFLINE && m_hPollingThread; +} + +bool CSteamProto::IsMe(const char *steamId) +{ + ptrA mySteamId(getStringA("SteamID")); + if (!lstrcmpA(steamId, mySteamId)) + return true; + + return false; } void CSteamProto::SetServerStatusThread(void *arg) @@ -72,15 +81,195 @@ void CSteamProto::SetServerStatusThread(void *arg) if (sendResult.IsSuccess()) ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus); + else + m_iStatus = oldStatus; } void CSteamProto::Authorize(SteamWebApi::AuthorizationApi::AuthResult *authResult) { ptrW username(getWStringA("Username")); - ptrA password(getStringA("Password")); + ptrA base64RsaEncryptedPassword; + + DBVARIANT dbv; + CMStringA timestamp; + DWORD encryptedSize = 0; + BYTE *encryptedPassword = NULL; + db_get(NULL, this->m_szModuleName, "EncryptedPassword", &dbv); + if (dbv.type == DBVT_BLOB && dbv.pbVal && dbv.cpbVal > 0) + { + encryptedSize = dbv.cpbVal; + encryptedPassword = (BYTE*)mir_alloc(dbv.cpbVal); + memcpy(encryptedPassword, dbv.pbVal, dbv.cpbVal); + + timestamp = getStringA("RsaTimestamp"); + } + else + { + // get rsa public key + SteamWebApi::RsaKeyApi::RsaKey rsaKey; + SteamWebApi::RsaKeyApi::GetRsaKey(m_hNetlibUser, username, &rsaKey); + if (!rsaKey.IsSuccess()) + { + db_free(&dbv); + return; + } + + timestamp = rsaKey.GetTimestamp(); + setString("RsaTimestamp", timestamp); + + ptrA password(getStringA("Password")); + + // OpenSSL rsa is depricated + /*BIGNUM *n = BN_new(); + if (!BN_hex2bn(&n, rsaKey.GetModulus())) + { + BN_free(n); + return; + } + + BIGNUM *e = BN_new(); + if (!BN_hex2bn(&e, rsaKey.GetExponent())) + { + BN_free(e); + return; + } + + RSA *rsa = RSA_new(); + rsa->n = n; + rsa->e = e; + + encryptedSize = RSA_size(rsa); + encryptedPassword = (BYTE*)mir_alloc(encryptedSize); + if (RSA_public_encrypt(strlen(password), (unsigned char*)(char*)password, encryptedPassword, rsa, RSA_PKCS1_PADDING) < 0) + { + RSA_free(rsa); + return; + } + + BN_free(e); + BN_free(n); + RSA_free(rsa);*/ + + const char *pszModulus = rsaKey.GetModulus(); + DWORD cchModulus = strlen(pszModulus); + + // convert hex string to byte array + DWORD cbLen = 0, dwSkip = 0, dwFlags = 0; + if (!CryptStringToBinaryA(pszModulus, cchModulus, CRYPT_STRING_HEX, NULL, &cbLen, &dwSkip, &dwFlags)) + { + DWORD dwResult = GetLastError(); + int i = 0; + } + + // Allocate a new buffer. + BYTE *pbBuffer = (BYTE*)malloc(cbLen); + if (!CryptStringToBinaryA(pszModulus, cchModulus, CRYPT_STRING_HEX, pbBuffer, &cbLen, &dwSkip, &dwFlags)) + { + DWORD dwResult = GetLastError(); + int i = 0; + } + + // reverse byte array, because of microsoft + for (int i = 0; i < cbLen / 2; ++i) + { + BYTE temp = pbBuffer[cbLen - i - 1]; + pbBuffer[cbLen - i - 1] = pbBuffer[i]; + pbBuffer[i] = temp; + } + + HCRYPTPROV hCSP = 0; + if (!CryptAcquireContext(&hCSP, NULL, NULL, PROV_RSA_AES, CRYPT_SILENT) || + !CryptAcquireContext(&hCSP, NULL, NULL, PROV_RSA_AES, CRYPT_SILENT | CRYPT_NEWKEYSET)) + { + DWORD dwResult = GetLastError(); + int i = 0; + } + + // Move the key into the key container. + DWORD cbKeyBlob = sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY) + cbLen; + BYTE *pKeyBlob = (BYTE*)malloc(cbKeyBlob); + + // Fill in the data. + PUBLICKEYSTRUC *pPublicKey = (PUBLICKEYSTRUC*)pKeyBlob; + pPublicKey->bType = PUBLICKEYBLOB; + pPublicKey->bVersion = CUR_BLOB_VERSION; // Always use this value. + pPublicKey->reserved = 0; // Must be zero. + pPublicKey->aiKeyAlg = CALG_RSA_KEYX; // RSA public-key key exchange. + + // The next block of data is the RSAPUBKEY structure. + RSAPUBKEY *pRsaPubKey = (RSAPUBKEY*)(pKeyBlob + sizeof(PUBLICKEYSTRUC)); + pRsaPubKey->magic = 0x31415352; // RSA1 // Use public key + pRsaPubKey->bitlen = cbLen * 8; // Number of bits in the modulus. + pRsaPubKey->pubexp = 0x10001; // "010001" // Exponent. + + // Copy the modulus into the blob. Put the modulus directly after the + // RSAPUBKEY structure in the blob. + BYTE *pKey = (BYTE*)(((BYTE *)pRsaPubKey) + sizeof(RSAPUBKEY)); + //pKeyBlob + sizeof(BLOBHEADER)+ sizeof(RSAPUBKEY); + memcpy(pKey, pbBuffer, cbLen); + + // Now import public key + HCRYPTKEY phKey = 0; + if (!CryptImportKey(hCSP, pKeyBlob, cbKeyBlob, 0, 0, &phKey)) + { + DWORD err = GetLastError(); + int i = 0; + } + + // encrypt password + DWORD pwdLength = strlen(password); + if (!CryptEncrypt(phKey, 0, TRUE, 0, NULL, &encryptedSize, pwdLength)) + { + DWORD err = GetLastError(); + int i = 0; + } + + encryptedPassword = (BYTE*)mir_calloc(encryptedSize); + memcpy(encryptedPassword, password, pwdLength); + if (!CryptEncrypt(phKey, 0, TRUE, 0, encryptedPassword, &pwdLength, encryptedSize)) + { + DWORD err = GetLastError(); + int i = 0; + } + + // reverse byte array again + for (int i = 0; i < encryptedSize / 2; ++i) + { + BYTE temp = encryptedPassword[encryptedSize - i - 1]; + encryptedPassword[encryptedSize - i - 1] = encryptedPassword[i]; + encryptedPassword[i] = temp; + } + + // save rsa encrypted password to db + db_set_blob(NULL, this->m_szModuleName, "EncryptedPassword", encryptedPassword, encryptedSize); + + CryptDestroyKey(phKey); + free(pKeyBlob); + CryptReleaseContext(hCSP, 0); + free(pbBuffer); + } + db_free(&dbv); + + //DWORD cbLen = 0, dwSkip = 0, dwFlags = 0; + //if (!CryptBinaryToStringA(encryptedPassword, encryptedSize, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &cbLen)) + //{ + // DWORD dwResult = GetLastError(); + // int i = 0; + //} + + //// Allocate a new buffer. + //char *pbBuffer = (char*)malloc(cbLen); + //if (!CryptBinaryToStringA(encryptedPassword, encryptedSize, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, pbBuffer, &cbLen)) + //{ + // DWORD dwResult = GetLastError(); + // int i = 0; + //} + + base64RsaEncryptedPassword = mir_base64_encode(encryptedPassword, encryptedSize); + mir_free(encryptedPassword); // try to authorize - SteamWebApi::AuthorizationApi::Authorize(m_hNetlibUser, username, password, authResult); + SteamWebApi::AuthorizationApi::Authorize(m_hNetlibUser, username, base64RsaEncryptedPassword, timestamp, authResult); if (authResult->IsEmailAuthNeeded() || authResult->IsCaptchaNeeded()) { do @@ -108,15 +297,18 @@ void CSteamProto::Authorize(SteamWebApi::AuthorizationApi::AuthResult *authResul } // try to authorize with emailauthcode or captcha taxt - SteamWebApi::AuthorizationApi::Authorize(m_hNetlibUser, username, password, authResult); + SteamWebApi::AuthorizationApi::Authorize(m_hNetlibUser, username, base64RsaEncryptedPassword, timestamp, authResult); } while (authResult->IsEmailAuthNeeded() || authResult->IsCaptchaNeeded()); } } void CSteamProto::LogInThread(void* param) { + if (this->IsOnline()) + return; + CMStringA token(getStringA("TokenSecret")); - if (token.IsEmpty()/* && !this->IsOnline()*/) + if (token.IsEmpty()) { SteamWebApi::AuthorizationApi::AuthResult authResult; Authorize(&authResult); @@ -154,13 +346,6 @@ void CSteamProto::LogInThread(void* param) ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_CONNECTING, m_iDesiredStatus); } - // start pooling thread - if (m_hPollingThread == NULL && !m_bTerminated) - { - m_bTerminated = false; - m_hPollingThread = ForkThreadEx(&CSteamProto::PollingThread, NULL, NULL); - } - // get contact list SteamWebApi::FriendListApi::FriendList friendList; SteamWebApi::FriendListApi::Load(m_hNetlibUser, token, loginResult.GetSteamId(), &friendList); @@ -178,6 +363,13 @@ void CSteamProto::LogInThread(void* param) } } } + + // start pooling thread + if (m_hPollingThread == NULL && !m_bTerminated) + { + m_bTerminated = false; + m_hPollingThread = ForkThreadEx(&CSteamProto::PollingThread, NULL, NULL); + } } void CSteamProto::LogOutThread(void*) diff --git a/protocols/Steam/src/steam_contacts.cpp b/protocols/Steam/src/steam_contacts.cpp index 9e01da2e33..8e9aa87f9f 100644 --- a/protocols/Steam/src/steam_contacts.cpp +++ b/protocols/Steam/src/steam_contacts.cpp @@ -59,4 +59,46 @@ MCONTACT CSteamProto::AddContact(const SteamWebApi::FriendApi::Friend &contact) } return hContact; +} + +void CSteamProto::SearchByIdThread(void* arg) +{ + ptrW steamIdW((wchar_t*)arg); + ptrA steamId(mir_u2a(steamIdW)); + + ptrA token(getStringA("TokenSecret")); + + SteamWebApi::FriendApi::Friend rFriend; + SteamWebApi::FriendApi::LoadSummaries(m_hNetlibUser, token, steamId, &rFriend); + + if (!rFriend.IsSuccess()) + { + ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)STEAM_SEARCH_BYID, 0); + return; + } + + PROTOSEARCHRESULT psr = { 0 }; + psr.cbSize = sizeof(psr); + psr.flags = PSR_TCHAR; + + psr.id = mir_wstrdup(steamIdW); + psr.nick = mir_wstrdup(rFriend.GetNickname()); + + const wchar_t *realname = rFriend.GetRealname(); + const wchar_t *p = wcschr(realname, ' '); + if (p == NULL) + psr.firstName = mir_wstrdup(realname); + else + { + int length = p - realname; + psr.firstName = (wchar_t*)mir_alloc(sizeof(wchar_t) * (length + 1)); + wmemcpy(psr.firstName, realname, length); + psr.firstName[length] = '\0'; + psr.lastName = mir_wstrdup(p + 1); + } + + //psr.reserved[0] = &psr; + + ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)STEAM_SEARCH_BYID, (LPARAM)&psr); + ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)STEAM_SEARCH_BYID, 0); } \ No newline at end of file diff --git a/protocols/Steam/src/steam_proto.cpp b/protocols/Steam/src/steam_proto.cpp index 7b5b808339..6fe6ac82e9 100644 --- a/protocols/Steam/src/steam_proto.cpp +++ b/protocols/Steam/src/steam_proto.cpp @@ -36,6 +36,10 @@ CSteamProto::~CSteamProto() MCONTACT __cdecl CSteamProto::AddToList(int flags, PROTOSEARCHRESULT* psr) { + /*CContact::Ref contact; + this->GetContact((char *)mir_ptr(::mir_utf8encodeW(psr->id)), contact); + return this->AddContact(contact);*/ + return 0; } @@ -89,7 +93,7 @@ DWORD_PTR __cdecl CSteamProto:: GetCaps(int type, MCONTACT hContact) switch(type) { case PFLAGNUM_1: - return PF1_IM; + return PF1_IM | PF1_BASICSEARCH; case PFLAGNUM_2: return PF2_ONLINE; case PFLAG_UNIQUEIDTEXT: @@ -105,7 +109,12 @@ int __cdecl CSteamProto::GetInfo(MCONTACT hContact, int infoType ) { return 0; } HANDLE __cdecl CSteamProto::SearchBasic(const TCHAR* id) { - return 0; + if (!this->IsOnline()) + return 0; + + ForkThread(&CSteamProto::SearchByIdThread, mir_wstrdup(id)); + + return (HANDLE)STEAM_SEARCH_BYID; } HANDLE __cdecl CSteamProto::SearchByEmail(const TCHAR* email) @@ -211,6 +220,7 @@ int CSteamProto::SetStatus(int new_status) if (IsOnline()) { ForkThread(&CSteamProto::SetServerStatusThread, &new_status); + return 0; } diff --git a/protocols/Steam/src/steam_proto.h b/protocols/Steam/src/steam_proto.h index e3cc2ea3d2..d6e579a2fb 100644 --- a/protocols/Steam/src/steam_proto.h +++ b/protocols/Steam/src/steam_proto.h @@ -1,6 +1,9 @@ #ifndef _STEAM_PROTO_H_ #define _STEAM_PROTO_H_ +#define STEAM_SEARCH_BYID 1001 +#define STEAM_SEARCH_BYNAME 1002 + struct GuardParam { char code[10]; @@ -90,7 +93,7 @@ protected: static int CompareProtos(const CSteamProto *p1, const CSteamProto *p2); // pooling thread - int PollStatus(const char *sessionId, const char *steamId, UINT32 messageId); + void PollStatus(const char *sessionId, const char *steamId, UINT32 messageId, SteamWebApi::PollApi::PollResult *pollResult); void __cdecl PollingThread(void*); // account @@ -98,6 +101,7 @@ protected: static int MirandaToSteamState(int status); bool IsOnline(); + bool IsMe(const char *steamId); void Authorize(SteamWebApi::AuthorizationApi::AuthResult *authResult); void __cdecl LogInThread(void*); void __cdecl LogOutThread(void*); @@ -110,6 +114,8 @@ protected: MCONTACT FindContact(const char *steamId); MCONTACT AddContact(const SteamWebApi::FriendApi::Friend &contact); + void __cdecl SearchByIdThread(void*); + // messages void __cdecl SendMessageThread(void*); void __cdecl SendTypingThread(void*); diff --git a/protocols/Steam/src/steam_thread.cpp b/protocols/Steam/src/steam_thread.cpp index 4bb62c23d7..3249ef629e 100644 --- a/protocols/Steam/src/steam_thread.cpp +++ b/protocols/Steam/src/steam_thread.cpp @@ -1,34 +1,23 @@ #include "common.h" -int CSteamProto::PollStatus(const char *token, const char *sessionId, UINT32 messageId) +void CSteamProto::PollStatus(const char *token, const char *sessionId, UINT32 messageId, SteamWebApi::PollApi::PollResult *pollResult) { - SteamWebApi::PollApi::PollResult pollResult; - SteamWebApi::PollApi::PollStatus(m_hNetlibUser, token, sessionId, messageId, &pollResult); - - if (!pollResult.IsSuccess()) - return 0; - - if (pollResult.IsNeedRelogin()) - { - SteamWebApi::LoginApi::LoginResult loginResult; - SteamWebApi::LoginApi::Logon(m_hNetlibUser, token, &loginResult); - - if (!loginResult.IsSuccess()) - return 0; + SteamWebApi::PollApi::PollStatus(m_hNetlibUser, token, sessionId, messageId, pollResult); - return messageId; - } + if (!pollResult->IsSuccess()) + return; - for (int i = 0; i < pollResult.GetItemCount(); i++) + for (int i = 0; i < pollResult->GetItemCount(); i++) { - switch (pollResult[i]->GetType()) + const SteamWebApi::PollApi::PoolItem *item = pollResult->operator[](i); + switch (item->GetType()) { - case SteamWebApi::PollApi::POOL_TYPE::TYPING: + case SteamWebApi::PollApi::POOL_TYPE_TYPING: break; - case SteamWebApi::PollApi::POOL_TYPE::MESSAGE: + case SteamWebApi::PollApi::POOL_TYPE_MESSAGE: { - SteamWebApi::PollApi::Message *message = (SteamWebApi::PollApi::Message*)pollResult[i]; + SteamWebApi::PollApi::Message *message = (SteamWebApi::PollApi::Message*)item; MCONTACT hContact = FindContact(message->GetSteamId()); if (hContact) @@ -45,9 +34,9 @@ int CSteamProto::PollStatus(const char *token, const char *sessionId, UINT32 mes } break; - case SteamWebApi::PollApi::POOL_TYPE::MYMESSAGE: + case SteamWebApi::PollApi::POOL_TYPE_MYMESSAGE: { - SteamWebApi::PollApi::Message *message = (SteamWebApi::PollApi::Message*)pollResult[i]; + SteamWebApi::PollApi::Message *message = (SteamWebApi::PollApi::Message*)item; MCONTACT hContact = FindContact(message->GetSteamId()); if (hContact) @@ -67,31 +56,31 @@ int CSteamProto::PollStatus(const char *token, const char *sessionId, UINT32 mes } break; - case SteamWebApi::PollApi::POOL_TYPE::STATE: + case SteamWebApi::PollApi::POOL_TYPE_STATE: { - SteamWebApi::PollApi::State *state = (SteamWebApi::PollApi::State*)pollResult[i]; + SteamWebApi::PollApi::State *state = (SteamWebApi::PollApi::State*)item; WORD status = CSteamProto::SteamToMirandaStatus(state->GetStatus()); - const char *cSteamId = state->GetSteamId(); + const char *steamId = state->GetSteamId(); const wchar_t *nickname = state->GetNickname(); - ptrA steamId(getStringA("SteamID")); - if (!lstrcmpA(steamId, cSteamId)) + if (IsMe(steamId)) { const wchar_t *oldNickname = getWStringA("Nick"); if (lstrcmp(oldNickname, nickname)) setWString("Nick", nickname); - SetStatus(status); - + + SetStatus(status); } else { - MCONTACT hContact = FindContact(cSteamId); + MCONTACT hContact = FindContact(steamId); if (hContact) { const wchar_t *oldNickname = getWStringA(hContact, "Nick"); if (lstrcmp(oldNickname, nickname)) setWString(hContact, "Nick", nickname); + SetContactStatus(hContact, status); } } @@ -99,8 +88,6 @@ int CSteamProto::PollStatus(const char *token, const char *sessionId, UINT32 mes break; } } - - return pollResult.GetMessageId(); } void CSteamProto::PollingThread(void*) @@ -111,15 +98,34 @@ void CSteamProto::PollingThread(void*) ptrA sessionId(getStringA("SessionID")); UINT32 messageId = getDword("MessageID", 0); + SteamWebApi::PollApi::PollResult pollResult; while (!m_bTerminated) { - messageId = PollStatus(token, sessionId, messageId); - if (messageId == 0) + PollStatus(token, sessionId, messageId, &pollResult); + + if (pollResult.IsNeedRelogin()) + debugLogA("CSteamProto::PollingThread: need to relogin"); + /*{ + SteamWebApi::LoginApi::LoginResult loginResult; + SteamWebApi::LoginApi::Logon(m_hNetlibUser, token, &loginResult); + + if (!loginResult.IsSuccess()) + break; + + sessionId = mir_strdup(loginResult.GetSessionId()); + setString("SessionID", sessionId); + }*/ + + if (!pollResult.IsSuccess()) break; + + messageId = pollResult.GetMessageId(); } - if (messageId > 0) + if (pollResult.IsSuccess()) setDword("MessageID", messageId); + else + SetStatus(ID_STATUS_OFFLINE); m_hPollingThread = NULL; debugLogA("CSteamProto::PollingThread: leaving"); -- cgit v1.2.3