1 /* Copyright (C) 2018 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "JSInterface_Lobby.h"
22 #include "gui/GUIManager.h"
24 #include "lobby/IXmppClient.h"
25 #include "network/NetServer.h"
26 #include "ps/CLogger.h"
29 #include "scriptinterface/ScriptInterface.h"
30 #include "scriptinterface/ScriptVal.h"
32 #include "third_party/encryption/pkcs5_pbkdf2.h"
36 void JSI_Lobby::RegisterScriptFunctions(const ScriptInterface
& scriptInterface
)
39 scriptInterface
.RegisterFunction
<bool, &JSI_Lobby::HasXmppClient
>("HasXmppClient");
40 scriptInterface
.RegisterFunction
<bool, &JSI_Lobby::IsRankedGame
>("IsRankedGame");
41 scriptInterface
.RegisterFunction
<void, bool, &JSI_Lobby::SetRankedGame
>("SetRankedGame");
42 #if CONFIG2_LOBBY // Allow the lobby to be disabled
43 scriptInterface
.RegisterFunction
<void, std::wstring
, std::wstring
, std::wstring
, std::wstring
, int, &JSI_Lobby::StartXmppClient
>("StartXmppClient");
44 scriptInterface
.RegisterFunction
<void, std::wstring
, std::wstring
, &JSI_Lobby::StartRegisterXmppClient
>("StartRegisterXmppClient");
45 scriptInterface
.RegisterFunction
<void, &JSI_Lobby::StopXmppClient
>("StopXmppClient");
46 scriptInterface
.RegisterFunction
<void, &JSI_Lobby::ConnectXmppClient
>("ConnectXmppClient");
47 scriptInterface
.RegisterFunction
<void, &JSI_Lobby::DisconnectXmppClient
>("DisconnectXmppClient");
48 scriptInterface
.RegisterFunction
<bool, &JSI_Lobby::IsXmppClientConnected
>("IsXmppClientConnected");
49 scriptInterface
.RegisterFunction
<void, &JSI_Lobby::SendGetBoardList
>("SendGetBoardList");
50 scriptInterface
.RegisterFunction
<void, std::wstring
, &JSI_Lobby::SendGetProfile
>("SendGetProfile");
51 scriptInterface
.RegisterFunction
<void, JS::HandleValue
, &JSI_Lobby::SendRegisterGame
>("SendRegisterGame");
52 scriptInterface
.RegisterFunction
<void, JS::HandleValue
, &JSI_Lobby::SendGameReport
>("SendGameReport");
53 scriptInterface
.RegisterFunction
<void, &JSI_Lobby::SendUnregisterGame
>("SendUnregisterGame");
54 scriptInterface
.RegisterFunction
<void, std::wstring
, std::wstring
, &JSI_Lobby::SendChangeStateGame
>("SendChangeStateGame");
55 scriptInterface
.RegisterFunction
<JS::Value
, &JSI_Lobby::GetPlayerList
>("GetPlayerList");
56 scriptInterface
.RegisterFunction
<void, &JSI_Lobby::LobbyClearPresenceUpdates
>("LobbyClearPresenceUpdates");
57 scriptInterface
.RegisterFunction
<JS::Value
, &JSI_Lobby::GetGameList
>("GetGameList");
58 scriptInterface
.RegisterFunction
<JS::Value
, &JSI_Lobby::GetBoardList
>("GetBoardList");
59 scriptInterface
.RegisterFunction
<JS::Value
, &JSI_Lobby::GetProfile
>("GetProfile");
60 scriptInterface
.RegisterFunction
<JS::Value
, &JSI_Lobby::LobbyGuiPollNewMessage
>("LobbyGuiPollNewMessage");
61 scriptInterface
.RegisterFunction
<JS::Value
, &JSI_Lobby::LobbyGuiPollHistoricMessages
>("LobbyGuiPollHistoricMessages");
62 scriptInterface
.RegisterFunction
<void, std::wstring
, &JSI_Lobby::LobbySendMessage
>("LobbySendMessage");
63 scriptInterface
.RegisterFunction
<void, std::wstring
, &JSI_Lobby::LobbySetPlayerPresence
>("LobbySetPlayerPresence");
64 scriptInterface
.RegisterFunction
<void, std::wstring
, &JSI_Lobby::LobbySetNick
>("LobbySetNick");
65 scriptInterface
.RegisterFunction
<std::wstring
, &JSI_Lobby::LobbyGetNick
>("LobbyGetNick");
66 scriptInterface
.RegisterFunction
<void, std::wstring
, std::wstring
, &JSI_Lobby::LobbyKick
>("LobbyKick");
67 scriptInterface
.RegisterFunction
<void, std::wstring
, std::wstring
, &JSI_Lobby::LobbyBan
>("LobbyBan");
68 scriptInterface
.RegisterFunction
<std::wstring
, std::wstring
, &JSI_Lobby::LobbyGetPlayerPresence
>("LobbyGetPlayerPresence");
69 scriptInterface
.RegisterFunction
<std::wstring
, std::wstring
, &JSI_Lobby::LobbyGetPlayerRole
>("LobbyGetPlayerRole");
70 scriptInterface
.RegisterFunction
<std::wstring
, std::wstring
, std::wstring
, &JSI_Lobby::EncryptPassword
>("EncryptPassword");
71 scriptInterface
.RegisterFunction
<std::wstring
, &JSI_Lobby::LobbyGetRoomSubject
>("LobbyGetRoomSubject");
72 #endif // CONFIG2_LOBBY
75 bool JSI_Lobby::HasXmppClient(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
80 bool JSI_Lobby::IsRankedGame(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
85 void JSI_Lobby::SetRankedGame(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), bool isRanked
)
87 g_rankedGame
= isRanked
;
92 void JSI_Lobby::StartXmppClient(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& username
, const std::wstring
& password
, const std::wstring
& room
, const std::wstring
& nick
, int historyRequestSize
)
94 ENSURE(!g_XmppClient
);
96 g_XmppClient
= IXmppClient::create(utf8_from_wstring(username
), utf8_from_wstring(password
),
97 utf8_from_wstring(room
), utf8_from_wstring(nick
), historyRequestSize
);
101 void JSI_Lobby::StartRegisterXmppClient(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& username
, const std::wstring
& password
)
103 ENSURE(!g_XmppClient
);
105 g_XmppClient
= IXmppClient::create(utf8_from_wstring(username
), utf8_from_wstring(password
),
109 void JSI_Lobby::StopXmppClient(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
111 ENSURE(g_XmppClient
);
112 SAFE_DELETE(g_XmppClient
);
113 g_rankedGame
= false;
116 void JSI_Lobby::ConnectXmppClient(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
118 ENSURE(g_XmppClient
);
119 g_XmppClient
->connect();
122 void JSI_Lobby::DisconnectXmppClient(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
124 ENSURE(g_XmppClient
);
125 g_XmppClient
->disconnect();
128 bool JSI_Lobby::IsXmppClientConnected(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
130 ENSURE(g_XmppClient
);
131 return g_XmppClient
->isConnected();
134 void JSI_Lobby::SendGetBoardList(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
138 g_XmppClient
->SendIqGetBoardList();
141 void JSI_Lobby::SendGetProfile(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& player
)
145 g_XmppClient
->SendIqGetProfile(utf8_from_wstring(player
));
148 void JSI_Lobby::SendGameReport(ScriptInterface::CxPrivate
* pCxPrivate
, JS::HandleValue data
)
153 g_XmppClient
->SendIqGameReport(*(pCxPrivate
->pScriptInterface
), data
);
156 void JSI_Lobby::SendRegisterGame(ScriptInterface::CxPrivate
* pCxPrivate
, JS::HandleValue data
)
161 // Prevent JS mods to register matches in the lobby that were started with lobby authentication disabled
162 if (!g_NetServer
|| !g_NetServer
->UseLobbyAuth())
164 LOGERROR("Registering games in the lobby requires lobby authentication to be enabled!");
168 g_XmppClient
->SendIqRegisterGame(*(pCxPrivate
->pScriptInterface
), data
);
171 void JSI_Lobby::SendUnregisterGame(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
175 g_XmppClient
->SendIqUnregisterGame();
178 void JSI_Lobby::SendChangeStateGame(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& nbp
, const std::wstring
& players
)
182 g_XmppClient
->SendIqChangeStateGame(utf8_from_wstring(nbp
), utf8_from_wstring(players
));
185 JS::Value
JSI_Lobby::GetPlayerList(ScriptInterface::CxPrivate
* pCxPrivate
)
188 return JS::UndefinedValue();
190 JSContext
* cx
= pCxPrivate
->pScriptInterface
->GetContext();
191 JSAutoRequest
rq(cx
);
193 JS::RootedValue
playerList(cx
);
194 g_XmppClient
->GUIGetPlayerList(*(pCxPrivate
->pScriptInterface
), &playerList
);
199 void JSI_Lobby::LobbyClearPresenceUpdates(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
204 g_XmppClient
->ClearPresenceUpdates();
207 JS::Value
JSI_Lobby::GetGameList(ScriptInterface::CxPrivate
* pCxPrivate
)
210 return JS::UndefinedValue();
212 JSContext
* cx
= pCxPrivate
->pScriptInterface
->GetContext();
213 JSAutoRequest
rq(cx
);
215 JS::RootedValue
gameList(cx
);
216 g_XmppClient
->GUIGetGameList(*(pCxPrivate
->pScriptInterface
), &gameList
);
221 JS::Value
JSI_Lobby::GetBoardList(ScriptInterface::CxPrivate
* pCxPrivate
)
224 return JS::UndefinedValue();
226 JSContext
* cx
= pCxPrivate
->pScriptInterface
->GetContext();
227 JSAutoRequest
rq(cx
);
229 JS::RootedValue
boardList(cx
);
230 g_XmppClient
->GUIGetBoardList(*(pCxPrivate
->pScriptInterface
), &boardList
);
235 JS::Value
JSI_Lobby::GetProfile(ScriptInterface::CxPrivate
* pCxPrivate
)
238 return JS::UndefinedValue();
240 JSContext
* cx
= pCxPrivate
->pScriptInterface
->GetContext();
241 JSAutoRequest
rq(cx
);
243 JS::RootedValue
profileFetch(cx
);
244 g_XmppClient
->GUIGetProfile(*(pCxPrivate
->pScriptInterface
), &profileFetch
);
249 JS::Value
JSI_Lobby::LobbyGuiPollNewMessage(ScriptInterface::CxPrivate
* pCxPrivate
)
252 return JS::UndefinedValue();
254 return g_XmppClient
->GuiPollNewMessage(*(pCxPrivate
->pScriptInterface
));
257 JS::Value
JSI_Lobby::LobbyGuiPollHistoricMessages(ScriptInterface::CxPrivate
* pCxPrivate
)
260 return JS::UndefinedValue();
262 return g_XmppClient
->GuiPollHistoricMessages(*(pCxPrivate
->pScriptInterface
));
265 void JSI_Lobby::LobbySendMessage(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& message
)
270 g_XmppClient
->SendMUCMessage(utf8_from_wstring(message
));
273 void JSI_Lobby::LobbySetPlayerPresence(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& presence
)
278 g_XmppClient
->SetPresence(utf8_from_wstring(presence
));
281 void JSI_Lobby::LobbySetNick(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& nick
)
286 g_XmppClient
->SetNick(utf8_from_wstring(nick
));
289 std::wstring
JSI_Lobby::LobbyGetNick(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
295 g_XmppClient
->GetNick(nick
);
296 return wstring_from_utf8(nick
);
299 void JSI_Lobby::LobbyKick(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& nick
, const std::wstring
& reason
)
304 g_XmppClient
->kick(utf8_from_wstring(nick
), utf8_from_wstring(reason
));
307 void JSI_Lobby::LobbyBan(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& nick
, const std::wstring
& reason
)
312 g_XmppClient
->ban(utf8_from_wstring(nick
), utf8_from_wstring(reason
));
315 std::wstring
JSI_Lobby::LobbyGetPlayerPresence(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& nickname
)
320 std::string presence
;
321 g_XmppClient
->GetPresence(utf8_from_wstring(nickname
), presence
);
322 return wstring_from_utf8(presence
);
325 std::wstring
JSI_Lobby::LobbyGetPlayerRole(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& nickname
)
331 g_XmppClient
->GetRole(utf8_from_wstring(nickname
), role
);
332 return wstring_from_utf8(role
);
335 // Non-public secure PBKDF2 hash function with salting and 1,337 iterations
337 // TODO: We should use libsodium's crypto_pwhash instead of this. The first reason is that
338 // libsodium doesn't propose a bare PBKDF2 hash in its API and it's too bad to rely on custom
339 // code when we have a fully-fledged library available; the second reason is that Argon2 (the
340 // default algorithm for crypto_pwhash) is better than what we use (and it's the default one
341 // in the lib for a reason).
342 // However changing the hashing method should be planned carefully, by trying to login with a
343 // password hashed the old way, and, if successful, updating the password in the database using
344 // the new hashing method. Dropping the old hashing code can only be done either by giving users
345 // a way to reset their password, or by keeping track of successful password updates and dropping
346 // old unused accounts after some time.
347 std::string
JSI_Lobby::EncryptPassword(const std::string
& password
, const std::string
& username
)
349 ENSURE(sodium_init() >= 0);
351 const int DIGESTSIZE
= crypto_hash_sha256_BYTES
;
352 const int ITERATIONS
= 1337;
354 cassert(DIGESTSIZE
== 32);
356 static const unsigned char salt_base
[DIGESTSIZE
] = {
357 244, 243, 249, 244, 32, 33, 34, 35, 10, 11, 12, 13, 14, 15, 16, 17,
358 18, 19, 20, 32, 33, 244, 224, 127, 129, 130, 140, 153, 133, 123, 234, 123 };
360 // initialize the salt buffer
361 unsigned char salt_buffer
[DIGESTSIZE
] = {0};
362 crypto_hash_sha256_state state
;
363 crypto_hash_sha256_init(&state
);
365 crypto_hash_sha256_update(&state
, salt_base
, sizeof(salt_base
));
366 crypto_hash_sha256_update(&state
, (unsigned char*)username
.c_str(), username
.length());
368 crypto_hash_sha256_final(&state
, salt_buffer
);
370 // PBKDF2 to create the buffer
371 unsigned char encrypted
[DIGESTSIZE
];
372 pbkdf2(encrypted
, (unsigned char*)password
.c_str(), password
.length(), salt_buffer
, DIGESTSIZE
, ITERATIONS
);
374 return CStr(Hexify(encrypted
, DIGESTSIZE
)).UpperCase();
377 std::wstring
JSI_Lobby::EncryptPassword(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const std::wstring
& pass
, const std::wstring
& user
)
379 return wstring_from_utf8(JSI_Lobby::EncryptPassword(utf8_from_wstring(pass
), utf8_from_wstring(user
)));
382 std::wstring
JSI_Lobby::LobbyGetRoomSubject(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
388 g_XmppClient
->GetSubject(subject
);
389 return wstring_from_utf8(subject
);