From 7cb1539b0dbcf585472dd2341129adf174bb0bb5 Mon Sep 17 00:00:00 2001
From: aunsane <aunsane@gmail.com>
Date: Sun, 14 Jan 2018 22:02:44 +0300
Subject: Steam: refactoring pt.2

---
 protocols/Steam/src/api/app_info.h      |  12 +
 protocols/Steam/src/api/authorization.h |   2 +-
 protocols/Steam/src/api/enums.h         |  38 ++-
 protocols/Steam/src/api/friend.h        |  26 +-
 protocols/Steam/src/api/friend_list.h   |  39 +++
 protocols/Steam/src/api/history.h       |  15 +
 protocols/Steam/src/api/login.h         |  15 +
 protocols/Steam/src/api/pending.h       |  18 +-
 protocols/Steam/src/api/poll.h          |  56 ++++
 protocols/Steam/src/api/rsa_key.h       |   5 +-
 protocols/Steam/src/api/search.h        |  16 +-
 protocols/Steam/src/api/session.h       |  11 +-
 protocols/Steam/src/http_request.h      |   4 +-
 protocols/Steam/src/main.cpp            |   6 +-
 protocols/Steam/src/stdafx.h            |   3 +
 protocols/Steam/src/steam_accounts.cpp  |  35 ++
 protocols/Steam/src/steam_contacts.cpp  | 573 ++++++++++++++++----------------
 protocols/Steam/src/steam_crypt.cpp     | 114 +++++++
 protocols/Steam/src/steam_dialogs.cpp   |   6 +-
 protocols/Steam/src/steam_events.cpp    |   2 +-
 protocols/Steam/src/steam_history.cpp   |   8 +-
 protocols/Steam/src/steam_instances.cpp |  44 ---
 protocols/Steam/src/steam_login.cpp     | 259 ++++++---------
 protocols/Steam/src/steam_menus.cpp     |  88 +++--
 protocols/Steam/src/steam_messages.cpp  |  17 +-
 protocols/Steam/src/steam_options.cpp   |   6 +-
 protocols/Steam/src/steam_polling.cpp   | 111 +++----
 protocols/Steam/src/steam_proto.cpp     | 144 +++-----
 protocols/Steam/src/steam_proto.h       |  99 +++---
 protocols/Steam/src/steam_request.cpp   | 107 +++---
 protocols/Steam/src/steam_utils.cpp     | 161 ++-------
 31 files changed, 1101 insertions(+), 939 deletions(-)
 create mode 100644 protocols/Steam/src/steam_accounts.cpp
 create mode 100644 protocols/Steam/src/steam_crypt.cpp
 delete mode 100644 protocols/Steam/src/steam_instances.cpp

(limited to 'protocols/Steam')

diff --git a/protocols/Steam/src/api/app_info.h b/protocols/Steam/src/api/app_info.h
index b08a3f38d6..7aeb26f9d4 100644
--- a/protocols/Steam/src/api/app_info.h
+++ b/protocols/Steam/src/api/app_info.h
@@ -11,6 +11,18 @@ public:
 			<< CHAR_PARAM("access_token", token)
 			<< CHAR_PARAM("appIds", appIds);
 	}
+
+	//{
+	//	"apps": [
+	//		{
+	//			"appid": 271590,
+	//			"name" : "Grand Theft Auto V",
+	//			"iconurl" : "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/271590/1e72f87eb927fa1485e68aefaff23c7fd7178251.jpg",
+	//			"logourl" : "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/271590/e447e82f8b0c67f9e001498503c62f2a187bc609.jpg",
+	//			"logosmallurl" : "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/271590/e447e82f8b0c67f9e001498503c62f2a187bc609_thumb.jpg"
+	//		}
+	//	]
+	//}
 };
 
 #endif //_STEAM_REQUEST_APP_INFO_H_
diff --git a/protocols/Steam/src/api/authorization.h b/protocols/Steam/src/api/authorization.h
index 3d02c5deb5..b2ba2639c0 100644
--- a/protocols/Steam/src/api/authorization.h
+++ b/protocols/Steam/src/api/authorization.h
@@ -25,7 +25,7 @@ public:
 			<< CHAR_PARAM("captcha_text", captchaText)
 			<< CHAR_PARAM("rsatimestamp", timestamp)
 			<< BOOL_PARAM("rememberlogin", false)
-			<< INT64_PARAM("donotcache", time(NULL));
+			<< INT64_PARAM("donotcache", now());
 	}
 };
 
diff --git a/protocols/Steam/src/api/enums.h b/protocols/Steam/src/api/enums.h
index a24ce48b34..1a1643a15d 100644
--- a/protocols/Steam/src/api/enums.h
+++ b/protocols/Steam/src/api/enums.h
@@ -1,6 +1,15 @@
 #ifndef _STEAM_ENUMS_H_
 #define _STEAM_ENUMS_H_
 
+enum VisibilityState
+{
+	Private = 1,
+	FriendsOnly = 2,
+	FriendsOfFriends = 3,
+	UsersOnly = 4,
+	Public = 5,
+};
+
 enum PersonaState
 {
 	Offline = 0,
@@ -10,10 +19,19 @@ enum PersonaState
 	Snooze = 4,
 	LookingToTrade = 5,
 	LookingToPlay = 6,
-	Max = 7,
 };
 
-enum StatusFlags
+enum PersonaStateFlag
+{
+	None = 0,
+	HasRichPresence = 1,
+	InJoinableGame = 2,
+	OnlineUsingWeb = 256,
+	OnlineUsingMobile = 512,
+	OnlineUsingBigPicture = 1024,
+};
+
+enum PersonaStatusFlag
 {
 	Status = 1,
 	PlayerName = 2,
@@ -27,6 +45,20 @@ enum StatusFlags
 	GameDataBlob = 512,
 	ClanTag = 1024,
 	Facebook = 2048,
-}
+};
+
+enum PersonaRelationshipAction
+{
+	// friend removed from contact list
+	Remove = 0,
+	// friend added you to ignore list
+	Ignore = 1,
+	// friend requested auth
+	AuthRequest = 2,
+	// friend added you to contact list
+	AddToList = 3,
+	// friend got (or approved?) your auth request
+	AuthRequested = 4,
+};
 
 #endif //_STEAM_ENUMS_H_
diff --git a/protocols/Steam/src/api/friend.h b/protocols/Steam/src/api/friend.h
index 191e0b93fa..0997d42b0f 100644
--- a/protocols/Steam/src/api/friend.h
+++ b/protocols/Steam/src/api/friend.h
@@ -5,12 +5,36 @@ class GetUserSummariesRequest : public HttpRequest
 {
 public:
 	GetUserSummariesRequest(const char *token, const char *steamIds) :
-		HttpRequest(HttpGet, STEAM_API_URL "/ISteamUserOAuth/GetUserSummaries/v0001")
+		HttpRequest(HttpGet, STEAM_API_URL "/ISteamUserOAuth/GetUserSummaries/v0002")
 	{
 		Uri
 			<< CHAR_PARAM("access_token", token)
 			<< CHAR_PARAM("steamids", steamIds);
 	}
+
+	//{
+	//	"players": [
+	//		{
+	//			"steamid": "76561197960435530",
+	//			"communityvisibilitystate" : 3,
+	//			"profilestate" : 1,
+	//			"personaname" : "Robin",
+	//			"lastlogoff" : 1514966746,
+	//			"profileurl" : "http://steamcommunity.com/id/robinwalker/",
+	//			"avatar" : "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/f1/f1dd60a188883caf82d0cbfccfe6aba0af1732d4.jpg",
+	//			"avatarmedium" : "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/f1/f1dd60a188883caf82d0cbfccfe6aba0af1732d4_medium.jpg",
+	//			"avatarfull" : "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/f1/f1dd60a188883caf82d0cbfccfe6aba0af1732d4_full.jpg",
+	//			"personastate" : 0,
+	//			"realname" : "Robin Walker",
+	//			"primaryclanid" : "103582791429521412",
+	//			"timecreated" : 1063407589,
+	//			"personastateflags" : 0,
+	//			"loccountrycode" : "US",
+	//			"locstatecode" : "WA",
+	//			"loccityid" : 3961
+	//		}
+	//	]
+	//}
 };
 
 #endif //_STEAM_REQUEST_FRIEND_H_
diff --git a/protocols/Steam/src/api/friend_list.h b/protocols/Steam/src/api/friend_list.h
index 604e797a2d..50c9a7b5bc 100644
--- a/protocols/Steam/src/api/friend_list.h
+++ b/protocols/Steam/src/api/friend_list.h
@@ -12,6 +12,17 @@ public:
 			<< CHAR_PARAM("steamid", steamId)
 			<< CHAR_PARAM("relationship", relationship);
 	}
+
+	//{
+	//	"friends": [
+	//		{
+	//			"steamid": "XXXXXXXXXXXXXXXXX",
+	//			"relationship" : "friend",
+	//			"friend_since" : 1514314629
+	//		}
+	//	]
+	//}
+
 };
 
 class AddFriendRequest : public HttpRequest
@@ -32,6 +43,12 @@ public:
 			<< CHAR_PARAM("sessionID", sessionId)
 			<< CHAR_PARAM("steamid", who);
 	}
+
+	// "true"
+
+	// {"invited":["XXXXXXXXXXXXXXXXX"], "success" : 1}
+
+	// {"failed_invites":["XXXXXXXXXXXXXXXXX"], "failed_invites_result" : [24], "success" : 1}
 };
 
 class BlockFriendRequest : public HttpRequest
@@ -55,6 +72,28 @@ public:
 	}
 };
 
+class UnblockFriendRequest : public HttpRequest
+{
+public:
+	UnblockFriendRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
+		HttpRequest(HttpPost, STEAM_WEB_URL "/actions/BlockUserAjax")
+	{
+		char login[MAX_PATH];
+		mir_snprintf(login, "%s||oauth:%s", steamId, token);
+
+		char cookie[MAX_PATH];
+		mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
+
+		Headers << CHAR_PARAM("Cookie", cookie);
+
+		Content = new FormUrlEncodedContent(this)
+			<< CHAR_PARAM("sessionID", sessionId)
+			<< CHAR_PARAM("steamid", who)
+			<< CHAR_PARAM("action", "unignore")
+			<< INT_PARAM("block", 0);
+	}
+};
+
 class RemoveFriendRequest : public HttpRequest
 {
 public:
diff --git a/protocols/Steam/src/api/history.h b/protocols/Steam/src/api/history.h
index f825622953..0eaea38fb1 100644
--- a/protocols/Steam/src/api/history.h
+++ b/protocols/Steam/src/api/history.h
@@ -9,6 +9,21 @@ public:
 	{
 		Uri << CHAR_PARAM("access_token", token);
 	}
+
+	//{
+	//	"response": {
+	//		"message_sessions": [
+	//			{
+	//				"accountid_friend": XXXXXXXXX,
+	//				"last_message" : 1514975719,
+	//				"last_view" : 1514975719,
+	//				"unread_message_count" : 0
+	//			}
+	//		]
+	//		,
+	//		"timestamp": 1515007542
+	//	}
+	//}
 };
 
 class GetHistoryMessagesRequest : public HttpRequest
diff --git a/protocols/Steam/src/api/login.h b/protocols/Steam/src/api/login.h
index 3479ae75ff..e061d4236c 100644
--- a/protocols/Steam/src/api/login.h
+++ b/protocols/Steam/src/api/login.h
@@ -14,6 +14,17 @@ public:
 			<< CHAR_PARAM("access_token", token)
 			<< CHAR_PARAM("ui_mode", "web");
 	}
+
+	//{
+	//	"steamid": "XXXXXXXXXXXXXXXXX",
+	//	"error" : "OK",
+	//	"umqid" : "XXXXXXXXXXXXXXXXXXX",
+	//	"timestamp" : 16955891,
+	//	"utc_timestamp" : 1514974537,
+	//	"message" : 1,
+	//	"push" : 0
+	//}
+
 };
 
 class LogoffRequest : public HttpRequest
@@ -26,6 +37,10 @@ public:
 			<< CHAR_PARAM("access_token", token)
 			<< CHAR_PARAM("umqid", umqId);
 	}
+
+	//{
+	//	"error": "OK"
+	//}
 };
 
 #endif //_STEAM_REQUEST_LOGIN_H_
diff --git a/protocols/Steam/src/api/pending.h b/protocols/Steam/src/api/pending.h
index a808fd172c..9aeed4e182 100644
--- a/protocols/Steam/src/api/pending.h
+++ b/protocols/Steam/src/api/pending.h
@@ -7,11 +7,9 @@ public:
 	ApprovePendingRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
 		HttpRequest(HttpPost, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
 	{
-		char login[MAX_PATH];
-		mir_snprintf(login, "%s||oauth:%s", steamId, token);
-
 		char cookie[MAX_PATH];
-		mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
+		mir_snprintf(cookie, "steamLogin=%s||oauth:%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios",
+			steamId, token, sessionId);
 
 		Headers << CHAR_PARAM("Cookie", cookie);
 
@@ -32,11 +30,9 @@ public:
 	IgnorePendingRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
 		HttpRequest(HttpPost, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
 	{
-		char login[MAX_PATH];
-		mir_snprintf(login, "%s||oauth:%s", steamId, token);
-
 		char cookie[MAX_PATH];
-		mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
+		mir_snprintf(cookie, "steamLogin=%s||oauth:%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios",
+			steamId, token, sessionId);
 
 		Headers << CHAR_PARAM("Cookie", cookie);
 
@@ -57,11 +53,9 @@ public:
 	BlockPendingRequest(const char *token, const char *sessionId, const char *steamId, const char *who) :
 		HttpRequest(HttpPost, FORMAT, STEAM_WEB_URL "/profiles/%s/home_process", steamId)
 	{
-		char login[MAX_PATH];
-		mir_snprintf(login, "%s||oauth:%s", steamId, token);
-
 		char cookie[MAX_PATH];
-		mir_snprintf(cookie, "steamLogin=%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios", login, sessionId);
+		mir_snprintf(cookie, "steamLogin=%s||oauth:%s;sessionid=%s;mobileClientVersion=1291812;forceMobile=1;mobileClient=ios",
+			steamId, token, sessionId);
 
 		Headers << CHAR_PARAM("Cookie", cookie);
 
diff --git a/protocols/Steam/src/api/poll.h b/protocols/Steam/src/api/poll.h
index 278dfb0439..6336b212e9 100644
--- a/protocols/Steam/src/api/poll.h
+++ b/protocols/Steam/src/api/poll.h
@@ -19,6 +19,62 @@ public:
 			<< INT_PARAM("secidletime", idleSeconds)
 			<< INT_PARAM("sectimeout", STEAM_API_TIMEOUT);
 	}
+
+	//{
+	//	"pollid": 0,
+	//	"sectimeout" : 30,
+	//	"error" : "Timeout"
+	//}
+
+	//{
+	//	"pollid": 0,
+	//	"messages": [
+	//		{
+	//			"type": "typing",
+	//			"timestamp": 17276041,
+	//			"utc_timestamp": 1514974857,
+	//			"steamid_from": "XXXXXXXXXXXXXXXXX",
+	//			"text": ""
+	//		},
+	//		{
+	//			"type": "saytext",
+	//			"timestamp" : 17380133,
+	//			"utc_timestamp" : 1514974961,
+	//			"steamid_from" : "XXXXXXXXXXXXXXXXX",
+	//			"text" : "message"
+	//		},
+	//		{
+	//			"type": "personarelationship",
+	//			"timestamp" : 7732750,
+	//			"utc_timestamp" : 1515187192,
+	//			"steamid_from" : "XXXXXXXXXXXXXXXXX",
+	//			"status_flags" : 1,
+	//			"persona_state" : 2
+	//		},
+	//		{
+	//			"type": "personastate",
+	//			"timestamp" : 366860,
+	//			"utc_timestamp" : 1515007523,
+	//			"steamid_from" : "XXXXXXXXXXXXXXXXX",
+	//			"status_flags" : 9055,
+	//			"persona_state" : 1,
+	//			"persona_name" : "nickname"
+	//		},
+	//		{
+	//			"type": "notificationcountupdate",
+	//			"timestamp" : 11605105,
+	//			"utc_timestamp" : 1515191064
+	//		}
+	//	]
+	//	,
+	//	"messagelast": 4,
+	//	"timestamp": 17276041,
+	//	"utc_timestamp": 1514974857,
+	//	"messagebase": 3,
+	//	"sectimeout": 14,
+	//	"error": "OK"
+	//}
+
 };
 
 #endif //_STEAM_REQUEST_POLL_H_
diff --git a/protocols/Steam/src/api/rsa_key.h b/protocols/Steam/src/api/rsa_key.h
index 27a002b717..a0369f4db4 100644
--- a/protocols/Steam/src/api/rsa_key.h
+++ b/protocols/Steam/src/api/rsa_key.h
@@ -9,12 +9,9 @@ public:
 	{
 		flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP;
 
-		CMStringA data;
-		data.AppendFormat("username=%s&donotcache=%lld", ptrA(mir_urlEncode(username)), time(NULL));
-
 		Content = new FormUrlEncodedContent(this)
 			<< CHAR_PARAM("username", username)
-			<< INT64_PARAM("donotcache", time(NULL));
+			<< INT64_PARAM("donotcache", now());
 	}
 };
 
diff --git a/protocols/Steam/src/api/search.h b/protocols/Steam/src/api/search.h
index a34c2e8398..4484af4ba8 100644
--- a/protocols/Steam/src/api/search.h
+++ b/protocols/Steam/src/api/search.h
@@ -10,11 +10,23 @@ public:
 		Uri
 			<< CHAR_PARAM("access_token", token)
 			<< CHAR_PARAM("keywords", text)
-			<< INT_PARAM("offset=%d", offset)
-			<< INT_PARAM("count=%d", count)
+			<< INT_PARAM("offset", offset)
+			<< INT_PARAM("count", count)
 			<< CHAR_PARAM("targets", "users")
 			<< CHAR_PARAM("fields", "all");
 	}
+
+	//{
+	//	"count": 1,
+	//	"total" : 336,
+	//	"success" : true,
+	//	"results" : [
+	//		{
+	//			"steamid": "XXXXXXXXXXXXXXXXX",
+	//			"type" : "user"
+	//		}
+	//	]
+	//}
 };
 
 #endif //_STEAM_REQUEST_SEARCH_H_
