summaryrefslogtreecommitdiff
path: root/protocols/Steam/src/steam_proto.h
blob: 9fe393f51ba21c673391410d7377669091a72d43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
#ifndef _STEAM_PROTO_H_
#define _STEAM_PROTO_H_

#define STEAM_SEARCH_BYID 1001
#define STEAM_SEARCH_BYNAME 1002
#define STEAM_TYPING_TIME 10

struct SendAuthParam
{
	MCONTACT hContact;
	HANDLE hAuth;
};

struct STEAM_SEARCH_RESULT
{
	PROTOSEARCHRESULT psr;
	const JSONNode *data;
};

enum
{
	CMI_BLOCK,
	CMI_UNBLOCK,
	CMI_JOIN_GAME,
	CMI_MAX   // this item shall be the last one
};

typedef void(CSteamProto::*HttpCallback)(const HttpResponse&, void*);
typedef void(CSteamProto::*JsonCallback)(const JSONNode&, void*);

struct RequestQueueItem
{
	HttpRequest *request;
	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>
{
	friend CSteamPasswordEditor;
	friend CSteamOptionsMain;
	friend CSteamOptionsBlockList;

public:
	// PROTO_INTERFACE
	CSteamProto(const char *protoName, const wchar_t *userName);
	~CSteamProto();

	// PROTO_INTERFACE
	MCONTACT AddToList(int flags, PROTOSEARCHRESULT *psr) override;

	int      Authorize(MEVENT hDbEvent) override;
	int      AuthRecv(MCONTACT, PROTORECVEVENT*) override;
	int      AuthDeny(MEVENT hDbEvent, const wchar_t *szReason) override;
	int      AuthRequest(MCONTACT hContact, const wchar_t *szMessage) override;

	INT_PTR  GetCaps(int type, MCONTACT hContact = NULL) override;
	HANDLE   GetAwayMsg(MCONTACT hContact) override;

	HANDLE   SearchBasic(const wchar_t *id) override;
	HANDLE   SearchByName(const wchar_t *nick, const wchar_t *firstName, const wchar_t *lastName) override;

	int      SendMsg(MCONTACT hContact, int flags, const char *msg) override;

	int      SetStatus(int iNewStatus) override;

	int      UserIsTyping(MCONTACT hContact, int type) override;

	void     OnContactDeleted(MCONTACT) override;
	void     OnModulesLoaded() override;

	// menus
	static void InitMenus();

protected:
	ptrW m_password;
	ptrW m_defaultGroup;
	bool isLoginAgain;
	time_t m_idleTS;
	// requests
	bool m_isTerminated;
	mir_cs m_requestQueueLock;
	HANDLE m_hRequestsQueueEvent;
	HANDLE m_hRequestQueueThread;
	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;

	/**
	 * Used only to compare in steam_history.cpp, others should write such value directly to db profile, because PollingThread
	 * may start sooner than steam_history requests so it could possibly break getting history messages from server
	 */
	time_t m_lastMessageTS;

	// requests
	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 = nullptr);
	void PushRequest(HttpRequest *request, JsonCallback callback, void *param = nullptr);
	void PushToRequestQueue(RequestQueueItem *item);
	RequestQueueItem *PopFromRequestQueue();
	void ProcessRequestQueue();
	void __cdecl RequestQueueThread(void*);

	// pooling
	void ParsePollData(const JSONNode &data);
	void OnGotPoll(const HttpResponse &response, void *arg);
	void __cdecl PollingThread(void*);

	// login
	bool IsOnline();
	bool IsMe(const char *steamId);
	
	void Login();
	void Logout();

	void OnGotRsaKey(const JSONNode &root, void*);

	void OnGotCaptcha(const HttpResponse &response, void *arg);
	
	void OnAuthorization(const HttpResponse &response, void*);
	void OnAuthorizationError(const JSONNode &root);
	void OnAuthorizationSuccess(const JSONNode &root);
	void OnGotSession(const HttpResponse &response, void*);

	void OnLoggedOn(const HttpResponse &response, void*);
	void OnReLogin(const JSONNode &root, void*);

	void HandleTokenExpired();
	void DeleteAuthSettings();

	// contacts
	void SetAllContactStatuses(int status);
	void SetContactStatus(MCONTACT hContact, WORD status);

	MCONTACT GetContactFromAuthEvent(MEVENT hEvent);

	void UpdateContactDetails(MCONTACT hContact, const JSONNode &data);
	void UpdateContactRelationship(MCONTACT hContact, const JSONNode &data);
	void OnGotAppInfo(const JSONNode &root, void *arg);

