LCOV - code coverage report
Current view: top level - source/lobby/scripting - JSInterface_Lobby.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 36 99 36.4 %
Date: 2023-01-19 00:18:29 Functions: 3 12 25.0 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       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.
       8             :  *
       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.
      13             :  *
      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/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "JSInterface_Lobby.h"
      21             : 
      22             : #include "gui/GUIManager.h"
      23             : #include "lib/utf8.h"
      24             : #include "lobby/IXmppClient.h"
      25             : #include "network/NetServer.h"
      26             : #include "ps/CLogger.h"
      27             : #include "ps/CStr.h"
      28             : #include "ps/Util.h"
      29             : #include "scriptinterface/FunctionWrapper.h"
      30             : 
      31             : #include "third_party/encryption/pkcs5_pbkdf2.h"
      32             : 
      33             : #include <string>
      34             : 
      35             : namespace JSI_Lobby
      36             : {
      37           0 : bool HasXmppClient()
      38             : {
      39           0 :     return g_XmppClient;
      40             : }
      41             : 
      42           0 : void SetRankedGame(bool isRanked)
      43             : {
      44           0 :     g_rankedGame = isRanked;
      45           0 : }
      46             : 
      47             : #if CONFIG2_LOBBY
      48             : 
      49           0 : void StartXmppClient(const ScriptRequest& rq, const std::wstring& username, const std::wstring& password, const std::wstring& room, const std::wstring& nick, int historyRequestSize)
      50             : {
      51           0 :     if (g_XmppClient)
      52             :     {
      53           0 :         ScriptException::Raise(rq, "Cannot call StartXmppClient with an already initialized XmppClient!");
      54           0 :         return;
      55             :     }
      56             : 
      57           0 :     g_XmppClient =
      58           0 :         IXmppClient::create(
      59           0 :             g_GUI->GetScriptInterface().get(),
      60           0 :             utf8_from_wstring(username),
      61           0 :             utf8_from_wstring(password),
      62           0 :             utf8_from_wstring(room),
      63           0 :             utf8_from_wstring(nick),
      64             :             historyRequestSize);
      65             : 
      66           0 :     g_rankedGame = true;
      67             : }
      68             : 
      69           0 : void StartRegisterXmppClient(const ScriptRequest& rq, const std::wstring& username, const std::wstring& password)
      70             : {
      71           0 :     if (g_XmppClient)
      72             :     {
      73           0 :         ScriptException::Raise(rq, "Cannot call StartRegisterXmppClient with an already initialized XmppClient!");
      74           0 :         return;
      75             :     }
      76             : 
      77           0 :     g_XmppClient =
      78           0 :         IXmppClient::create(
      79           0 :             g_GUI->GetScriptInterface().get(),
      80           0 :             utf8_from_wstring(username),
      81           0 :             utf8_from_wstring(password),
      82           0 :             std::string(),
      83           0 :             std::string(),
      84             :             0,
      85             :             true);
      86             : }
      87             : 
      88           0 : void StopXmppClient(const ScriptRequest& rq)
      89             : {
      90           0 :     if (!g_XmppClient)
      91             :     {
      92           0 :         ScriptException::Raise(rq, "Cannot call StopXmppClient without an initialized XmppClient!");
      93           0 :         return;
      94             :     }
      95             : 
      96           0 :     SAFE_DELETE(g_XmppClient);
      97           0 :     g_rankedGame = false;
      98             : }
      99             : 
     100             : ////////////////////////////////////////////////
     101             : ////////////////////////////////////////////////
     102             : 
     103           0 : IXmppClient* XmppGetter(const ScriptRequest&, JS::CallArgs&)
     104             : {
     105           0 :     if (!g_XmppClient)
     106             :     {
     107           0 :         LOGERROR("Cannot use XMPPClient functions without an initialized XmppClient!");
     108           0 :         return nullptr;
     109             :     }
     110           0 :     return g_XmppClient;
     111             : }
     112             : 
     113           0 : void SendRegisterGame(const ScriptInterface& scriptInterface, JS::HandleValue data)
     114             : {
     115           0 :     if (!g_XmppClient)
     116             :     {
     117           0 :         ScriptRequest rq(scriptInterface);
     118           0 :         ScriptException::Raise(rq, "Cannot call SendRegisterGame without an initialized XmppClient!");
     119           0 :         return;
     120             :     }
     121             : 
     122             :     // Prevent JS mods to register matches in the lobby that were started with lobby authentication disabled
     123           0 :     if (!g_NetServer || !g_NetServer->UseLobbyAuth())
     124             :     {
     125           0 :         LOGERROR("Registering games in the lobby requires lobby authentication to be enabled!");
     126           0 :         return;
     127             :     }
     128             : 
     129           0 :     g_XmppClient->SendIqRegisterGame(scriptInterface, data);
     130             : }
     131             : 
     132             : // Unlike other functions, this one just returns Undefined if XmppClient isn't initialised.
     133           0 : JS::Value GuiPollNewMessages(const ScriptInterface& scriptInterface)
     134             : {
     135           0 :     if (!g_XmppClient)
     136           0 :         return JS::UndefinedValue();
     137             : 
     138           0 :     return g_XmppClient->GuiPollNewMessages(scriptInterface);
     139             : }
     140             : 
     141             : 
     142             : // Non-public secure PBKDF2 hash function with salting and 1,337 iterations
     143             : //
     144             : // TODO: We should use libsodium's crypto_pwhash instead of this. The first reason is that
     145             : // libsodium doesn't propose a bare PBKDF2 hash in its API and it's too bad to rely on custom
     146             : // code when we have a fully-fledged library available; the second reason is that Argon2 (the
     147             : // default algorithm for crypto_pwhash) is better than what we use (and it's the default one
     148             : // in the lib for a reason).
     149             : // However changing the hashing method should be planned carefully, by trying to login with a
     150             : // password hashed the old way, and, if successful, updating the password in the database using
     151             : // the new hashing method. Dropping the old hashing code can only be done either by giving users
     152             : // a way to reset their password, or by keeping track of successful password updates and dropping
     153             : // old unused accounts after some time.
     154           0 : std::string EncryptPassword(const std::string& password, const std::string& username)
     155             : {
     156           0 :     ENSURE(sodium_init() >= 0);
     157             : 
     158           0 :     const int DIGESTSIZE = crypto_hash_sha256_BYTES;
     159           0 :     const int ITERATIONS = 1337;
     160             : 
     161             :     cassert(DIGESTSIZE == 32);
     162             : 
     163             :     static const unsigned char salt_base[DIGESTSIZE] = {
     164             :             244, 243, 249, 244, 32, 33, 34, 35, 10, 11, 12, 13, 14, 15, 16, 17,
     165             :             18, 19, 20, 32, 33, 244, 224, 127, 129, 130, 140, 153, 133, 123, 234, 123 };
     166             : 
     167             :     // initialize the salt buffer
     168           0 :     unsigned char salt_buffer[DIGESTSIZE] = {0};
     169             :     crypto_hash_sha256_state state;
     170           0 :     crypto_hash_sha256_init(&state);
     171             : 
     172           0 :     crypto_hash_sha256_update(&state, salt_base, sizeof(salt_base));
     173           0 :     crypto_hash_sha256_update(&state, (unsigned char*)username.c_str(), username.length());
     174             : 
     175           0 :     crypto_hash_sha256_final(&state, salt_buffer);
     176             : 
     177             :     // PBKDF2 to create the buffer
     178             :     unsigned char encrypted[DIGESTSIZE];
     179           0 :     pbkdf2(encrypted, (unsigned char*)password.c_str(), password.length(), salt_buffer, DIGESTSIZE, ITERATIONS);
     180             : 
     181           0 :     return CStr(Hexify(encrypted, DIGESTSIZE)).UpperCase();
     182             : }
     183             : 
     184             : #endif
     185             : 
     186          12 : void RegisterScriptFunctions(const ScriptRequest& rq)
     187             : {
     188             :     // Lobby functions
     189          12 :     ScriptFunction::Register<&HasXmppClient>(rq, "HasXmppClient");
     190          12 :     ScriptFunction::Register<&SetRankedGame>(rq, "SetRankedGame");
     191             : #if CONFIG2_LOBBY // Allow the lobby to be disabled
     192          12 :     ScriptFunction::Register<&StartXmppClient>(rq, "StartXmppClient");
     193          12 :     ScriptFunction::Register<&StartRegisterXmppClient>(rq, "StartRegisterXmppClient");
     194          12 :     ScriptFunction::Register<&StopXmppClient>(rq, "StopXmppClient");
     195             : 
     196             : #define REGISTER_XMPP(func, name) \
     197             :     ScriptFunction::Register<&IXmppClient::func, &XmppGetter>(rq, name)
     198             : 
     199          12 :     REGISTER_XMPP(connect, "ConnectXmppClient");
     200          12 :     REGISTER_XMPP(disconnect, "DisconnectXmppClient");
     201          12 :     REGISTER_XMPP(isConnected, "IsXmppClientConnected");
     202          12 :     REGISTER_XMPP(SendIqGetBoardList, "SendGetBoardList");
     203          12 :     REGISTER_XMPP(SendIqGetProfile, "SendGetProfile");
     204          12 :     REGISTER_XMPP(SendIqGameReport, "SendGameReport");
     205          12 :     ScriptFunction::Register<&SendRegisterGame>(rq, "SendRegisterGame");
     206          12 :     REGISTER_XMPP(SendIqUnregisterGame, "SendUnregisterGame");
     207          12 :     REGISTER_XMPP(SendIqChangeStateGame, "SendChangeStateGame");
     208          12 :     REGISTER_XMPP(GUIGetPlayerList, "GetPlayerList");
     209          12 :     REGISTER_XMPP(GUIGetGameList, "GetGameList");
     210          12 :     REGISTER_XMPP(GUIGetBoardList, "GetBoardList");
     211          12 :     REGISTER_XMPP(GUIGetProfile, "GetProfile");
     212             : 
     213          12 :     ScriptFunction::Register<&GuiPollNewMessages>(rq, "LobbyGuiPollNewMessages");
     214          12 :     REGISTER_XMPP(GuiPollHistoricMessages, "LobbyGuiPollHistoricMessages");
     215          12 :     REGISTER_XMPP(GuiPollHasPlayerListUpdate, "LobbyGuiPollHasPlayerListUpdate");
     216          12 :     REGISTER_XMPP(SendMUCMessage, "LobbySendMessage");
     217          12 :     REGISTER_XMPP(SetPresence, "LobbySetPlayerPresence");
     218          12 :     REGISTER_XMPP(SetNick, "LobbySetNick");
     219          12 :     REGISTER_XMPP(GetNick, "LobbyGetNick");
     220          12 :     REGISTER_XMPP(GetJID, "LobbyGetJID");
     221          12 :     REGISTER_XMPP(kick, "LobbyKick");
     222          12 :     REGISTER_XMPP(ban, "LobbyBan");
     223          12 :     REGISTER_XMPP(GetPresence, "LobbyGetPlayerPresence");
     224          12 :     REGISTER_XMPP(GetRole, "LobbyGetPlayerRole");
     225          12 :     REGISTER_XMPP(GetRating, "LobbyGetPlayerRating");
     226          12 :     REGISTER_XMPP(GetSubject, "LobbyGetRoomSubject");
     227             : #undef REGISTER_XMPP
     228             : 
     229          12 :     ScriptFunction::Register<&EncryptPassword>(rq, "EncryptPassword");
     230             : #endif // CONFIG2_LOBBY
     231          12 : }
     232           3 : }

Generated by: LCOV version 1.13