diff --git a/protocols/Steam/src/api/session.h b/protocols/Steam/src/api/session.h
index d4e91721cb..dec2e0aa0a 100644
--- a/protocols/Steam/src/api/session.h
+++ b/protocols/Steam/src/api/session.h
@@ -26,10 +26,15 @@ public:
 class GetSessionRequest2 : public HttpRequest
 {
 public:
-	GetSessionRequest2() :
-		HttpRequest(HttpGet, STEAM_WEB_URL)
+	GetSessionRequest2(const char *token, const char *steamId) :
+		HttpRequest(HttpGet, STEAM_WEB_URL "/mobilesettings/GetManifest/v0001")
 	{
-		flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMP;
+		flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMPHEADERS;
+
+		char cookie[MAX_PATH];
+		mir_snprintf(cookie, "steamLogin=%s||oauth:%s", steamId, token);
+
+		Headers << CHAR_PARAM("Cookie", cookie);
 	}
 };
 
diff --git a/protocols/Steam/src/http_request.h b/protocols/Steam/src/http_request.h
index 43bb68d464..696b04f2a8 100644
--- a/protocols/Steam/src/http_request.h
+++ b/protocols/Steam/src/http_request.h
@@ -35,7 +35,7 @@ private:
 
 	void FormatV(const char *urlFormat, va_list args)
 	{
-		m_uri.AppendFormatV(urlFormat, args);
+		m_uri.FormatV(urlFormat, args);
 		if (m_request)
 			m_request->szUrl = m_uri.GetBuffer();
 	}
@@ -125,7 +125,7 @@ public:
 	{
 		return m_request
 			? &m_request->headers[idx]
-			: NULL;
+			: nullptr;
 	}
 
 	size_t GetSize() const
diff --git a/protocols/Steam/src/main.cpp b/protocols/Steam/src/main.cpp
index 0f14e6ec0e..eefe4967c6 100644
--- a/protocols/Steam/src/main.cpp
+++ b/protocols/Steam/src/main.cpp
@@ -41,8 +41,8 @@ extern "C" int __declspec(dllexport) Load(void)
 	pd.cbSize = sizeof(pd);
 	pd.szName = "STEAM";
 	pd.type = PROTOTYPE_PROTOCOL;
-	pd.fnInit = (pfnInitProto)CSteamProto::InitProtoInstance;
-	pd.fnUninit = (pfnUninitProto)CSteamProto::UninitProtoInstance;
+	pd.fnInit = (pfnInitProto)CSteamProto::InitAccount;
+	pd.fnUninit = (pfnUninitProto)CSteamProto::UninitAccount;
 	Proto_RegisterModule(&pd);
 
 	char iconName[100];
@@ -58,7 +58,5 @@ extern "C" int __declspec(dllexport) Load(void)
 
 extern "C" int __declspec(dllexport) Unload(void)
 {
-	CSteamProto::UninitProtoInstances();
-	CSteamProto::UninitMenus();
 	return 0;
 }
diff --git a/protocols/Steam/src/stdafx.h b/protocols/Steam/src/stdafx.h
index 7b2a070da4..a8aa969ca3 100644
--- a/protocols/Steam/src/stdafx.h
+++ b/protocols/Steam/src/stdafx.h
@@ -59,9 +59,12 @@ extern HANDLE hExtraXStatus;
 #define STEAM_DB_GETEVENTTEXT_CHATSTATES    "/GetEventText2000"
 #define STEAM_DB_EVENT_CHATSTATES_GONE      1
 
+#define now() time(NULL)
+
 #include "steam_dialogs.h"
 #include "steam_options.h"
 #include "http_request.h"
+#include "api/enums.h"
 #include "api/app_info.h"
 #include "api/authorization.h"
 #include "api/authorization.h"
diff --git a/protocols/Steam/src/steam_accounts.cpp b/protocols/Steam/src/steam_accounts.cpp
new file mode 100644
index 0000000000..43ec787c5f
--- /dev/null
+++ b/protocols/Steam/src/steam_accounts.cpp
@@ -0,0 +1,35 @@
+#include "stdafx.h"
+
+LIST<CSteamProto> CSteamProto::Accounts(1, CSteamProto::CompareProtos);
+
+int CSteamProto::CompareProtos(const CSteamProto *p1, const CSteamProto *p2)
+{
+	return mir_wstrcmp(p1->m_tszUserName, p2->m_tszUserName);
+}
+
+CSteamProto* CSteamProto::InitAccount(const char* protoName, const wchar_t* userName)
+{
+	CSteamProto *ppro = new CSteamProto(protoName, userName);
+	Accounts.insert(ppro);
+	return ppro;
+}
+
+int CSteamProto::UninitAccount(CSteamProto* ppro)
+{
+	Accounts.remove(ppro);
+	delete ppro;
+	return 0;
+}
+
+CSteamProto* CSteamProto::GetContactAccount(MCONTACT hContact)
+{
+	char *proto = GetContactProto(hContact);
+	if (proto == nullptr)
+		return nullptr;
+
+	for (int i = 0; i < Accounts.getCount(); i++)
+		if (!mir_strcmp(proto, Accounts[i]->m_szModuleName))
+			return Accounts[i];
+
+	return nullptr;
+}
\ No newline at end of file
diff --git a/protocols/Steam/src/steam_contacts.cpp b/protocols/Steam/src/steam_contacts.cpp
index 98f06662de..894853f46b 100644
--- a/protocols/Steam/src/steam_contacts.cpp
+++ b/protocols/Steam/src/steam_contacts.cpp
@@ -2,6 +2,9 @@
 
 void CSteamProto::SetContactStatus(MCONTACT hContact, WORD status)
 {
+	if (!hContact)
+		return;
+
 	WORD oldStatus = getWord(hContact, "Status", ID_STATUS_OFFLINE);
 	if (oldStatus != status)
 	{
@@ -11,19 +14,13 @@ void CSteamProto::SetContactStatus(MCONTACT hContact, WORD status)
 		switch (status)
 		{
 		case ID_STATUS_FREECHAT:
-			{
-				// Contact is looking to play, save it to as status message
-				if (hContact)
-					db_set_ws(hContact, "CList", "StatusMsg", TranslateT("Looking to play"));
-			}
+			// Contact is looking to play, save it to as status message
+			db_set_ws(hContact, "CList", "StatusMsg", TranslateT("Looking to play"));
 			break;
 
 		case ID_STATUS_OUTTOLUNCH:
-			{
-				// Contact is looking to trade, save it to as status message
-				if (hContact)
-					db_set_ws(hContact, "CList", "StatusMsg", TranslateT("Looking to trade"));
-			}
+			// Contact is looking to trade, save it to as status message
+			db_set_ws(hContact, "CList", "StatusMsg", TranslateT("Looking to trade"));
 			break;
 
 		case ID_STATUS_OFFLINE:
@@ -33,16 +30,12 @@ void CSteamProto::SetContactStatus(MCONTACT hContact, WORD status)
 				delSetting(hContact, "XStatusName");
 				delSetting(hContact, "XStatusMsg");
 
-				if (hContact)
-					SetContactExtraIcon(hContact, NULL);
+				SetContactExtraIcon(hContact, NULL);
 			}
 			// no break intentionally
 
 		default:
-			{
-				if (hContact)
-					db_unset(hContact, "CList", "StatusMsg");
-			}
+			db_unset(hContact, "CList", "StatusMsg");
 			break;
 		}
 	}
@@ -67,20 +60,14 @@ MCONTACT CSteamProto::GetContactFromAuthEvent(MEVENT hEvent)
 	return DbGetAuthEventContact(&dbei);
 }
 
-MCONTACT CSteamProto::FindContact(const char *steamId)
+MCONTACT CSteamProto::GetContact(const char *steamId)
 {
-	MCONTACT hContact = NULL;
-
-	mir_cslock lck(this->contact_search_lock);
-
-	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))
-			break;
+	for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) {
+		ptrA cSteamId(getStringA(hContact, "SteamID"));
+		if (!mir_strcmp(cSteamId, steamId))
+			return hContact;
 	}
-
-	return hContact;
+	return NULL;
 }
 
 void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
@@ -97,26 +84,21 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
 
 	// set name
 	JSONNode node = data["realname"];
