diff options
author | aunsane <aunsane@gmail.com> | 2017-12-28 21:21:10 +0300 |
---|---|---|
committer | aunsane <aunsane@gmail.com> | 2017-12-28 21:21:28 +0300 |
commit | 0156ed41dceeef48f070adf67f14d4ba4c4f6d61 (patch) | |
tree | d0e1dca4c2c9c3c056ae9cb69dde10ea59316481 | |
parent | f0075f2b956969f29acd3bc9289c8a5f78faddad (diff) |
Steam: refactoring
- reworking http requests
- added ability to get game name by appid
- another attempt to fix #633
- minor refactoring
30 files changed, 1242 insertions, 1238 deletions
diff --git a/protocols/Steam/res/Resource.rc b/protocols/Steam/res/Resource.rc index 2cb5633c29..fe899696d1 100644 --- a/protocols/Steam/res/Resource.rc +++ b/protocols/Steam/res/Resource.rc @@ -17,7 +17,7 @@ #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUD)
LANGUAGE LANG_NEUTRAL, SUBLANG_DEFAULT
-#pragma code_page(1250)
+#pragma code_page(1251)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
@@ -63,7 +63,9 @@ LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_STEAM ICON "steam.ico"
+
IDI_GAMING ICON "gaming.ico"
+
#endif // Russian (Russia) resources
/////////////////////////////////////////////////////////////////////////////
@@ -113,7 +115,8 @@ BEGIN END
IDD_CAPTCHA DIALOGEX 0, 0, 143, 81
-STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOPMOST
CAPTION "Captcha"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
@@ -123,7 +126,8 @@ BEGIN END
IDD_GUARD DIALOGEX 0, 0, 193, 93
-STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOPMOST
CAPTION "Steam Guard"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
@@ -146,7 +150,7 @@ END IDD_PASSWORD_EDITOR DIALOGEX 0, 0, 209, 75
STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
-EXSTYLE WS_EX_TOOLWINDOW | WS_EX_APPWINDOW
+EXSTYLE WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_APPWINDOW
CAPTION "Enter password"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
@@ -158,8 +162,9 @@ BEGIN END
IDD_TWOFACTOR DIALOGEX 0, 0, 193, 83
-STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "Steam Guard"
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOPMOST
+CAPTION "Sms code"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
EDITTEXT IDC_TEXT,7,41,179,14,ES_AUTOHSCROLL
@@ -237,6 +242,32 @@ BEGIN END
#endif // APSTUDIO_INVOKED
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// AFX_DIALOG_LAYOUT
+//
+
+IDD_GUARD AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_CAPTCHA AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_PASSWORD_EDITOR AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+IDD_TWOFACTOR AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
#endif // English resources
/////////////////////////////////////////////////////////////////////////////
diff --git a/protocols/Steam/src/api/app_info.h b/protocols/Steam/src/api/app_info.h new file mode 100644 index 0000000000..b08a3f38d6 --- /dev/null +++ b/protocols/Steam/src/api/app_info.h @@ -0,0 +1,16 @@ +#ifndef _STEAM_REQUEST_APP_INFO_H_ +#define _STEAM_REQUEST_APP_INFO_H_ + +class GetAppInfoRequest : public HttpRequest +{ +public: + GetAppInfoRequest(const char *token, const char *appIds) : + HttpRequest(HttpGet, STEAM_API_URL "/ISteamGameOAuth/GetAppInfo/v0001") + { + Uri + << CHAR_PARAM("access_token", token) + << CHAR_PARAM("appIds", appIds); + } +}; + +#endif //_STEAM_REQUEST_APP_INFO_H_ diff --git a/protocols/Steam/src/api/authorization.h b/protocols/Steam/src/api/authorization.h index cba8d4d274..3d02c5deb5 100644 --- a/protocols/Steam/src/api/authorization.h +++ b/protocols/Steam/src/api/authorization.h @@ -5,27 +5,27 @@ class AuthorizationRequest : public HttpRequest {
public:
AuthorizationRequest(const char *username, const char *password, const char *timestamp, const char *twoFactorCode, const char *guardCode, const char *guardId = "", const char *captchaId = "-1", const char *captchaText = "") :
- HttpRequest(REQUEST_POST, STEAM_WEB_URL "/mobilelogin/dologin/")
+ HttpRequest(HttpPost, STEAM_WEB_URL "/mobilelogin/dologin/")
{
flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP;
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
- AddHeader("Referer", STEAM_WEB_URL "/mobilelogin/dologin?oauth_client_id=3638BFB1&oauth_scope=read_profile%20write_profile%20read_client%20write_client");
- AddHeader("Cookie", "mobileClientVersion=1291812;forceMobile=1;mobileClient=ios");
+ Headers
+ << CHAR_PARAM("Referer", STEAM_WEB_URL "/mobilelogin/dologin?oauth_client_id=3638BFB1&oauth_scope=read_profile%20write_profile%20read_client%20write_client")
+ << CHAR_PARAM("Cookie", "mobileClientVersion=1291812;forceMobile=1;mobileClient=ios");
- CMStringA data;
- data.AppendFormat("password=%s&username=%s&twofactorcode=%s&emailauth=%s&loginfriendlyname=%s&oauth_client_id=3638BFB1&captchagid=%s&captcha_text=%s&emailsteamid=%s&rsatimestamp=%s&rememberlogin=false&donotcache=%lld",
- ptrA(mir_urlEncode(password)),
- ptrA(mir_urlEncode(username)),
- twoFactorCode,
- guardCode,
- "Miranda%20NG",
- captchaId,
- ptrA(mir_urlEncode(captchaText)),
- guardId,
- timestamp,
- time(NULL));
- SetData(data, data.GetLength());
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("oauth_client_id", "3638BFB1")
+ << CHAR_PARAM("loginfriendlyname", "Miranda NG")
+ << CHAR_PARAM("password", password)
+ << CHAR_PARAM("username", username)
+ << CHAR_PARAM("twofactorcode", twoFactorCode)
+ << CHAR_PARAM("emailsteamid", guardId)
+ << CHAR_PARAM("emailauth", guardCode)
+ << CHAR_PARAM("captchagid", captchaId)
+ << CHAR_PARAM("captcha_text", captchaText)
+ << CHAR_PARAM("rsatimestamp", timestamp)
+ << BOOL_PARAM("rememberlogin", false)
+ << INT64_PARAM("donotcache", time(NULL));
}
};
diff --git a/protocols/Steam/src/api/avatar.h b/protocols/Steam/src/api/avatar.h index 28e573d9d1..521536550e 100644 --- a/protocols/Steam/src/api/avatar.h +++ b/protocols/Steam/src/api/avatar.h @@ -5,7 +5,7 @@ class GetAvatarRequest : public HttpRequest {
public:
GetAvatarRequest(const char *url) :
- HttpRequest(REQUEST_GET, url)
+ HttpRequest(HttpGet, url)
{
flags = NLHRF_HTTP11 | NLHRF_NODUMP;
}
diff --git a/protocols/Steam/src/api/captcha.h b/protocols/Steam/src/api/captcha.h index 9619a09b54..0b8ba9191a 100644 --- a/protocols/Steam/src/api/captcha.h +++ b/protocols/Steam/src/api/captcha.h @@ -5,9 +5,11 @@ class GetCaptchaRequest : public HttpRequest {
public:
GetCaptchaRequest(const char *captchaId) :
- HttpRequest(REQUEST_GET, FORMAT, STEAM_WEB_URL "/public/captcha.php?gid=%s", captchaId)
+ HttpRequest(HttpGet, STEAM_WEB_URL "/public/captcha.php")
{
flags = NLHRF_HTTP11 | NLHRF_NODUMP;
+
+ Uri << CHAR_PARAM("gid", captchaId);
}
};
diff --git a/protocols/Steam/src/api/enums.h b/protocols/Steam/src/api/enums.h new file mode 100644 index 0000000000..a24ce48b34 --- /dev/null +++ b/protocols/Steam/src/api/enums.h @@ -0,0 +1,32 @@ +#ifndef _STEAM_ENUMS_H_ +#define _STEAM_ENUMS_H_ + +enum PersonaState +{ + Offline = 0, + Online = 1, + Busy = 2, + Away = 3, + Snooze = 4, + LookingToTrade = 5, + LookingToPlay = 6, + Max = 7, +}; + +enum StatusFlags +{ + Status = 1, + PlayerName = 2, + QueryPort = 4, + SourceID = 8, + Presence = 16, + Metadata = 32, + LastSeen = 64, + ClanInfo = 128, + GameExtraInfo = 256, + GameDataBlob = 512, + ClanTag = 1024, + Facebook = 2048, +} + +#endif //_STEAM_ENUMS_H_ diff --git a/protocols/Steam/src/api/friend.h b/protocols/Steam/src/api/friend.h index ade553f2c0..191e0b93fa 100644 --- a/protocols/Steam/src/api/friend.h +++ b/protocols/Steam/src/api/friend.h @@ -5,10 +5,11 @@ class GetUserSummariesRequest : public HttpRequest {
public:
GetUserSummariesRequest(const char *token, const char *steamIds) :
- HttpRequest(REQUEST_GET, STEAM_API_URL "/ISteamUserOAuth/GetUserSummaries/v0001")
+ HttpRequest(HttpGet, STEAM_API_URL "/ISteamUserOAuth/GetUserSummaries/v0001")
{
- AddParameter("access_token", token);
- AddParameter("steamids", steamIds);
+ Uri
+ << CHAR_PARAM("access_token", token)
+ << CHAR_PARAM("steamids", steamIds);
}
};
diff --git a/protocols/Steam/src/api/friend_list.h b/protocols/Steam/src/api/friend_list.h index b6ab98402b..604e797a2d 100644 --- a/protocols/Steam/src/api/friend_list.h +++ b/protocols/Steam/src/api/friend_list.h @@ -5,11 +5,12 @@ class GetFriendListRequest : public HttpRequest {
public:
GetFriendListRequest(const char *token, const char *steamId, const char *relationship = "friend,ignoredfriend,requestrecipient") :
- HttpRequest(REQUEST_GET, STEAM_API_URL "/ISteamUserOAuth/GetFriendList/v0001")
+ HttpRequest(HttpGet, STEAM_API_URL "/ISteamUserOAuth/GetFriendList/v0001")
{
- AddParameter("access_token", token);
- AddParameter("steamid", steamId);
- AddParameter("relationship", relationship);
+ Uri
+ << CHAR_PARAM("access_token", token)
+ << CHAR_PARAM("steamid", steamId)
+ << CHAR_PARAM("relationship", relationship);
}
};
@@ -17,7 +18,7 @@ class AddFriendRequest : public HttpRequest {
public:
AddFriendRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
- HttpRequest(REQUEST_POST, STEAM_WEB_URL "/actions/AddFriendAjax")
+ HttpRequest(HttpPost, STEAM_WEB_URL "/actions/AddFriendAjax")
{
char login[MAX_PATH];
mir_snprintf(login, "%s||oauth:%s", steamId, token);
@@ -25,15 +26,11 @@ public: char cookie[MAX_PATH];
mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
- char data[128];
- mir_snprintf(data, _countof(data),
- "sessionID=%s&steamid=%s",
- sessionId,
- who);
+ Headers << CHAR_PARAM("Cookie", cookie);
- SetData(data, strlen(data));
- AddHeader("Cookie", cookie);
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("sessionID", sessionId)
+ << CHAR_PARAM("steamid", who);
}
};
@@ -41,7 +38,7 @@ class BlockFriendRequest : public HttpRequest {
public:
BlockFriendRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
- HttpRequest(REQUEST_POST, STEAM_WEB_URL "/actions/BlockUserAjax")
+ HttpRequest(HttpPost, STEAM_WEB_URL "/actions/BlockUserAjax")
{
char login[MAX_PATH];
mir_snprintf(login, "%s||oauth:%s", steamId, token);
@@ -49,15 +46,12 @@ public: char cookie[MAX_PATH];
mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
- char data[128];
- mir_snprintf(data, _countof(data),
- "sessionID=%s&action=ignore&steamid=%s",
- sessionId,
- who);
+ Headers << CHAR_PARAM("Cookie", cookie);
- SetData(data, strlen(data));
- AddHeader("Cookie", cookie);
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("sessionID", sessionId)
+ << CHAR_PARAM("steamid", who)
+ << CHAR_PARAM("action", "ignore");
}
};
@@ -65,7 +59,7 @@ class RemoveFriendRequest : public HttpRequest {
public:
RemoveFriendRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
- HttpRequest(REQUEST_POST, STEAM_WEB_URL "/actions/RemoveFriendAjax")
+ HttpRequest(HttpPost, STEAM_WEB_URL "/actions/RemoveFriendAjax")
{
char login[MAX_PATH];
mir_snprintf(login, "%s||oauth:%s", steamId, token);
@@ -73,15 +67,11 @@ public: char cookie[MAX_PATH];
mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
- char data[128];
- mir_snprintf(data, _countof(data),
- "sessionID=%s&steamid=%s",
- sessionId,
- who);
+ Headers << CHAR_PARAM("Cookie", cookie);
- SetData(data, strlen(data));
- AddHeader("Cookie", cookie);
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("sessionID", sessionId)
+ << CHAR_PARAM("steamid", who);
}
};
diff --git a/protocols/Steam/src/api/history.h b/protocols/Steam/src/api/history.h index 09eed8a49c..f825622953 100644 --- a/protocols/Steam/src/api/history.h +++ b/protocols/Steam/src/api/history.h @@ -5,9 +5,9 @@ class GetConversationsRequest : public HttpRequest { public: GetConversationsRequest(const char *token) : - HttpRequest(REQUEST_GET, STEAM_API_URL "/IFriendMessagesService/GetActiveMessageSessions/v0001") + HttpRequest(HttpGet, STEAM_API_URL "/IFriendMessagesService/GetActiveMessageSessions/v0001") { - AddParameter("access_token", token); + Uri << CHAR_PARAM("access_token", token); } }; @@ -15,13 +15,14 @@ class GetHistoryMessagesRequest : public HttpRequest { public: GetHistoryMessagesRequest(const char *token, const char *steamId, const char *who, time_t since) : - HttpRequest(REQUEST_GET, STEAM_API_URL "/IFriendMessagesService/GetRecentMessages/v0001") + HttpRequest(HttpGet, STEAM_API_URL "/IFriendMessagesService/GetRecentMessages/v0001") { - AddParameter("access_token", token); - AddParameter("steamid1", steamId); - AddParameter("steamid2", who); - // Steam somehow doesn't respect too precise start time parameter, so we better request older time and then do own filtering again - AddParameter("rtime32_start_time=%d", since - 1500); + Uri + << CHAR_PARAM("access_token", token) + << CHAR_PARAM("steamid1", steamId) + << CHAR_PARAM("steamid2", who) + // Steam somehow doesn't respect too precise start time parameter, so we better request older time and then do own filtering again + << INT64_PARAM("rtime32_start_time", since - 1500); } }; diff --git a/protocols/Steam/src/api/login.h b/protocols/Steam/src/api/login.h index 35ea1d8c74..3479ae75ff 100644 --- a/protocols/Steam/src/api/login.h +++ b/protocols/Steam/src/api/login.h @@ -5,13 +5,14 @@ class LogonRequest : public HttpRequest {
public:
LogonRequest(const char *token) :
- HttpRequest(REQUEST_POST, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Logon/v0001")
+ HttpRequest(HttpPost, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Logon/v0001")
{
char data[256];
mir_snprintf(data, "access_token=%s&ui_mode=web", token);
- SetData(data, strlen(data));
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("access_token", token)
+ << CHAR_PARAM("ui_mode", "web");
}
};
@@ -19,13 +20,11 @@ class LogoffRequest : public HttpRequest {
public:
LogoffRequest(const char *token, const char *umqId) :
- HttpRequest(REQUEST_POST, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Logoff/v0001")
+ HttpRequest(HttpPost, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Logoff/v0001")
{
- char data[256];
- mir_snprintf(data, "access_token=%s&umqid=%s", token, umqId);
-
- SetData(data, strlen(data));
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("access_token", token)
+ << CHAR_PARAM("umqid", umqId);
}
};
diff --git a/protocols/Steam/src/api/message.h b/protocols/Steam/src/api/message.h index 77adb4a240..eee4c43b28 100644 --- a/protocols/Steam/src/api/message.h +++ b/protocols/Steam/src/api/message.h @@ -5,17 +5,14 @@ class SendMessageRequest : public HttpRequest {
public:
SendMessageRequest(const char *token, const char *umqId, const char *steamId, const char *text) :
- HttpRequest(REQUEST_POST, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Message/v0001")
+ HttpRequest(HttpPost, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Message/v0001")
{
- CMStringA data;
- data.AppendFormat("access_token=%s&umqid=%s&steamid_dst=%s&type=saytext&text=%s",
- token,
- umqId,
- steamId,
- ptrA(mir_urlEncode(text)));
-
- SetData(data, data.GetLength());
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("access_token", token)
+ << CHAR_PARAM("umqid", umqId)
+ << CHAR_PARAM("steamid_dst", steamId)
+ << CHAR_PARAM("type", "saytext")
+ << CHAR_PARAM("text", text);
}
};
@@ -23,16 +20,13 @@ class SendTypingRequest : public HttpRequest {
public:
SendTypingRequest(const char *token, const char *umqId, const char *steamId) :
- HttpRequest(REQUEST_POST, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Message/v0001")
+ HttpRequest(HttpPost, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Message/v0001")
{
- CMStringA data;
- data.AppendFormat("access_token=%s&umqid=%s&steamid_dst=%s&type=typing",
- token,
- umqId,
- steamId);
-
- SetData(data, data.GetLength());
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("access_token", token)
+ << CHAR_PARAM("umqid", umqId)
+ << CHAR_PARAM("steamid_dst", steamId)
+ << CHAR_PARAM("type", "typing");
}
};
diff --git a/protocols/Steam/src/api/pending.h b/protocols/Steam/src/api/pending.h index 7c1964b6c5..a808fd172c 100644 --- a/protocols/Steam/src/api/pending.h +++ b/protocols/Steam/src/api/pending.h @@ -5,7 +5,7 @@ class ApprovePendingRequest : public HttpRequest {
public:
ApprovePendingRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
- HttpRequest(REQUEST_POST, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
+ HttpRequest(HttpPost, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
{
char login[MAX_PATH];
mir_snprintf(login, "%s||oauth:%s", steamId, token);
@@ -13,12 +13,16 @@ public: char cookie[MAX_PATH];
mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
- char data[MAX_PATH];
- mir_snprintf(data, "sessionID=%s&id=%s&perform=accept&action=approvePending&itype=friend&json=1&xml=0", sessionId, who);
+ Headers << CHAR_PARAM("Cookie", cookie);
- SetData(data, strlen(data));
- AddHeader("Cookie", cookie);
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("sessionID", sessionId)
+ << CHAR_PARAM("id", who)
+ << CHAR_PARAM("perform", "accept")
+ << CHAR_PARAM("action", "approvePending")
+ << CHAR_PARAM("itype", "friend")
+ << INT_PARAM("json", 1)
+ << INT_PARAM("xml", 0);
}
};
@@ -26,7 +30,7 @@ class IgnorePendingRequest : public HttpRequest {
public:
IgnorePendingRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
- HttpRequest(REQUEST_POST, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
+ HttpRequest(HttpPost, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
{
char login[MAX_PATH];
mir_snprintf(login, "%s||oauth:%s", steamId, token);
@@ -34,12 +38,16 @@ public: char cookie[MAX_PATH];
mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
- char data[MAX_PATH];
- mir_snprintf(data, "sessionID=%s&id=%s&perform=ignore&action=approvePending&itype=friend&json=1&xml=0", sessionId, who);
+ Headers << CHAR_PARAM("Cookie", cookie);
- SetData(data, strlen(data));
- AddHeader("Cookie", cookie);
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("sessionID", sessionId)
+ << CHAR_PARAM("id", who)
+ << CHAR_PARAM("perform", "ignore")
+ << CHAR_PARAM("action", "approvePending")
+ << CHAR_PARAM("itype", "friend")
+ << INT_PARAM("json", 1)
+ << INT_PARAM("xml", 0);
}
};
@@ -47,7 +55,7 @@ class BlockPendingRequest : public HttpRequest {
public:
BlockPendingRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
- HttpRequest(REQUEST_POST, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
+ HttpRequest(HttpPost, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
{
char login[MAX_PATH];
mir_snprintf(login, "%s||oauth:%s", steamId, token);
@@ -55,12 +63,16 @@ public: char cookie[MAX_PATH];
mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
- char data[MAX_PATH];
- mir_snprintf(data, "sessionID=%s&id=%s&perform=block&action=approvePending&itype=friend&json=1&xml=0", sessionId, who);
+ Headers << CHAR_PARAM("Cookie", cookie);
- SetData(data, strlen(data));
- AddHeader("Cookie", cookie);
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("sessionID", sessionId)
+ << CHAR_PARAM("id", who)
+ << CHAR_PARAM("perform", "block")
+ << CHAR_PARAM("action", "approvePending")
+ << CHAR_PARAM("itype", "friend")
+ << INT_PARAM("json", 1)
+ << INT_PARAM("xml", 0);
}
};
diff --git a/protocols/Steam/src/api/poll.h b/protocols/Steam/src/api/poll.h index 5c5da5674c..278dfb0439 100644 --- a/protocols/Steam/src/api/poll.h +++ b/protocols/Steam/src/api/poll.h @@ -5,23 +5,19 @@ class PollRequest : public HttpRequest { public: PollRequest(const char *token, const char *umqId, UINT32 messageId, int idleSeconds) : - HttpRequest(REQUEST_POST, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Poll/v0001") + HttpRequest(HttpPost, STEAM_API_URL "/ISteamWebUserPresenceOAuth/Poll/v0001") { timeout = (STEAM_API_TIMEOUT + 5) * 1000; // flags |= NLHRF_PERSISTENT; - CMStringA data; - data.AppendFormat("access_token=%s&umqid=%s&message=%u&secidletime=%d§imeout=%d", - token, - umqId, - messageId, - idleSeconds, - STEAM_API_TIMEOUT); + Headers << CHAR_PARAM("Connection", "keep-alive"); - SetData(data, data.GetLength()); - - AddHeader("Connection", "keep-alive"); - AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"); + Content = new FormUrlEncodedContent(this) + << CHAR_PARAM("access_token", token) + << CHAR_PARAM("umqid", umqId) + << INT64_PARAM("message", messageId) + << INT_PARAM("secidletime", idleSeconds) + << INT_PARAM("sectimeout", STEAM_API_TIMEOUT); } }; diff --git a/protocols/Steam/src/api/rsa_key.h b/protocols/Steam/src/api/rsa_key.h index 9f89a70403..27a002b717 100644 --- a/protocols/Steam/src/api/rsa_key.h +++ b/protocols/Steam/src/api/rsa_key.h @@ -5,15 +5,16 @@ class GetRsaKeyRequest : public HttpRequest {
public:
GetRsaKeyRequest(const char *username) :
- HttpRequest(REQUEST_POST, STEAM_WEB_URL "/mobilelogin/getrsakey/")
+ HttpRequest(HttpPost, STEAM_WEB_URL "/mobilelogin/getrsakey/")
{
flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP;
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
-
CMStringA data;
data.AppendFormat("username=%s&donotcache=%lld", ptrA(mir_urlEncode(username)), time(NULL));
- SetData(data, data.GetLength());
+
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("username", username)
+ << INT64_PARAM("donotcache", time(NULL));
}
};
diff --git a/protocols/Steam/src/api/search.h b/protocols/Steam/src/api/search.h index a22ed64431..a34c2e8398 100644 --- a/protocols/Steam/src/api/search.h +++ b/protocols/Steam/src/api/search.h @@ -5,13 +5,15 @@ class SearchRequest : public HttpRequest {
public:
SearchRequest(const char *token, const char *text, int offset = 0, int count = 30) :
- HttpRequest(REQUEST_GET, STEAM_API_URL "/ISteamUserOAuth/Search/v0001")
+ HttpRequest(HttpGet, STEAM_API_URL "/ISteamUserOAuth/Search/v0001")
{
- AddParameter("access_token", token);
- AddParameter("keywords", ptrA(mir_urlEncode(text)));
- AddParameter("offset=%d", offset);
- AddParameter("count=%d", count);
- AddParameter("targets=users&fields=all");
+ Uri
+ << CHAR_PARAM("access_token", token)
+ << CHAR_PARAM("keywords", text)
+ << INT_PARAM("offset=%d", offset)
+ << INT_PARAM("count=%d", count)
+ << CHAR_PARAM("targets", "users")
+ << CHAR_PARAM("fields", "all");
}
};
diff --git a/protocols/Steam/src/api/session.h b/protocols/Steam/src/api/session.h index b00e470da6..d4e91721cb 100644 --- a/protocols/Steam/src/api/session.h +++ b/protocols/Steam/src/api/session.h @@ -5,19 +5,21 @@ class GetSessionRequest : public HttpRequest {
public:
GetSessionRequest(const char *token, const char *steamId, const char *cookie) :
- HttpRequest(REQUEST_POST, STEAM_WEB_URL "/mobileloginsucceeded")
+ HttpRequest(HttpPost, STEAM_WEB_URL "/mobileloginsucceeded")
{
flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP;
+ Content = new FormUrlEncodedContent(this)
+ << CHAR_PARAM("oauth_token", token)
+ << CHAR_PARAM("steamid", steamId)
+ << CHAR_PARAM("webcookie", cookie);
+
char data[512];
mir_snprintf(data, _countof(data),
"oauth_token=%s&steamid=%s&webcookie=%s",
token,
steamId,
cookie);
-
- SetData(data, strlen(data));
- AddHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
}
};
@@ -25,7 +27,7 @@ class GetSessionRequest2 : public HttpRequest {
public:
GetSessionRequest2() :
- HttpRequest(REQUEST_GET, STEAM_WEB_URL)
+ HttpRequest(HttpGet, STEAM_WEB_URL)
{
flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP;
}
diff --git a/protocols/Steam/src/http_request.h b/protocols/Steam/src/http_request.h index 841910dc32..43bb68d464 100644 --- a/protocols/Steam/src/http_request.h +++ b/protocols/Steam/src/http_request.h @@ -1,166 +1,377 @@ #ifndef _HTTP_REQUEST_H_ #define _HTTP_REQUEST_H_ -class HttpResponse : public NETLIBHTTPREQUEST, public MZeroedObject +class HttpRequest; +class HttpResponse; + +class HttpUri { + friend class HttpRequest; + private: - bool isEmptyResponse; + CMStringA m_uri; + NETLIBHTTPREQUEST *m_request; + + HttpUri(NETLIBHTTPREQUEST *request, const char *uri) + : m_request(request), m_uri(uri) + { + if (m_request) + m_request->szUrl = m_uri.GetBuffer(); + } + + HttpUri(NETLIBHTTPREQUEST *request, const char *urlFormat, va_list args) + : m_request(request) + { + m_uri.AppendFormatV(urlFormat, args); + if (m_request) + m_request->szUrl = m_uri.GetBuffer(); + } + + ~HttpUri() + { + if (m_request) + m_request->szUrl = NULL; + } + + void FormatV(const char *urlFormat, va_list args) + { + m_uri.AppendFormatV(urlFormat, args); + if (m_request) + m_request->szUrl = m_uri.GetBuffer(); + } + + void AppendFormat(const char *fmt, ...) + { + va_list args; + va_start(args, fmt); + m_uri += (m_uri.Find('?') == -1) ? '?' : '&'; + m_uri.AppendFormatV(fmt, args); + va_end(args); + + if (m_request) + m_request->szUrl = m_uri.GetBuffer(); + } public: - const NETLIBHTTPREQUEST* request; + HttpUri& operator=(const HttpUri&); // to prevent copying; - HttpResponse(const NETLIBHTTPREQUEST* response, const NETLIBHTTPREQUEST* request = NULL) + operator const char*() const { - this->request = request; - isEmptyResponse = (response == NULL); - if (response) - { - cbSize = response->cbSize; - requestType = response->requestType; - flags = response->flags; - szUrl = mir_strdup(response->szUrl); - headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * response->headersCount); - headersCount = response->headersCount; - for (int i = 0; i < headersCount; i++) - { - headers[i].szName = mir_strdup(response->headers[i].szName); - headers[i].szValue = mir_strdup(response->headers[i].szValue); - } - pData = (char*)mir_alloc(response->dataLength); - dataLength = response->dataLength; - memcpy(pData, response->pData, dataLength); - resultCode = response->resultCode; - szResultDescr = mir_strdup(response->szResultDescr); - nlc = response->nlc; - timeout = response->timeout; - } - else if (request != NULL) - { - // when response is null, we must get resultCode from the request object - resultCode = request->resultCode; - } + return m_request + ? m_request->szUrl + : NULL; } - bool const operator !() const + HttpUri &operator<<(const PARAM ¶m) { - return isEmptyResponse; + AppendFormat(param.szName); + return *this; } - ~HttpResponse() + HttpUri &operator<<(const INT_PARAM ¶m) { - for (int i = 0; i < headersCount; i++) - { - mir_free(headers[i].szName); - mir_free(headers[i].szValue); - } - mir_free(szUrl); - mir_free(headers); - mir_free(pData); - mir_free(szResultDescr); + AppendFormat("%s=%i", param.szName, param.iValue); + return *this; + } + + HttpUri &operator<<(const INT64_PARAM ¶m) + { + AppendFormat("%s=%lld", param.szName, param.iValue); + return *this; + } + + HttpUri &operator<<(const CHAR_PARAM ¶m) + { + AppendFormat("%s=%s", param.szName, ptrA(mir_urlEncode(param.szValue))); + return *this; } }; -class HttpRequest : public NETLIBHTTPREQUEST, public MZeroedObject +class HttpHeaders { + friend class HttpContent; + friend class HttpRequest; + friend class HttpResponse; + private: - CMStringA m_url; + NETLIBHTTPREQUEST *m_request; + + HttpHeaders(NETLIBHTTPREQUEST *request) + : m_request(request) + { + } + + void Set(LPCSTR szName) + { + Set(szName, ""); + } + + void Set(LPCSTR szName, LPCSTR szValue) + { + if (!m_request) + return; + + m_request->headers = (NETLIBHTTPHEADER*)mir_realloc(m_request->headers, + sizeof(NETLIBHTTPHEADER)*(m_request->headersCount + 1)); + m_request->headers[m_request->headersCount].szName = mir_strdup(szName); + m_request->headers[m_request->headersCount].szValue = mir_strdup(szValue); + m_request->headersCount++; + } + +public: + HttpHeaders& operator=(const HttpHeaders&); // to prevent copying; + + const NETLIBHTTPHEADER* operator[](size_t idx) const + { + return m_request + ? &m_request->headers[idx] + : NULL; + } + + size_t GetSize() const + { + return m_request + ? m_request->headersCount + : 0; + } + + HttpHeaders& operator<<(const PARAM ¶m) + { + Set(param.szName); + return *this; + } + + HttpHeaders& operator<<(const CHAR_PARAM ¶m) + { + Set(param.szName, param.szValue); + return *this; + } +}; + +class HttpContent +{ + friend class HttpRequest; + friend class HttpResponse; protected: - enum HttpRequestUrlFormat { FORMAT }; + HttpHeaders Headers; + NETLIBHTTPREQUEST *m_request; - void Init(int type) + HttpContent(NETLIBHTTPREQUEST *request) + : Headers(request), m_request(request) { - cbSize = sizeof(NETLIBHTTPREQUEST); - requestType = type; - flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMPSEND | NLHRF_DUMPASTEXT; - AddHeader("user-agent", "Steam 1.2.0 / iPhone"); } - void AddHeader(LPCSTR szName, LPCSTR szValue) + virtual ~HttpContent() + { + if (m_request) + { + m_request->pData = nullptr; + m_request->dataLength = 0; + } + } + +public: + HttpContent& operator=(const HttpContent&); // to prevent copying; + + operator bool() const + { + return m_request && m_request->pData && m_request->dataLength; + } + + operator const char*() const { - headers = (NETLIBHTTPHEADER*)mir_realloc(headers, sizeof(NETLIBHTTPHEADER)*(headersCount + 1)); - headers[headersCount].szName = mir_strdup(szName); - headers[headersCount].szValue = mir_strdup(szValue); - headersCount++; + return m_request + ? m_request->pData + : nullptr; } - void AddParameter(const char *fmt, ...) + const char* GetData() const + { + return m_request + ? m_request->pData + : nullptr; + } + + size_t GetSize() const + { + return m_request + ? m_request->dataLength + : 0; + } +}; + +class FormUrlEncodedContent : public HttpContent +{ + friend FormUrlEncodedContent* operator<<(FormUrlEncodedContent*, const PARAM&); + friend FormUrlEncodedContent* operator<<(FormUrlEncodedContent*, const BOOL_PARAM&); + friend FormUrlEncodedContent* operator<<(FormUrlEncodedContent*, const INT_PARAM&); + friend FormUrlEncodedContent* operator<<(FormUrlEncodedContent*, const INT64_PARAM&); + friend FormUrlEncodedContent* operator<<(FormUrlEncodedContent*, const CHAR_PARAM&); + +private: + CMStringA m_content; + + void AppendFormat(const char *fmt, ...) { va_list args; va_start(args, fmt); - m_url += m_url.Find('?') == -1 ? '?' : '&'; - m_url.AppendFormatV(fmt, args); + if (!m_content.IsEmpty()) + m_content += '&'; + m_content.AppendFormatV(fmt, args); va_end(args); + + if (m_request) + { + m_request->pData = m_content.GetBuffer(); + m_request->dataLength = m_content.GetLength(); + } } - void AddParameter(LPCSTR name, LPCSTR value) +public: + FormUrlEncodedContent(NETLIBHTTPREQUEST *request) + : HttpContent(request) { - AddParameter("%s=%s", name, value); + Headers << CHAR_PARAM("Content-Type", "application/x-www-form-urlencoded"); } +}; + +__forceinline FormUrlEncodedContent* operator<<(FormUrlEncodedContent *content, const PARAM ¶m) +{ + content->AppendFormat(param.szName); + return content; +} - void SetData(const char *data, size_t size) +__forceinline FormUrlEncodedContent* operator<<(FormUrlEncodedContent *content, const BOOL_PARAM ¶m) +{ + content->AppendFormat("%s=%s", param.szName, param.bValue ? "true" : "false"); + return content; +} + +__forceinline FormUrlEncodedContent* operator<<(FormUrlEncodedContent *content, const INT_PARAM ¶m) +{ + content->AppendFormat("%s=%i", param.szName, param.iValue); + return content; +} + +__forceinline FormUrlEncodedContent* operator<<(FormUrlEncodedContent *content, const INT64_PARAM ¶m) +{ + content->AppendFormat("%s=%lld", param.szName, param.iValue); + return content; +} + +__forceinline FormUrlEncodedContent* operator<<(FormUrlEncodedContent *content, const CHAR_PARAM ¶m) +{ + content->AppendFormat("%s=%s", param.szName, ptrA(mir_urlEncode(param.szValue))); + return content; +} + +enum HttpMethod +{ + HttpGet = 1, + HttpPost +}; + +class HttpResponse +{ + friend class HttpRequest; + +private: + NETLIBHTTPREQUEST *m_response; + +public: + HttpRequest *Request; + HttpHeaders Headers; + HttpContent Content; + + HttpResponse(HttpRequest *request, NETLIBHTTPREQUEST *response) + : Request(request), + m_response(response), + Headers(response), + Content(response) + { + } + + ~HttpResponse() + { + Netlib_FreeHttpRequest(m_response); + } + + bool operator!() const { - if (pData != NULL) - mir_free(pData); + return !m_response || !m_response->pData; + } + + operator bool() const + { + return m_response && m_response->pData; + } - dataLength = (int)size; - pData = (char*)mir_alloc(size + 1); - memcpy(pData, data, size); - pData[size] = 0; + bool IsSuccess() const + { + return m_response && + m_response->resultCode >= HTTP_CODE_OK && + m_response->resultCode <= HTTP_CODE_MULTI_STATUS; + } + + int GetStatusCode() const + { + return m_response->resultCode; } +}; + +class HttpRequest : protected NETLIBHTTPREQUEST, public MZeroedObject +{ + friend class HttpUri; + friend class HttpHeaders; + friend class HttpContent; + +protected: + enum HttpRequestUrlFormat { FORMAT }; public: - HttpRequest(int type, LPCSTR url) + HttpUri Uri; + HttpHeaders Headers; + HttpContent *Content; + + HttpRequest(HttpMethod method, const char *url) + : Uri(this, url), Headers(this), Content(nullptr) { - Init(type); + cbSize = sizeof(NETLIBHTTPREQUEST); + requestType = method; + flags = NLHRF_HTTP11 | NLHRF_SSL; - m_url = url; + Content = new HttpContent(this); } - HttpRequest(int type, HttpRequestUrlFormat, LPCSTR urlFormat, ...) + HttpRequest(HttpMethod method, HttpRequestUrlFormat, const char *urlFormat, ...) + : Uri(this, urlFormat), Headers(this), Content(nullptr) { - Init(type); + cbSize = sizeof(NETLIBHTTPREQUEST); + requestType = method; + flags = NLHRF_HTTP11 | NLHRF_SSL; va_list formatArgs; va_start(formatArgs, urlFormat); - m_url.AppendFormatV(urlFormat, formatArgs); + Uri.FormatV(urlFormat, formatArgs); va_end(formatArgs); + + Content = new HttpContent(this); } ~HttpRequest() { - for (int i = 0; i < headersCount; i++) + if (Content != nullptr) { - mir_free(headers[i].szName); - mir_free(headers[i].szValue); + delete Content; + Content = nullptr; } - mir_free(headers); - - if (pData != NULL) - mir_free(pData); } - HttpResponse* Send(HNETLIBUSER nlu) + operator NETLIBHTTPREQUEST*() { - szUrl = m_url.GetBuffer(); - - Netlib_Logf(nlu, "Send request to %s", szUrl); - - NETLIBHTTPREQUEST* response = Netlib_HttpTransaction(nlu, this); - HttpResponse* result = new HttpResponse(response, this); - Netlib_FreeHttpRequest(response); - - return result; + return this; } }; - -bool __forceinline ResponseHttpOk(const HttpResponse *response) { - return (response && response->pData && (response->resultCode == HTTP_CODE_OK)); -} - -bool __forceinline CheckResponse(const HttpResponse *response) { - return (response && response->pData); -} - #endif //_HTTP_REQUEST_H_
\ No newline at end of file diff --git a/protocols/Steam/src/request_queue.cpp b/protocols/Steam/src/request_queue.cpp deleted file mode 100644 index 84d6414c40..0000000000 --- a/protocols/Steam/src/request_queue.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/*
-Copyright (c) 2015-17 Miranda NG project (https://miranda-ng.org)
-
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation version 2
-of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-RequestQueue::RequestQueue(HNETLIBUSER hConnection) :
- hConnection(hConnection), requests(1)
-{
- isTerminated = true;
- hRequestQueueThread = nullptr;
- hRequestQueueEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
-}
-
-RequestQueue::~RequestQueue()
-{
- requests.destroy();
- CloseHandle(hRequestQueueEvent);
-}
-
-void RequestQueue::Start()
-{
- if (!isTerminated)
- return;
-
- isTerminated = false;
- if (hRequestQueueThread == nullptr)
- hRequestQueueThread = mir_forkthread((pThreadFunc)&RequestQueue::WorkerThread, this);
-}
-
-void RequestQueue::Stop()
-{
- if (isTerminated)
- return;
-
- isTerminated = true;
- SetEvent(hRequestQueueEvent);
-}
-
-void RequestQueue::Push(HttpRequest *request, HttpResponseCallback response, void *arg, HttpFinallyCallback last)
-{
- if (isTerminated)
- return;
-
- RequestQueueItem *item = new RequestQueueItem(request, response, arg, last);
- {
- mir_cslock lock(requestQueueLock);
-
- requests.insert(item);
- }
- SetEvent(hRequestQueueEvent);
-}
-
-void RequestQueue::Send(HttpRequest *request, HttpResponseCallback response, void *arg, HttpFinallyCallback last)
-{
- RequestQueueItem *item = new RequestQueueItem(request, response, arg, last);
- mir_forkthreadowner((pThreadFuncOwner)&RequestQueue::AsyncSendThread, this, item, nullptr);
-}
-
-void RequestQueue::Execute(RequestQueueItem *item)
-{
- HttpResponse *response = item->request->Send(hConnection);
- if (item->responseCallback != nullptr)
- item->responseCallback(response, item->arg);
- if (item->finallyCallback != nullptr)
- item->finallyCallback(item->arg);
- delete item;
- delete response;
-}
-
-unsigned int RequestQueue::AsyncSendThread(void *owner, void *arg)
-{
- RequestQueue *queue = (RequestQueue*)owner;
- RequestQueueItem *item = (RequestQueueItem*)arg;
-
- queue->Execute(item);
-
- return 0;
-}
-
-unsigned int RequestQueue::WorkerThread(void *arg)
-{
- RequestQueue *queue = (RequestQueue*)arg;
-
- while (!queue->isTerminated)
- {
- WaitForSingleObject(queue->hRequestQueueEvent, INFINITE);
- while (true)
- {
- RequestQueueItem *item = nullptr;
- {
- mir_cslock lock(queue->requestQueueLock);
-
- if (queue->requests.getCount() == 0)
- break;
-
- item = queue->requests[0];
- queue->requests.remove(0);
- }
- if (item != nullptr)
- queue->Execute(item);
- }
- }
-
- queue->hRequestQueueThread = nullptr;
- return 0;
-}
\ No newline at end of file diff --git a/protocols/Steam/src/request_queue.h b/protocols/Steam/src/request_queue.h deleted file mode 100644 index 6d14209fb8..0000000000 --- a/protocols/Steam/src/request_queue.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _REQUEST_QUEUE_H_
-#define _REQUEST_QUEUE_H_
-
-typedef void(*HttpResponseCallback)(const HttpResponse *response, void *arg);
-typedef void(*HttpFinallyCallback)(void *arg);
-
-struct RequestQueueItem
-{
- void *arg;
- HttpRequest *request;
- HttpResponseCallback responseCallback;
- HttpFinallyCallback finallyCallback;
-
- RequestQueueItem(HttpRequest *request, void *arg, HttpFinallyCallback finallyCallback) :
- request(request), responseCallback(NULL), arg(arg), finallyCallback(finallyCallback)
- {
- }
-
- RequestQueueItem(HttpRequest *request, HttpResponseCallback response, void *arg, HttpFinallyCallback finallyCallback) :
- request(request), responseCallback(response), arg(arg), finallyCallback(finallyCallback)
- {
- }
-
- ~RequestQueueItem()
- {
- delete request;
- request = NULL;
- responseCallback = NULL;
- finallyCallback = NULL;
- }
-};
-
-class RequestQueue
-{
- bool isTerminated;
- HNETLIBUSER hConnection;
- mir_cs requestQueueLock;
- LIST<RequestQueueItem> requests;
- HANDLE hRequestQueueEvent, hRequestQueueThread;
-
- void Execute(RequestQueueItem *item);
-
- static unsigned int __cdecl AsyncSendThread(void*, void*);
- static unsigned int __cdecl WorkerThread(void*);
-
-public:
- RequestQueue(HNETLIBUSER hConnection);
- ~RequestQueue();
-
- void Start();
- void Stop();
-
- void Push(HttpRequest *request, HttpResponseCallback response = NULL, void *arg = NULL, HttpFinallyCallback last = NULL);
- void Send(HttpRequest *request, HttpResponseCallback response = NULL, void *arg = NULL, HttpFinallyCallback last = NULL);
-
-};
-
-#endif //_REQUEST_QUEUE_H_
\ No newline at end of file diff --git a/protocols/Steam/src/resource.h b/protocols/Steam/src/resource.h index bafc927a3b..fa9145b922 100644 --- a/protocols/Steam/src/resource.h +++ b/protocols/Steam/src/resource.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
-// Used by D:\Development\Miranda NG\Miranda NG\protocols\Steam\res\Resource.rc
+// Used by D:\Projects\c++\miranda-ng\protocols\Steam\res\Resource.rc
//
#define IDD_ACCMGR 9
#define IDD_OPT_MAIN 10
@@ -39,7 +39,7 @@ //
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 121
+#define _APS_NEXT_RESOURCE_VALUE 125
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1085
#define _APS_NEXT_SYMED_VALUE 101
diff --git a/protocols/Steam/src/stdafx.h b/protocols/Steam/src/stdafx.h index 3abce22f89..7b2a070da4 100644 --- a/protocols/Steam/src/stdafx.h +++ b/protocols/Steam/src/stdafx.h @@ -62,7 +62,7 @@ extern HANDLE hExtraXStatus; #include "steam_dialogs.h"
#include "steam_options.h"
#include "http_request.h"
-#include "request_queue.h"
+#include "api/app_info.h"
#include "api/authorization.h"
#include "api/authorization.h"
#include "api/avatar.h"
diff --git a/protocols/Steam/src/steam_contacts.cpp b/protocols/Steam/src/steam_contacts.cpp index 7546f33d40..fddad8a4a5 100644 --- a/protocols/Steam/src/steam_contacts.cpp +++ b/protocols/Steam/src/steam_contacts.cpp @@ -50,13 +50,8 @@ void CSteamProto::SetContactStatus(MCONTACT hContact, WORD status) void CSteamProto::SetAllContactsStatus(WORD status) { - for (MCONTACT hContact = db_find_first(this->m_szModuleName); hContact; hContact = db_find_next(hContact, this->m_szModuleName)) - { - if (this->isChatRoom(hContact)) - continue; - + for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) SetContactStatus(hContact, status); - } } MCONTACT CSteamProto::GetContactFromAuthEvent(MEVENT hEvent) @@ -84,7 +79,7 @@ MCONTACT CSteamProto::FindContact(const char *steamId) mir_cslock lck(this->contact_search_lock); - for (hContact = db_find_first(this->m_szModuleName); hContact; hContact = db_find_next(hContact, this->m_szModuleName)) + for (hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { ptrA cSteamId(db_get_sa(hContact, this->m_szModuleName, "SteamID")); if (!lstrcmpA(cSteamId, steamId)) @@ -94,34 +89,34 @@ MCONTACT CSteamProto::FindContact(const char *steamId) return hContact; } -void CSteamProto::UpdateContactDetails(MCONTACT hContact, JSONNode *data) +void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data) { // set common data - JSONNode *node = json_get(data, "personaname"); - setWString(hContact, "Nick", ptrW(json_as_string(node))); + CMStringW nick = data["personaname"].as_mstring(); + setWString(hContact, "Nick", nick); - node = json_get(data, "profileurl"); - setString(hContact, "Homepage", _T2A(ptrW(json_as_string(node)))); + json_string homepage = data["profileurl"].as_string(); + setString(hContact, "Homepage", homepage.c_str()); - node = json_get(data, "primaryclanid"); - setString(hContact, "PrimaryClanID", _T2A(ptrW(json_as_string(node)))); + json_string primaryClanId = data["primaryclanid"].as_string(); + setString(hContact, "PrimaryClanID", primaryClanId.c_str()); // set name - node = json_get(data, "realname"); - if (node != nullptr) + JSONNode node = data["realname"]; + if (!node.isnull()) { - std::wstring realname = (wchar_t*)ptrW(json_as_string(node)); - if (!realname.empty()) + CMStringW realName = node.as_mstring(); + if (!realName.IsEmpty()) { - size_t pos = realname.find(L' ', 1); - if (pos != std::wstring::npos) + int pos = realName.Find(L' ', 1); + if (pos != -1) { - setWString(hContact, "FirstName", realname.substr(0, pos).c_str()); - setWString(hContact, "LastName", realname.substr(pos + 1).c_str()); + setWString(hContact, "FirstName", realName.Mid(0, pos)); + setWString(hContact, "LastName", realName.Mid(pos + 1)); } else { - setWString(hContact, "FirstName", realname.c_str()); + setWString(hContact, "FirstName", realName); delSetting(hContact, "LastName"); } } @@ -134,42 +129,41 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, JSONNode *data) // avatar bool biggerAvatars = getBool("UseBigAvatars", false); - node = json_get(data, biggerAvatars ? "avatarfull" : "avatarmedium"); - std::string avatarUrl = (char*)_T2A(ptrW(json_as_string(node))); + json_string avatarUrl = data[biggerAvatars ? "avatarfull" : "avatarmedium"].as_string(); CheckAvatarChange(hContact, avatarUrl); // set country - node = json_get(data, "loccountrycode"); - if (node != nullptr) + node = data["loccountrycode"]; + if (!node.isnull()) { - const char *iso = ptrA(mir_u2a(ptrW(json_as_string(node)))); - char *country = (char *)CallService(MS_UTILS_GETCOUNTRYBYISOCODE, (WPARAM)iso, 0); + json_string countryCode = node.as_string(); + char *country = (char *)CallService(MS_UTILS_GETCOUNTRYBYISOCODE, (WPARAM)countryCode.c_str(), 0); setString(hContact, "Country", country); } else delSetting(hContact, "Country"); // state code - node = json_get(data, "locstatecode"); - if (node) - setDword(hContact, "StateCode", json_as_int(node)); + node = data["locstatecode"]; + if (!node.isnull()) + setDword(hContact, "StateCode", node.as_int()); else delSetting(hContact, "StateCode"); // city id - node = json_get(data, "loccityid"); - if (node) - setDword(hContact, "CityID", json_as_int(node)); + node = data["loccityid"]; + if (!node.isnull()) + setDword(hContact, "CityID", node.as_int()); else delSetting(hContact, "CityID"); // account created - node = json_get(data, "timecreated"); - setDword(hContact, "MemberTS", json_as_int(node)); + node = data["timecreated"]; + setDword(hContact, "MemberTS", node.as_int()); // last logout time - node = json_get(data, "lastlogoff"); - setDword(hContact, "LogoffTS", json_as_int(node)); + node = data["lastlogoff"]; + setDword(hContact, "LogoffTS", node.as_int()); // status // NOTE: this here is wrong info, probably depending on publicity of steam profile, but we don't need this at all, we get status updates by polling @@ -179,9 +173,8 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, JSONNode *data) */ // client - node = json_get(data, "personastateflags"); - int stateflags = node ? json_as_int(node) : -1; - + node = data["personastateflags"]; + int stateflags = !node.isnull() ? node.as_int() : -1; if (stateflags == 0) { // nothing special, either standard client or in different status (only online, I want to play, I want to trade statuses support this flags) WORD status = getWord(hContact, "Status", ID_STATUS_OFFLINE); @@ -208,31 +201,36 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, JSONNode *data) // none/unknown (e.g. when contact is offline) delSetting(hContact, "MirVer"); } - - // playing game - node = json_get(data, "gameid"); - DWORD gameId = node ? atol(_T2A(ptrW(json_as_string(node)))) : 0; - - node = json_get(data, "gameextrainfo"); - ptrW gameInfo(json_as_string(node)); - if (gameId > 0 || gameInfo[0] != '\0') + // playing game + json_string appId = data["gameid"].as_string(); + CMStringW gameInfo = data["gameextrainfo"].as_mstring(); + if (!appId.empty() || !gameInfo.IsEmpty()) { - node = json_get(data, "gameserverip"); - ptrW serverIP(json_as_string(node)); - - node = json_get(data, "gameserversteamid"); - ptrW serverID (json_as_string(node)); + DWORD gameId = atol(appId.c_str()); + json_string serverIP = data["gameserverip"].as_string(); + json_string serverID = data["gameserversteamid"].as_string(); setDword(hContact, "GameID", gameId); - setString(hContact, "ServerIP", _T2A(serverIP)); - setString(hContact, "ServerID", _T2A(serverID)); + setString(hContact, "ServerIP", serverIP.c_str()); + setString(hContact, "ServerID", serverID.c_str()); CMStringW message(gameInfo); - if (!gameId) - message.Append(TranslateT(" (Non-Steam)")); - if (serverIP[0] != '\0') - message.AppendFormat(TranslateT(" on server %s"), serverIP); + if (gameId && message.IsEmpty()) + { + ptrA token(getStringA("TokenSecret")); + PushRequest( + new GetAppInfoRequest(token, appId.c_str()), + &CSteamProto::OnGotAppInfo, + (void*)hContact); + } + else + { + if (!gameId) + message.Append(TranslateT(" (Non-Steam)")); + if (!serverIP.empty()) + message.AppendFormat(TranslateT(" on server %s"), serverIP.c_str()); + } setDword(hContact, "XStatusId", gameId); setWString(hContact, "XStatusName", TranslateT("Playing")); @@ -388,86 +386,75 @@ MCONTACT CSteamProto::AddContact(const char *steamId, bool isTemporary) return hContact; } -void CSteamProto::UpdateContactRelationship(MCONTACT hContact, JSONNode *data) +void CSteamProto::UpdateContactRelationship(MCONTACT hContact, const JSONNode &data) { - JSONNode *node = json_get(data, "friend_since"); - if (node) - db_set_dw(hContact, "UserInfo", "ContactAddTime", json_as_int(node)); + JSONNode node = data["friend_since"]; + if (!node.isnull()) + db_set_dw(hContact, "UserInfo", "ContactAddTime", node.as_int()); - node = json_get(data, "relationship"); - if (node == nullptr) + node = data["relationship"]; + if (node.isnull()) return; - ptrA relationship(mir_u2a(ptrW(json_as_string(node)))); - if (!lstrcmpiA(relationship, "friend")) - { + json_string relationship = node.as_string(); + if (!lstrcmpiA(relationship.c_str(), "friend")) ContactIsFriend(hContact); - } - else if (!lstrcmpiA(relationship, "ignoredfriend")) - { + else if (!lstrcmpiA(relationship.c_str(), "ignoredfriend")) ContactIsIgnored(hContact); - } - else if (!lstrcmpiA(relationship, "requestrecipient")) - { + else if (!lstrcmpiA(relationship.c_str(), "requestrecipient")) ContactIsAskingAuth(hContact); - } } -void CSteamProto::OnGotFriendList(const HttpResponse *response) +void CSteamProto::OnGotAppInfo(const JSONNode &root, void *arg) { - if (!CheckResponse(response)) - return; + MCONTACT hContact = (UINT_PTR)arg; - JSONROOT root(response->pData); - if (root == nullptr) + JSONNode apps = root["apps"].as_array(); + for (const JSONNode &app : apps) + { + DWORD gameId = app["appid"].as_int(); + CMStringW message = app["name"].as_mstring(); + + setDword(hContact, "XStatusId", gameId); + setWString(hContact, "XStatusName", TranslateT("Playing")); + setWString(hContact, "XStatusMsg", message); + } +} + +void CSteamProto::OnGotFriendList(const JSONNode &root, void*) +{ + if (root.isnull()) return; + // Comma-separated list of steam ids to update summaries std::string steamIds = (char*)ptrA(getStringA("SteamID")); - std::map<std::string, JSONNode*> friends; - // Remember contacts on server - JSONNode *node = json_get(root, "friends"); - JSONNode *nroot = json_as_array(node); - if (nroot != nullptr) + std::map<json_string, JSONNode*> friendsMap; + JSONNode friends = root["friends"].as_array(); + for (const JSONNode &_friend : friends) { - for (size_t i = 0; i < json_size(nroot); i++) - { - JSONNode *child = json_at(nroot, i); - if (child == nullptr) - break; - - node = json_get(child, "steamid"); - if (node == nullptr) - continue; - - std::string steamId = (char*)_T2A(ptrW(json_as_string(node))); - friends.insert(std::make_pair(steamId, child)); - } + json_string steamId = _friend["steamid"].as_string(); + friendsMap.insert(std::make_pair(steamId, json_copy(&_friend))); } - { - // Check and update contacts in database + { // Check and update contacts in database mir_cslock lck(this->contact_search_lock); for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) { - if (isChatRoom(hContact)) - continue; - - ptrA id(getStringA(hContact, "SteamID")); - if (id == NULL) + ptrA steamId(getStringA(hContact, "SteamID")); + if (steamId == nullptr) continue; - std::map<std::string, JSONNode*>::iterator it = friends.find(std::string(id)); - - if (it != friends.end()) + auto it = friendsMap.find((char*)steamId); + if (it != friendsMap.end()) { // Contact is on server-list, update (and eventually notify) it - UpdateContactRelationship(hContact, it->second); - + UpdateContactRelationship(hContact, *it->second); steamIds.append(",").append(it->first); - friends.erase(it); + json_delete(it->second); + friendsMap.erase(it); } else { @@ -478,19 +465,19 @@ void CSteamProto::OnGotFriendList(const HttpResponse *response) } // Check remaining contacts in map and add them to contact list - for (std::map<std::string, JSONNode*>::iterator it = friends.begin(); it != friends.end();) + for (auto it : friendsMap) { // Contact is on server-list, but not in database, add (but not notify) it - MCONTACT hContact = AddContact(it->first.c_str()); - UpdateContactRelationship(hContact, it->second); + MCONTACT hContact = AddContact(it.first.c_str()); + UpdateContactRelationship(hContact, *it.second); - steamIds.append(",").append(it->first); - it = friends.erase(it); + steamIds.append(",").append(it.first); + json_delete(it.second); } - friends.clear(); + friendsMap.clear(); // We need to delete nroot here at the end, because we had references to JSONNode objects stored in friends map - json_delete(nroot); + // json_delete(nroot); ptrA token(getStringA("TokenSecret")); @@ -509,93 +496,68 @@ void CSteamProto::OnGotFriendList(const HttpResponse *response) &CSteamProto::OnGotConversations); } -void CSteamProto::OnGotBlockList(const HttpResponse *response) +void CSteamProto::OnGotBlockList(const JSONNode &root, void*) { - if (!CheckResponse(response)) - return; - - JSONROOT root(response->pData); - if (root == nullptr) + if (root.isnull()) return; //std::string steamIds; - JSONNode *node = json_get(root, "friends"); - JSONNode *nroot = json_as_array(node); - if (nroot != nullptr) - { - for (size_t i = 0; i < json_size(nroot); i++) - { - JSONNode *child = json_at(nroot, i); - if (child == nullptr) - break; + JSONNode friends = root["friends"].as_array(); + if (friends.isnull()) + return; - node = json_get(child, "steamid"); - ptrA steamId(mir_u2a(ptrW(json_as_string(node)))); + for (size_t i = 0; i < friends.size(); i++) + { + JSONNode node = friends[i].as_node(); - /*MCONTACT hContact = FindContact(steamId); - if (!hContact) - { - hContact = AddContact(steamId); - steamIds.append(steamId).append(","); - }*/ + json_string steamId = node["steamid"].as_string(); - node = json_get(child, "relationship"); - ptrA relationship(mir_u2a(ptrW(json_as_string(node)))); + /*MCONTACT hContact = FindContact(steamId); + if (!hContact) + { + hContact = AddContact(steamId); + steamIds.append(steamId).append(","); + }*/ - if (!lstrcmpiA(relationship, "ignoredfriend")) - { - // todo: open block list - } - else continue; + json_string relationship = node["relationship"].as_string(); + if (!lstrcmpiA(relationship.c_str(), "ignoredfriend")) + { + // todo: open block list } - json_delete(nroot); + else continue; } } -void CSteamProto::OnGotUserSummaries(const HttpResponse *response) +void CSteamProto::OnGotUserSummaries(const JSONNode &root, void*) { - if (!CheckResponse(response)) + JSONNode players = root["players"].as_array(); + if (players.isnull()) return; - JSONROOT root(response->pData); - if (root == nullptr) - return; - - JSONNode *node = json_get(root, "players"); - JSONNode *nroot = json_as_array(node); - if (nroot != nullptr) + for (size_t i = 0; i < players.size(); i++) { - for (size_t i = 0; i < json_size(nroot); i++) - { - JSONNode *item = json_at(nroot, i); - if (item == nullptr) - break; - - node = json_get(item, "steamid"); - ptrA steamId(mir_u2a(ptrW(json_as_string(node)))); + JSONNode player = players[i]; + json_string steamId = player["steamid"].as_string(); - MCONTACT hContact = NULL; - if (!IsMe(steamId)) { - hContact = AddContact(steamId); - } + MCONTACT hContact = NULL; + if (!IsMe(steamId.c_str())) + hContact = AddContact(steamId.c_str()); - UpdateContactDetails(hContact, item); - } - json_delete(nroot); + UpdateContactDetails(hContact, player); } } -void CSteamProto::OnGotAvatar(const HttpResponse *response, void *arg) +void CSteamProto::OnGotAvatar(const HttpResponse &response, void *arg) { PROTO_AVATAR_INFORMATION ai = { 0 }; ai.hContact = (UINT_PTR)arg; GetDbAvatarInfo(ai); - if (!ResponseHttpOk(response)) + if (!response.IsSuccess()) { ptrA steamId(getStringA(ai.hContact, "SteamID")); - debugLogA("CSteamProto::OnGotAvatar: failed to get avatar %s", steamId); + debugLogA(__FUNCTION__ ": failed to get avatar %s", steamId); if (ai.hContact) ProtoBroadcastAck(ai.hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, (HANDLE)&ai, 0); @@ -605,7 +567,7 @@ void CSteamProto::OnGotAvatar(const HttpResponse *response, void *arg) FILE *fp = _wfopen(ai.filename, L"wb"); if (fp) { - fwrite(response->pData, sizeof(char), response->dataLength, fp); + fwrite(response.Content, sizeof(char), response.Content.GetSize(), fp); fclose(fp); if (ai.hContact) @@ -615,14 +577,14 @@ void CSteamProto::OnGotAvatar(const HttpResponse *response, void *arg) } } -void CSteamProto::OnFriendAdded(const HttpResponse *response, void *arg) +void CSteamProto::OnFriendAdded(const HttpResponse &response, void *arg) { SendAuthParam *param = (SendAuthParam*)arg; - if (!response || response->resultCode != HTTP_CODE_OK || lstrcmpiA(response->pData, "true")) + if (!response.IsSuccess() || lstrcmpiA(response.Content, "true")) { ptrA steamId(getStringA(param->hContact, "SteamID")); - debugLogA("CSteamProto::OnFriendAdded: failed to add friend %s", steamId); + debugLogA(__FUNCTION__ ": failed to add friend %s", steamId); ProtoBroadcastAck(param->hContact, ACKTYPE_AUTHREQ, ACKRESULT_FAILED, param->hAuth, 0); @@ -637,24 +599,26 @@ void CSteamProto::OnFriendAdded(const HttpResponse *response, void *arg) ProtoBroadcastAck(param->hContact, ACKTYPE_AUTHREQ, ACKRESULT_SUCCESS, param->hAuth, 0); } -void CSteamProto::OnFriendBlocked(const HttpResponse *response, void *arg) +void CSteamProto::OnFriendBlocked(const HttpResponse &response, void *arg) { - if (!response || response->resultCode != HTTP_CODE_OK || lstrcmpiA(response->pData, "true")) + ptrA steamId((char*)arg); + + if (!response.IsSuccess() || lstrcmpiA(response.Content, "true")) { - debugLogA("CSteamProto::OnFriendIgnored: failed to ignore friend %s", (char*)arg); + debugLogA(__FUNCTION__ ": failed to ignore friend %s", (char*)arg); return; } } -void CSteamProto::OnFriendRemoved(const HttpResponse *response, void *arg) +void CSteamProto::OnFriendRemoved(const HttpResponse &response, void *arg) { MCONTACT hContact = (UINT_PTR)arg; - if (!response || response->resultCode != HTTP_CODE_OK || lstrcmpiA(response->pData, "true")) + if (!response.IsSuccess() || lstrcmpiA(response.Content, "true")) { ptrA who(getStringA(hContact, "SteamID")); - debugLogA("CSteamProto::OnFriendRemoved: failed to remove friend %s", who); + debugLogA(__FUNCTION__ ": failed to remove friend %s", who); return; } @@ -662,155 +626,123 @@ void CSteamProto::OnFriendRemoved(const HttpResponse *response, void *arg) setDword(hContact, "DeletedTS", ::time(nullptr)); } -void CSteamProto::OnAuthRequested(const HttpResponse *response, void *arg) +void CSteamProto::OnAuthRequested(const JSONNode &root, void*) { - if (!ResponseHttpOk(response)) - { - debugLogA("CSteamProto::OnAuthRequested: failed to request info for %s", (char*)arg); + if (root.isnull()) return; - } - - JSONROOT root(response->pData); - if (root == nullptr) - return; - - JSONNode *node = json_get(root, "players"); - JSONNode *nodes = json_as_array(node); - JSONNode *nroot = json_at(nodes, 0); - if (nroot != nullptr) + int first = 0; + JSONNode players = root["players"].as_array(); + JSONNode player = players[first]; + if (!player.isnull()) { - node = json_get(nroot, "steamid"); - ptrA steamId(mir_u2a(ptrW(json_as_string(node)))); - - MCONTACT hContact = AddContact(steamId); - - UpdateContactDetails(hContact, nroot); - + json_string steamId = player["steamid"].as_string(); + MCONTACT hContact = AddContact(steamId.c_str()); + UpdateContactDetails(hContact, player); ContactIsAskingAuth(hContact); } - - json_delete(nodes); } -void CSteamProto::OnPendingApproved(const HttpResponse *response, void *arg) +void CSteamProto::OnPendingApproved(const JSONNode &root, void *arg) { - if (!ResponseHttpOk(response)) - { - debugLogA("CSteamProto::OnPendingApproved: failed to approve pending from %s", (char*)arg); - return; - } + ptrA steamId((char*)arg); - JSONROOT root(response->pData); - if (root == nullptr) + if (root.isnull()) return; - JSONNode *node = json_get(root, "success"); - if (json_as_int(node) == 0) + int success = root["success"].as_int(); + if (success == 0) { - node = json_get(root, "error_text"); - debugLogA("CSteamProto::OnPendingApproved: failed to approve pending from %s (%s)", (char*)arg, ptrA(mir_utf8encodeW(ptrW(json_as_string(node))))); + json_string error = root["error_text"].as_string(); + debugLogA(__FUNCTION__ ": failed to approve pending from %s (%s)", steamId, ptrA(mir_utf8decodeA(error.c_str()))); } } -void CSteamProto::OnPendingIgnoreded(const HttpResponse *response, void *arg) +void CSteamProto::OnPendingIgnoreded(const JSONNode &root, void *arg) { - if (!ResponseHttpOk(response)) - { - debugLogA("CSteamProto::OnPendingIgnored: failed to ignore pending from %s", (char*)arg); - return; - } + ptrA steamId((char*)arg); - JSONROOT root(response->pData); - if (root == nullptr) + if (root.isnull()) return; - JSONNode *node = json_get(root, "success"); - if (json_as_int(node) == 0) + int success = root["success"].as_int(); + if (success == 0) { - node = json_get(root, "error_text"); - debugLogA("CSteamProto::OnPendingIgnored: failed to ignore pending from %s (%s)", (char*)arg, ptrA(mir_utf8encodeW(ptrW(json_as_string(node))))); + json_string error = root["error_text"].as_string(); + debugLogA(__FUNCTION__ ": failed to ignore pending from %s (%s)", steamId, ptrA(mir_utf8decodeA(error.c_str()))); } } -void CSteamProto::OnSearchResults(const HttpResponse *response, void *arg) +void CSteamProto::OnSearchResults(const HttpResponse &response, void *arg) { HANDLE searchType = (HANDLE)arg; - if (!ResponseHttpOk(response)) + if (!response.IsSuccess()) { ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, searchType, 0); - debugLogA("CSteamProto::AddSearchResults: failed to get summaries"); + debugLogA(__FUNCTION__ ": failed to get summaries"); return; } - JSONROOT root(response->pData); - if (root == nullptr) + JSONNode root = JSONNode::parse(response.Content); + if (root.isnull()) { ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, searchType, 0); return; } - JSONNode *node = json_get(root, "players"); - JSONNode *nroot = json_as_array(node); - if (nroot != nullptr) + JSONNode players = root["players"].as_array(); + for (size_t i = 0; i < players.size(); i++) { - for (size_t i = 0; i < json_size(nroot); i++) - { - JSONNode *child = json_at(nroot, i); - if (child == nullptr) - break; + JSONNode player = players[i]; - STEAM_SEARCH_RESULT ssr = { 0 }; - ssr.hdr.cbSize = sizeof(STEAM_SEARCH_RESULT); - ssr.hdr.flags = PSR_UNICODE; + STEAM_SEARCH_RESULT ssr = { 0 }; + ssr.hdr.cbSize = sizeof(STEAM_SEARCH_RESULT); + ssr.hdr.flags = PSR_UNICODE; - node = json_get(child, "steamid"); - ssr.hdr.id.w = mir_wstrdup(ptrW(json_as_string(node))); + CMStringW steamId = player["steamid"].as_mstring(); + ssr.hdr.id.w = steamId.Detach(); - node = json_get(child, "personaname"); - ssr.hdr.nick.w = mir_wstrdup(ptrW(json_as_string(node))); + CMStringW nick = player["personaname"].as_mstring(); + ssr.hdr.nick.w = nick.Detach(); - node = json_get(child, "realname"); - if (node != nullptr) + JSONNode node = player["realname"]; + if (!node.isnull()) + { + CMStringW realName = node.as_mstring(); + if (!realName.IsEmpty()) { - std::wstring realname = (wchar_t*)ptrW(json_as_string(node)); - if (!realname.empty()) + int pos = realName.Find(' ', 1); + if (pos != -1) { - size_t pos = realname.find(' ', 1); - if (pos != std::wstring::npos) - { - ssr.hdr.firstName.w = mir_wstrdup(realname.substr(0, pos).c_str()); - ssr.hdr.lastName.w = mir_wstrdup(realname.substr(pos + 1).c_str()); - } - else - ssr.hdr.firstName.w = mir_wstrdup(realname.c_str()); + ssr.hdr.firstName.w = realName.Mid(0, pos).Detach(); + ssr.hdr.lastName.w = realName.Mid(pos + 1).Detach(); } + else + ssr.hdr.firstName.w = realName.Detach(); } + } - //ssr.contact = contact; - ssr.data = json_copy(child); // FIXME: is this needed and safe (no memleak) to be here? + //ssr.contact = contact; + ssr.data = json_copy(&player); // FIXME: is this needed and safe (no memleak) to be here? - ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, searchType, (LPARAM)&ssr); - } + ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, searchType, (LPARAM)&ssr); } ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, searchType, 0); - - json_delete(nroot); } -void CSteamProto::OnSearchByNameStarted(const HttpResponse *response, void *arg) +void CSteamProto::OnSearchByNameStarted(const HttpResponse &response, void *arg) { - if (!ResponseHttpOk(response)) + if (!response.IsSuccess()) { ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)arg, 0); - debugLogA("CSteamProto::OnSearchByNameEnded: failed to get results"); + debugLogA(__FUNCTION__ ": failed to get results"); return; } - JSONROOT root(response->pData); - if (root == nullptr) + JSONNode root = JSONNode::parse(response.Content); + if (root.isnull()) { ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)arg, 0); return; @@ -822,24 +754,12 @@ void CSteamProto::OnSearchByNameStarted(const HttpResponse *response, void *arg) std::string steamIds; - JSONNode *node = json_get(root, "results"); - JSONNode *nroot = json_as_array(node); - if (nroot != nullptr) + JSONNode results = root["results"].as_array(); + for (size_t i = 0; i < results.size(); i++) { - for (size_t i = 0; i < json_size(nroot); i++) - { - JSONNode *child = json_at(nroot, i); - if (child == nullptr) - break; - - node = json_get(child, "steamid"); - if (node == nullptr) - continue; - - std::string steamId = (char*)_T2A(ptrW(json_as_string(node))); - steamIds.append(steamId).append(","); - } - json_delete(nroot); + JSONNode result = results[i]; + json_string steamId = result["steamid"].as_string(); + steamIds.append(steamId).append(","); } if (!steamIds.empty()) diff --git a/protocols/Steam/src/steam_history.cpp b/protocols/Steam/src/steam_history.cpp index 3b62aa21bc..a897037bae 100644 --- a/protocols/Steam/src/steam_history.cpp +++ b/protocols/Steam/src/steam_history.cpp @@ -1,92 +1,64 @@ #include "stdafx.h" -void CSteamProto::OnGotConversations(const HttpResponse *response) +void CSteamProto::OnGotConversations(const JSONNode &root, void*) { // Don't load any messages when we don't have lastMessageTS, as it may cause duplicates if (m_lastMessageTS <= 0) return; - if (!ResponseHttpOk(response)) + if (root.isnull()) return; - JSONROOT root(response->pData); - if (root == nullptr) + JSONNode response = root["response"]; + JSONNode sessions = response["message_sessions"].as_array(); + if (sessions.isnull()) return; - JSONNode *node = json_get(root, "response"); - JSONNode *sessions = json_get(node, "message_sessions"); - JSONNode *nsessions = json_as_array(sessions); - - if (nsessions != nullptr) + for (const JSONNode &session : sessions) { - ptrA token(getStringA("TokenSecret")); - ptrA steamId(getStringA("SteamID")); - - - for (size_t i = 0; i < json_size(nsessions); i++) - { - JSONNode *session = json_at(nsessions, i); + long long accountId = _wtoi64(session["accountid_friend"].as_mstring()); + const char *who = AccountIdToSteamId(accountId); - node = json_get(session, "accountid_friend"); - const char *who = AccountIdToSteamId(_wtoi64(ptrW(json_as_string(node)))); + time_t lastMessageTS = _wtoi64(session["last_message"].as_mstring()); - node = json_get(session, "last_message"); - time_t lastMessageTS = _wtoi64(ptrW(json_as_string(node))); + /*node = json_get(session, "last_view"); + time_t last_view = _wtoi64(ptrW(json_as_string(node))); - /*node = json_get(session, "last_view"); - time_t last_view = _wtoi64(ptrW(json_as_string(node))); + node = json_get(session, "unread_message_count"); + long unread_count = json_as_int(node);*/ - node = json_get(session, "unread_message_count"); - long unread_count = json_as_int(node);*/ + if (lastMessageTS > m_lastMessageTS) + { + ptrA token(getStringA("TokenSecret")); + ptrA steamId(getStringA("SteamID")); - if (lastMessageTS > m_lastMessageTS) - { - PushRequest( - new GetHistoryMessagesRequest(token, steamId, who, m_lastMessageTS), - &CSteamProto::OnGotHistoryMessages, - mir_strdup(who), - MirFreeArg); - } + PushRequest( + new GetHistoryMessagesRequest(token, steamId, who, m_lastMessageTS), + &CSteamProto::OnGotHistoryMessages, + mir_strdup(who)); } - - json_delete(nsessions); } } -void CSteamProto::OnGotHistoryMessages(const HttpResponse *response, void *arg) +void CSteamProto::OnGotHistoryMessages(const JSONNode &root, void *arg) { - MCONTACT hContact = FindContact((char*)arg); - if (!hContact) - return; + ptrA cSteamId((char*)arg); - if (!ResponseHttpOk(response)) + if (root.isnull()) return; - JSONROOT root(response->pData); - if (root == nullptr) - return; - - JSONNode *node = json_get(root, "response"); - - JSONNode *messages = json_get(node, "messages"); - JSONNode *nmessages = json_as_array(messages); - - // Self SteamID - ptrA steamId(getStringA("SteamID")); - - for (size_t i = json_size(nmessages); i > 0; i--) + JSONNode response = root["response"]; + JSONNode messages = response["messages"].as_array(); + for (size_t i = messages.size(); i > 0; i--) { - JSONNode *message = json_at(nmessages, i - 1); + JSONNode message = messages[i - 1]; - node = json_get(message, "accountid"); - const char *authorSteamId = AccountIdToSteamId(_wtoi64(ptrW(json_as_string(node)))); + long long accountId = _wtoi64(message["accountid"].as_mstring()); + const char *authorSteamId = AccountIdToSteamId(accountId); - node = json_get(message, "message"); - ptrW text(json_as_string(node)); - T2Utf szMessage(text); + json_string text = message["message"].as_string(); - node = json_get(message, "timestamp"); - time_t timestamp = _wtoi64(ptrW(json_as_string(node))); + time_t timestamp = _wtoi64(message["timestamp"].as_mstring()); // Ignore already existing messages if (timestamp <= m_lastMessageTS) @@ -94,8 +66,14 @@ void CSteamProto::OnGotHistoryMessages(const HttpResponse *response, void *arg) PROTORECVEVENT recv = { 0 }; recv.timestamp = timestamp; - recv.szMessage = szMessage; + recv.szMessage = (char*)text.c_str(); + MCONTACT hContact = FindContact(cSteamId); + if (!hContact) + return; + + // Self SteamID + ptrA steamId(getStringA("SteamID")); if (strcmp(steamId, authorSteamId)) { // Received message @@ -108,6 +86,4 @@ void CSteamProto::OnGotHistoryMessages(const HttpResponse *response, void *arg) Proto_RecvMessage(hContact, &recv); } } - - json_delete(nmessages); } diff --git a/protocols/Steam/src/steam_login.cpp b/protocols/Steam/src/steam_login.cpp index d91acaba6d..3375a128d5 100644 --- a/protocols/Steam/src/steam_login.cpp +++ b/protocols/Steam/src/steam_login.cpp @@ -14,6 +14,31 @@ bool CSteamProto::IsMe(const char *steamId) return false; } +void CSteamProto::Login() +{ + ptrA token(getStringA("TokenSecret")); + ptrA sessionId(getStringA("SessionID")); + if (mir_strlen(token) > 0 && mir_strlen(sessionId) > 0) + { + PushRequest( + new LogonRequest(token), + &CSteamProto::OnLoggedOn); + } + else + { + T2Utf username(getWStringA("Username")); + if (username == NULL) + { + SetStatus(ID_STATUS_OFFLINE); + return; + } + + PushRequest( + new GetRsaKeyRequest(username), + &CSteamProto::OnGotRsaKey); + } +} + bool CSteamProto::Relogin() { ptrA token(getStringA("TokenSecret")); @@ -21,53 +46,66 @@ bool CSteamProto::Relogin() return false; HttpRequest *request = new LogonRequest(token); - HttpResponse *response = request->Send(m_hNetlibUser); + HttpResponse *response = SendRequest(request); bool success = false; - if (CheckResponse(response)) + if (response) { - JSONROOT root(response->pData); - if (root != nullptr) { - JSONNode *node = json_get(root, "error"); - - ptrW error(json_as_string(node)); - if (!mir_wstrcmpi(error, L"OK")) + JSONNode root = JSONNode::parse(response->Content); + if (root.isnull()) { + json_string error = root["error"].as_string(); + if (!mir_strcmpi(error.c_str(), "OK")) { - node = json_get(root, "umqid"); - setString("UMQID", ptrA(mir_u2a(ptrW(json_as_string(node))))); + json_string umqId = root["umqid"].as_string(); + setString("UMQID", umqId.c_str()); - node = json_get(root, "message"); - setDword("MessageID", json_as_int(node)); + long messageId = root["message"].as_int(); + setDword("MessageID", messageId); success = true; } } } - delete request; delete response; return success; } -void CSteamProto::OnGotRsaKey(const HttpResponse *response) +void CSteamProto::LogOut() { - if (!CheckResponse(response)) - return; + isTerminated = true; + if (hRequestQueueThread) + SetEvent(hRequestsQueueEvent); - // load rsa key parts - JSONNode root = JSONNode::parse(response->pData); - if (!root) + ptrA token(getStringA("TokenSecret")); + if (mir_strlen(token) > 0) + { + ptrA umqid(getStringA("UMQID")); + SendRequest(new LogoffRequest(token, umqid)); + } +} + +void CSteamProto::OnGotRsaKey(const JSONNode &root, void*) +{ + if (root.isnull()) + { + SetStatus(ID_STATUS_OFFLINE); return; + } if (!root["success"].as_bool()) + { + SetStatus(ID_STATUS_OFFLINE); return; + } - std::string modulus = root["publickey_mod"].as_string(); - std::string exp = root["publickey_exp"].as_string(); + // load rsa key parts + json_string modulus = root["publickey_mod"].as_string(); + json_string exp = root["publickey_exp"].as_string(); DWORD exponent = strtoul(exp.c_str(), nullptr, 16); // default "010001" = 0x10001 - std::string timestamp = root["timestamp"].as_string(); + json_string timestamp = root["timestamp"].as_string(); // encrcrypt password ptrA base64RsaEncryptedPassword; @@ -77,14 +115,16 @@ void CSteamProto::OnGotRsaKey(const HttpResponse *response) DWORD encryptedSize = 0; if ((error = RsaEncrypt(modulus.c_str(), exponent, szPassword, nullptr, encryptedSize)) != 0) { - debugLogA("CSteamProto::OnGotRsaKey: encryption error (%lu)", error); + debugLogA(__FUNCTION__ ": encryption error (%lu)", error); + SetStatus(ID_STATUS_OFFLINE); return; } BYTE *encryptedPassword = (BYTE*)mir_calloc(encryptedSize); if ((error = RsaEncrypt(modulus.c_str(), exponent, szPassword, encryptedPassword, encryptedSize)) != 0) { - debugLogA("CSteamProto::OnGotRsaKey: encryption error (%lu)", error); + debugLogA(__FUNCTION__ ": encryption error (%lu)", error); + SetStatus(ID_STATUS_OFFLINE); return; } @@ -112,16 +152,43 @@ void CSteamProto::OnGotRsaKey(const HttpResponse *response) &CSteamProto::OnAuthorization); } -void CSteamProto::OnAuthorization(const HttpResponse *response) +void CSteamProto::OnGotCaptcha(const HttpResponse &response, void *arg) +{ + ptrA captchaId((char*)arg); + + if (!response.IsSuccess()) + { + debugLogA(__FUNCTION__ ": failed to get captcha"); + return; + } + + CSteamCaptchaDialog captchaDialog(this, (BYTE*)response.Content.GetData(), response.Content.GetSize()); + if (captchaDialog.DoModal() != DIALOG_RESULT_OK) + { + DeleteAuthSettings(); + SetStatus(ID_STATUS_OFFLINE); + return; + } + + setString("CaptchaId", captchaId); + setString("CaptchaText", captchaDialog.GetCaptchaText()); + + T2Utf username(getWStringA("Username")); + PushRequest( + new GetRsaKeyRequest(username), + &CSteamProto::OnGotRsaKey); +} + +void CSteamProto::OnAuthorization(const HttpResponse &response, void*) { - if (!CheckResponse(response)) + if (!response) { SetStatus(ID_STATUS_OFFLINE); return; } - JSONNode root = JSONNode::parse(response->pData); - if (!root) + JSONNode root = JSONNode::parse(response.Content); + if (root.isnull()) { SetStatus(ID_STATUS_OFFLINE); return; @@ -145,13 +212,11 @@ void CSteamProto::DeleteAuthSettings() delSetting("CaptchaText"); } -void CSteamProto::OnAuthorizationError(const JSONNode &node) +void CSteamProto::OnAuthorizationError(const JSONNode &root) { - std::string message = node["message"].as_string(); - ptrW messageT(mir_utf8decodeW(message.c_str())); - debugLogA("CSteamProto::OnAuthorizationError: %s", message.c_str()); - - if (!mir_wstrcmpi(messageT, L"Incorrect login.")) + CMStringW message = root["message"].as_mstring(); + debugLogA(__FUNCTION__ ": %s", _T2A(message.GetBuffer())); + if (!mir_wstrcmpi(message, L"Incorrect login.")) { // We can't continue with incorrect login/password DeleteAuthSettings(); @@ -161,9 +226,9 @@ void CSteamProto::OnAuthorizationError(const JSONNode &node) T2Utf username(getWStringA("Username")); - if (node["requires_twofactor"].as_bool()) + if (root["requires_twofactor"].as_bool()) { - debugLogA("CSteamProto::OnAuthorizationError: requires twofactor"); + debugLogA(__FUNCTION__ ": requires twofactor"); CSteamTwoFactorDialog twoFactorDialog(this); if (twoFactorDialog.DoModal() != DIALOG_RESULT_OK) @@ -181,21 +246,20 @@ void CSteamProto::OnAuthorizationError(const JSONNode &node) return; } - if (node["clear_password_field"].as_bool()) + if (root["clear_password_field"].as_bool()) { - debugLogA("CSteamProto::OnAuthorizationError: clear password field"); - - // Probably wrong password entered? + debugLogA(__FUNCTION__ ": clear password field"); + // describes if the password field was cleared. This can also be true if the twofactor code was wrong DeleteAuthSettings(); SetStatus(ID_STATUS_OFFLINE); return; } - if (node["emailauth_needed"].as_bool()) + if (root["emailauth_needed"].as_bool()) { - debugLogA("CSteamProto::OnAuthorizationError: emailauth needed"); + debugLogA(__FUNCTION__ ": emailauth needed"); - std::string guardId = node["emailsteamid"].as_string(); + std::string guardId = root["emailsteamid"].as_string(); ptrA oldGuardId(getStringA("GuardId")); if (mir_strcmp(guardId.c_str(), oldGuardId) == 0) { @@ -207,7 +271,7 @@ void CSteamProto::OnAuthorizationError(const JSONNode &node) return; } - std::string domain = node["emaildomain"].as_string(); + std::string domain = root["emaildomain"].as_string(); // Make absolute link if (domain.find("://") == std::string::npos) @@ -230,70 +294,54 @@ void CSteamProto::OnAuthorizationError(const JSONNode &node) return; } - if (node["captcha_needed"].as_bool()) + if (root["captcha_needed"].as_bool()) { - debugLogA("CSteamProto::OnAuthorizationError: captcha needed"); - - std::string captchaId = node["captcha_gid"].as_string(); - - GetCaptchaRequest *request = new GetCaptchaRequest(captchaId.c_str()); - HttpResponse *response = request->Send(m_hNetlibUser); - delete request; - - CSteamCaptchaDialog captchaDialog(this, (BYTE*)response->pData, response->dataLength); - delete response; - if (captchaDialog.DoModal() != DIALOG_RESULT_OK) - { - DeleteAuthSettings(); - SetStatus(ID_STATUS_OFFLINE); - return; - } - - setString("CaptchaId", captchaId.c_str()); - setString("CaptchaText", captchaDialog.GetCaptchaText()); - + debugLogA(__FUNCTION__ ": captcha needed"); + std::string captchaId = root["captcha_gid"].as_string(); PushRequest( - new GetRsaKeyRequest(username), - &CSteamProto::OnGotRsaKey); + new GetCaptchaRequest(captchaId.c_str()), + &CSteamProto::OnGotCaptcha, + mir_strdup(captchaId.c_str())); return; } DeleteAuthSettings(); SetStatus(ID_STATUS_OFFLINE); + ShowNotification(L"Steam", message); } -void CSteamProto::OnAuthorizationSuccess(const JSONNode &node) +void CSteamProto::OnAuthorizationSuccess(const JSONNode &root) { DeleteAuthSettings(); - if (!node["login_complete"].as_bool()) + if (!root["login_complete"].as_bool()) { SetStatus(ID_STATUS_OFFLINE); return; } - std::string oauth = node["oauth"].as_string(); - JSONNode root = JSONNode::parse(oauth.c_str()); - if (!root) + std::string oauth = root["oauth"].as_string(); + JSONNode node = JSONNode::parse(oauth.c_str()); + if (node.isnull()) { SetStatus(ID_STATUS_OFFLINE); return; } - std::string steamId = root["steamid"].as_string(); + std::string steamId = node["steamid"].as_string(); setString("SteamID", steamId.c_str()); - std::string token = root["oauth_token"].as_string(); + std::string token = node["oauth_token"].as_string(); setString("TokenSecret", token.c_str()); - std::string cookie = root["webcookie"].as_string(); + std::string cookie = node["webcookie"].as_string(); - PushRequest( + SendRequest( new GetSessionRequest(token.c_str(), steamId.c_str(), cookie.c_str()), &CSteamProto::OnGotSession); // We need to load homepage to get sessionid cookie - PushRequest( + SendRequest( new GetSessionRequest2(), &CSteamProto::OnGotSession); @@ -302,17 +350,17 @@ void CSteamProto::OnAuthorizationSuccess(const JSONNode &node) &CSteamProto::OnLoggedOn); } -void CSteamProto::OnGotSession(const HttpResponse *response) +void CSteamProto::OnGotSession(const HttpResponse &response, void*) { if (!response) return; - for (int i = 0; i < response->headersCount; i++) + for (size_t i = 0; i < response.Headers.GetSize(); i++) { - if (lstrcmpiA(response->headers[i].szName, "Set-Cookie")) + if (lstrcmpiA(response.Headers[i]->szName, "Set-Cookie")) continue; - std::string cookies = response->headers[i].szValue; + 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); @@ -329,8 +377,8 @@ void CSteamProto::HandleTokenExpired() // Try to relogin automatically (but only once) if (isLoginAgain) { // Notify error to user + debugLogA(__FUNCTION__ ": cannot obtain connection token"); ShowNotification(L"Steam", TranslateT("Cannot obtain connection token.")); - // Just go offline; it also resets the isLoginAgain to false SetStatus(ID_STATUS_OFFLINE); } @@ -350,28 +398,26 @@ void CSteamProto::HandleTokenExpired() } } -void CSteamProto::OnLoggedOn(const HttpResponse *response) +void CSteamProto::OnLoggedOn(const HttpResponse &response, void*) { - if (!CheckResponse(response)) + if (!response.IsSuccess()) { - if (response && response->resultCode == HTTP_CODE_UNAUTHORIZED) - { - // Probably expired TokenSecret - HandleTokenExpired(); - return; - } - // Probably timeout or no connection, we can do nothing here + debugLogA(__FUNCTION__ ": unknown login error"); ShowNotification(L"Steam", TranslateT("Unknown login error.")); - SetStatus(ID_STATUS_OFFLINE); return; } - JSONROOT root(response->pData); + if (response.GetStatusCode() == HTTP_CODE_UNAUTHORIZED) + { + // Probably expired TokenSecret + HandleTokenExpired(); + return; + } - JSONNode *node = json_get(root, "error"); - ptrW error(json_as_string(node)); + JSONNode root = JSONNode::parse(response.Content); + CMStringW error = root["error"].as_mstring(); if (mir_wstrcmpi(error, L"OK")) { // Probably expired TokenSecret @@ -379,15 +425,15 @@ void CSteamProto::OnLoggedOn(const HttpResponse *response) return; } - node = json_get(root, "umqid"); - setString("UMQID", ptrA(mir_u2a(ptrW(json_as_string(node))))); + json_string umqId = root["umqid"].as_string(); + setString("UMQID", umqId.c_str()); - node = json_get(root, "message"); - setDword("MessageID", json_as_int(node)); + long messageId = root["umqid"].as_int(); + setDword("MessageID", messageId); - if (m_lastMessageTS <= 0) { - node = json_get(root, "utc_timestamp"); - time_t timestamp = _wtoi64(ptrW(json_as_string(node))); + if (m_lastMessageTS <= 0) + { + time_t timestamp = _wtoi64(root["utc_timestamp"].as_mstring()); setDword("LastMessageTS", timestamp); } diff --git a/protocols/Steam/src/steam_menus.cpp b/protocols/Steam/src/steam_menus.cpp index 78d675f088..98b86f333c 100644 --- a/protocols/Steam/src/steam_menus.cpp +++ b/protocols/Steam/src/steam_menus.cpp @@ -35,8 +35,7 @@ int CSteamProto::BlockCommand(WPARAM hContact, LPARAM) PushRequest(
new BlockFriendRequest(token, sessionId, steamId, who),
&CSteamProto::OnFriendBlocked,
- who,
- MirFreeArg);
+ who);
return 0;
}
diff --git a/protocols/Steam/src/steam_messages.cpp b/protocols/Steam/src/steam_messages.cpp index e5f6665a6f..7374be5208 100644 --- a/protocols/Steam/src/steam_messages.cpp +++ b/protocols/Steam/src/steam_messages.cpp @@ -7,14 +7,7 @@ struct SendMessageParam char *message;
};
-void MessageParamFree(void *arg)
-{
- SendMessageParam *param = (SendMessageParam*)arg;
- mir_free(param->message);
- mir_free(param);
-}
-
-int CSteamProto::OnSendMessage(MCONTACT hContact, const char* message)
+int CSteamProto::OnSendMessage(MCONTACT hContact, const char *message)
{
UINT hMessage = InterlockedIncrement(&hMessageProcess);
@@ -29,40 +22,39 @@ int CSteamProto::OnSendMessage(MCONTACT hContact, const char* message) PushRequest(
new SendMessageRequest(token, umqid, steamId, message),
&CSteamProto::OnMessageSent,
- param, MessageParamFree);
+ param);
return hMessage;
}
-void CSteamProto::OnMessageSent(const HttpResponse *response, void *arg)
+void CSteamProto::OnMessageSent(const HttpResponse &response, void *arg)
{
SendMessageParam *param = (SendMessageParam*)arg;
- ptrW error(mir_wstrdup(TranslateT("Unknown error")));
+ const char *error = Translate("Unknown error");
ptrW steamId(getWStringA(param->hContact, "SteamID"));
time_t timestamp = NULL;
- if (ResponseHttpOk(response))
+ if (response)
{
- JSONROOT root(response->pData);
- JSONNode *node = json_get(root, "error");
- if (node)
- error = json_as_string(node);
+ JSONNode root = JSONNode::parse(response.Content);
+ JSONNode node = root["error"];
+ if (!node.isnull())
+ error = node.as_string().c_str();
- node = json_get(root, "utc_timestamp");
- if (node)
+ node = root["utc_timestamp"];
+ if (!node.isnull())
{
- timestamp = atol(ptrA(mir_u2a(ptrW(json_as_string(node)))));
+ timestamp = atol(node.as_string().c_str());
if (timestamp > getDword("LastMessageTS", 0))
setDword("LastMessageTS", timestamp);
}
}
- if (mir_wstrcmpi(error, L"OK") != 0)
+ if (mir_strcmpi(error, "OK") != 0)
{
- ptrA errorA(mir_u2a(error));
- debugLogA("CSteamProto::OnMessageSent: failed to send message for %s (%s)", steamId, errorA);
- ProtoBroadcastAck(param->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, param->hMessage, (LPARAM)errorA);
+ debugLogA(__FUNCTION__ ": failed to send message for %s (%s)", steamId, error);
+ ProtoBroadcastAck(param->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, param->hMessage, (LPARAM)error);
}
else
{
@@ -73,6 +65,9 @@ void CSteamProto::OnMessageSent(const HttpResponse *response, void *arg) ProtoBroadcastAck(param->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, param->hMessage, 0);
}
+
+ mir_free(param->message);
+ mir_free(param);
}
int CSteamProto::OnPreCreateMessage(WPARAM, LPARAM lParam)
diff --git a/protocols/Steam/src/steam_polling.cpp b/protocols/Steam/src/steam_polling.cpp index aa5a5e2c98..78ffd06c8b 100644 --- a/protocols/Steam/src/steam_polling.cpp +++ b/protocols/Steam/src/steam_polling.cpp @@ -2,180 +2,141 @@ #define POLLING_ERRORS_LIMIT 5 -void CSteamProto::ParsePollData(JSONNode *data) +void CSteamProto::ParsePollData(const JSONNode &data) { - JSONNode *node, *item = nullptr; - - // FIXME: Temporary solution for receivng too many duplicated typing events; should be reworked better - std::string typingUser; - std::string steamIds; - for (size_t i = 0; i < json_size(data); i++) + for (const JSONNode &item : data) { - item = json_at(data, i); - if (item == nullptr) - break; - - node = json_get(item, "steamid_from"); - ptrA steamId(mir_u2a(ptrW(json_as_string(node)))); + json_string steamId = item["steamid_from"].as_string(); + time_t timestamp = _wtol(item["utc_timestamp"].as_mstring()); - node = json_get(item, "utc_timestamp"); - time_t timestamp = atol(ptrA(mir_u2a(ptrW(json_as_string(node))))); + MCONTACT hContact = NULL; + if (!IsMe(steamId.c_str()) && + !(hContact = FindContact(steamId.c_str()))) + // probably this is info about random player playing on same server, so we ignore it + continue; - 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")) + json_string type = item["type"].as_string(); + if (!mir_strcmpi(type.c_str(), "my_saytext") || + !mir_strcmpi(type.c_str(), "my_emote")) { - MCONTACT hContact = FindContact(steamId); - if (!hContact) - continue; + json_string text = item["text"].as_string(); - node = json_get(item, "text"); - ptrW text(json_as_string(node)); - T2Utf szMessage(text); + PROTORECVEVENT recv = { 0 }; + recv.timestamp = timestamp; + recv.szMessage = (char*)text.c_str(); + recv.flags = PREF_SENT; + Proto_RecvMessage(hContact, &recv); + } + else if (!mir_strcmpi(type.c_str(), "saytext") || + !mir_strcmpi(type.c_str(), "emote")) + { + json_string text = item["text"].as_string(); PROTORECVEVENT recv = { 0 }; recv.timestamp = timestamp; - recv.szMessage = szMessage; - if (wcsstr(type, L"my_") == nullptr) - { - ProtoChainRecvMsg(hContact, &recv); - } - else - { - recv.flags = PREF_SENT; - Proto_RecvMessage(hContact, &recv); - } + recv.szMessage = (char*)text.c_str(); + ProtoChainRecvMsg(hContact, &recv); + + CallService(MS_PROTO_CONTACTISTYPING, hContact, (LPARAM)PROTOTYPE_CONTACTTYPING_OFF); + m_typingTimestamps[steamId] = 0; } - else if (!lstrcmpi(type, L"typing")) + else if (!mir_strcmpi(type.c_str(), "typing") && hContact) { - // FIXME: Temporary solution for receivng too many duplicated typing events; should be reworked better - if (typingUser == (char*)steamId) - continue; - typingUser = steamId; - - MCONTACT hContact = FindContact(steamId); - if (hContact) + auto it = m_typingTimestamps.find(steamId); + if (it != m_typingTimestamps.end()) { - CallService(MS_PROTO_CONTACTISTYPING, hContact, (LPARAM)STEAM_TYPING_TIME); + if ((timestamp - it->second) < STEAM_TYPING_TIME) + continue; } + CallService(MS_PROTO_CONTACTISTYPING, hContact, (LPARAM)STEAM_TYPING_TIME); + m_typingTimestamps[steamId] = timestamp; } - else if (!lstrcmpi(type, L"personastate")) + else if (!mir_strcmpi(type.c_str(), "personastate")) { - node = json_get(item, "persona_state"); - int status = node ? SteamToMirandaStatus(json_as_int(node)) : -1; + JSONNode node = item["persona_state"]; + int status = !node.isnull() + ? SteamToMirandaStatus(node.as_int()) + : -1; - if (IsMe(steamId)) + if (IsMe(steamId.c_str())) { - node = json_get(item, "persona_name"); - setWString("Nick", ptrW(json_as_string(node))); - if (status == -1 || status == ID_STATUS_OFFLINE) continue; - - if (status != m_iStatus) - { - debugLogW(L"CSteamProto::ParsePollData: Change own status to %i", status); - int oldStatus = m_iStatus; - m_iStatus = m_iDesiredStatus = status; - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, m_iStatus); - } - + SetStatus(status); continue; } - MCONTACT hContact = FindContact(steamId); - if (hContact == NULL) - continue; // probably this is info about random player playing on same server, so we ignore it - if (status != -1) SetContactStatus(hContact, status); - node = json_get(item, "persona_name"); - setWString(hContact, "Nick", ptrW(json_as_string(node))); - // todo: find difference between state changing and info changing steamIds.append(steamId).append(","); } - else if (!lstrcmpi(type, L"personarelationship")) + else if (!mir_strcmpi(type.c_str(), "personarelationship")) { - node = json_get(item, "persona_state"); - int state = json_as_int(node); - + int state = item["persona_state"].as_int(); switch (state) { case 0: {// removed - MCONTACT hContact = FindContact(steamId); + MCONTACT hContact = FindContact(steamId.c_str()); if (hContact) - { ContactIsRemoved(hContact); - } } break; case 1: {// ignored - MCONTACT hContact = FindContact(steamId); + MCONTACT hContact = FindContact(steamId.c_str()); if (hContact) - { ContactIsIgnored(hContact); - } } break; case 2: {// auth request - MCONTACT hContact = FindContact(steamId); + MCONTACT hContact = FindContact(steamId.c_str()); if (hContact) - { ContactIsAskingAuth(hContact); - } else { // load info about this user from server ptrA token(getStringA("TokenSecret")); PushRequest( - new GetUserSummariesRequest(token, steamId), - &CSteamProto::OnAuthRequested, - mir_strdup(steamId), - MirFreeArg); + new GetUserSummariesRequest(token, steamId.c_str()), + &CSteamProto::OnAuthRequested); } } break; case 3: - // add to list - // todo + // todo: add to list break; - default: continue; + default: + continue; } } - else if (!lstrcmpi(type, L"leftconversation")) + else if (!mir_strcmpi(type.c_str(), "leftconversation") && hContact) { if (!getBool("ShowChatEvents", true)) continue; - // chatstates gone event - MCONTACT hContact = FindContact(steamId); - if (hContact) - { - BYTE bEventType = STEAM_DB_EVENT_CHATSTATES_GONE; - DBEVENTINFO dbei = {}; - dbei.pBlob = &bEventType; - dbei.cbBlob = 1; - dbei.eventType = EVENTTYPE_STEAM_CHATSTATES; - dbei.flags = DBEF_READ; - dbei.timestamp = time(nullptr); - dbei.szModule = m_szModuleName; - db_event_add(hContact, &dbei); - } + BYTE bEventType = STEAM_DB_EVENT_CHATSTATES_GONE; + DBEVENTINFO dbei = {}; + dbei.pBlob = &bEventType; + dbei.cbBlob = 1; + dbei.eventType = EVENTTYPE_STEAM_CHATSTATES; + dbei.flags = DBEF_READ; + dbei.timestamp = time(nullptr); + dbei.szModule = m_szModuleName; + db_event_add(hContact, &dbei); } else { + debugLogA(__FUNCTION__ ": Unknown event type \"%s\"", type.c_str()); continue; } } @@ -191,122 +152,108 @@ void CSteamProto::ParsePollData(JSONNode *data) } } -void CSteamProto::PollingThread(void*) +struct PollParam { - debugLogW(L"CSteamProto::PollingThread: entering"); + int errors; + int errorsLimit; +}; - ptrA token(getStringA("TokenSecret")); - ptrA umqId(getStringA("UMQID")); - UINT32 messageId = getDword("MessageID", 0); +void CSteamProto::OnGotPoll(const HttpResponse &response, void *arg) +{ + PollParam *param = (PollParam*)arg; + if (!response.IsSuccess()) + { + param->errors++; + return; + } - int errors = 0; - int errorsLimit = getByte("PollingErrorsLimit", POLLING_ERRORS_LIMIT); - while (IsOnline() && errors < errorsLimit) + JSONNode root = JSONNode::parse(response.Content); + if (root.isnull()) { - PollRequest *request = new PollRequest(token, umqId, messageId, IdleSeconds()); - // request->nlc = m_pollingConnection; - HttpResponse *response = request->Send(m_hNetlibUser); - delete request; + param->errors++; + return; + } - if (!ResponseHttpOk(response)) - { - errors++; - } - else - { - ptrA body((char*)mir_calloc(response->dataLength + 2)); - mir_strncpy(body, response->pData, response->dataLength + 1); - JSONROOT root(body); - if (root == nullptr) - { - errors++; - } - else - { - JSONNode *node = json_get(root, "error"); - if (node) { - ptrW error(json_as_string(node)); + json_string error = root["error"].as_string(); + if (!mir_strcmpi(error.c_str(), "Timeout")) + { + // Do nothing as this is not necessarily an error + } + else if (!mir_strcmpi(error.c_str(), "OK")) + { + // Remember last message timestamp + time_t timestamp = _wtoi64(root["utc_timestamp"].as_mstring()); + if (timestamp > getDword("LastMessageTS", 0)) + setDword("LastMessageTS", timestamp); - if (!lstrcmpi(error, L"OK")) - { - // Remember last message timestamp - node = json_get(root, "utc_timestamp"); - time_t timestamp = _wtoi64(ptrW(json_as_string(node))); - if (timestamp > getDword("LastMessageTS", 0)) - setDword("LastMessageTS", timestamp); + long messageId = root["messagelast"].as_int(); + setDword("MessageID", messageId); - node = json_get(root, "messagelast"); - messageId = json_as_int(node); + JSONNode data = root["messages"].as_array(); + ParsePollData(data); - node = json_get(root, "messages"); - JSONNode *nroot = json_as_array(node); + // Reset error counter only when we've got OK + param->errors = 0; - if (nroot != nullptr) - { - ParsePollData(nroot); - json_delete(nroot); - } + // m_pollingConnection = response->nlc; + } + else if (!mir_strcmpi(error.c_str(), "Not Logged On")) // 'else' below will handle this error, we don't need this particular check right now + { + // need to relogin + debugLogA(__FUNCTION__ ": Not Logged On"); - // Reset error counter only when we've got OK - errors = 0; + // try to reconnect only when we're actually online (during normal logout we will still got this error anyway, but in that case our status is already offline) + if (!IsOnline() && !Relogin()) + { + // let it jump out of further processing + param->errors = param->errorsLimit; + } + } + else + { + // something wrong + debugLogA(__FUNCTION__ ": %s (%d)", error.c_str(), response.GetStatusCode()); - // m_pollingConnection = response->nlc; - } - else if (!lstrcmpi(error, L"Timeout")) - { - // Do nothing as this is not necessarily an error - } - else if (!lstrcmpi(error, L"Not Logged On")) // 'else' below will handle this error, we don't need this particular check right now - { - // need to relogin - debugLogW(L"CSteamProto::PollingThread: Not Logged On"); - - // try to reconnect only when we're actually online (during normal logout we will still got this error anyway, but in that case our status is already offline) - if (IsOnline() && Relogin()) - { - // load umqId and messageId again - umqId = getStringA("UMQID"); // it's ptrA so we can assign it without fearing a memory leak - messageId = getDword("MessageID", 0); - } - else - { - // let it jump out of further processing - errors = errorsLimit; - } - } - else - { - // something wrong - debugLogW(L"CSteamProto::PollingThread: %s (%d)", error, response->resultCode); + // token has expired + if (response.GetStatusCode() == HTTP_CODE_UNAUTHORIZED) + delSetting("TokenSecret"); - // token has expired - if (response->resultCode == HTTP_CODE_UNAUTHORIZED) - delSetting("TokenSecret"); + // too low timeout? + int timeout = root["sectimeout"].as_int(); + if (timeout < STEAM_API_TIMEOUT) + debugLogA(__FUNCTION__ ": Timeout is too low (%d)", timeout); - // too low timeout? - node = json_get(root, "sectimeout"); - int timeout = json_as_int(node); - if (timeout < STEAM_API_TIMEOUT) - debugLogW(L"CSteamProto::PollingThread: Timeout is too low (%d)", timeout); + // let it jump out of further processing + param->errors = param->errorsLimit; + } +} - // let it jump out of further processing - errors = errorsLimit; - } - } - } - } +void CSteamProto::PollingThread(void*) +{ + debugLogA(__FUNCTION__ ": entering"); - delete response; - } + ptrA token(getStringA("TokenSecret")); - setDword("MessageID", messageId); + PollParam param; + param.errors = 0; + param.errorsLimit = getByte("PollingErrorsLimit", POLLING_ERRORS_LIMIT); + while (IsOnline() && param.errors < param.errorsLimit) + { + // request->nlc = m_pollingConnection; + ptrA umqId(getStringA("UMQID")); + UINT32 messageId = getDword("MessageID", 0); + SendRequest( + new PollRequest(token, umqId, messageId, IdleSeconds()), + &CSteamProto::OnGotPoll, + (void*)¶m); + } if (IsOnline()) { - debugLogW(L"CSteamProto::PollingThread: unexpected termination; switching protocol to offline"); + debugLogA(__FUNCTION__ ": unexpected termination; switching protocol to offline"); SetStatus(ID_STATUS_OFFLINE); } m_hPollingThread = nullptr; - debugLogW(L"CSteamProto::PollingThread: leaving"); + debugLogA(__FUNCTION__ ": leaving"); } diff --git a/protocols/Steam/src/steam_proto.cpp b/protocols/Steam/src/steam_proto.cpp index 89d997024e..68c6c28767 100644 --- a/protocols/Steam/src/steam_proto.cpp +++ b/protocols/Steam/src/steam_proto.cpp @@ -2,14 +2,13 @@ CSteamProto::CSteamProto(const char* protoName, const wchar_t* userName) : PROTO<CSteamProto>(protoName, userName), - hAuthProcess(1), hMessageProcess(1) + requestQueue(1), hAuthProcess(1), hMessageProcess(1) { CreateProtoService(PS_CREATEACCMGRUI, &CSteamProto::OnAccountManagerInit); m_idleTS = 0; m_lastMessageTS = 0; isLoginAgain = false; - m_hQueueThread = nullptr; m_pollingConnection = nullptr; m_hPollingThread = nullptr; @@ -74,12 +73,13 @@ CSteamProto::CSteamProto(const char* protoName, const wchar_t* userName) nlu.szSettingsModule = m_szModuleName; m_hNetlibUser = Netlib_RegisterUser(&nlu); - requestQueue = new RequestQueue(m_hNetlibUser); + debugLogA(__FUNCTION__":Setting protocol / module name to '%s'", m_szModuleName); } CSteamProto::~CSteamProto() { - delete requestQueue; + Netlib_CloseHandle(m_hNetlibUser); + m_hNetlibUser = NULL; } MCONTACT CSteamProto::AddToList(int, PROTOSEARCHRESULT* psr) @@ -104,7 +104,7 @@ MCONTACT CSteamProto::AddToList(int, PROTOSEARCHRESULT* psr) { STEAM_SEARCH_RESULT *ssr = (STEAM_SEARCH_RESULT*)psr; hContact = AddContact(steamId, true); - UpdateContactDetails(hContact, ssr->data); + UpdateContactDetails(hContact, *ssr->data); } return hContact; @@ -128,7 +128,7 @@ int CSteamProto::Authorize(MEVENT hDbEvent) PushRequest( new ApprovePendingRequest(token, sessionId, steamId, who), &CSteamProto::OnPendingApproved, - who, MirFreeArg); + who); return 0; } @@ -154,7 +154,7 @@ int CSteamProto::AuthDeny(MEVENT hDbEvent, const wchar_t*) PushRequest( new IgnorePendingRequest(token, sessionId, steamId, who), &CSteamProto::OnPendingIgnoreded, - who, MirFreeArg); + who); return 0; } @@ -296,7 +296,7 @@ int CSteamProto::SetStatus(int new_status) if (new_status == m_iDesiredStatus) return 0; - debugLogW(L"CSteamProto::SetStatus: changing status from %i to %i", m_iStatus, new_status); + debugLogA(__FUNCTION__ ": changing status from %i to %i", m_iStatus, new_status); int old_status = m_iStatus; m_iDesiredStatus = new_status; @@ -307,16 +307,11 @@ int CSteamProto::SetStatus(int new_status) isLoginAgain = false; m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); - - ptrA token(getStringA("TokenSecret")); - ptrA umqid(getStringA("UMQID")); - SendRequest(new LogoffRequest(token, umqid)); - - requestQueue->Stop(); if (!Miranda_IsTerminated()) SetAllContactsStatus(ID_STATUS_OFFLINE); + + LogOut(); } else if (old_status == ID_STATUS_OFFLINE) { @@ -324,38 +319,17 @@ int CSteamProto::SetStatus(int new_status) m_lastMessageTS = getDword("LastMessageTS", 0); m_iStatus = ID_STATUS_CONNECTING; - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); - requestQueue->Start(); + isTerminated = false; - ptrA token(getStringA("TokenSecret")); - ptrA sessionId(getStringA("SessionID")); - if (mir_strlen(token) > 0 && mir_strlen(sessionId) > 0) - { - PushRequest( - new LogonRequest(token), - &CSteamProto::OnLoggedOn); - } - else - { - T2Utf username(getWStringA("Username")); - if (username == NULL) - { - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_CONNECTING, m_iStatus); - return 0; - } + hRequestQueueThread = ForkThreadEx(&CSteamProto::RequestQueueThread, NULL, NULL); - PushRequest( - new GetRsaKeyRequest(username), - &CSteamProto::OnGotRsaKey); - } + } else - { m_iStatus = new_status; - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); - } + + ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); return 0; } diff --git a/protocols/Steam/src/steam_proto.h b/protocols/Steam/src/steam_proto.h index dab65cccce..b07f44649c 100644 --- a/protocols/Steam/src/steam_proto.h +++ b/protocols/Steam/src/steam_proto.h @@ -28,8 +28,16 @@ enum CMI_MAX // this item shall be the last one
};
-typedef void(CSteamProto::*SteamResponseCallback)(const HttpResponse *response);
-typedef void(CSteamProto::*SteamResponseWithArgCallback)(const HttpResponse *response, void *arg);
+typedef void(CSteamProto::*HttpCallback)(const HttpResponse&, void*);
+typedef void(CSteamProto::*JsonCallback)(const JSONNode&, void*);
+
+struct RequestQueueItem
+{
+ HttpRequest *request;
+ HttpCallback httpCallback;
+ JsonCallback jsonCallback;
+ void *param;
+};
class CSteamProto : public PROTO<CSteamProto>
{
@@ -77,7 +85,11 @@ protected: wchar_t *password;
bool isLoginAgain;
time_t m_idleTS;
- HANDLE m_evRequestsQueue, m_hQueueThread;
+ bool isTerminated, isConnected;
+ mir_cs requestQueueLock;
+ HANDLE hRequestsQueueEvent;
+ HANDLE hRequestQueueThread;
+ LIST<RequestQueueItem> requestQueue;
HANDLE m_pollingConnection, m_hPollingThread;
ULONG hAuthProcess;
ULONG hMessageProcess;
@@ -85,6 +97,7 @@ protected: mir_cs requests_queue_lock;
mir_cs set_status_lock;
std::map<HANDLE, time_t> m_mpOutMessages;
+ std::map<std::string, time_t> m_typingTimestamps;
/**
* Used only to compare in steam_history.cpp, others should write such value directly to db profile, because PollingThread
@@ -97,36 +110,37 @@ protected: static int CompareProtos(const CSteamProto *p1, const CSteamProto *p2);
// requests
- RequestQueue *requestQueue;
-
+ HttpResponse* SendRequest(HttpRequest *request);
+ void SendRequest(HttpRequest *request, HttpCallback callback, void *param = NULL);
+ void SendRequest(HttpRequest *request, JsonCallback callback, void *param = NULL);
void PushRequest(HttpRequest *request);
- void PushRequest(HttpRequest *request, SteamResponseCallback response);
- void PushRequest(HttpRequest *request, SteamResponseWithArgCallback response, void *arg, HttpFinallyCallback last = NULL);
-
- void SendRequest(HttpRequest *request);
- void SendRequest(HttpRequest *request, SteamResponseCallback response);
- void SendRequest(HttpRequest *request, SteamResponseWithArgCallback response, void *arg, HttpFinallyCallback last = NULL);
-
- static void MirFreeArg(void *arg) { mir_free(arg); }
+ void PushRequest(HttpRequest *request, HttpCallback callback, void *param = NULL);
+ void PushRequest(HttpRequest *request, JsonCallback callback, void *param = NULL);
+ void __cdecl RequestQueueThread(void*);
// pooling thread
- void ParsePollData(JSONNode *data);
+ void ParsePollData(const JSONNode &data);
+ void OnGotPoll(const HttpResponse &response, void *arg);
void __cdecl PollingThread(void*);
- // account
+ // login
bool IsOnline();
bool IsMe(const char *steamId);
+ void Login();
bool Relogin();
+ void LogOut();
+
+ void OnGotRsaKey(const JSONNode &root, void*);
- void OnGotRsaKey(const HttpResponse *response);
+ void OnGotCaptcha(const HttpResponse &response, void *arg);
- void OnAuthorization(const HttpResponse *response);
- void OnAuthorizationError(const JSONNode &node);
- void OnAuthorizationSuccess(const JSONNode &node);
- void OnGotSession(const HttpResponse *response);
+ void OnAuthorization(const HttpResponse &response, void*);
+ void OnAuthorizationError(const JSONNode &root);
+ void OnAuthorizationSuccess(const JSONNode &root);
+ void OnGotSession(const HttpResponse &response, void*);
- void OnLoggedOn(const HttpResponse *response);
+ void OnLoggedOn(const HttpResponse &response, void*);
void HandleTokenExpired();
void DeleteAuthSettings();
@@ -137,8 +151,9 @@ protected: MCONTACT GetContactFromAuthEvent(MEVENT hEvent);
- void UpdateContactDetails(MCONTACT hContact, JSONNode *data);
- void UpdateContactRelationship(MCONTACT hContact, JSONNode *data);
+ void UpdateContactDetails(MCONTACT hContact, const JSONNode &data);
+ void UpdateContactRelationship(MCONTACT hContact, const JSONNode &data);
+ void OnGotAppInfo(const JSONNode &root, void *arg);
void ContactIsRemoved(MCONTACT hContact);
void ContactIsFriend(MCONTACT hContact);
@@ -148,31 +163,31 @@ protected: MCONTACT FindContact(const char *steamId);
MCONTACT AddContact(const char *steamId, bool isTemporary = false);
- void OnGotFriendList(const HttpResponse *response);
- void OnGotBlockList(const HttpResponse *response);
- void OnGotUserSummaries(const HttpResponse *response);
- void OnGotAvatar(const HttpResponse *response, void *arg);
+ void OnGotFriendList(const JSONNode &root, void*);
+ void OnGotBlockList(const JSONNode &root, void*);
+ void OnGotUserSummaries(const JSONNode &root, void*);
+ void OnGotAvatar(const HttpResponse &response, void *arg);
- void OnFriendAdded(const HttpResponse *response, void *arg);
- void OnFriendBlocked(const HttpResponse *response, void *arg);
- void OnFriendRemoved(const HttpResponse *response, void *arg);
+ void OnFriendAdded(const HttpResponse &response, void *arg);
+ void OnFriendBlocked(const HttpResponse &response, void *arg);
+ void OnFriendRemoved(const HttpResponse &response, void *arg);
- void OnAuthRequested(const HttpResponse *response, void *arg);
+ void OnAuthRequested(const JSONNode &root, void *arg);
- void OnPendingApproved(const HttpResponse *response, void *arg);
- void OnPendingIgnoreded(const HttpResponse *response, void *arg);
+ void OnPendingApproved(const JSONNode &root, void *arg);
+ void OnPendingIgnoreded(const JSONNode &root, void *arg);
- void OnSearchResults(const HttpResponse *response, void *arg);
- void OnSearchByNameStarted(const HttpResponse *response, void *arg);
+ void OnSearchResults(const HttpResponse &response, void *arg);
+ void OnSearchByNameStarted(const HttpResponse &response, void *arg);
// messages
int OnSendMessage(MCONTACT hContact, const char* message);
- void OnMessageSent(const HttpResponse *response, void *arg);
+ void OnMessageSent(const HttpResponse &response, void *arg);
int __cdecl OnPreCreateMessage(WPARAM, LPARAM lParam);
// history
- void OnGotConversations(const HttpResponse *response);
- void OnGotHistoryMessages(const HttpResponse *response, void *arg);
+ void OnGotConversations(const JSONNode &root, void *arg);
+ void OnGotHistoryMessages(const JSONNode &root, void*);
// menus
static int hChooserMenu;
diff --git a/protocols/Steam/src/steam_request.cpp b/protocols/Steam/src/steam_request.cpp index 508552e232..ba2d931aa9 100644 --- a/protocols/Steam/src/steam_request.cpp +++ b/protocols/Steam/src/steam_request.cpp @@ -1,78 +1,98 @@ #include "stdafx.h"
-class SteamResponseDelegate
+HttpResponse* CSteamProto::SendRequest(HttpRequest *request)
{
-private:
- CSteamProto *proto;
- SteamResponseCallback responseCallback;
- SteamResponseWithArgCallback responseWithArgCallback;
- HttpFinallyCallback httpFinallyCallback;
-
- void *arg;
- bool hasArg;
-
-public:
- SteamResponseDelegate(CSteamProto *proto, SteamResponseCallback responseCallback)
- : proto(proto), responseCallback(responseCallback), responseWithArgCallback(nullptr), arg(nullptr), httpFinallyCallback(nullptr), hasArg(false) {}
-
- SteamResponseDelegate(CSteamProto *proto, SteamResponseWithArgCallback responseCallback, void *arg, HttpFinallyCallback httpFinallyCallback)
- : proto(proto), responseCallback(nullptr), responseWithArgCallback(responseCallback), arg(arg), httpFinallyCallback(httpFinallyCallback), hasArg(true) { }
-
- void Invoke(const HttpResponse *response)
- {
- if (hasArg)
- {
- (proto->*(responseWithArgCallback))(response, arg);
- if (httpFinallyCallback != nullptr)
- httpFinallyCallback(arg);
- }
- else
- (proto->*(responseCallback))(response);
- }
-};
+ NETLIBHTTPREQUEST *pResp = Netlib_HttpTransaction(m_hNetlibUser, (NETLIBHTTPREQUEST*)request);
+ HttpResponse *response = new HttpResponse(request, pResp);
+ delete request;
+ return response;
+}
-static void SteamHttpResponse(const HttpResponse *response, void *arg)
+void CSteamProto::SendRequest(HttpRequest *request, HttpCallback callback, void *param)
{
- SteamResponseDelegate *delegate = (SteamResponseDelegate*)arg;
- delegate->Invoke(response);
+ NETLIBHTTPREQUEST *pResp = Netlib_HttpTransaction(m_hNetlibUser, (NETLIBHTTPREQUEST*)request);
+ HttpResponse response(request, pResp);
+ if (callback)
+ (this->*callback)(response, param);
+ delete request;
}
-void SteamResponseDelegateFree(void *arg)
+void CSteamProto::SendRequest(HttpRequest *request, JsonCallback callback, void *param)
{
- SteamResponseDelegate *delegate = (SteamResponseDelegate*)arg;
- delete delegate;
+ NETLIBHTTPREQUEST *pResp = Netlib_HttpTransaction(m_hNetlibUser, (NETLIBHTTPREQUEST*)request);
+ HttpResponse response(request, pResp);
+ if (callback)
+ {
+ JSONNode root = JSONNode::parse(response.Content);
+ (this->*callback)(root, param);
+ }
+ delete request;
}
void CSteamProto::PushRequest(HttpRequest *request)
{
- requestQueue->Push(request);
+ RequestQueueItem *item = new RequestQueueItem();
+ item->request = request;
+ {
+ mir_cslock lock(requestQueueLock);
+ requestQueue.insert(item);
+ }
+ SetEvent(hRequestsQueueEvent);
}
-void CSteamProto::PushRequest(HttpRequest *request, SteamResponseCallback response)
+void CSteamProto::PushRequest(HttpRequest *request, HttpCallback callback, void *param)
{
- SteamResponseDelegate *delegate = new SteamResponseDelegate(this, response);
- requestQueue->Push(request, SteamHttpResponse, delegate, SteamResponseDelegateFree);
+ RequestQueueItem *item = new RequestQueueItem();
+ item->request = request;
+ item->httpCallback = callback;
+ item->param = param;
+ {
+ mir_cslock lock(requestQueueLock);
+ requestQueue.insert(item);
+ }
+ SetEvent(hRequestsQueueEvent);
}
-void CSteamProto::PushRequest(HttpRequest *request, SteamResponseWithArgCallback response, void *arg, HttpFinallyCallback last)
+void CSteamProto::PushRequest(HttpRequest *request, JsonCallback callback, void *param)
{
- SteamResponseDelegate *delegate = new SteamResponseDelegate(this, response, arg, last);
- requestQueue->Push(request, SteamHttpResponse, delegate, SteamResponseDelegateFree);
+ RequestQueueItem *item = new RequestQueueItem();
+ item->request = request;
+ item->jsonCallback = callback;
+ item->param = param;
+ {
+ mir_cslock lock(requestQueueLock);
+ requestQueue.insert(item);
+ }
+ SetEvent(hRequestsQueueEvent);
}
-void CSteamProto::SendRequest(HttpRequest *request)
+void CSteamProto::RequestQueueThread(void*)
{
- requestQueue->Send(request);
-}
+ Login();
-void CSteamProto::SendRequest(HttpRequest *request, SteamResponseCallback response)
-{
- SteamResponseDelegate *delegate = new SteamResponseDelegate(this, response);
- requestQueue->Send(request, SteamHttpResponse, delegate, SteamResponseDelegateFree);
-}
+ do
+ {
+ RequestQueueItem *item;
+ while (true)
+ {
+ {
+ mir_cslock lock(requestQueueLock);
+ if (!requestQueue.getCount())
+ break;
-void CSteamProto::SendRequest(HttpRequest *request, SteamResponseWithArgCallback response, void *arg, HttpFinallyCallback last)
-{
- SteamResponseDelegate *delegate = new SteamResponseDelegate(this, response, arg, last);
- requestQueue->Send(request, SteamHttpResponse, delegate, SteamResponseDelegateFree);
-}
+ item = requestQueue[0];
+ requestQueue.remove(0);
+ }
+ if (item->httpCallback)
+ SendRequest(item->request, item->httpCallback, item->param);
+ else if (item->jsonCallback)
+ SendRequest(item->request, item->jsonCallback, item->param);
+ else
+ SendRequest(item->request);
+ delete item;
+ }
+ WaitForSingleObject(hRequestsQueueEvent, 1000);
+ } while (!isTerminated);
+
+ hRequestQueueThread = NULL;
+}
\ No newline at end of file |