	void ContactIsRemoved(MCONTACT hContact);
	void ContactIsFriend(MCONTACT hContact);
	void ContactIsBlocked(MCONTACT hContact);
	void ContactIsUnblocked(MCONTACT hContact);
	void ContactIsAskingAuth(MCONTACT hContact);

	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*);
	void OnGotUserSummaries(const JSONNode &root, void*);
	void OnGotAvatar(const HttpResponse &response, void *arg);

	void OnFriendAdded(const HttpResponse &response, void *arg);
	void OnFriendBlocked(const HttpResponse &response, void *arg);
	void OnFriendUnblocked(const HttpResponse &response, void *arg);
	void OnFriendRemoved(const HttpResponse &response, void *arg);

	void OnAuthRequested(const JSONNode &root, void *arg);

	void OnPendingApproved(const JSONNode &root, void *arg);
	void OnPendingIgnoreded(const JSONNode &root, void *arg);

	void OnSearchResults(const HttpResponse &response, void *arg);
	void OnSearchByNameStarted(const HttpResponse &response, void *arg);

	// messages
	int OnSendMessage(MCONTACT hContact, const char* message);
	void OnMessageSent(const HttpResponse &response, void *arg);
	int __cdecl OnPreCreateMessage(WPARAM, LPARAM lParam);

	// history
	void OnGotConversations(const JSONNode &root, void *arg);
	void OnGotHistoryMessages(const JSONNode &root, void*);

	// menus
	static int hChooserMenu;
	static HGENMENU contactMenuItems[CMI_MAX];

	INT_PTR __cdecl AuthRequestCommand(WPARAM, LPARAM);
	INT_PTR __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 PrebuildContactMenu(WPARAM wParam, LPARAM lParam);
	int OnPrebuildContactMenu(WPARAM wParam, LPARAM);

	void OnInitStatusMenu();

	// avatars
	wchar_t* GetAvatarFilePath(MCONTACT hContact);
	bool GetDbAvatarInfo(PROTO_AVATAR_INFORMATION &pai);
	void CheckAvatarChange(MCONTACT hContact, std::string avatarUrl);

	INT_PTR __cdecl GetAvatarInfo(WPARAM, LPARAM);
	INT_PTR __cdecl GetAvatarCaps(WPARAM, LPARAM);
	INT_PTR __cdecl GetMyAvatar(WPARAM, LPARAM);

	// xstatuses
	INT_PTR  __cdecl OnGetXStatusEx(WPARAM wParam, LPARAM lParam);
	INT_PTR  __cdecl OnGetXStatusIcon(WPARAM wParam, LPARAM lParam);
	INT_PTR  __cdecl OnRequestAdvStatusIconIdx(WPARAM wParam, LPARAM lParam);
	HICON GetXStatusIcon(int status, UINT flags);
	int GetContactXStatus(MCONTACT hContact);

	void __cdecl GetAwayMsgThread(void *arg);

	// events
	int __cdecl OnIdleChanged(WPARAM, LPARAM);
	int __cdecl OnOptionsInit(WPARAM wParam, LPARAM lParam);
	INT_PTR __cdecl OnAccountManagerInit(WPARAM wParam, LPARAM lParam);

	// utils
	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);

	static void ShowNotification(const wchar_t *message, int flags = 0, MCONTACT hContact = NULL);
	static void ShowNotification(const wchar_t *caption, const wchar_t *message, int flags = 0, MCONTACT hContact = NULL);

	INT_PTR __cdecl OnGetEventTextChatStates(WPARAM wParam, LPARAM lParam);

	// helpers
	inline int IdleSeconds() {
		// Based on idle time we report Steam server will mark us as online/away/snooze
		switch (m_iStatus) {
		case ID_STATUS_AWAY:
			return STEAM_API_IDLEOUT_AWAY;
		case ID_STATUS_NA:
			return STEAM_API_IDLEOUT_SNOOZE;
		default:
			return 0;
		}

		// ... or we can report real idle info
		// return m_idleTS ? time(0) - m_idleTS : 0;
	}

	inline const char *AccountIdToSteamId(long long accountId)
	{
		static char steamId[20];
		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;
	}
};

struct CMPlugin : public ACCPROTOPLUGIN<CSteamProto>
{
	CMPlugin();

	int Load() override;
};

int OnReloadIcons(WPARAM wParam, LPARAM lParam);
void SetContactExtraIcon(MCONTACT hContact, int status);

#endif //_STEAM_PROTO_H_