-	if (!node.isnull())
-	{
+	if (!node.isnull()) {
 		CMStringW realName = node.as_mstring();
-		if (!realName.IsEmpty())
-		{
+		if (!realName.IsEmpty()) {
 			int pos = realName.Find(L' ', 1);
-			if (pos != -1)
-			{
+			if (pos != -1) {
 				setWString(hContact, "FirstName", realName.Mid(0, pos));
 				setWString(hContact, "LastName", realName.Mid(pos + 1));
 			}
-			else
-			{
+			else {
 				setWString(hContact, "FirstName", realName);
 				delSetting(hContact, "LastName");
 			}
 		}
 	}
-	else
-	{
+	else {
 		delSetting(hContact, "FirstName");
 		delSetting(hContact, "LastName");
 	}
@@ -128,8 +110,7 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
 
 	// set country
 	node = data["loccountrycode"];
-	if (!node.isnull())
-	{
+	if (!node.isnull()) {
 		json_string countryCode = node.as_string();
 		char *country = (char *)CallService(MS_UTILS_GETCOUNTRYBYISOCODE, (WPARAM)countryCode.c_str(), 0);
 		setString(hContact, "Country", country);
@@ -138,17 +119,25 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
 		delSetting(hContact, "Country");
 
 	// state code
-	node = data["locstatecode"];
-	if (!node.isnull())
-		setDword(hContact, "StateCode", node.as_int());
-	else
+	// note: it seems that steam sends "incorrect" state code
+	//node = data["locstatecode"];
+	//if (!node.isnull())
+	//{
+	//	json_string stateCode = node.as_string();
+	//	setString(hContact, "State", stateCode.c_str());
+	//}
+	//else
+	//{
+	//	delSetting(hContact, "State");
 		delSetting(hContact, "StateCode");
+	//}
 
 	// city id
-	node = data["loccityid"];
-	if (!node.isnull())
-		setDword(hContact, "CityID", node.as_int());
-	else
+	// note: steam no longer sends state city id
+	//node = data["loccityid"];
+	//if (!node.isnull())
+	//	setDword(hContact, "CityID", node.as_int());
+	//else
 		delSetting(hContact, "CityID");
 
 	// account created
@@ -160,34 +149,38 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
 	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
-	/*node = json_get(data, "personastate");
-	status = SteamToMirandaStatus(json_as_int(node));
-	SetContactStatus(hContact, status);
-	*/
+	node = data["lastlogoff"];
+	// note: this here is often wrong info, probably depending on publicity of steam profile
+	// but sometimes polling does not get status at all
+	WORD oldStatus = getWord(hContact, "Status", ID_STATUS_OFFLINE);
+	// so, set status only if contact offline
+	if (oldStatus == ID_STATUS_OFFLINE) {
+		WORD status = SteamToMirandaStatus((PersonaState)node.as_int());
+		SetContactStatus(hContact, status);
+	}
 
 	// client
 	node = data["personastateflags"];
-	int stateflags = !node.isnull() ? node.as_int() : -1;
+	long 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);
 		if (status == ID_STATUS_ONLINE || status == ID_STATUS_OUTTOLUNCH || status == ID_STATUS_FREECHAT)
 			setWString(hContact, "MirVer", L"Steam");
 	}
-	else if (stateflags & 2) {
+	else if (stateflags & PersonaStateFlag::InJoinableGame) {
 		// game
 		setWString(hContact, "MirVer", L"Steam (in game)");
 	}
-	else if (stateflags & 256) {
+	else if (stateflags & PersonaStateFlag::OnlineUsingWeb) {
 		// on website
 		setWString(hContact, "MirVer", L"Steam (website)");
 	}
-	else if (stateflags & 512) {
+	else if (stateflags & PersonaStateFlag::OnlineUsingMobile) {
 		// on mobile
 		setWString(hContact, "MirVer", L"Steam (mobile)");
 	}
-	else if (stateflags & 1024) {
+	else if (stateflags & PersonaStateFlag::OnlineUsingBigPicture) {
 		// big picture mode
 		setWString(hContact, "MirVer", L"Steam (Big Picture)");
 	}
@@ -199,8 +192,7 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
 	// playing game
 	json_string appId = data["gameid"].as_string();
 	CMStringW gameInfo = data["gameextrainfo"].as_mstring();
-	if (!appId.empty() || !gameInfo.IsEmpty())
-	{
+	if (!appId.empty() || !gameInfo.IsEmpty()) {
 		DWORD gameId = atol(appId.c_str());
 		json_string serverIP = data["gameserverip"].as_string();
 		json_string serverID = data["gameserversteamid"].as_string();
@@ -210,16 +202,14 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
 		setString(hContact, "ServerID", serverID.c_str());
 
 		CMStringW message(gameInfo);
-		if (gameId && message.IsEmpty())
-		{
+		if (gameId && message.IsEmpty()) {
 			ptrA token(getStringA("TokenSecret"));
 			PushRequest(
 				new GetAppInfoRequest(token, appId.c_str()),
 				&CSteamProto::OnGotAppInfo,
 				(void*)hContact);
 		}
-		else
-		{
+		else {
 			if (!gameId)
 				message.Append(TranslateT(" (Non-Steam)"));
 			if (!serverIP.empty())
@@ -232,8 +222,7 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
 
 		SetContactExtraIcon(hContact, gameId);
 	}
-	else
-	{
+	else {
 		delSetting(hContact, "GameID");
 		delSetting(hContact, "ServerIP");
 		delSetting(hContact, "ServerID");
@@ -246,136 +235,125 @@ void CSteamProto::UpdateContactDetails(MCONTACT hContact, const JSONNode &data)
 	}
 }
 
-void CSteamProto::ContactIsRemoved(MCONTACT hContact)
+void CSteamProto::ContactIsFriend(MCONTACT hContact)
 {
 	delSetting(hContact, "AuthAsked");
+	delSetting(hContact, "Auth");
+	delSetting(hContact, "Grant");
+	db_unset(hContact, "CList", "NotOnList");
 
-	// If this contact was authorized and now is not (and isn't filled time of deletion), notify it 
-	if (!getDword(hContact, "DeletedTS", 0) && getByte(hContact, "Auth", 0) == 0)
-	{
-		setDword(hContact, "DeletedTS", ::time(nullptr));
+	// Check if this contact was removed someday and if so, notify he's back
+	if (getDword(hContact, "DeletedTS", 0) && !getByte(hContact, "Auth", 0)) {
+		delSetting(hContact, "DeletedTS");
 
 		ptrW nick(getWStringA(hContact, "Nick"));
 		wchar_t message[MAX_PATH];
-		mir_snwprintf(message, MAX_PATH, TranslateT("%s has been removed from your contact list"), nick);
+		mir_snwprintf(message, MAX_PATH, TranslateT("%s is back in your contact list"), nick);
 
-		ShowNotification(L"Steam", message);
+		ShowNotification(message);
 	}
+}
 
-	if (getByte(hContact, "Auth", 0) != 1)
-	{
-		setByte(hContact, "Auth", 1);
-	}
+void CSteamProto::ContactIsBlocked(MCONTACT hContact)
+{
+	// todo
+	setByte(hContact, "Block", 1);
+}
 
-	SetContactStatus(hContact, ID_STATUS_OFFLINE);
+void CSteamProto::ContactIsUnblocked(MCONTACT hContact)
+{
+	delSetting(hContact, "Block");
 }
 
-void CSteamProto::ContactIsFriend(MCONTACT hContact)
+void CSteamProto::ContactIsRemoved(MCONTACT hContact)
 {
 	delSetting(hContact, "AuthAsked");
 
-	// Check if this contact was removed someday and if so, notify he's back
-	if (getDword(hContact, "DeletedTS", 0) && getByte(hContact, "Auth", 0) != 0)
-	{
-		delSetting(hContact, "DeletedTS");
+	// If this contact was authorized and now is not (and isn't filled time of deletion), notify it
+	if (!getDword(hContact, "DeletedTS", 0) && !getByte(hContact, "Auth", 0)) {
+		setDword(hContact, "DeletedTS", now());
 
 		ptrW nick(getWStringA(hContact, "Nick"));
 		wchar_t message[MAX_PATH];
-		mir_snwprintf(message, MAX_PATH, TranslateT("%s is back in your contact list"), nick);
-
-		ShowNotification(L"Steam", message);
-	}
+		mir_snwprintf(message, MAX_PATH, TranslateT("%s has been removed from your contact list"), nick);
 
-	if (getByte(hContact, "Auth", 0) != 0 || getByte(hContact, "Grant", 0) != 0)
-	{
-		delSetting(hContact, "Auth");
-		delSetting(hContact, "Grant");
+		ShowNotification(message);
 	}
-}
 
-void CSteamProto::ContactIsIgnored(MCONTACT hContact)
-{
-	// todo
-	setByte(hContact, "Block", 1);
+	setByte(hContact, "Auth", 1);
+	SetContactStatus(hContact, ID_STATUS_OFFLINE);
 }
 
 void CSteamProto::ContactIsAskingAuth(MCONTACT hContact)
 {
-	if (getByte(hContact, "AuthAsked", 0) != 0) {
+	//if (getByte(hContact, "AuthAsked", 0))
 		// auth request was already showed, do nothing here
-		return;
-	}
+		//return;
 
-	if (getByte(hContact, "Auth", 0) == 0) {
+	/*if (getByte(hContact, "Auth", 0) == 0) {
 		// user was just added or he already has authorization, but because we've just got auth request, he was probably deleted and requested again
 		ContactIsRemoved(hContact);
-	}
+	}*/
 
 	// create auth request event
 	ptrA steamId(getStringA(hContact, "SteamID"));
 	ptrA nickName(getStringA(hContact, "Nick"));
+	if (nickName == nullptr)
+		nickName = mir_strdup(steamId);
 	ptrA firstName(getStringA(hContact, "FirstName"));
-	if (firstName == NULL)
+	if (firstName == nullptr)
 		firstName = mir_strdup("");
 	ptrA lastName(getStringA(hContact, "LastName"));
-	if (lastName == NULL)
+	if (lastName == nullptr)
 		lastName = mir_strdup("");
 
 	char reason[MAX_PATH];
-	mir_snprintf(reason, Translate("%s has added you to his or her Friend List"), nickName);
+	mir_snprintf(reason, Translate("%s has added you to contact list"), nickName);
 
 	DB_AUTH_BLOB blob(hContact, nickName, firstName, lastName, steamId, reason);
 
 	PROTORECVEVENT recv = { 0 };
-	recv.timestamp = time(nullptr);
+	recv.timestamp = now();
 	recv.szMessage = blob;
 	recv.lParam = blob.size();
 	ProtoChainRecv(hContact, PSR_AUTH, 0, (LPARAM)&recv);
-
-	// remember to not create this event again, unless authorization status changes again
-	setByte(hContact, "AuthAsked", 1);
 }
 
-MCONTACT CSteamProto::AddContact(const char *steamId, bool isTemporary)
+MCONTACT CSteamProto::AddContact(const char *steamId, const wchar_t *nick, bool isTemporary)
 {
-	MCONTACT hContact = NULL;
-
-	mir_cslock lck(this->contact_search_lock);
+	mir_cslock lock(m_addContactLock);
 
-	for (hContact = db_find_first(this->m_szModuleName); hContact; hContact = db_find_next(hContact, this->m_szModuleName))
-	{
-		ptrA cSteamId(db_get_sa(hContact, this->m_szModuleName, "SteamID"));
-		if (!lstrcmpA(cSteamId, steamId))
-			break;
+	if (!steamId || !mir_strlen(steamId)) {
+		debugLogA(__FUNCTION__ ": empty steam id");
+		return NULL;
 	}
 
-	if (!hContact)
-	{
-		// create contact
-		hContact = db_add_contact();
-		Proto_AddToContact(hContact, this->m_szModuleName);
+	MCONTACT hContact = GetContact(steamId);
+	if (hContact)
+		return hContact;
 
-		setString(hContact, "SteamID", steamId);
+	// create contact
+	hContact = db_add_contact();
+	Proto_AddToContact(hContact, m_szModuleName);
 
-		// update info
-		//UpdateContact(hContact, contact);
+	setString(hContact, "SteamID", steamId);
+	if (mir_wstrlen(nick)) {
+		setWString(hContact, "Nick", nick);
+		db_set_ws(hContact, "CList", "MyHandle", nick);
+	}
 
-		if (isTemporary)
-		{
-			setByte(hContact, "Auth", 1);
-			//setByte(hContact, "Grant", 1);
-			db_set_b(hContact, "CList", "NotOnList", 1);
-		}
+	// update info
+	//UpdateContact(hContact, contact);
 
-		// move to default group
-		DBVARIANT dbv;
-		if (!getWString("DefaultGroup", &dbv))
-		{
-			if(Clist_GroupExists(dbv.ptszVal))
-				db_set_ws(hContact, "CList", "Group", dbv.ptszVal);
-			db_free(&dbv);
-		}
-	}
+	if (isTemporary)
+		db_set_b(hContact, "CList", "NotOnList", 1);
+
+	setByte(hContact, "Auth", 1);
+	//setByte(hContact, "Grant", 1);
+
+	// move to default group
+	if (m_defaultGroup)
+		db_set_ws(hContact, "CList", "Group", m_defaultGroup);
 
 	return hContact;
 }
@@ -391,11 +369,11 @@ void CSteamProto::UpdateContactRelationship(MCONTACT hContact, const JSONNode &d
 		return;
 
 	json_string relationship = node.as_string();
-	if (!lstrcmpiA(relationship.c_str(), "friend"))
+	if (!mir_strcmp(relationship.c_str(), "friend"))
 		ContactIsFriend(hContact);
-	else if (!lstrcmpiA(relationship.c_str(), "ignoredfriend"))
-		ContactIsIgnored(hContact);
-	else if (!lstrcmpiA(relationship.c_str(), "requestrecipient"))
+	else if (!mir_strcmp(relationship.c_str(), "ignoredfriend"))
+		ContactIsBlocked(hContact);
+	else if (!mir_strcmp(relationship.c_str(), "requestrecipient"))
 		ContactIsAskingAuth(hContact);
 }
 
@@ -404,8 +382,7 @@ void CSteamProto::OnGotAppInfo(const JSONNode &root, void *arg)
 	MCONTACT hContact = (UINT_PTR)arg;
 
 	JSONNode apps = root["apps"].as_array();
-	for (const JSONNode &app : apps)
-	{
+	for (const JSONNode &app : apps) {
 		DWORD gameId = app["appid"].as_int();
 		CMStringW message = app["name"].as_mstring();
 
@@ -424,67 +401,69 @@ void CSteamProto::OnGotFriendList(const JSONNode &root, void*)
 	std::string steamIds = (char*)ptrA(getStringA("SteamID"));
 
 	// Remember contacts on server
-		std::map<json_string, JSONNode*> friendsMap;
+	std::map<json_string, JSONNode*> friendsMap;
 	JSONNode friends = root["friends"].as_array();
-	for (const JSONNode &_friend : friends)
-	{
+	for (const JSONNode &_friend : friends) {
 		json_string steamId = _friend["steamid"].as_string();
 		friendsMap.insert(std::make_pair(steamId, json_copy(&_friend)));
 	}
 
-	{ // 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))
-		{
-			ptrA steamId(getStringA(hContact, "SteamID"));
-			if (steamId == nullptr)
-				continue;
+	// Check and update contacts in database
+	for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) {
+		ptrA steamId(getStringA(hContact, "SteamID"));
+		if (steamId == nullptr)
+			continue;
 
-			auto it = friendsMap.find((char*)steamId);
-			if (it != friendsMap.end())
-			{
-				// Contact is on server-list, update (and eventually notify) it
-				UpdateContactRelationship(hContact, *it->second);
-				steamIds.append(",").append(it->first);
-				json_delete(it->second);
-				friendsMap.erase(it);
-			}
-			else
-			{
-				// Contact was removed from server-list, notify it
-				ContactIsRemoved(hContact);
-			}
+		auto it = friendsMap.find((char*)steamId);
+		if (it == friendsMap.end()) {
+			// Contact was removed from server-list, notify it
+			ContactIsRemoved(hContact);
+			continue;
 		}
+
+		JSONNode _friend = *it->second;
+
+		// Contact is on server-list, update (and eventually notify) it
+		UpdateContactRelationship(hContact, _friend);
+
+		// Do not update summary for non friends
+		json_string relationship = _friend["relationship"].as_string();
+		if (relationship == "friend")
+			steamIds.append(",").append(it->first);
+
+		json_delete(it->second);
+		friendsMap.erase(it);
 	}
 
 	// Check remaining contacts in map and add them to contact list
-	for (auto it : friendsMap)
-	{
+	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);
-		
-		steamIds.append(",").append(it.first);
+		JSONNode _friend = *it.second;
+
+		json_string relationship = _friend["relationship"].as_string();
+
+		MCONTACT hContact = AddContact(it.first.c_str(), nullptr, relationship != "friend");
+		UpdateContactRelationship(hContact, _friend);
+
+		if (relationship == "friend")
+			steamIds.append(",").append(it.first);
+
 		json_delete(it.second);
 	}
 	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);
-
 	ptrA token(getStringA("TokenSecret"));
 
 	if (!steamIds.empty())
 	{
-		//steamIds.pop_back();
+		steamIds.pop_back();
 
 		PushRequest(
 			new GetUserSummariesRequest(token, steamIds.c_str()),
 			&CSteamProto::OnGotUserSummaries);
 	}
 
-	// Download last messages
+	// Load last conversations
 	PushRequest(
 		new GetConversationsRequest(token),
 		&CSteamProto::OnGotConversations);
@@ -501,11 +480,9 @@ void CSteamProto::OnGotBlockList(const JSONNode &root, void*)
 	if (friends.isnull())
 		return;
 
-	for (size_t i = 0; i < friends.size(); i++)
+	for (const JSONNode &_friend: friends)
 	{
-		JSONNode node = friends[i].as_node();
-
-		json_string steamId = node["steamid"].as_string();
+		json_string steamId = _friend["steamid"].as_string();
 
 		/*MCONTACT hContact = FindContact(steamId);
 		if (!hContact)
@@ -514,8 +491,8 @@ void CSteamProto::OnGotBlockList(const JSONNode &root, void*)
 			steamIds.append(steamId).append(",");
 		}*/
 
-		json_string relationship = node["relationship"].as_string();
-		if (!lstrcmpiA(relationship.c_str(), "ignoredfriend"))
+		json_string relationship = _friend["relationship"].as_string();
+		if (!mir_strcmp(relationship.c_str(), "ignoredfriend"))
 		{
 			// todo: open block list
 		}
@@ -529,15 +506,12 @@ void CSteamProto::OnGotUserSummaries(const JSONNode &root, void*)
 	if (players.isnull())
 		return;
 
-	for (size_t i = 0; i < players.size(); i++)
-	{
-		JSONNode player = players[i];
+	for (const JSONNode &player : players) {
 		json_string steamId = player["steamid"].as_string();
-
-		MCONTACT hContact = NULL;
-		if (!IsMe(steamId.c_str()))
-			hContact = AddContact(steamId.c_str());
-
+		CMStringW nick = player["personaname"].as_mstring();
+		MCONTACT hContact = !IsMe(steamId.c_str())
+			? hContact = AddContact(steamId.c_str(), nick)
+			: NULL;
 		UpdateContactDetails(hContact, player);
 	}
 }
@@ -548,8 +522,7 @@ void CSteamProto::OnGotAvatar(const HttpResponse &response, void *arg)
 	ai.hContact = (UINT_PTR)arg;
 	GetDbAvatarInfo(ai);
 
-	if (!response.IsSuccess())
-	{
+	if (!response.IsSuccess()) {
 		ptrA steamId(getStringA(ai.hContact, "SteamID"));
 		debugLogA(__FUNCTION__ ": failed to get avatar %s", steamId);
 
@@ -558,11 +531,10 @@ void CSteamProto::OnGotAvatar(const HttpResponse &response, void *arg)
 		return;
 	}
 
-	FILE *fp = _wfopen(ai.filename, L"wb");
-	if (fp)
-	{
-		fwrite(response.Content, sizeof(char), response.Content.GetSize(), fp);
-		fclose(fp);
+	FILE *file = _wfopen(ai.filename, L"wb");
+	if (file) {
+		fwrite(response.Content, sizeof(char), response.Content.GetSize(), file);
+		fclose(file);
 
 		if (ai.hContact)
 			ProtoBroadcastAck(ai.hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, (HANDLE)&ai, 0);
@@ -575,21 +547,63 @@ void CSteamProto::OnFriendAdded(const HttpResponse &response, void *arg)
 {
 	SendAuthParam *param = (SendAuthParam*)arg;
 
-	if (!response.IsSuccess() || lstrcmpiA(response.Content, "true"))
-	{
-		ptrA steamId(getStringA(param->hContact, "SteamID"));
-		debugLogA(__FUNCTION__ ": failed to add friend %s", steamId);
+	if (!response.IsSuccess() || mir_strcmp(response.Content, "true")) {
+		
+		ptrW steamId(getWStringA(param->hContact, "SteamID"));
+		ptrW who(getWStringA(param->hContact, "Nick"));
+		if (!who)
+			who = mir_wstrdup(steamId);
 
-		ProtoBroadcastAck(param->hContact, ACKTYPE_AUTHREQ, ACKRESULT_FAILED, param->hAuth, 0);
+		wchar_t message[MAX_PATH];
+		mir_snwprintf(message, L"Error adding friend %s", who);
+
+		JSONNode root = JSONNode::parse(response.Content);
+		if (root) {
+			int success = root["success"].as_int();
+			if (success == 1) {
+				JSONNode invited = root["invited"].as_array();
+				if (invited.empty()) {
+					JSONNode errors = root["failed_invites_result"].as_array();
+					if (!errors.empty()) {
+						int first = 0;
+						int errorCode = errors[first].as_int();
+						switch (errorCode)
+						{
+						case 11:
+							mir_snwprintf(message, L"All communication with %s is blocked. Communication is only possible if you have lifted the blocking. To do this, visit the user's Steam Community page.", who);
+							break;
+						case 15:
+							mir_snwprintf(message, L"Request to %s can not be sent. His/her friends list is full.", who);
+							break;
+						case 24:
+							mir_wstrcpy(message, L"Your account does not meet the requirements to use this feature. Visit Steam Support to get more information.");
+							break;
+						case 25:
+							mir_snwprintf(message, L"Request to %s can not be sent. Your friends list is full.", who);
+							break;
+						case 40:
+							mir_snwprintf(message, L"Error adding friend %s. The communication between you and the other Steam member is blocked.", who);
+							break;
+						case 84:
+							mir_wstrcpy(message, L"You've been sending too many invitations lately. Please try again in a day or two.");
+							break;
+						}
+					}
+				}
+				else {
+					ContactIsFriend(param->hContact);
+					ProtoBroadcastAck(param->hContact, ACKTYPE_AUTHREQ, ACKRESULT_SUCCESS, param->hAuth, 0);
+					return;
+				}
+			}
+		}
 
+		ShowNotification(message, MB_ICONERROR);
+		ProtoBroadcastAck(param->hContact, ACKTYPE_AUTHREQ, ACKRESULT_FAILED, param->hAuth, 0);
 		return;
 	}
 
-	delSetting(param->hContact, "Auth");
-	delSetting(param->hContact, "Grant");
-	delSetting(param->hContact, "DeletedTS");
-	db_unset(param->hContact, "CList", "NotOnList");
-
+	ContactIsFriend(param->hContact);
 	ProtoBroadcastAck(param->hContact, ACKTYPE_AUTHREQ, ACKRESULT_SUCCESS, param->hAuth, 0);
 }
 
@@ -597,27 +611,42 @@ void CSteamProto::OnFriendBlocked(const HttpResponse &response, void *arg)
 {
 	ptrA steamId((char*)arg);
 
-	if (!response.IsSuccess() || lstrcmpiA(response.Content, "true"))
-	{
-		debugLogA(__FUNCTION__ ": failed to ignore friend %s", (char*)arg);
+	if (!response.IsSuccess() || mir_strcmp(response.Content, "true")) {
+		debugLogA(__FUNCTION__ ": failed to ignore friend %s", (char*)steamId);
 		return;
 	}
+
+	MCONTACT hContact = GetContact(steamId);
+	if (hContact)
+		ContactIsBlocked(hContact);
 }
 
-void CSteamProto::OnFriendRemoved(const HttpResponse &response, void *arg)
+void CSteamProto::OnFriendUnblocked(const HttpResponse &response, void *arg)
 {
-	MCONTACT hContact = (UINT_PTR)arg;
+	ptrA steamId((char*)arg);
 
-	if (!response.IsSuccess() || lstrcmpiA(response.Content, "true"))
-	{
-		ptrA who(getStringA(hContact, "SteamID"));
+	if (!response.IsSuccess() || mir_strcmp(response.Content, "true")) {
+		debugLogA(__FUNCTION__ ": failed to unignore friend %s", (char*)steamId);
+		return;
+	}
+
+	MCONTACT hContact = GetContact(steamId);
+	if (hContact)
+		ContactIsUnblocked(hContact);
+}
 
-		debugLogA(__FUNCTION__ ": failed to remove friend %s", who);
+void CSteamProto::OnFriendRemoved(const HttpResponse &response, void *arg)
+{
+	ptrA steamId((char*)arg);
+
+	if (!response.IsSuccess() || mir_strcmp(response.Content, "true")) {
+		debugLogA(__FUNCTION__ ": failed to remove friend %s", (char*)steamId);
 		return;
 	}
 
-	setByte(hContact, "Auth", 1);
-	setDword(hContact, "DeletedTS", ::time(nullptr));
+	MCONTACT hContact = GetContact(steamId);
+	if (hContact)
+		ContactIsRemoved(hContact);
 }
 
 void CSteamProto::OnAuthRequested(const JSONNode &root, void*)
@@ -628,13 +657,14 @@ void CSteamProto::OnAuthRequested(const JSONNode &root, void*)
 	int first = 0;
 	JSONNode players = root["players"].as_array();
 	JSONNode player = players[first];
-	if (!player.isnull())
-	{
-		json_string steamId = player["steamid"].as_string();
-		MCONTACT hContact = AddContact(steamId.c_str());
-		UpdateContactDetails(hContact, player);
-		ContactIsAskingAuth(hContact);
-	}
+	if (player.isnull())
+		return;
+	
+	json_string steamId = player["steamid"].as_string();
+	CMStringW nick = player["personaname"].as_mstring();
+	MCONTACT hContact = AddContact(steamId.c_str(), nick);
+	UpdateContactDetails(hContact, player);
+	ContactIsAskingAuth(hContact);
 }
 
 void CSteamProto::OnPendingApproved(const JSONNode &root, void *arg)
@@ -645,8 +675,7 @@ void CSteamProto::OnPendingApproved(const JSONNode &root, void *arg)
 		return;
 
 	int success = root["success"].as_int();
-	if (success == 0)
-	{
+	if (success == 0) {
 		json_string error = root["error_text"].as_string();
 		debugLogA(__FUNCTION__ ": failed to approve pending from %s (%s)", steamId, ptrA(mir_utf8decodeA(error.c_str())));
 	}
@@ -660,8 +689,7 @@ void CSteamProto::OnPendingIgnoreded(const JSONNode &root, void *arg)
 		return;
 
 	int success = root["success"].as_int();
-	if (success == 0)
-	{
+	if (success == 0) {
 		json_string error = root["error_text"].as_string();
 		debugLogA(__FUNCTION__ ": failed to ignore pending from %s (%s)", steamId, ptrA(mir_utf8decodeA(error.c_str())));
 	}
@@ -671,54 +699,47 @@ void CSteamProto::OnSearchResults(const HttpResponse &response, void *arg)
 {
 	HANDLE searchType = (HANDLE)arg;
 
-	if (!response.IsSuccess())
-	{
+	if (!response.IsSuccess()) {
 		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, searchType, 0);
 		debugLogA(__FUNCTION__ ": failed to get summaries");
 		return;
 	}
 
 	JSONNode root = JSONNode::parse(response.Content);
-	if (root.isnull())
-	{
+	if (root.isnull()) {
 		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, searchType, 0);
+		debugLogA(__FUNCTION__ ": no data");
 		return;
 	}
 
 	JSONNode players = root["players"].as_array();
-	for (size_t i = 0; i < players.size(); i++)
-	{
-		JSONNode player = players[i];
-
+	for (const JSONNode &player : players) {
 		STEAM_SEARCH_RESULT ssr = { 0 };
-		ssr.hdr.cbSize = sizeof(STEAM_SEARCH_RESULT);
-		ssr.hdr.flags = PSR_UNICODE;
+		ssr.psr.cbSize = sizeof(STEAM_SEARCH_RESULT);
+		ssr.psr.flags = PSR_UNICODE;
 
 		CMStringW steamId = player["steamid"].as_mstring();
-		ssr.hdr.id.w = steamId.Detach();
+		ssr.psr.id.w = steamId.Detach();
 
 		CMStringW nick = player["personaname"].as_mstring();
-		ssr.hdr.nick.w = nick.Detach();
+		ssr.psr.nick.w = nick.Detach();
 
 		JSONNode node = player["realname"];
-		if (!node.isnull())
-		{
+		if (!node.isnull()) {
 			CMStringW realName = node.as_mstring();
-			if (!realName.IsEmpty())
-			{
+			if (!realName.IsEmpty()) {
 				int pos = realName.Find(' ', 1);
-				if (pos != -1)
-				{
-					ssr.hdr.firstName.w = realName.Mid(0, pos).Detach();
-					ssr.hdr.lastName.w = realName.Mid(pos + 1).Detach();
+				if (pos != -1) {
+					ssr.psr.firstName.w = realName.Mid(0, pos).Detach();
+					ssr.psr.lastName.w = realName.Mid(pos + 1).Detach();
 				}
 				else
-					ssr.hdr.firstName.w = realName.Detach();
+					ssr.psr.firstName.w = realName.Detach();
 			}
 		}
 
-		//ssr.contact = contact;
-		ssr.data = json_copy(&player); // FIXME: is this needed and safe (no memleak) to be here?
+		// todo: is this needed and safe (no memleak) to be here?
+		ssr.data = json_copy(&player);
 
 		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, searchType, (LPARAM)&ssr);
 	}
@@ -728,17 +749,16 @@ void CSteamProto::OnSearchResults(const HttpResponse &response, void *arg)
 
 void CSteamProto::OnSearchByNameStarted(const HttpResponse &response, void *arg)
 {
-	if (!response.IsSuccess())
-	{
+	if (!response.IsSuccess()) {
 		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)arg, 0);
 		debugLogA(__FUNCTION__ ": failed to get results");
 		return;
 	}
 
 	JSONNode root = JSONNode::parse(response.Content);
-	if (root.isnull())
-	{
+	if (root.isnull()) {
 		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)arg, 0);
+		debugLogA(__FUNCTION__ ": no data");
 		return;
 	}
 
@@ -749,27 +769,22 @@ void CSteamProto::OnSearchByNameStarted(const HttpResponse &response, void *arg)
 	std::string steamIds;
 
 	JSONNode results = root["results"].as_array();
-	for (size_t i = 0; i < results.size(); i++)
-	{
-		JSONNode result = results[i];
+	for (const JSONNode &result : results) {
 		json_string steamId = result["steamid"].as_string();
 		steamIds.append(steamId).append(",");
 	}
 
-	if (!steamIds.empty())
-	{
-		// remove trailing ","
-		steamIds.pop_back();
-
-		ptrA token(getStringA("TokenSecret"));
-
-		PushRequest(
-			new GetUserSummariesRequest(token, steamIds.c_str()),
-			&CSteamProto::OnSearchResults,
-			(HANDLE)arg);
-	}
-	else
-	{
+	if (steamIds.empty()) {
 		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)arg, 0);
+		return;
 	}
+
+	// remove trailing ","
+	steamIds.pop_back();
+
+	ptrA token(getStringA("TokenSecret"));
+	PushRequest(
+		new GetUserSummariesRequest(token, steamIds.c_str()),
+		&CSteamProto::OnSearchResults,
+		(HANDLE)arg);
 }
diff --git a/protocols/Steam/src/steam_crypt.cpp b/protocols/Steam/src/steam_crypt.cpp
new file mode 100644
index 0000000000..7c7fa07312
--- /dev/null
+++ b/protocols/Steam/src/steam_crypt.cpp
@@ -0,0 +1,114 @@
+#include "stdafx.h"
+
+#pragma comment(lib, "crypt32.lib")
+
+int CSteamProto::RsaEncrypt(const char *pszModulus, DWORD &exponent, const char *data, BYTE *encryptedData, DWORD &encryptedSize)
+{
+	DWORD cchModulus = (DWORD)mir_strlen(pszModulus);
+	int result = 0;
+	BYTE *pbBuffer = nullptr;
+	BYTE *pKeyBlob = nullptr;
+	HCRYPTKEY phKey = 0;
+	HCRYPTPROV hCSP = 0;
+
+	// convert hex string to byte array
+	DWORD cbLen = 0, dwSkip = 0, dwFlags = 0;
+	if (!CryptStringToBinaryA(pszModulus, cchModulus, CRYPT_STRING_HEX, nullptr, &cbLen, &dwSkip, &dwFlags))
+	{
+		result = GetLastError();
+		goto exit;
+	}
+
+	// allocate a new buffer.
+	pbBuffer = (BYTE*)malloc(cbLen);
+	if (!CryptStringToBinaryA(pszModulus, cchModulus, CRYPT_STRING_HEX, pbBuffer, &cbLen, &dwSkip, &dwFlags))
+	{
+		result = GetLastError();
+		goto exit;
+	}
+
+	// reverse byte array, because of microsoft
+	for (int i = 0; i < (int)(cbLen / 2); ++i)
+	{
+		BYTE temp = pbBuffer[cbLen - i - 1];
+		pbBuffer[cbLen - i - 1] = pbBuffer[i];
+		pbBuffer[i] = temp;
+	}
+	
+	if (!CryptAcquireContext(&hCSP, nullptr, nullptr, PROV_RSA_AES, CRYPT_SILENT) &&
+		!CryptAcquireContext(&hCSP, nullptr, nullptr, PROV_RSA_AES, CRYPT_SILENT | CRYPT_NEWKEYSET))
+	{
+		result = GetLastError();
+		goto exit;
+	}
+
+	// Move the key into the key container.
+	DWORD cbKeyBlob = sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY) + cbLen;
+	pKeyBlob = (BYTE*)malloc(cbKeyBlob);
+
+	// Fill in the data.
+	PUBLICKEYSTRUC *pPublicKey = (PUBLICKEYSTRUC*)pKeyBlob;
+	pPublicKey->bType = PUBLICKEYBLOB; 
+	pPublicKey->bVersion = CUR_BLOB_VERSION;  // Always use this value.
+	pPublicKey->reserved = 0;                 // Must be zero.
+	pPublicKey->aiKeyAlg = CALG_RSA_KEYX;     // RSA public-key key exchange. 
+
+	// The next block of data is the RSAPUBKEY structure.
+	RSAPUBKEY *pRsaPubKey = (RSAPUBKEY*)(pKeyBlob + sizeof(PUBLICKEYSTRUC));
+	pRsaPubKey->magic = 0x31415352; // RSA1 // Use public key
+	pRsaPubKey->bitlen = cbLen * 8;  // Number of bits in the modulus.
+	pRsaPubKey->pubexp = exponent; // Exponent.
+
+	// Copy the modulus into the blob. Put the modulus directly after the
+	// RSAPUBKEY structure in the blob.
+	BYTE *pKey = (BYTE*)(((BYTE *)pRsaPubKey) + sizeof(RSAPUBKEY));
+	//pKeyBlob + sizeof(BLOBHEADER)+ sizeof(RSAPUBKEY); 
+	memcpy(pKey, pbBuffer, cbLen);
+
+	// Now import public key	
+	if (!CryptImportKey(hCSP, pKeyBlob, cbKeyBlob, 0, 0, &phKey))
+	{
+		result = GetLastError();
+		goto exit;
+	}
+
+	DWORD dataSize = (DWORD)mir_strlen(data);
+
+	// if data is not allocated just renurn size
+	if (encryptedData == nullptr)
+	{
+		// get length of encrypted data
+		if (!CryptEncrypt(phKey, 0, TRUE, 0, nullptr, &encryptedSize, dataSize))
+			result = GetLastError();
+		goto exit;
+	}
+
+	// encrypt password
+	memcpy(encryptedData, data, dataSize);
+	if (!CryptEncrypt(phKey, 0, TRUE, 0, encryptedData, &dataSize, encryptedSize))
+	{
+		result = GetLastError();
+		goto exit;
+	}
+
+	// reverse byte array again
+	for (int i = 0; i < (int)(encryptedSize / 2); ++i)
+	{
+		BYTE temp = encryptedData[encryptedSize - i - 1];
+		encryptedData[encryptedSize - i - 1] = encryptedData[i];
+		encryptedData[i] = temp;
+	}
+
+exit:
+	if (pKeyBlob)
+		free(pKeyBlob);
+	if (phKey)
+		CryptDestroyKey(phKey);
+	
+	if (pbBuffer)
+		free(pbBuffer);
+	if (hCSP)
+		CryptReleaseContext(hCSP, 0);
+
+	return 0;
+}
diff --git a/protocols/Steam/src/steam_dialogs.cpp b/protocols/Steam/src/steam_dialogs.cpp
index 0565040d8d..b3731551b0 100644
--- a/protocols/Steam/src/steam_dialogs.cpp
+++ b/protocols/Steam/src/steam_dialogs.cpp
@@ -20,11 +20,9 @@ void CSteamPasswordEditor::OnInitDialog()
 
 void CSteamPasswordEditor::OnOk(CCtrlButton*)
 {
-	if (m_proto->password != nullptr)
-		mir_free(m_proto->password);
-	m_proto->password = m_password.GetText();
+	m_proto->m_password = m_password.GetText();
 	if (m_savePermanently.Enabled())
-		m_proto->setWString("Password", m_proto->password);
+		m_proto->setWString("Password", m_proto->m_password);
 
 	EndDialog(m_hwnd, DIALOG_RESULT_OK);
 }
diff --git a/protocols/Steam/src/steam_events.cpp b/protocols/Steam/src/steam_events.cpp
index ef2d59ed40..0a5f7d4fde 100644
--- a/protocols/Steam/src/steam_events.cpp
+++ b/protocols/Steam/src/steam_events.cpp
@@ -69,7 +69,7 @@ int CSteamProto::OnIdleChanged(WPARAM, LPARAM lParam)
 		Idle_GetInfo(mii);
 
 		// Compute time when user really became idle
-		m_idleTS = time(nullptr) - mii.idleTime * 60;
+		m_idleTS = now() - mii.idleTime * 60;
 		setDword("IdleTS", m_idleTS);
 	}
 	else
diff --git a/protocols/Steam/src/steam_history.cpp b/protocols/Steam/src/steam_history.cpp
index a897037bae..a7f0d22e61 100644
--- a/protocols/Steam/src/steam_history.cpp
+++ b/protocols/Steam/src/steam_history.cpp
@@ -54,7 +54,7 @@ void CSteamProto::OnGotHistoryMessages(const JSONNode &root, void *arg)
 		JSONNode message = messages[i - 1];
 
 		long long accountId = _wtoi64(message["accountid"].as_mstring());
-		const char *authorSteamId = AccountIdToSteamId(accountId);
+		const char *steamId = AccountIdToSteamId(accountId);
 
 		json_string text = message["message"].as_string();
 
@@ -68,13 +68,11 @@ void CSteamProto::OnGotHistoryMessages(const JSONNode &root, void *arg)
 		recv.timestamp = timestamp;
 		recv.szMessage = (char*)text.c_str();
 
-		MCONTACT hContact = FindContact(cSteamId);
+		MCONTACT hContact = GetContact(cSteamId);
 		if (!hContact)
 			return;
 
-		// Self SteamID
-		ptrA steamId(getStringA("SteamID"));
-		if (strcmp(steamId, authorSteamId))
+		if (IsMe(steamId))
 		{
 			// Received message
 			ProtoChainRecvMsg(hContact, &recv);
diff --git a/protocols/Steam/src/steam_instances.cpp b/protocols/Steam/src/steam_instances.cpp
deleted file mode 100644
index b88ada2bb4..0000000000
--- a/protocols/Steam/src/steam_instances.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "stdafx.h"
-
-int CSteamProto::CompareProtos(const CSteamProto *p1, const CSteamProto *p2)
-{
-	return lstrcmp(p1->m_tszUserName, p2->m_tszUserName);
-}
-
-LIST<CSteamProto> CSteamProto::InstanceList(1, CSteamProto::CompareProtos);
-
-CSteamProto* CSteamProto::InitProtoInstance(const char* protoName, const wchar_t* userName)
-{
-	CSteamProto *ppro = new CSteamProto(protoName, userName);
-	InstanceList.insert(ppro);
-
-	return ppro;
-}
-
-int CSteamProto::UninitProtoInstance(CSteamProto* ppro)
-{
-	InstanceList.remove(ppro);
-	delete ppro;
-
-	return 0;
-}
-
-void CSteamProto::UninitProtoInstances()
-{
-	for (int i = InstanceList.getCount(); i > 0; i--)
-		UninitProtoInstance(InstanceList[i]);
-	InstanceList.destroy();
-}
-
-CSteamProto* CSteamProto::GetContactProtoInstance(MCONTACT hContact)
-{
-	char *proto = GetContactProto(hContact);
-	if (proto == nullptr)
-		return nullptr;
-
-	for (int i = 0; i < InstanceList.getCount(); i++)
-		if (!mir_strcmp(proto, InstanceList[i]->m_szModuleName))
-			return InstanceList[i];
-
-	return nullptr;
-}
\ No newline at end of file
diff --git a/protocols/Steam/src/steam_login.cpp b/protocols/Steam/src/steam_login.cpp
index def14a9d26..773556e84b 100644
--- a/protocols/Steam/src/steam_login.cpp
+++ b/protocols/Steam/src/steam_login.cpp
@@ -8,79 +8,39 @@ bool CSteamProto::IsOnline()
 bool CSteamProto::IsMe(const char *steamId)
 {
 	ptrA mySteamId(getStringA("SteamID"));
-	if (!lstrcmpA(steamId, mySteamId))
-		return true;
-
-	return false;
+	return mir_strcmp(steamId, mySteamId) == 0;
 }
 
 void CSteamProto::Login()
 {
 	ptrA token(getStringA("TokenSecret"));
 	ptrA sessionId(getStringA("SessionID"));
-	if (mir_strlen(token) > 0 && mir_strlen(sessionId) > 0)
-	{
+	if (mir_strlen(token) > 0 && mir_strlen(sessionId) > 0) {
 		PushRequest(
 			new LogonRequest(token),
 			&CSteamProto::OnLoggedOn);
+		return;
 	}
-	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"));
-	if (mir_strlen(token) <= 0)
-		return false;
-
-	HttpRequest *request = new LogonRequest(token);
-	HttpResponse *response = SendRequest(request);
-
-	bool success = false;
-	if (response)
-	{
-		JSONNode root = JSONNode::parse(response->Content);
-		if (root.isnull()) {
-			json_string error = root["error"].as_string();
-			if (!mir_strcmpi(error.c_str(), "OK"))
-			{
-				json_string umqId = root["umqid"].as_string();
-				setString("UMQID", umqId.c_str());
-
-				long messageId = root["message"].as_int();
-				setDword("MessageID", messageId);
-
-				success = true;
-			}
-		}
+	
+	T2Utf username(getWStringA("Username"));
+	if (username == NULL) {
+		SetStatus(ID_STATUS_OFFLINE);
+		return;
 	}
 
-	delete response;
-
-	return success;
+	PushRequest(
+		new GetRsaKeyRequest(username),
+		&CSteamProto::OnGotRsaKey);
 }
 
-void CSteamProto::LogOut()
+void CSteamProto::Logout()
 {
-	isTerminated = true;
+	m_isTerminated = true;
 	if (m_hRequestQueueThread)
 		SetEvent(m_hRequestsQueueEvent);
 
 	ptrA token(getStringA("TokenSecret"));
-	if (mir_strlen(token) > 0)
-	{
+	if (mir_strlen(token) > 0) {
 		ptrA umqid(getStringA("UMQID"));
 		SendRequest(new LogoffRequest(token, umqid));
 	}
@@ -88,14 +48,12 @@ void CSteamProto::LogOut()
 
 void CSteamProto::OnGotRsaKey(const JSONNode &root, void*)
 {
-	if (root.isnull())
-	{
+	if (root.isnull()) {
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
 	}
 
-	if (!root["success"].as_bool())
-	{
+	if (!root["success"].as_bool()) {
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
 	}
@@ -113,16 +71,14 @@ void CSteamProto::OnGotRsaKey(const JSONNode &root, void*)
 
 	DWORD error = 0;
 	DWORD encryptedSize = 0;
-	if ((error = RsaEncrypt(modulus.c_str(), exponent, szPassword, nullptr, encryptedSize)) != 0)
-	{
+	if ((error = RsaEncrypt(modulus.c_str(), exponent, szPassword, nullptr, encryptedSize)) != 0) {
 		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)
-	{
+	if ((error = RsaEncrypt(modulus.c_str(), exponent, szPassword, encryptedPassword, encryptedSize)) != 0) {
 		debugLogA(__FUNCTION__ ": encryption error (%lu)", error);
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
@@ -135,20 +91,26 @@ void CSteamProto::OnGotRsaKey(const JSONNode &root, void*)
 	T2Utf username(getWStringA("Username"));
 
 	ptrA twoFactorCode(getStringA("TwoFactorCode"));
-	if (!twoFactorCode) twoFactorCode = mir_strdup("");
+	if (!twoFactorCode)
+		twoFactorCode = mir_strdup("");
 
 	ptrA guardId(getStringA("GuardId"));
-	if (!guardId) guardId = mir_strdup("");
+	if (!guardId)
+		guardId = mir_strdup("");
 	ptrA guardCode(getStringA("GuardCode"));
-	if (!guardCode) guardCode = mir_strdup("");
+	if (!guardCode)
+		guardCode = mir_strdup("");
 
 	ptrA captchaId(getStringA("CaptchaId"));
-	if (!captchaId) captchaId = mir_strdup("-1");
+	if (!captchaId)
+		captchaId = mir_strdup("-1");
 	ptrA captchaText(getStringA("CaptchaText"));
-	if (!captchaText) captchaText = mir_strdup("");
+	if (!captchaText)
+		captchaText = mir_strdup("");
 
 	PushRequest(
-		new AuthorizationRequest(username, base64RsaEncryptedPassword, timestamp.c_str(), twoFactorCode, guardCode, guardId, captchaId, captchaText),
+		new AuthorizationRequest(username, base64RsaEncryptedPassword, timestamp.c_str(), twoFactorCode,
+			guardCode, guardId, captchaId, captchaText),
 		&CSteamProto::OnAuthorization);
 }
 
@@ -156,15 +118,13 @@ void CSteamProto::OnGotCaptcha(const HttpResponse &response, void *arg)
 {
 	ptrA captchaId((char*)arg);
 
-	if (!response.IsSuccess())
-	{
+	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)
-	{
+	if (captchaDialog.DoModal() != DIALOG_RESULT_OK) {
 		DeleteAuthSettings();
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
@@ -181,21 +141,18 @@ void CSteamProto::OnGotCaptcha(const HttpResponse &response, void *arg)
 
 void CSteamProto::OnAuthorization(const HttpResponse &response, void*)
 {
-	if (!response)
-	{
+	if (!response) {
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
 	}
 
 	JSONNode root = JSONNode::parse(response.Content);
-	if (root.isnull())
-	{
+	if (root.isnull()) {
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
 	}
 
-	if (!root["success"].as_bool())
-	{
+	if (!root["success"].as_bool()) {
 		OnAuthorizationError(root);
 		return;
 	}
@@ -215,24 +172,33 @@ void CSteamProto::DeleteAuthSettings()
 void CSteamProto::OnAuthorizationError(const JSONNode &root)
 {
 	CMStringW message = root["message"].as_mstring();
-	debugLogA(__FUNCTION__ ": %s", _T2A(message.GetBuffer()));
-	if (!mir_wstrcmpi(message, L"Incorrect login."))
-	{
+	if (message == L"Incorrect login.") {
 		// We can't continue with incorrect login/password
+		debugLogA(__FUNCTION__ ": incorrect login");
 		DeleteAuthSettings();
 		SetStatus(ID_STATUS_OFFLINE);
+		ShowNotification(message);
+		return;
+	}
+
+	if (root["clear_password_field"].as_bool()) {
+		// describes if the password field was cleared. This can also be true if the twofactor code was wrong
+		debugLogA(__FUNCTION__ ": clear password field");
+		delSetting("Passowrd"); // experiment
+		delSetting("TwoFactorCode");
+		SetStatus(ID_STATUS_OFFLINE);
+		ShowNotification(message);
 		return;
 	}
 
 	T2Utf username(getWStringA("Username"));
 
-	if (root["requires_twofactor"].as_bool())
-	{
+	if (root["requires_twofactor"].as_bool()) {
 		debugLogA(__FUNCTION__ ": requires twofactor");
+		delSetting("TwoFactorCode");
 
 		CSteamTwoFactorDialog twoFactorDialog(this);
-		if (twoFactorDialog.DoModal() != DIALOG_RESULT_OK)
-		{
+		if (twoFactorDialog.DoModal() != DIALOG_RESULT_OK) {
 			DeleteAuthSettings();
 			SetStatus(ID_STATUS_OFFLINE);
 			return;
@@ -246,40 +212,24 @@ void CSteamProto::OnAuthorizationError(const JSONNode &root)
 		return;
 	}
 
-	if (root["clear_password_field"].as_bool())
-	{
-		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 (root["emailauth_needed"].as_bool())
-	{
+	if (root["emailauth_needed"].as_bool()) {
 		debugLogA(__FUNCTION__ ": emailauth needed");
 
 		std::string guardId = root["emailsteamid"].as_string();
 		ptrA oldGuardId(getStringA("GuardId"));
-		if (mir_strcmp(guardId.c_str(), oldGuardId) == 0)
-		{
+		if (mir_strcmp(guardId.c_str(), oldGuardId) == 0) {
 			delSetting("GuardId");
 			delSetting("GuardCode");
-			PushRequest(
-				new GetRsaKeyRequest(username),
-				&CSteamProto::OnGotRsaKey);
-			return;
 		}
 
-		std::string domain = root["emaildomain"].as_string();
+		json_string domain = root["emaildomain"].as_string();
 
 		// Make absolute link
 		if (domain.find("://") == std::string::npos)
 			domain = "http://" + domain;
 
 		CSteamGuardDialog guardDialog(this, domain.c_str());
-		if (guardDialog.DoModal() != DIALOG_RESULT_OK)
-		{
+		if (guardDialog.DoModal() != DIALOG_RESULT_OK) {
 			DeleteAuthSettings();
 			SetStatus(ID_STATUS_OFFLINE);
 			return;
@@ -294,10 +244,11 @@ void CSteamProto::OnAuthorizationError(const JSONNode &root)
 		return;
 	}
 
-	if (root["captcha_needed"].as_bool())
-	{
+	if (root["captcha_needed"].as_bool()) {
 		debugLogA(__FUNCTION__ ": captcha needed");
-		std::string captchaId = root["captcha_gid"].as_string();
+		delSetting("CaptchaId");
+		delSetting("CaptchaText");
+		json_string captchaId = root["captcha_gid"].as_string();
 		PushRequest(
 			new GetCaptchaRequest(captchaId.c_str()),
 			&CSteamProto::OnGotCaptcha,
@@ -305,44 +256,36 @@ void CSteamProto::OnAuthorizationError(const JSONNode &root)
 		return;
 	}
 
+	// unhadled error
 	DeleteAuthSettings();
 	SetStatus(ID_STATUS_OFFLINE);
-	ShowNotification(L"Steam", message);
+	ShowNotification(message);
 }
 
 void CSteamProto::OnAuthorizationSuccess(const JSONNode &root)
 {
 	DeleteAuthSettings();
 
-	if (!root["login_complete"].as_bool())
-	{
+	if (!root["login_complete"].as_bool()) {
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
 	}
 
-	std::string oauth = root["oauth"].as_string();
+	json_string oauth = root["oauth"].as_string();
 	JSONNode node = JSONNode::parse(oauth.c_str());
-	if (node.isnull())
-	{
+	if (node.isnull()) {
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
 	}
 
-	std::string steamId = node["steamid"].as_string();
+	json_string steamId = node["steamid"].as_string();
 	setString("SteamID", steamId.c_str());
 
-	std::string token = node["oauth_token"].as_string();
+	json_string token = node["oauth_token"].as_string();
 	setString("TokenSecret", token.c_str());
 
-	std::string cookie = node["webcookie"].as_string();
-
-	SendRequest(
-		new GetSessionRequest(token.c_str(), steamId.c_str(), cookie.c_str()),
-		&CSteamProto::OnGotSession);
-
-	// We need to load homepage to get sessionid cookie
 	SendRequest(
-		new GetSessionRequest2(),
+		new GetSessionRequest2(token.c_str(), steamId.c_str()),
 		&CSteamProto::OnGotSession);
 
 	PushRequest(
@@ -352,12 +295,13 @@ void CSteamProto::OnAuthorizationSuccess(const JSONNode &root)
 
 void CSteamProto::OnGotSession(const HttpResponse &response, void*)
 {
-	if (!response)
+	if (!response) {
+		debugLogA(__FUNCTION__ ": failed to get session id");
 		return;
+	}
 
-	for (size_t i = 0; i < response.Headers.GetSize(); i++)
-	{
-		if (lstrcmpiA(response.Headers[i]->szName, "Set-Cookie"))
+	for (size_t i = 0; i < response.Headers.GetSize(); i++) {
+		if (mir_strcmpi(response.Headers[i]->szName, "Set-Cookie"))
 			continue;
 
 		std::string cookies = response.Headers[i]->szValue;
@@ -378,48 +322,38 @@ void CSteamProto::HandleTokenExpired()
 	if (isLoginAgain) {
 		// Notify error to user
 		debugLogA(__FUNCTION__ ": cannot obtain connection token");
-		ShowNotification(L"Steam", TranslateT("Cannot obtain connection token."));
+		ShowNotification(TranslateT("Cannot obtain connection token."));
 		// Just go offline; it also resets the isLoginAgain to false
 		SetStatus(ID_STATUS_OFFLINE);
+		return;
 	}
-	else
-	{
-		// Remember we are trying to relogin
-		isLoginAgain = true;
-
-		// Remember status user wanted
-		int desiredStatus = m_iDesiredStatus;
 
-		// Set status to offline
-		SetStatus(ID_STATUS_OFFLINE);
+	// Remember we are trying to relogin
+	isLoginAgain = true;
 
-		// Try to login again automatically
-		SetStatus(desiredStatus);
-	}
+	Login();
+	return;
 }
 
 void CSteamProto::OnLoggedOn(const HttpResponse &response, void*)
 {
-	if (!response.IsSuccess())
-	{
+	if (!response.IsSuccess()) {
 		// Probably timeout or no connection, we can do nothing here
 		debugLogA(__FUNCTION__ ": unknown login error");
-		ShowNotification(L"Steam", TranslateT("Unknown login error."));
+		ShowNotification(TranslateT("Unknown login error."));
 		SetStatus(ID_STATUS_OFFLINE);
 		return;
 	}
 
-	if (response.GetStatusCode() == HTTP_CODE_UNAUTHORIZED)
-	{
+	if (response.GetStatusCode() == HTTP_CODE_UNAUTHORIZED) {
 		// Probably expired TokenSecret
 		HandleTokenExpired();
 		return;
 	}
 
 	JSONNode root = JSONNode::parse(response.Content);
-	CMStringW error = root["error"].as_mstring();
-	if (mir_wstrcmpi(error, L"OK"))
-	{
+	json_string error = root["error"].as_string();
+	if (error != "OK") {
 		// Probably expired TokenSecret
 		HandleTokenExpired();
 		return;
@@ -431,8 +365,7 @@ void CSteamProto::OnLoggedOn(const HttpResponse &response, void*)
 	long messageId = root["umqid"].as_int();
 	setDword("MessageID", messageId);
 	
-	if (m_lastMessageTS <= 0)
-	{
+	if (m_lastMessageTS <= 0) {
 		time_t timestamp = _wtoi64(root["utc_timestamp"].as_mstring());
 		setDword("LastMessageTS", timestamp);
 	}
@@ -441,6 +374,10 @@ void CSteamProto::OnLoggedOn(const HttpResponse &response, void*)
 	ptrA token(getStringA("TokenSecret"));
 	ptrA steamId(getStringA("SteamID"));
 
+	SendRequest(
+		new GetSessionRequest2(token, steamId),
+		&CSteamProto::OnGotSession);
+
 	// send this request immediately, so we can start polling thread with already loaded all contacts
 	SendRequest(
 		new GetFriendListRequest(token, steamId),
@@ -452,3 +389,23 @@ void CSteamProto::OnLoggedOn(const HttpResponse &response, void*)
 	// start polling thread
 	m_hPollingThread = ForkThreadEx(&CSteamProto::PollingThread, nullptr, nullptr);
 }
+
+void CSteamProto::OnReLogin(const JSONNode &root, void*)
+{
+	if (root.isnull()) {
+		SetStatus(ID_STATUS_OFFLINE);
+		return;
+	}
+
+	json_string error = root["error"].as_string();
+	if (error != "OK") {
+		SetStatus(ID_STATUS_OFFLINE);
+		return;
+	}
+
+	json_string umqId = root["umqid"].as_string();
+	setString("UMQID", umqId.c_str());
+
+	long messageId = root["message"].as_int();
+	setDword("MessageID", messageId);
+}
\ No newline at end of file
diff --git a/protocols/Steam/src/steam_menus.cpp b/protocols/Steam/src/steam_menus.cpp
index 98b86f333c..2387f19f9d 100644
--- a/protocols/Steam/src/steam_menus.cpp
+++ b/protocols/Steam/src/steam_menus.cpp
@@ -6,21 +6,27 @@ HGENMENU CSteamProto::contactMenuItems[CMI_MAX];
 template<int(__cdecl CSteamProto::*Service)(WPARAM, LPARAM)>
 INT_PTR GlobalService(WPARAM wParam, LPARAM lParam)
 {
-	CSteamProto *ppro = CSteamProto::GetContactProtoInstance((MCONTACT)wParam);
+	CSteamProto *ppro = CSteamProto::GetContactAccount((MCONTACT)wParam);
 	return ppro ? (ppro->*Service)(wParam, lParam) : 0;
 }
 
-INT_PTR CSteamProto::MenuChooseService(WPARAM wParam, LPARAM lParam)
+int CSteamProto::AuthRequestCommand(WPARAM hContact, LPARAM)
 {
-	if (lParam)
-		*(void**)lParam = (void*)wParam;
+	ProtoChainSend(hContact, PSS_AUTHREQUEST, 0, 0);
 
 	return 0;
 }
 
-int CSteamProto::AuthRequestCommand(WPARAM hContact, LPARAM)
+int CSteamProto::AuthRevokeCommand(WPARAM hContact, LPARAM)
 {
-	ProtoChainSend(hContact, PSS_AUTHREQUEST, 0, 0);
+	ptrA token(getStringA("TokenSecret"));
+	ptrA sessionId(getStringA("SessionID"));
+	ptrA steamId(getStringA("SteamID"));
+	char *who = getStringA(hContact, "SteamID");
+	PushRequest(
+		new RemoveFriendRequest(token, sessionId, steamId, who),
+		&CSteamProto::OnFriendRemoved,
+		(void*)who);
 
 	return 0;
 }
@@ -40,12 +46,28 @@ int CSteamProto::BlockCommand(WPARAM hContact, LPARAM)
 	return 0;
 }
 
+int CSteamProto::UnblockCommand(WPARAM hContact, LPARAM)
+{
+	ptrA token(getStringA("TokenSecret"));
+	ptrA sessionId(getStringA("SessionID"));
+	ptrA steamId(getStringA("SteamID"));
+	char *who = getStringA(hContact, "SteamID");
+
+	PushRequest(
+		new UnblockFriendRequest(token, sessionId, steamId, who),
+		&CSteamProto::OnFriendUnblocked,
+		who);
+
+	return 0;
+}
+
 int CSteamProto::JoinToGameCommand(WPARAM hContact, LPARAM)
 {
 	char url[MAX_PATH];
 	DWORD gameId = getDword(hContact, "GameID", 0);
 	mir_snprintf(url, "steam://rungameid/%lu", gameId);
 	Utils_OpenUrl(url);
+
 	return 0;
 }
 
@@ -53,7 +75,6 @@ INT_PTR CSteamProto::OpenBlockListCommand(WPARAM, LPARAM)
 {
 	ptrA token(getStringA("TokenSecret"));
 	ptrA steamId(getStringA("SteamID"));
-
 	PushRequest(
 		new GetFriendListRequest(token, steamId, "ignoredfriend"),
 		&CSteamProto::OnGotBlockList);
@@ -67,29 +88,31 @@ int CSteamProto::OnPrebuildContactMenu(WPARAM wParam, LPARAM)
 	if (!hContact)
 		return 0;
 
-	if (!this->IsOnline() || lstrcmpA(GetContactProto(hContact), m_szModuleName))
+	if (!IsOnline() || mir_strcmp(GetContactProto(hContact), m_szModuleName))
 		return 0;
 
-	//bool ctrlPressed = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
+	bool ctrlPressed = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
 
 	bool authNeeded = getBool(hContact, "Auth", 0);
-	Menu_ShowItem(contactMenuItems[CMI_AUTH_REQUEST], authNeeded);
+	Menu_ShowItem(contactMenuItems[CMI_AUTH_REQUEST], authNeeded || ctrlPressed);
+	Menu_ShowItem(contactMenuItems[CMI_AUTH_REVOKE], !authNeeded || ctrlPressed);
 
 	bool isBlocked = getBool(hContact, "Block", 0);
-	Menu_ShowItem(contactMenuItems[CMI_BLOCK], !isBlocked);
+	Menu_ShowItem(contactMenuItems[CMI_BLOCK], !isBlocked || ctrlPressed);
+	Menu_ShowItem(contactMenuItems[CMI_UNBLOCK], isBlocked || ctrlPressed);
 
 	DWORD gameId = getDword(hContact, "GameID", 0);
-	Menu_ShowItem(contactMenuItems[CMI_JOIN_GAME], gameId > 0);
+	Menu_ShowItem(contactMenuItems[CMI_JOIN_GAME], gameId || ctrlPressed);
 
 	return 0;
 }
 
 int CSteamProto::PrebuildContactMenu(WPARAM wParam, LPARAM lParam)
 {
-	for (int i = 0; i < _countof(CSteamProto::contactMenuItems); i++)
+	for (int i = 0; i < CMI_MAX; i++)
 		Menu_ShowItem(CSteamProto::contactMenuItems[i], false);
 
-	CSteamProto* ppro = CSteamProto::GetContactProtoInstance((MCONTACT)wParam);
+	CSteamProto* ppro = CSteamProto::GetContactAccount((MCONTACT)wParam);
 	return (ppro) ? ppro->OnPrebuildContactMenu(wParam, lParam) : 0;
 }
 
@@ -100,11 +123,11 @@ void CSteamProto::OnInitStatusMenu()
 	mi.root = Menu_GetProtocolRoot(this);
 
 	// Show block list
-	mi.pszService = "/BlockList";
-	CreateProtoService(mi.pszService, &CSteamProto::OpenBlockListCommand);
-	mi.name.w = LPGENW("Blocked contacts");
-	mi.position = 200000 + SMI_BLOCKED_LIST;
-	Menu_AddProtoMenuItem(&mi, m_szModuleName);
+	//mi.pszService = "/BlockList";
+	//CreateProtoService(mi.pszService, &CSteamProto::OpenBlockListCommand);
+	//mi.name.w = LPGENW("Blocked contacts");
+	//mi.position = 200000 + SMI_BLOCKED_LIST;
+	//Menu_AddProtoMenuItem(&mi, m_szModuleName);
 }
 
 void CSteamProto::InitMenus()
@@ -125,15 +148,33 @@ void CSteamProto::InitMenus()
 	contactMenuItems[CMI_AUTH_REQUEST] = Menu_AddContactMenuItem(&mi);
 	CreateServiceFunction(mi.pszService, GlobalService<&CSteamProto::AuthRequestCommand>);
 
+	// "Revoke authorization"
+	SET_UID(mi, 0x619efdcb, 0x99c0, 0x44a8, 0xbf, 0x28, 0xc3, 0xe0, 0x2f, 0xb3, 0x7e, 0x77);
+	mi.pszService = MODULE "/RevokeAuth";
+	mi.name.w = LPGENW("Revoke authorization");
+	mi.position = -201001001 + CMI_AUTH_REVOKE;
+	mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REVOKE);
+	contactMenuItems[CMI_AUTH_REVOKE] = Menu_AddContactMenuItem(&mi);
+	CreateServiceFunction(mi.pszService, GlobalService<&CSteamProto::AuthRevokeCommand>);
+
 	// "Block"
 	SET_UID(mi, 0xc6169b8f, 0x53ab, 0x4242, 0xbe, 0x90, 0xe2, 0x4a, 0xa5, 0x73, 0x88, 0x32);
 	mi.pszService = MODULE "/Block";
 	mi.name.w = LPGENW("Block");
 	mi.position = -201001001 + CMI_BLOCK;
-	mi.hIcolibItem = Skin_GetIconHandle(SKINICON_AUTH_REQUEST);
+	mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_OFF);
 	contactMenuItems[CMI_BLOCK] = Menu_AddContactMenuItem(&mi);
 	CreateServiceFunction(mi.pszService, GlobalService<&CSteamProto::BlockCommand>);
 
+	// "Unblock"
+	SET_UID(mi, 0xc6169b8f, 0x53ab, 0x4242, 0xbe, 0x90, 0xe2, 0x4a, 0xa5, 0x73, 0x88, 0x32);
+	mi.pszService = MODULE "/Unblock";
+	mi.name.w = LPGENW("Unblock");
+	mi.position = -201001001 + CMI_UNBLOCK;
+	mi.hIcolibItem = Skin_GetIconHandle(SKINICON_OTHER_ON);
+	contactMenuItems[CMI_UNBLOCK] = Menu_AddContactMenuItem(&mi);
+	CreateServiceFunction(mi.pszService, GlobalService<&CSteamProto::UnblockCommand>);
+
 	mi.flags |= CMIF_NOTOFFLINE;
 
 	// "Join to game"
@@ -145,10 +186,3 @@ void CSteamProto::InitMenus()
 	contactMenuItems[CMI_JOIN_GAME] = Menu_AddContactMenuItem(&mi);
 	CreateServiceFunction(mi.pszService, GlobalService<&CSteamProto::JoinToGameCommand>);
 }
-
-void CSteamProto::UninitMenus()
-{
-	Menu_RemoveItem(contactMenuItems[CMI_AUTH_REQUEST]);
-	Menu_RemoveItem(contactMenuItems[CMI_BLOCK]);
-	Menu_RemoveItem(contactMenuItems[CMI_JOIN_GAME]);
-}
diff --git a/protocols/Steam/src/steam_messages.cpp b/protocols/Steam/src/steam_messages.cpp
index a4c8ebee94..e4d90643be 100644
--- a/protocols/Steam/src/steam_messages.cpp
+++ b/protocols/Steam/src/steam_messages.cpp
@@ -4,7 +4,6 @@ struct SendMessageParam
 {
 	MCONTACT hContact;
 	HANDLE hMessage;
-	char *message;
 };
 
 int CSteamProto::OnSendMessage(MCONTACT hContact, const char *message)
@@ -14,7 +13,6 @@ int CSteamProto::OnSendMessage(MCONTACT hContact, const char *message)
 	SendMessageParam *param = (SendMessageParam*)mir_calloc(sizeof(SendMessageParam));
 	param->hContact = hContact;
 	param->hMessage = (HANDLE)hMessage;
-	param->message = mir_strdup(message);
 
 	ptrA token(getStringA("TokenSecret"));
 	ptrA umqid(getStringA("UMQID"));
@@ -66,7 +64,6 @@ 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);
 }
 
@@ -89,13 +86,13 @@ int CSteamProto::OnPreCreateMessage(WPARAM, LPARAM lParam)
 int CSteamProto::UserIsTyping(MCONTACT hContact, int type)
 {
 	// NOTE: Steam doesn't support sending "user stopped typing" so we're sending only positive info
-	if (hContact && IsOnline() && type == PROTOTYPE_SELFTYPING_ON)
-	{
-		ptrA token(getStringA("TokenSecret"));
-		ptrA umqid(getStringA("UMQID"));
-		ptrA steamId(getStringA(hContact, "SteamID"));
-		PushRequest(new SendTypingRequest(token, umqid, steamId));
-	}
+	if (type == PROTOTYPE_SELFTYPING_OFF)
+		return 0;
+	
+	ptrA token(getStringA("TokenSecret"));
+	ptrA umqid(getStringA("UMQID"));
+	ptrA steamId(getStringA(hContact, "SteamID"));
+	PushRequest(new SendTypingRequest(token, umqid, steamId));
 
 	return 0;
 }
diff --git a/protocols/Steam/src/steam_options.cpp b/protocols/Steam/src/steam_options.cpp
index ea55af1147..c5898bea86 100644
--- a/protocols/Steam/src/steam_options.cpp
+++ b/protocols/Steam/src/steam_options.cpp
@@ -25,9 +25,11 @@ void CSteamOptionsMain::OnInitDialog()
 
 void CSteamOptionsMain::OnApply()
 {
-	wchar_t *group = m_group.GetText();
-	if (mir_wstrlen(group) > 0 && !Clist_GroupExists(group))
+	ptrW group(m_group.GetText());
+	if (mir_wstrcmp(group, m_proto->m_defaultGroup)) {
+		m_proto->m_defaultGroup = mir_wstrdup(group);
 		Clist_GroupCreate(0, group);
+	}
 
 	if (m_proto->IsOnline())
 	{
diff --git a/protocols/Steam/src/steam_polling.cpp b/protocols/Steam/src/steam_polling.cpp
index 182f9df783..26c5c63e88 100644
--- a/protocols/Steam/src/steam_polling.cpp
+++ b/protocols/Steam/src/steam_polling.cpp
@@ -4,21 +4,17 @@
 
 void CSteamProto::ParsePollData(const JSONNode &data)
 {
-	std::string steamIds;
-	for (const JSONNode &item : data)
-	{
+	for (const JSONNode &item : data) {
 		json_string steamId = item["steamid_from"].as_string();
 		time_t timestamp = _wtol(item["utc_timestamp"].as_mstring());
 
 		MCONTACT hContact = NULL;
-		if (!IsMe(steamId.c_str()) && !(hContact = FindContact(steamId.c_str())))
+		if (!IsMe(steamId.c_str()) && !(hContact = GetContact(steamId.c_str())))
 			// probably this is info about random player playing on same server, so we ignore it
 			continue;
 
 		json_string type = item["type"].as_string();
-		if (!mir_strcmpi(type.c_str(), "my_saytext") ||
-			!mir_strcmpi(type.c_str(), "my_emote"))
-		{
+		if (type == "my_saytext" || type =="my_emote") {
 			json_string text = item["text"].as_string();
 
 			PROTORECVEVENT recv = { 0 };
@@ -27,9 +23,7 @@ void CSteamProto::ParsePollData(const JSONNode &data)
 			recv.flags = PREF_SENT;
 			Proto_RecvMessage(hContact, &recv);
 		}
-		else if (!mir_strcmpi(type.c_str(), "saytext") ||
-			!mir_strcmpi(type.c_str(), "emote"))
-		{
+		else if (type == "saytext" || type =="emote") {
 			json_string text = item["text"].as_string();
 
 			PROTORECVEVENT recv = { 0 };
@@ -40,81 +34,70 @@ void CSteamProto::ParsePollData(const JSONNode &data)
 			CallService(MS_PROTO_CONTACTISTYPING, hContact, (LPARAM)PROTOTYPE_CONTACTTYPING_OFF);
 			m_typingTimestamps[steamId] = 0;
 		}
-		else if (!mir_strcmpi(type.c_str(), "typing") && hContact)
-		{
+		else if (type == "typing") {
 			auto it = m_typingTimestamps.find(steamId);
-			if (it != m_typingTimestamps.end())
-			{
+			if (it != m_typingTimestamps.end()) {
 				if ((timestamp - it->second) < STEAM_TYPING_TIME)
 					continue;
 			}
 			CallService(MS_PROTO_CONTACTISTYPING, hContact, (LPARAM)STEAM_TYPING_TIME);
 			m_typingTimestamps[steamId] = timestamp;
 		}
-		else if (!mir_strcmpi(type.c_str(), "personastate"))
-		{
-			JSONNode node = item["persona_state"];
-			int status = !node.isnull()
-				? SteamToMirandaStatus(node.as_int())
-				: -1;
-
-			if (IsMe(steamId.c_str()))
-			{
-				if (status == -1 || status == ID_STATUS_OFFLINE)
-					continue;
-				SetStatus(status);
-				continue;
+		else if (type == "personastate") {
+			if (!IsMe(steamId.c_str())) {
+				// there no sense to change own status
+				JSONNode node = item["persona_state"];
+				if (!node.isnull()) {
+					int status = SteamToMirandaStatus((PersonaState)node.as_int());
+					SetContactStatus(hContact, status);
+				}
 			}
-			
-			if (status != -1)
-				SetContactStatus(hContact, status);
 
-			// todo: find difference between state changing and info changing
-			steamIds.append(steamId).append(",");
+			int statusFlags = item["status_flags"].as_int();
+			if ((statusFlags & PersonaStatusFlag::PlayerName) == PersonaStatusFlag::PlayerName) {
+				CMStringW nick = item["persona_name"].as_mstring();
+				if (!nick.IsEmpty())
+					setWString(hContact, "Nick", nick);
+			}
 		}
-		else if (!mir_strcmpi(type.c_str(), "personarelationship"))
-		{
+		else if (type == "personarelationship") {
 			int state = item["persona_state"].as_int();
-			switch (state)
-			{
-			case 0:
-				hContact = FindContact(steamId.c_str());
+			switch (state) {
+			case PersonaRelationshipAction::Remove:
+				hContact = GetContact(steamId.c_str());
 				if (hContact)
 					ContactIsRemoved(hContact);
 				break;
 
-			case 1:
-				hContact = FindContact(steamId.c_str());
+			case PersonaRelationshipAction::Ignore:
+				hContact = GetContact(steamId.c_str());
 				if (hContact)
-					ContactIsIgnored(hContact);
+					ContactIsBlocked(hContact);
 				break;
 
-			case 2:
-				// auth request
-				hContact = FindContact(steamId.c_str());
+			case PersonaRelationshipAction::AuthRequest:
+				hContact = GetContact(steamId.c_str());
 				if (hContact)
 					ContactIsAskingAuth(hContact);
-				else
-				{
+				else {
 					// load info about this user from server
 					ptrA token(getStringA("TokenSecret"));
-
 					PushRequest(
 						new GetUserSummariesRequest(token, steamId.c_str()),
 						&CSteamProto::OnAuthRequested);
 				}
 				break;
 
-			case 3:
-				// todo: add to list
-				break;
+			case PersonaRelationshipAction::AuthRequested:
+				hContact = GetContact(steamId.c_str());
+				if (hContact)
+					ContactIsFriend(hContact);
 
 			default:
 				continue;
 			}
 		}
-		else if (!mir_strcmpi(type.c_str(), "leftconversation") && hContact)
-		{
+		else if (type == "leftconversation") {
 			if (!getBool("ShowChatEvents", true))
 				continue;
 
@@ -124,26 +107,24 @@ void CSteamProto::ParsePollData(const JSONNode &data)
 			dbei.cbBlob = 1;
 			dbei.eventType = EVENTTYPE_STEAM_CHATSTATES;
 			dbei.flags = DBEF_READ;
-			dbei.timestamp = time(nullptr);
+			dbei.timestamp = now();
 			dbei.szModule = m_szModuleName;
 			db_event_add(hContact, &dbei);
 		}
-		else
-		{
+		else {
 			debugLogA(__FUNCTION__ ": Unknown event type \"%s\"", type.c_str());
 			continue;
 		}
 	}
 
-	if (!steamIds.empty())
-	{
+	/*if (!steamIds.empty()) {
 		steamIds.pop_back();
-		ptrA token(getStringA("TokenSecret"));
 
+		ptrA token(getStringA("TokenSecret"));
 		PushRequest(
 			new GetUserSummariesRequest(token, steamIds.c_str()),
 			&CSteamProto::OnGotUserSummaries);
-	}
+	}*/
 }
 
 struct PollParam
@@ -172,8 +153,10 @@ void CSteamProto::OnGotPoll(const HttpResponse &response, void *arg)
 	if (!mir_strcmpi(error.c_str(), "Timeout"))
 	{
 		// Do nothing as this is not necessarily an error
+		return;
 	}
-	else if (!mir_strcmpi(error.c_str(), "OK"))
+
+	if (!mir_strcmpi(error.c_str(), "OK"))
 	{
 		// Remember last message timestamp
 		time_t timestamp = _wtoi64(root["utc_timestamp"].as_mstring());
@@ -197,10 +180,12 @@ void CSteamProto::OnGotPoll(const HttpResponse &response, void *arg)
 		debugLogA(__FUNCTION__ ": 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())
+		if (IsOnline())
 		{
-			// let it jump out of further processing
-			param->errors = param->errorsLimit;
+			ptrA token(getStringA("TokenSecret"));
+			SendRequest(
+				new LogonRequest(token),
+				&CSteamProto::OnReLogin);
 		}
 	}
 	else
diff --git a/protocols/Steam/src/steam_proto.cpp b/protocols/Steam/src/steam_proto.cpp
index 52022b10d5..b3ba3024bc 100644
--- a/protocols/Steam/src/steam_proto.cpp
+++ b/protocols/Steam/src/steam_proto.cpp
@@ -2,17 +2,22 @@
 
 CSteamProto::CSteamProto(const char* protoName, const wchar_t* userName)
 	: PROTO<CSteamProto>(protoName, userName),
-	requestQueue(1), hAuthProcess(1), hMessageProcess(1)
+	m_requestQueue(1), hAuthProcess(1), hMessageProcess(1)
 {
 	CreateProtoService(PS_CREATEACCMGRUI, &CSteamProto::OnAccountManagerInit);
 
 	m_idleTS = 0;
 	m_lastMessageTS = 0;
 	isLoginAgain = false;
-	m_pollingConnection = nullptr;
 	m_hPollingThread = nullptr;
 	m_hRequestsQueueEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 
+	// default group
+	m_defaultGroup = getWStringA("DefaultGroup");
+	if (m_defaultGroup == nullptr)
+		m_defaultGroup = mir_wstrdup(L"Steam");
+	Clist_GroupCreate(0, m_defaultGroup);
+
 	// icons
 	wchar_t filePath[MAX_PATH];
 	GetModuleFileName(g_hInstance, filePath, MAX_PATH);
@@ -48,9 +53,6 @@ CSteamProto::CSteamProto(const char* protoName, const wchar_t* userName)
 
 	setAllContactStatuses(ID_STATUS_OFFLINE);
 
-	// services
-	CreateServiceFunction(MODULE"/MenuChoose", CSteamProto::MenuChooseService);
-
 	// avatar API
 	CreateProtoService(PS_GETAVATARINFO, &CSteamProto::GetAvatarInfo);
 	CreateProtoService(PS_GETAVATARCAPS, &CSteamProto::GetAvatarCaps);
@@ -91,28 +93,13 @@ CSteamProto::~CSteamProto()
 	}
 }
 
-MCONTACT CSteamProto::AddToList(int, PROTOSEARCHRESULT* psr)
+MCONTACT CSteamProto::AddToList(int, PROTOSEARCHRESULT *psr)
 {
-	MCONTACT hContact = NULL;
-	ptrA steamId(mir_u2a(psr->id.w));
-	if (psr->cbSize == sizeof(PROTOSEARCHRESULT))
-	{
-		if (!FindContact(steamId))
-		{
-			//hContact = AddContact(steamId, true);
-			//ForkThread(&CSteamProto::UpdateContactsThread, (void*)mir_strdup(steamId));
+	_T2A steamId(psr->id.w);
+	MCONTACT hContact = AddContact(steamId, psr->nick.w, true);
 
-			ptrA token(getStringA("TokenSecret"));
-
-			PushRequest(
-				new GetUserSummariesRequest(token, steamId),
-				&CSteamProto::OnGotUserSummaries);
-		}
-	}
-	else if (psr->cbSize == sizeof(STEAM_SEARCH_RESULT))
-	{
+	if (psr->cbSize == sizeof(STEAM_SEARCH_RESULT)) {
 		STEAM_SEARCH_RESULT *ssr = (STEAM_SEARCH_RESULT*)psr;
-		hContact = AddContact(steamId, true);
 		UpdateContactDetails(hContact, *ssr->data);
 	}
 
@@ -121,14 +108,11 @@ MCONTACT CSteamProto::AddToList(int, PROTOSEARCHRESULT* psr)
 
 int CSteamProto::Authorize(MEVENT hDbEvent)
 {
-	if (IsOnline() && hDbEvent)
-	{
+	if (IsOnline() && hDbEvent) {
 		MCONTACT hContact = GetContactFromAuthEvent(hDbEvent);
 		if (hContact == INVALID_CONTACT_ID)
 			return 1;
 
-		//ForkThread(&CSteamProto::AuthAllowThread, (void*)hContact);
-
 		ptrA token(getStringA("TokenSecret"));
 		ptrA sessionId(getStringA("SessionID"));
 		ptrA steamId(getStringA("SteamID"));
@@ -145,16 +129,20 @@ int CSteamProto::Authorize(MEVENT hDbEvent)
 	return 1;
 }
 
+int CSteamProto::AuthRecv(MCONTACT hContact, PROTORECVEVENT *pre)
+{
+	// remember to not create this event again, unless authorization status changes again
+	setByte(hContact, "AuthAsked", 1);
+	return Proto_AuthRecv(m_szModuleName, pre);
+}
+
 int CSteamProto::AuthDeny(MEVENT hDbEvent, const wchar_t*)
 {
-	if (IsOnline() && hDbEvent)
-	{
+	if (IsOnline() && hDbEvent) {
 		MCONTACT hContact = GetContactFromAuthEvent(hDbEvent);
 		if (hContact == INVALID_CONTACT_ID)
 			return 1;
 
-		//ForkThread(&CSteamProto::AuthDenyThread, (void*)hContact);
-
 		ptrA token(getStringA("TokenSecret"));
 		ptrA sessionId(getStringA("SessionID"));
 		ptrA steamId(getStringA("SteamID"));
@@ -173,32 +161,18 @@ int CSteamProto::AuthDeny(MEVENT hDbEvent, const wchar_t*)
 
 int CSteamProto::AuthRequest(MCONTACT hContact, const wchar_t*)
 {
-	if (IsOnline() && hContact)
-	{
+	if (IsOnline() && hContact) {
 		UINT hAuth = InterlockedIncrement(&hAuthProcess);
 
 		SendAuthParam *param = (SendAuthParam*)mir_calloc(sizeof(SendAuthParam));
 		param->hContact = hContact;
 		param->hAuth = (HANDLE)hAuth;
 
-		//ForkThread(&CSteamProto::AddContactThread, param);
-
 		ptrA token(getStringA("TokenSecret"));
 		ptrA sessionId(getStringA("SessionID"));
 		ptrA steamId(getStringA("SteamID"));
 		ptrA who(getStringA(hContact, "SteamID"));
 
-		/*
-		posilame: (kdyz my zadame)
-		sessionID	MjYzNDM4NDgw
-		steamid	76561198166125402
-		accept_invite	0
-
-		pri uspesnem pozadavku vrati: {"invited":["76561198166125402"],"success":1}
-		kdyz nas ignoruje: {"failed_invites":["76561198166125402"],"failed_invites_result":[41],"success":1}
-
-		*/
-
 		PushRequest(
 			new AddFriendRequest(token, sessionId, steamId, who),
 			&CSteamProto::OnFriendAdded,
@@ -212,8 +186,7 @@ int CSteamProto::AuthRequest(MCONTACT hContact, const wchar_t*)
 
 DWORD_PTR CSteamProto:: GetCaps(int type, MCONTACT)
 {
-	switch(type)
-	{
+	switch (type) {
 	case PFLAGNUM_1:
 		return PF1_IM | PF1_BASICSEARCH | PF1_SEARCHBYNAME | PF1_AUTHREQ | PF1_SERVERCLIST | PF1_ADDSEARCHRES | PF1_MODEMSGRECV;
 	case PFLAGNUM_2:
@@ -249,7 +222,7 @@ HANDLE CSteamProto::SearchBasic(const wchar_t* id)
 
 HANDLE CSteamProto::SearchByName(const wchar_t* nick, const wchar_t* firstName, const wchar_t* lastName)
 {
-	if (!this->IsOnline())
+	if (!IsOnline())
 		return nullptr;
 
 	// Combine all fields to single text
@@ -257,7 +230,7 @@ HANDLE CSteamProto::SearchByName(const wchar_t* nick, const wchar_t* firstName,
 	mir_snwprintf(keywordsT, L"%s %s %s", nick, firstName, lastName);
 
 	ptrA token(getStringA("TokenSecret"));
-	ptrA keywords(mir_utf8encodeW(keywordsT));
+	ptrA keywords(mir_utf8encodeW(rtrimw(keywordsT)));
 
 	PushRequest(
 		new SearchRequest(token, keywords),
@@ -269,8 +242,7 @@ HANDLE CSteamProto::SearchByName(const wchar_t* nick, const wchar_t* firstName,
 
 int CSteamProto::SendMsg(MCONTACT hContact, int, const char *message)
 {
-	if (!IsOnline())
-	{
+	if (!IsOnline()) {
 		ProtoBroadcastAck(hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, nullptr, (LPARAM)Translate("You cannot send messages when you are offline."));
 		return 0;
 	}
@@ -280,11 +252,10 @@ int CSteamProto::SendMsg(MCONTACT hContact, int, const char *message)
 
 int CSteamProto::SetStatus(int new_status)
 {
-	mir_cslock lock(set_status_lock);
+	mir_cslock lock(m_setStatusLock);
 
 	// Routing statuses not supported by Steam
-	switch (new_status)
-	{
+	switch (new_status) {
 	case ID_STATUS_OFFLINE:
 	case ID_STATUS_AWAY:
 	case ID_STATUS_NA:
@@ -310,8 +281,7 @@ int CSteamProto::SetStatus(int new_status)
 	int old_status = m_iStatus;
 	m_iDesiredStatus = new_status;
 
-	if (new_status == ID_STATUS_OFFLINE)
-	{
+	if (new_status == ID_STATUS_OFFLINE) {
 		// Reset relogin flag
 		isLoginAgain = false;
 
@@ -320,20 +290,19 @@ int CSteamProto::SetStatus(int new_status)
 		if (!Miranda_IsTerminated())
 			setAllContactStatuses(ID_STATUS_OFFLINE);
 
-		LogOut();
+		Logout();
 	}
-	else if (old_status == ID_STATUS_OFFLINE)
-	{
+	else if (old_status == ID_STATUS_OFFLINE) {
 		// Load last message timestamp for correct loading of messages history
 		m_lastMessageTS = getDword("LastMessageTS", 0);
 
 		m_iStatus = ID_STATUS_CONNECTING;
 
-		isTerminated = false;
+		m_isTerminated = false;
 
-		m_hRequestQueueThread = ForkThreadEx(&CSteamProto::RequestQueueThread, NULL, NULL);
+		m_hRequestQueueThread = ForkThreadEx(&CSteamProto::RequestQueueThread, nullptr, nullptr);
 
-		
+		Login();
 	}
 	else
 		m_iStatus = new_status;
@@ -343,7 +312,7 @@ int CSteamProto::SetStatus(int new_status)
 	return 0;
 }
 
-void __cdecl CSteamProto::GetAwayMsgThread(void *arg)
+void CSteamProto::GetAwayMsgThread(void *arg)
 {
 	// Maybe not needed, but better to be sure that this won't happen faster than core handling return value of GetAwayMsg()
 	Sleep(50);
@@ -352,8 +321,7 @@ void __cdecl CSteamProto::GetAwayMsgThread(void *arg)
 	CMStringW message(db_get_wsa(hContact, "CList", "StatusMsg"));
 	
 	// if contact has no status message, get xstatus message
-	if (message.IsEmpty())
-	{
+	if (message.IsEmpty()) {
 		ptrW xStatusName(getWStringA(hContact, "XStatusName"));
 		ptrW xStatusMsg(getWStringA(hContact, "XStatusMsg"));
 
@@ -366,45 +334,37 @@ void __cdecl CSteamProto::GetAwayMsgThread(void *arg)
 	ProtoBroadcastAck(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)message.c_str());
 }
 
-HANDLE __cdecl CSteamProto::GetAwayMsg(MCONTACT hContact)
+HANDLE CSteamProto::GetAwayMsg(MCONTACT hContact)
 {
 	ForkThread(&CSteamProto::GetAwayMsgThread, (void*)hContact);
 	return (HANDLE)1;
 }
 
-int __cdecl CSteamProto::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam)
+int CSteamProto::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam)
 {
-	switch (eventType)
-	{
+	switch (eventType) {
 	case EV_PROTO_ONLOAD:
-		return this->OnModulesLoaded(wParam, lParam);
-
-	/*case EV_PROTO_ONOPTIONS:
-		return this->OnOptionsInit(wParam, lParam);*/
+		return OnModulesLoaded(wParam, lParam);
 
 	case EV_PROTO_ONCONTACTDELETED:
-		if (IsOnline())
-		{
+		if (IsOnline()) {
 			MCONTACT hContact = (MCONTACT)wParam;
-
-			ptrA token(getStringA("TokenSecret"));
-			ptrA sessionId(getStringA("SessionID"));
-			ptrA steamId(getStringA("SteamID"));
-			ptrA who(getStringA(hContact, "SteamID"));
-
-			// Don't request delete contact from server when we're not friends anyway
-			if (getByte(hContact, "Auth", 0) != 0)
-				return 0;
-
-			PushRequest(
-				new RemoveFriendRequest(token, sessionId, steamId, who),
-				&CSteamProto::OnFriendRemoved,
-				(void*)hContact);
+			// remove only authorized contacts
+			if (!getByte(hContact, "Auth", 0)) {
+				ptrA token(getStringA("TokenSecret"));
+				ptrA sessionId(getStringA("SessionID"));
+				ptrA steamId(getStringA("SteamID"));
+				char *who = getStringA(hContact, "SteamID");
+				PushRequest(
+					new RemoveFriendRequest(token, sessionId, steamId, who),
+					&CSteamProto::OnFriendRemoved,
+					(void*)who);
+			}
 		}
 		return 0;
 
 	case EV_PROTO_ONMENU:
-		this->OnInitStatusMenu();
+		//OnInitStatusMenu();
 		break;
 	}
 
diff --git a/protocols/Steam/src/steam_proto.h b/protocols/Steam/src/steam_proto.h
index d53e21b009..d1e5513baa 100644
--- a/protocols/Steam/src/steam_proto.h
+++ b/protocols/Steam/src/steam_proto.h
@@ -13,18 +13,19 @@ struct SendAuthParam
 
 struct STEAM_SEARCH_RESULT
 {
-	PROTOSEARCHRESULT hdr;
-	JSONNode *data;
+	PROTOSEARCHRESULT psr;
+	const JSONNode *data;
 };
 
 enum
 {
 	CMI_AUTH_REQUEST,
 	//CMI_AUTH_GRANT,
-	//CMI_AUTH_REVOKE,
+	CMI_AUTH_REVOKE,
 	CMI_BLOCK,
+	CMI_UNBLOCK,
 	CMI_JOIN_GAME,
-	SMI_BLOCKED_LIST,
+	//SMI_BLOCKED_LIST,
 	CMI_MAX   // this item shall be the last one
 };
 
@@ -37,6 +38,13 @@ struct RequestQueueItem
 	HttpCallback httpCallback;
 	JsonCallback jsonCallback;
 	void *param;
+
+	RequestQueueItem(HttpRequest *request)
+		: request(request), httpCallback(nullptr), jsonCallback(nullptr), param(nullptr) {}
+	RequestQueueItem(HttpRequest *request, HttpCallback callback, void *param)
+		: request(request), httpCallback(callback), jsonCallback(nullptr), param(param) {}
+	RequestQueueItem(HttpRequest *request, JsonCallback callback, void *param)
+		: request(request), httpCallback(nullptr), jsonCallback(callback), param(param) {}
 };
 
 class CSteamProto : public PROTO<CSteamProto>
@@ -54,15 +62,16 @@ public:
 	virtual	MCONTACT  __cdecl AddToList(int flags, PROTOSEARCHRESULT *psr);
 
 	virtual	int       __cdecl Authorize(MEVENT hDbEvent);
+	virtual int       __cdecl AuthRecv(MCONTACT, PROTORECVEVENT*);
 	virtual	int       __cdecl AuthDeny(MEVENT hDbEvent, const wchar_t *szReason);
-	virtual	int       __cdecl AuthRequest(MCONTACT hContact, const wchar_t * szMessage);
+	virtual	int       __cdecl AuthRequest(MCONTACT hContact, const wchar_t *szMessage);
 
 	virtual	DWORD_PTR __cdecl GetCaps(int type, MCONTACT hContact = NULL);
 
 	virtual	HANDLE    __cdecl SearchBasic(const wchar_t *id);
-	virtual HANDLE    __cdecl SearchByName(const wchar_t* nick, const wchar_t* firstName, const wchar_t* lastName);
+	virtual HANDLE    __cdecl SearchByName(const wchar_t *nick, const wchar_t *firstName, const wchar_t *lastName);
 
-	virtual	int       __cdecl SendMsg(MCONTACT hContact, int flags, const char* msg);
+	virtual	int       __cdecl SendMsg(MCONTACT hContact, int flags, const char *msg);
 
 	virtual	int       __cdecl SetStatus(int iNewStatus);
 
@@ -71,31 +80,31 @@ public:
 	virtual	int       __cdecl OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam);
 
 	// instances
-	static CSteamProto* InitProtoInstance(const char* protoName, const wchar_t* userName);
-	static int UninitProtoInstance(CSteamProto* ppro);
+	static CSteamProto* InitAccount(const char* protoName, const wchar_t *userName);
+	static int UninitAccount(CSteamProto* ppro);
 
-	static CSteamProto* GetContactProtoInstance(MCONTACT hContact);
-	static void UninitProtoInstances();
+	static CSteamProto* GetContactAccount(MCONTACT hContact);
 
 	// menus
 	static void InitMenus();
-	static void UninitMenus();
 
 protected:
-	wchar_t *password;
+	ptrW m_password;
+	ptrW m_defaultGroup;
 	bool isLoginAgain;
 	time_t m_idleTS;
-	bool isTerminated, isConnected;
-	mir_cs requestQueueLock;
+	// requests
+	bool m_isTerminated;
+	mir_cs m_requestQueueLock;
 	HANDLE m_hRequestsQueueEvent;
 	HANDLE m_hRequestQueueThread;
-	LIST<RequestQueueItem> requestQueue;
-	HANDLE m_pollingConnection, m_hPollingThread;
-	ULONG  hAuthProcess;
-	ULONG  hMessageProcess;
-	mir_cs contact_search_lock;
-	mir_cs requests_queue_lock;
-	mir_cs set_status_lock;
+	LIST<RequestQueueItem> m_requestQueue;
+	// pooling
+	HANDLE m_hPollingThread;
+	ULONG hAuthProcess;
+	ULONG hMessageProcess;
+	mir_cs m_addContactLock;
+	mir_cs m_setStatusLock;
 	std::map<HANDLE, time_t> m_mpOutMessages;
 	std::map<std::string, time_t> m_typingTimestamps;
 
@@ -106,19 +115,22 @@ protected:
 	time_t m_lastMessageTS;
 
 	// instances
-	static LIST<CSteamProto> InstanceList;
+	static LIST<CSteamProto> Accounts;
 	static int CompareProtos(const CSteamProto *p1, const CSteamProto *p2);
 
 	// requests
-	HttpResponse* SendRequest(HttpRequest *request);
-	void SendRequest(HttpRequest *request, HttpCallback callback, void *param = NULL);
-	void SendRequest(HttpRequest *request, JsonCallback callback, void *param = NULL);
+	void SendRequest(HttpRequest *request);
+	void SendRequest(HttpRequest *request, HttpCallback callback, void *param = nullptr);
+	void SendRequest(HttpRequest *request, JsonCallback callback, void *param = nullptr);
 	void PushRequest(HttpRequest *request);
-	void PushRequest(HttpRequest *request, HttpCallback callback, void *param = NULL);
-	void PushRequest(HttpRequest *request, JsonCallback callback, void *param = NULL);
+	void PushRequest(HttpRequest *request, HttpCallback callback, void *param = nullptr);
+	void PushRequest(HttpRequest *request, JsonCallback callback, void *param = nullptr);
+	void PushToRequestQueue(RequestQueueItem *item);
+	RequestQueueItem *PopFromRequestQueue();
+	void ProcessRequestQueue();
 	void __cdecl RequestQueueThread(void*);
 
-	// pooling thread
+	// pooling
 	void ParsePollData(const JSONNode &data);
 	void OnGotPoll(const HttpResponse &response, void *arg);
 	void __cdecl PollingThread(void*);
@@ -128,8 +140,7 @@ protected:
 	bool IsMe(const char *steamId);
 	
 	void Login();
-	bool Relogin();
-	void LogOut();
+	void Logout();
 
 	void OnGotRsaKey(const JSONNode &root, void*);
 
@@ -141,6 +152,7 @@ protected:
 	void OnGotSession(const HttpResponse &response, void*);
 
 	void OnLoggedOn(const HttpResponse &response, void*);
+	void OnReLogin(const JSONNode &root, void*);
 
 	void HandleTokenExpired();
 	void DeleteAuthSettings();
@@ -156,11 +168,12 @@ protected:
 
 	void ContactIsRemoved(MCONTACT hContact);
 	void ContactIsFriend(MCONTACT hContact);
-	void ContactIsIgnored(MCONTACT hContact);
+	void ContactIsBlocked(MCONTACT hContact);
+	void ContactIsUnblocked(MCONTACT hContact);
 	void ContactIsAskingAuth(MCONTACT hContact);
 
-	MCONTACT FindContact(const char *steamId);
-	MCONTACT AddContact(const char *steamId, bool isTemporary = false);
+	MCONTACT GetContact(const char *steamId);
+	MCONTACT AddContact(const char *steamId, const wchar_t *nick = nullptr, bool isTemporary = false);
 
 	void OnGotFriendList(const JSONNode &root, void*);
 	void OnGotBlockList(const JSONNode &root, void*);
@@ -169,6 +182,7 @@ protected:
 
 	void OnFriendAdded(const HttpResponse &response, void *arg);
 	void OnFriendBlocked(const HttpResponse &response, void *arg);
+	void OnFriendUnblocked(const HttpResponse &response, void *arg);
 	void OnFriendRemoved(const HttpResponse &response, void *arg);
 
 	void OnAuthRequested(const JSONNode &root, void *arg);
@@ -193,13 +207,13 @@ protected:
 	static HGENMENU contactMenuItems[CMI_MAX];
 
 	int __cdecl AuthRequestCommand(WPARAM, LPARAM);
+	int __cdecl AuthRevokeCommand(WPARAM, LPARAM);
 	int __cdecl BlockCommand(WPARAM, LPARAM);
+	int __cdecl UnblockCommand(WPARAM, LPARAM);
 	int __cdecl JoinToGameCommand(WPARAM, LPARAM);
 
 	INT_PTR __cdecl OpenBlockListCommand(WPARAM, LPARAM);
 
-	static INT_PTR MenuChooseService(WPARAM wParam, LPARAM lParam);
-
 	static int PrebuildContactMenu(WPARAM wParam, LPARAM lParam);
 	int OnPrebuildContactMenu(WPARAM wParam, LPARAM);
 
@@ -231,8 +245,8 @@ protected:
 	INT_PTR __cdecl OnAccountManagerInit(WPARAM wParam, LPARAM lParam);
 
 	// utils
-	static WORD SteamToMirandaStatus(int state);
-	static int MirandaToSteamState(int status);
+	static WORD SteamToMirandaStatus(PersonaState state);
+	static PersonaState MirandaToSteamState(int status);
 
 	static int RsaEncrypt(const char *pszModulus, DWORD &exponent, const char *data, BYTE *encrypted, DWORD &encryptedSize);
 
@@ -244,7 +258,7 @@ protected:
 	// helpers
 	inline int IdleSeconds() {
 		// Based on idle time we report Steam server will mark us as online/away/snooze
-		switch (this->m_iStatus) {
+		switch (m_iStatus) {
 		case ID_STATUS_AWAY:
 			return STEAM_API_IDLEOUT_AWAY;
 		case ID_STATUS_NA:
@@ -263,6 +277,13 @@ protected:
 		mir_snprintf(steamId, "%llu", accountId + 76561197960265728ll);
 		return steamId;
 	}
+
+	inline const char *SteamIdToAccountId(long long steamId)
+	{
+		static char accountId[10];
+		mir_snprintf(accountId, "%llu", steamId - 76561197960265728ll);
+		return accountId;
+	}
 };
 
 int OnReloadIcons(WPARAM wParam, LPARAM lParam);
diff --git a/protocols/Steam/src/steam_request.cpp b/protocols/Steam/src/steam_request.cpp
index 363f31a328..51b09ecfc3 100644
--- a/protocols/Steam/src/steam_request.cpp
+++ b/protocols/Steam/src/steam_request.cpp
@@ -1,11 +1,10 @@
 #include "stdafx.h"
 
-HttpResponse* CSteamProto::SendRequest(HttpRequest *request)
+void CSteamProto::SendRequest(HttpRequest *request)
 {
 	NETLIBHTTPREQUEST *pResp = Netlib_HttpTransaction(m_hNetlibUser, (NETLIBHTTPREQUEST*)request);
-	HttpResponse *response = new HttpResponse(request, pResp);
+	HttpResponse response(request, pResp);
 	delete request;
-	return response;
 }
 
 void CSteamProto::SendRequest(HttpRequest *request, HttpCallback callback, void *param)
@@ -21,8 +20,7 @@ void CSteamProto::SendRequest(HttpRequest *request, JsonCallback callback, void
 {
 	NETLIBHTTPREQUEST *pResp = Netlib_HttpTransaction(m_hNetlibUser, (NETLIBHTTPREQUEST*)request);
 	HttpResponse response(request, pResp);
-	if (callback)
-	{
+	if (callback) {
 		JSONNode root = JSONNode::parse(response.Content);
 		(this->*callback)(root, param);
 	}
@@ -31,68 +29,75 @@ void CSteamProto::SendRequest(HttpRequest *request, JsonCallback callback, void
 
 void CSteamProto::PushRequest(HttpRequest *request)
 {
-	RequestQueueItem *item = new RequestQueueItem();
-	item->request = request;
-	{
-		mir_cslock lock(requestQueueLock);
-		requestQueue.insert(item);
-	}
-	SetEvent(m_hRequestsQueueEvent);
+	RequestQueueItem *item = new RequestQueueItem(request);
+	PushToRequestQueue(item);
 }
 
 void CSteamProto::PushRequest(HttpRequest *request, HttpCallback callback, void *param)
 {
-	RequestQueueItem *item = new RequestQueueItem();
-	item->request = request;
-	item->httpCallback = callback;
-	item->param = param;
-	{
-		mir_cslock lock(requestQueueLock);
-		requestQueue.insert(item);
-	}
-	SetEvent(m_hRequestsQueueEvent);
+	RequestQueueItem *item = new RequestQueueItem(request, callback, param);
+	PushToRequestQueue(item);
 }
 
 void CSteamProto::PushRequest(HttpRequest *request, JsonCallback callback, void *param)
 {
-	RequestQueueItem *item = new RequestQueueItem();
-	item->request = request;
-	item->jsonCallback = callback;
-	item->param = param;
+	RequestQueueItem *item = new RequestQueueItem(request, callback, param);
+	PushToRequestQueue(item);
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+
+void CSteamProto::PushToRequestQueue(RequestQueueItem *item)
+{
+	if (m_isTerminated)
+		return;
 	{
-		mir_cslock lock(requestQueueLock);
-		requestQueue.insert(item);
+		mir_cslock lock(m_requestQueueLock);
+		m_requestQueue.insert(item);
 	}
 	SetEvent(m_hRequestsQueueEvent);
 }
 
-void CSteamProto::RequestQueueThread(void*)
+RequestQueueItem *CSteamProto::PopFromRequestQueue()
 {
-	Login();
+	mir_cslock lock(m_requestQueueLock);
+	if (!m_requestQueue.getCount())
+		return nullptr;
 
-	do
-	{
-		RequestQueueItem *item;
-		while (true)
-		{
-			{
-				mir_cslock lock(requestQueueLock);
-				if (!requestQueue.getCount())
-					break;
+	RequestQueueItem *item = m_requestQueue[0];
+	m_requestQueue.remove(0);
+	return item;
+}
 
-				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;
-		}
+void CSteamProto::ProcessRequestQueue()
+{
+	while (true) {
+		RequestQueueItem *item = PopFromRequestQueue();
+		if (item == nullptr)
+			break;
+		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;
+	}
+}
+
+void CSteamProto::RequestQueueThread(void*)
+{
+	do {
+		ProcessRequestQueue();
 		WaitForSingleObject(m_hRequestsQueueEvent, 1000);
-	} while (!isTerminated);
+	} while (!m_isTerminated);
+	{
+		mir_cslock lock(m_requestQueueLock);
+		for (int i = 0; i < m_requestQueue.getCount(); i++) {
+			delete m_requestQueue[i];
+			m_requestQueue.remove(i);
+		}
+	}
+	m_hRequestQueueThread = nullptr;
 
-	m_hRequestQueueThread = NULL;
 }
\ No newline at end of file
diff --git a/protocols/Steam/src/steam_utils.cpp b/protocols/Steam/src/steam_utils.cpp
index 409ce4276a..447a844b16 100644
--- a/protocols/Steam/src/steam_utils.cpp
+++ b/protocols/Steam/src/steam_utils.cpp
@@ -1,162 +1,49 @@
 #include "stdafx.h"
 
-#pragma comment(lib, "crypt32.lib")
-
-WORD CSteamProto::SteamToMirandaStatus(int state)
+WORD CSteamProto::SteamToMirandaStatus(PersonaState state)
 {
 	switch (state)
 	{
-	case 0: //Offline
+	case PersonaState::Offline:
 		return ID_STATUS_OFFLINE;
-	case 1: //Online
+	case PersonaState::Online:
 		return ID_STATUS_ONLINE;
-	case 2: //Busy
+	case PersonaState::Busy:
 		return ID_STATUS_DND;
-	case 3: //Away
+	case PersonaState::Away:
 		return ID_STATUS_AWAY;
-	case 4: //Snoozing
+	case PersonaState::Snooze:
 		return ID_STATUS_NA;
-	case 5: //Looking to trade
+	case PersonaState::LookingToTrade:
 		return ID_STATUS_OUTTOLUNCH;
-	case 6: //Looking to play
+	case PersonaState::LookingToPlay:
 		return ID_STATUS_FREECHAT;
 	default:
 		return ID_STATUS_ONLINE;
 	}
 }
 
-int CSteamProto::MirandaToSteamState(int status)
+PersonaState CSteamProto::MirandaToSteamState(int status)
 {
 	switch (status)
 	{
 	case ID_STATUS_OFFLINE:
-		return 0; //Offline
+		return PersonaState::Offline;
 	case ID_STATUS_ONLINE:
-		return 1; //Online
+		return PersonaState::Online;
 	case ID_STATUS_DND:
-		return 2; //Busy
+		return PersonaState::Busy;
 	case ID_STATUS_AWAY:
-		return 3; //Away
+		return PersonaState::Away;
 	case ID_STATUS_NA:
-		return 4; //Snoozing
+		return PersonaState::Snooze;
 	case ID_STATUS_OUTTOLUNCH:
-		return 5; //Looking to trade
+		return PersonaState::LookingToTrade;
 	case ID_STATUS_FREECHAT:
-		return 6; //Looking to play
+		return PersonaState::LookingToPlay;
 	default:
-		return 1;
-	}
-}
-
-int CSteamProto::RsaEncrypt(const char *pszModulus, DWORD &exponent, const char *data, BYTE *encryptedData, DWORD &encryptedSize)
-{
-	DWORD cchModulus = (DWORD)mir_strlen(pszModulus);
-	int result = 0;
-	BYTE *pbBuffer = nullptr;
-	BYTE *pKeyBlob = nullptr;
-	HCRYPTKEY phKey = 0;
-	HCRYPTPROV hCSP = 0;
-
-	// convert hex string to byte array
-	DWORD cbLen = 0, dwSkip = 0, dwFlags = 0;
-	if (!CryptStringToBinaryA(pszModulus, cchModulus, CRYPT_STRING_HEX, nullptr, &cbLen, &dwSkip, &dwFlags))
-	{
-		result = GetLastError();
-		goto exit;
-	}
-
-	// allocate a new buffer.
-	pbBuffer = (BYTE*)malloc(cbLen);
-	if (!CryptStringToBinaryA(pszModulus, cchModulus, CRYPT_STRING_HEX, pbBuffer, &cbLen, &dwSkip, &dwFlags))
-	{
-		result = GetLastError();
-		goto exit;
-	}
-
-	// reverse byte array, because of microsoft
-	for (int i = 0; i < (int)(cbLen / 2); ++i)
-	{
-		BYTE temp = pbBuffer[cbLen - i - 1];
-		pbBuffer[cbLen - i - 1] = pbBuffer[i];
-		pbBuffer[i] = temp;
-	}
-	
-	if (!CryptAcquireContext(&hCSP, nullptr, nullptr, PROV_RSA_AES, CRYPT_SILENT) &&
-		!CryptAcquireContext(&hCSP, nullptr, nullptr, PROV_RSA_AES, CRYPT_SILENT | CRYPT_NEWKEYSET))
-	{
-		result = GetLastError();
-		goto exit;
-	}
-
-	// Move the key into the key container.
-	DWORD cbKeyBlob = sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY) + cbLen;
-	pKeyBlob = (BYTE*)malloc(cbKeyBlob);
-
-	// Fill in the data.
-	PUBLICKEYSTRUC *pPublicKey = (PUBLICKEYSTRUC*)pKeyBlob;
-	pPublicKey->bType = PUBLICKEYBLOB; 
-	pPublicKey->bVersion = CUR_BLOB_VERSION;  // Always use this value.
-	pPublicKey->reserved = 0;                 // Must be zero.
-	pPublicKey->aiKeyAlg = CALG_RSA_KEYX;     // RSA public-key key exchange. 
-
-	// The next block of data is the RSAPUBKEY structure.
-	RSAPUBKEY *pRsaPubKey = (RSAPUBKEY*)(pKeyBlob + sizeof(PUBLICKEYSTRUC));
-	pRsaPubKey->magic = 0x31415352; // RSA1 // Use public key
-	pRsaPubKey->bitlen = cbLen * 8;  // Number of bits in the modulus.
-	pRsaPubKey->pubexp = exponent; // Exponent.
-
-	// Copy the modulus into the blob. Put the modulus directly after the
-	// RSAPUBKEY structure in the blob.
-	BYTE *pKey = (BYTE*)(((BYTE *)pRsaPubKey) + sizeof(RSAPUBKEY));
-	//pKeyBlob + sizeof(BLOBHEADER)+ sizeof(RSAPUBKEY); 
-	memcpy(pKey, pbBuffer, cbLen);
-
-	// Now import public key	
-	if (!CryptImportKey(hCSP, pKeyBlob, cbKeyBlob, 0, 0, &phKey))
-	{
-		result = GetLastError();
-		goto exit;
-	}
-
-	DWORD dataSize = (DWORD)mir_strlen(data);
-
-	// if data is not allocated just renurn size
-	if (encryptedData == nullptr)
-	{
-		// get length of encrypted data
-		if (!CryptEncrypt(phKey, 0, TRUE, 0, nullptr, &encryptedSize, dataSize))
-			result = GetLastError();
-		goto exit;
-	}
-
-	// encrypt password
-	memcpy(encryptedData, data, dataSize);
-	if (!CryptEncrypt(phKey, 0, TRUE, 0, encryptedData, &dataSize, encryptedSize))
-	{
-		result = GetLastError();
-		goto exit;
-	}
-
-	// reverse byte array again
-	for (int i = 0; i < (int)(encryptedSize / 2); ++i)
-	{
-		BYTE temp = encryptedData[encryptedSize - i - 1];
-		encryptedData[encryptedSize - i - 1] = encryptedData[i];
-		encryptedData[i] = temp;
+		return PersonaState::Online;
 	}
-
-exit:
-	if (pKeyBlob)
-		free(pKeyBlob);
-	if (phKey)
-		CryptDestroyKey(phKey);
-	
-	if (pbBuffer)
-		free(pbBuffer);
-	if (hCSP)
-		CryptReleaseContext(hCSP, 0);
-
-	return 0;
 }
 
 void CSteamProto::ShowNotification(const wchar_t *caption, const wchar_t *message, int flags, MCONTACT hContact)
@@ -184,18 +71,14 @@ void CSteamProto::ShowNotification(const wchar_t *message, int flags, MCONTACT h
 	ShowNotification(_A2W(MODULE), message, flags, hContact);
 }
 
-INT_PTR __cdecl CSteamProto::OnGetEventTextChatStates(WPARAM pEvent, LPARAM datatype)
+INT_PTR CSteamProto::OnGetEventTextChatStates(WPARAM pEvent, LPARAM datatype)
 {
 	// Retrieves a chat state description from an event
-
 	DBEVENTINFO *dbei = (DBEVENTINFO *)pEvent;
-	if (dbei->cbBlob > 0) {
-		if (dbei->pBlob[0] == STEAM_DB_EVENT_CHATSTATES_GONE) {
-			if (datatype == DBVT_WCHAR)
-				return (INT_PTR)mir_wstrdup(TranslateT("closed chat session"));
-			return (INT_PTR)mir_strdup(Translate("closed chat session"));
-		}
-	}
+	if (dbei->cbBlob > 0 && dbei->pBlob[0] == STEAM_DB_EVENT_CHATSTATES_GONE)
+		return (datatype == DBVT_WCHAR)
+			? (INT_PTR)mir_wstrdup(TranslateT("closed chat session"))
+			: (INT_PTR)mir_strdup(Translate("closed chat session"));
 
 	return NULL;
 }
-- 
cgit v1.2.3