2018-01-14 20:30:33 -08:00
/* Copyright (C) 2018 Wildfire Games.
2013-11-07 12:07:24 -08:00
* This file is part of 0 A . D .
*
* 0 A . D . is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* 0 A . D . is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "precompiled.h"
2018-08-24 04:29:38 -07:00
2013-11-07 12:07:24 -08:00
# include "XmppClient.h"
# include "StanzaExtensions.h"
STUN + XMPP ICE implementation.
Allows lobby players to host games without having to configure their
router.
Differential Revision: https://code.wildfiregames.com/D364
Fixes #2305
Patch By: fcxSanya.
StunClient based on code by SuperTuxKart, relicensed with approval of
the according authors hilnius, hiker, Auria, deveee, Flakebi, leper,
konstin and KroArtem.
Added rfc5245 (ejabberd) support, a GUI option, refactoring and segfault
fixes by myself.
Tested By: user1, Sandarac, Sestroretsk1714, Vladislav, Grugnas,
javiergodas
Partially Reviewed By: leper, Philip, echotangoecho
This was SVN commit r19703.
2017-05-31 23:33:52 -07:00
# ifdef WIN32
# include <winsock2.h>
# endif
2014-11-12 17:26:36 -08:00
# include "i18n/L10n.h"
STUN + XMPP ICE implementation.
Allows lobby players to host games without having to configure their
router.
Differential Revision: https://code.wildfiregames.com/D364
Fixes #2305
Patch By: fcxSanya.
StunClient based on code by SuperTuxKart, relicensed with approval of
the according authors hilnius, hiker, Auria, deveee, Flakebi, leper,
konstin and KroArtem.
Added rfc5245 (ejabberd) support, a GUI option, refactoring and segfault
fixes by myself.
Tested By: user1, Sandarac, Sestroretsk1714, Vladislav, Grugnas,
javiergodas
Partially Reviewed By: leper, Philip, echotangoecho
This was SVN commit r19703.
2017-05-31 23:33:52 -07:00
# include "lib/external_libraries/enet.h"
2013-11-09 15:26:17 -08:00
# include "lib/utf8.h"
STUN + XMPP ICE implementation.
Allows lobby players to host games without having to configure their
router.
Differential Revision: https://code.wildfiregames.com/D364
Fixes #2305
Patch By: fcxSanya.
StunClient based on code by SuperTuxKart, relicensed with approval of
the according authors hilnius, hiker, Auria, deveee, Flakebi, leper,
konstin and KroArtem.
Added rfc5245 (ejabberd) support, a GUI option, refactoring and segfault
fixes by myself.
Tested By: user1, Sandarac, Sestroretsk1714, Vladislav, Grugnas,
javiergodas
Partially Reviewed By: leper, Philip, echotangoecho
This was SVN commit r19703.
2017-05-31 23:33:52 -07:00
# include "network/NetServer.h"
# include "network/StunClient.h"
2013-11-07 12:07:24 -08:00
# include "ps/CLogger.h"
# include "ps/ConfigDB.h"
2015-08-01 16:03:13 -07:00
# include "ps/Pyrogenesis.h"
2014-11-12 17:26:36 -08:00
# include "scriptinterface/ScriptInterface.h"
2013-11-07 12:07:24 -08:00
2018-08-24 04:29:38 -07:00
# include <gloox/gloox.h>
2013-11-07 12:07:24 -08:00
//debug
# if 1
# define DbgXMPP(x)
# else
# define DbgXMPP(x) std::cout << x << std::endl;
static std : : string tag_xml ( const glooxwrapper : : IQ & iq )
{
std : : string ret ;
glooxwrapper : : Tag * tag = iq . tag ( ) ;
ret = tag - > xml ( ) . to_string ( ) ;
glooxwrapper : : Tag : : free ( tag ) ;
return ret ;
}
# endif
static std : : string tag_name ( const glooxwrapper : : IQ & iq )
{
std : : string ret ;
glooxwrapper : : Tag * tag = iq . tag ( ) ;
ret = tag - > name ( ) . to_string ( ) ;
glooxwrapper : : Tag : : free ( tag ) ;
return ret ;
}
2014-01-25 21:39:55 -08:00
IXmppClient * IXmppClient : : create ( const std : : string & sUsername , const std : : string & sPassword , const std : : string & sRoom , const std : : string & sNick , const int historyRequestSize , bool regOpt )
2013-11-07 12:07:24 -08:00
{
2014-01-25 21:39:55 -08:00
return new XmppClient ( sUsername , sPassword , sRoom , sNick , historyRequestSize , regOpt ) ;
2013-11-07 12:07:24 -08:00
}
/**
2014-01-25 21:39:55 -08:00
* Construct the XMPP client .
*
* @ param sUsername Username to login with of register .
* @ param sPassword Password to login with or register .
* @ param sRoom MUC room to join .
* @ param sNick Nick to join with .
* @ param historyRequestSize Number of stanzas of room history to request .
* @ param regOpt If we are just registering or not .
2013-11-07 12:07:24 -08:00
*/
2014-01-25 21:39:55 -08:00
XmppClient : : XmppClient ( const std : : string & sUsername , const std : : string & sPassword , const std : : string & sRoom , const std : : string & sNick , const int historyRequestSize , bool regOpt )
2018-03-11 17:23:40 -07:00
: m_client ( NULL ) , m_mucRoom ( NULL ) , m_registration ( NULL ) , m_username ( sUsername ) , m_password ( sPassword ) , m_room ( sRoom ) , m_nick ( sNick ) , m_initialLoadComplete ( false ) , m_isConnected ( false ) , m_sessionManager ( )
2013-11-07 12:07:24 -08:00
{
// Read lobby configuration from default.cfg
std : : string sXpartamupp ;
2018-04-14 05:50:54 -07:00
std : : string sEchelon ;
2018-03-11 17:23:40 -07:00
CFG_GET_VAL ( " lobby.server " , m_server ) ;
2014-11-17 15:29:49 -08:00
CFG_GET_VAL ( " lobby.xpartamupp " , sXpartamupp ) ;
2018-04-14 05:50:54 -07:00
CFG_GET_VAL ( " lobby.echelon " , sEchelon ) ;
2013-11-07 12:07:24 -08:00
2018-03-11 17:23:40 -07:00
m_xpartamuppId = sXpartamupp + " @ " + m_server + " /CC " ;
2018-04-14 05:50:54 -07:00
m_echelonId = sEchelon + " @ " + m_server + " /CC " ;
2018-03-11 17:23:40 -07:00
glooxwrapper : : JID clientJid ( sUsername + " @ " + m_server + " /0ad " ) ;
glooxwrapper : : JID roomJid ( m_room + " @conference. " + m_server + " / " + sNick ) ;
2013-11-07 12:07:24 -08:00
// If we are connecting, use the full jid and a password
// If we are registering, only use the server name
2015-06-19 10:25:28 -07:00
if ( ! regOpt )
2013-11-07 12:07:24 -08:00
m_client = new glooxwrapper : : Client ( clientJid , sPassword ) ;
else
2018-03-11 17:23:40 -07:00
m_client = new glooxwrapper : : Client ( m_server ) ;
2013-11-07 12:07:24 -08:00
2018-08-24 04:29:38 -07:00
// Optionally join without a TLS certificate, so a local server can be tested quickly.
// Security risks from malicious JS mods can be mitigated if this option and also the hostname and login are shielded from JS access.
2018-11-27 06:41:44 -08:00
bool tls = true ;
CFG_GET_VAL ( " lobby.tls " , tls ) ;
m_client - > setTls ( tls ? gloox : : TLSRequired : gloox : : TLSDisabled ) ;
2013-11-07 12:07:24 -08:00
// Disable use of the SASL PLAIN mechanism, to prevent leaking credentials
// if the server doesn't list any supported SASL mechanism or the response
// has been modified to exclude those.
const int mechs = gloox : : SaslMechAll ^ gloox : : SaslMechPlain ;
m_client - > setSASLMechanisms ( mechs ) ;
2014-09-20 08:35:26 -07:00
m_client - > registerConnectionListener ( this ) ;
2013-11-07 12:07:24 -08:00
m_client - > setPresence ( gloox : : Presence : : Available , - 1 ) ;
2015-08-01 16:03:13 -07:00
m_client - > disco ( ) - > setVersion ( " Pyrogenesis " , engine_version ) ;
2014-09-20 08:35:26 -07:00
m_client - > disco ( ) - > setIdentity ( " client " , " bot " ) ;
2013-11-07 12:07:24 -08:00
m_client - > setCompression ( false ) ;
2014-09-20 08:35:26 -07:00
m_client - > registerStanzaExtension ( new GameListQuery ( ) ) ;
2015-06-19 10:25:28 -07:00
m_client - > registerIqHandler ( this , EXTGAMELISTQUERY ) ;
2013-11-07 12:07:24 -08:00
2014-09-20 08:35:26 -07:00
m_client - > registerStanzaExtension ( new BoardListQuery ( ) ) ;
2015-06-19 10:25:28 -07:00
m_client - > registerIqHandler ( this , EXTBOARDLISTQUERY ) ;
2013-11-07 12:07:24 -08:00
2014-09-20 08:35:26 -07:00
m_client - > registerStanzaExtension ( new ProfileQuery ( ) ) ;
2015-06-19 10:25:28 -07:00
m_client - > registerIqHandler ( this , EXTPROFILEQUERY ) ;
2014-09-20 08:35:26 -07:00
2018-03-11 17:23:40 -07:00
m_client - > registerStanzaExtension ( new LobbyAuth ( ) ) ;
m_client - > registerIqHandler ( this , EXTLOBBYAUTH ) ;
2014-09-20 08:35:26 -07:00
m_client - > registerMessageHandler ( this ) ;
2013-11-07 12:07:24 -08:00
// Uncomment to see the raw stanzas
2013-12-27 19:34:40 -08:00
//m_client->getWrapped()->logInstance().registerLogHandler( gloox::LogLevelDebug, gloox::LogAreaAll, this );
2013-11-07 12:07:24 -08:00
if ( ! regOpt )
{
// Create a Multi User Chat Room
m_mucRoom = new glooxwrapper : : MUCRoom ( m_client , roomJid , this , 0 ) ;
2014-01-25 21:39:55 -08:00
// Get room history.
m_mucRoom - > setRequestHistory ( historyRequestSize , gloox : : MUCRoom : : HistoryMaxStanzas ) ;
2013-11-07 12:07:24 -08:00
}
else
{
// Registration
m_registration = new glooxwrapper : : Registration ( m_client ) ;
m_registration - > registerRegistrationHandler ( this ) ;
}
STUN + XMPP ICE implementation.
Allows lobby players to host games without having to configure their
router.
Differential Revision: https://code.wildfiregames.com/D364
Fixes #2305
Patch By: fcxSanya.
StunClient based on code by SuperTuxKart, relicensed with approval of
the according authors hilnius, hiker, Auria, deveee, Flakebi, leper,
konstin and KroArtem.
Added rfc5245 (ejabberd) support, a GUI option, refactoring and segfault
fixes by myself.
Tested By: user1, Sandarac, Sestroretsk1714, Vladislav, Grugnas,
javiergodas
Partially Reviewed By: leper, Philip, echotangoecho
This was SVN commit r19703.
2017-05-31 23:33:52 -07:00
m_sessionManager = new glooxwrapper : : SessionManager ( m_client , this ) ;
// Register plugins to allow gloox parse them in incoming sessions
m_sessionManager - > registerPlugins ( ) ;
2013-11-07 12:07:24 -08:00
}
/**
* Destroy the xmpp client
*/
XmppClient : : ~ XmppClient ( )
{
DbgXMPP ( " XmppClient destroyed " ) ;
delete m_registration ;
delete m_mucRoom ;
// Workaround for memory leak in gloox 1.0/1.0.1
m_client - > removePresenceExtension ( gloox : : ExtCaps ) ;
delete m_client ;
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : m_GameList )
glooxwrapper : : Tag : : free ( t ) ;
for ( const glooxwrapper : : Tag * const & t : m_BoardList )
glooxwrapper : : Tag : : free ( t ) ;
for ( const glooxwrapper : : Tag * const & t : m_Profile )
glooxwrapper : : Tag : : free ( t ) ;
2013-11-07 12:07:24 -08:00
}
/// Network
void XmppClient : : connect ( )
{
2015-08-30 05:56:48 -07:00
m_initialLoadComplete = false ;
2013-11-07 12:07:24 -08:00
m_client - > connect ( false ) ;
}
void XmppClient : : disconnect ( )
{
m_client - > disconnect ( ) ;
}
2018-01-14 20:30:33 -08:00
bool XmppClient : : isConnected ( )
{
return m_isConnected ;
}
2013-11-07 12:07:24 -08:00
void XmppClient : : recv ( )
{
m_client - > recv ( 1 ) ;
}
/**
* Log ( debug ) Handler
*/
void XmppClient : : handleLog ( gloox : : LogLevel level , gloox : : LogArea area , const std : : string & message )
{
std : : cout < < " log: level: " < < level < < " , area: " < < area < < " , message: " < < message < < std : : endl ;
}
/*****************************************************
* Connection handlers *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* Handle connection
*/
void XmppClient : : onConnect ( )
{
if ( m_mucRoom )
{
2018-01-14 20:30:33 -08:00
m_isConnected = true ;
2015-12-31 09:08:23 -08:00
CreateGUIMessage ( " system " , " connected " ) ;
2013-11-07 12:07:24 -08:00
m_mucRoom - > join ( ) ;
}
if ( m_registration )
m_registration - > fetchRegistrationFields ( ) ;
}
/**
* Handle disconnection
*/
void XmppClient : : onDisconnect ( gloox : : ConnectionError error )
{
// Make sure we properly leave the room so that
// everything works if we decide to come back later
if ( m_mucRoom )
m_mucRoom - > leave ( ) ;
// Clear game, board and player lists.
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : m_GameList )
glooxwrapper : : Tag : : free ( t ) ;
for ( const glooxwrapper : : Tag * const & t : m_BoardList )
glooxwrapper : : Tag : : free ( t ) ;
for ( const glooxwrapper : : Tag * const & t : m_Profile )
glooxwrapper : : Tag : : free ( t ) ;
2018-01-14 20:30:33 -08:00
2013-11-07 12:07:24 -08:00
m_BoardList . clear ( ) ;
m_GameList . clear ( ) ;
m_PlayerMap . clear ( ) ;
2014-09-20 08:35:26 -07:00
m_Profile . clear ( ) ;
2017-08-29 09:04:45 -07:00
m_HistoricGuiMessages . clear ( ) ;
2013-11-07 12:07:24 -08:00
2018-01-14 20:30:33 -08:00
m_isConnected = false ;
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " system " , " disconnected " , " reason " , ConnectionErrorToString ( error ) ) ;
2013-11-07 12:07:24 -08:00
}
/**
2018-08-24 04:29:38 -07:00
* Handle TLS connection .
2013-11-07 12:07:24 -08:00
*/
bool XmppClient : : onTLSConnect ( const glooxwrapper : : CertInfo & info )
{
DbgXMPP ( " onTLSConnect " ) ;
DbgXMPP (
" status: " < < info . status < <
" \n issuer: " < < info . issuer < <
" \n peer: " < < info . server < <
" \n protocol: " < < info . protocol < <
" \n mac: " < < info . mac < <
" \n cipher: " < < info . cipher < <
" \n compression: " < < info . compression ) ;
2018-08-24 04:29:38 -07:00
2018-10-09 10:50:08 -07:00
m_certStatus = static_cast < gloox : : CertStatus > ( info . status ) ;
2018-08-24 04:29:38 -07:00
// Optionally accept invalid certificates, see require_tls option.
bool verify_certificate = true ;
CFG_GET_VAL ( " lobby.verify_certificate " , verify_certificate ) ;
return info . status = = gloox : : CertOk | | ! verify_certificate ;
2013-11-07 12:07:24 -08:00
}
/**
* Handle MUC room errors
*/
void XmppClient : : handleMUCError ( glooxwrapper : : MUCRoom * , gloox : : StanzaError err )
{
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " system " , " error " , " text " , StanzaErrorToString ( err ) ) ;
2013-11-07 12:07:24 -08:00
}
/*****************************************************
* Requests to server *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* Request the leaderboard data from the server .
*/
void XmppClient : : SendIqGetBoardList ( )
{
2018-04-14 05:50:54 -07:00
glooxwrapper : : JID echelonJid ( m_echelonId ) ;
2013-11-07 12:07:24 -08:00
// Send IQ
2014-01-23 15:13:13 -08:00
BoardListQuery * b = new BoardListQuery ( ) ;
b - > m_Command = " getleaderboard " ;
2018-04-14 05:50:54 -07:00
glooxwrapper : : IQ iq ( gloox : : IQ : : Get , echelonJid , m_client - > getID ( ) ) ;
2014-01-23 15:13:13 -08:00
iq . addExtension ( b ) ;
2013-11-07 12:07:24 -08:00
DbgXMPP ( " SendIqGetBoardList [ " < < tag_xml ( iq ) < < " ] " ) ;
m_client - > send ( iq ) ;
}
2014-09-20 08:35:26 -07:00
/**
* Request the profile data from the server .
*/
void XmppClient : : SendIqGetProfile ( const std : : string & player )
{
2018-04-14 05:50:54 -07:00
glooxwrapper : : JID echelonJid ( m_echelonId ) ;
2014-09-20 08:35:26 -07:00
// Send IQ
ProfileQuery * b = new ProfileQuery ( ) ;
b - > m_Command = player ;
2018-04-14 05:50:54 -07:00
glooxwrapper : : IQ iq ( gloox : : IQ : : Get , echelonJid , m_client - > getID ( ) ) ;
2014-09-20 08:35:26 -07:00
iq . addExtension ( b ) ;
DbgXMPP ( " SendIqGetProfile [ " < < tag_xml ( iq ) < < " ] " ) ;
m_client - > send ( iq ) ;
}
2013-11-07 12:07:24 -08:00
/**
* Send game report containing numerous game properties to the server .
*
* @ param data A JS array of game statistics
*/
2017-08-23 17:32:42 -07:00
void XmppClient : : SendIqGameReport ( const ScriptInterface & scriptInterface , JS : : HandleValue data )
2013-11-07 12:07:24 -08:00
{
2018-04-14 05:50:54 -07:00
glooxwrapper : : JID echelonJid ( m_echelonId ) ;
2013-11-07 12:07:24 -08:00
// Setup some base stanza attributes
GameReport * game = new GameReport ( ) ;
glooxwrapper : : Tag * report = glooxwrapper : : Tag : : allocate ( " game " ) ;
// Iterate through all the properties reported and add them to the stanza.
std : : vector < std : : string > properties ;
2015-01-24 06:46:52 -08:00
scriptInterface . EnumeratePropertyNamesWithPrefix ( data , " " , properties ) ;
2015-06-19 10:25:28 -07:00
for ( const std : : string & p : properties )
2013-11-07 12:07:24 -08:00
{
2013-11-09 15:26:17 -08:00
std : : wstring value ;
2015-06-19 10:25:28 -07:00
scriptInterface . GetProperty ( data , p . c_str ( ) , value ) ;
report - > addAttribute ( p , utf8_from_wstring ( value ) ) ;
2013-11-07 12:07:24 -08:00
}
// Add stanza to IQ
2015-06-19 10:25:28 -07:00
game - > m_GameReport . emplace_back ( report ) ;
2013-11-07 12:07:24 -08:00
// Send IQ
2018-04-14 05:50:54 -07:00
glooxwrapper : : IQ iq ( gloox : : IQ : : Set , echelonJid , m_client - > getID ( ) ) ;
2013-11-07 12:07:24 -08:00
iq . addExtension ( game ) ;
DbgXMPP ( " SendGameReport [ " < < tag_xml ( iq ) < < " ] " ) ;
m_client - > send ( iq ) ;
} ;
/**
* Send a request to register a game to the server .
*
* @ param data A JS array of game attributes
*/
2017-08-23 17:32:42 -07:00
void XmppClient : : SendIqRegisterGame ( const ScriptInterface & scriptInterface , JS : : HandleValue data )
2013-11-07 12:07:24 -08:00
{
glooxwrapper : : JID xpartamuppJid ( m_xpartamuppId ) ;
// Setup some base stanza attributes
GameListQuery * g = new GameListQuery ( ) ;
g - > m_Command = " register " ;
glooxwrapper : : Tag * game = glooxwrapper : : Tag : : allocate ( " game " ) ;
// Add a fake ip which will be overwritten by the ip stamp XMPP module on the server.
game - > addAttribute ( " ip " , " fake " ) ;
// Iterate through all the properties reported and add them to the stanza.
std : : vector < std : : string > properties ;
2015-01-24 06:46:52 -08:00
scriptInterface . EnumeratePropertyNamesWithPrefix ( data , " " , properties ) ;
2015-06-19 10:25:28 -07:00
for ( const std : : string & p : properties )
2013-11-07 12:07:24 -08:00
{
2013-11-09 15:26:17 -08:00
std : : wstring value ;
2015-06-19 10:25:28 -07:00
scriptInterface . GetProperty ( data , p . c_str ( ) , value ) ;
game - > addAttribute ( p , utf8_from_wstring ( value ) ) ;
2013-11-07 12:07:24 -08:00
}
// Push the stanza onto the IQ
2015-06-19 10:25:28 -07:00
g - > m_GameList . emplace_back ( game ) ;
2013-11-07 12:07:24 -08:00
// Send IQ
2017-10-21 07:11:25 -07:00
glooxwrapper : : IQ iq ( gloox : : IQ : : Set , xpartamuppJid , m_client - > getID ( ) ) ;
2013-11-07 12:07:24 -08:00
iq . addExtension ( g ) ;
DbgXMPP ( " SendIqRegisterGame [ " < < tag_xml ( iq ) < < " ] " ) ;
m_client - > send ( iq ) ;
}
/**
* Send a request to unregister a game to the server .
*/
void XmppClient : : SendIqUnregisterGame ( )
{
glooxwrapper : : JID xpartamuppJid ( m_xpartamuppId ) ;
// Send IQ
GameListQuery * g = new GameListQuery ( ) ;
g - > m_Command = " unregister " ;
2015-06-19 10:25:28 -07:00
g - > m_GameList . emplace_back ( glooxwrapper : : Tag : : allocate ( " game " ) ) ;
2013-11-07 12:07:24 -08:00
2017-10-21 07:11:25 -07:00
glooxwrapper : : IQ iq ( gloox : : IQ : : Set , xpartamuppJid , m_client - > getID ( ) ) ;
2015-06-19 10:25:28 -07:00
iq . addExtension ( g ) ;
2013-11-07 12:07:24 -08:00
DbgXMPP ( " SendIqUnregisterGame [ " < < tag_xml ( iq ) < < " ] " ) ;
2015-06-19 10:25:28 -07:00
m_client - > send ( iq ) ;
2013-11-07 12:07:24 -08:00
}
/**
* Send a request to change the state of a registered game on the server .
*
* A game can either be in the ' running ' or ' waiting ' state - the server
* decides which - but we need to update the current players that are
* in - game so the server can make the calculation .
*/
void XmppClient : : SendIqChangeStateGame ( const std : : string & nbp , const std : : string & players )
{
glooxwrapper : : JID xpartamuppJid ( m_xpartamuppId ) ;
// Send IQ
GameListQuery * g = new GameListQuery ( ) ;
g - > m_Command = " changestate " ;
glooxwrapper : : Tag * game = glooxwrapper : : Tag : : allocate ( " game " ) ;
game - > addAttribute ( " nbp " , nbp ) ;
game - > addAttribute ( " players " , players ) ;
2015-06-19 10:25:28 -07:00
g - > m_GameList . emplace_back ( game ) ;
2013-11-07 12:07:24 -08:00
2017-10-21 07:11:25 -07:00
glooxwrapper : : IQ iq ( gloox : : IQ : : Set , xpartamuppJid , m_client - > getID ( ) ) ;
2015-06-19 10:25:28 -07:00
iq . addExtension ( g ) ;
2013-11-07 12:07:24 -08:00
DbgXMPP ( " SendIqChangeStateGame [ " < < tag_xml ( iq ) < < " ] " ) ;
m_client - > send ( iq ) ;
}
2018-03-11 17:23:40 -07:00
/*****************************************************
* iq to clients *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* Send lobby authentication token .
*/
void XmppClient : : SendIqLobbyAuth ( const std : : string & to , const std : : string & token )
{
LobbyAuth * auth = new LobbyAuth ( ) ;
auth - > m_Token = token ;
glooxwrapper : : JID clientJid ( to + " @ " + m_server + " /0ad " ) ;
glooxwrapper : : IQ iq ( gloox : : IQ : : Set , clientJid , m_client - > getID ( ) ) ;
iq . addExtension ( auth ) ;
DbgXMPP ( " SendIqLobbyAuth [ " < < tag_xml ( iq ) < < " ] " ) ;
m_client - > send ( iq ) ;
}
2013-11-07 12:07:24 -08:00
/*****************************************************
* Account registration *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void XmppClient : : handleRegistrationFields ( const glooxwrapper : : JID & , int fields , glooxwrapper : : string )
{
glooxwrapper : : RegistrationFields vals ;
vals . username = m_username ;
vals . password = m_password ;
m_registration - > createAccount ( fields , vals ) ;
}
void XmppClient : : handleRegistrationResult ( const glooxwrapper : : JID & , gloox : : RegistrationResult result )
{
if ( result = = gloox : : RegistrationSuccess )
2015-12-31 09:08:23 -08:00
CreateGUIMessage ( " system " , " registered " ) ;
2013-11-07 12:07:24 -08:00
else
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " system " , " error " , " text " , RegistrationResultToString ( result ) ) ;
2013-11-07 12:07:24 -08:00
disconnect ( ) ;
}
void XmppClient : : handleAlreadyRegistered ( const glooxwrapper : : JID & )
{
DbgXMPP ( " the account already exists " ) ;
}
void XmppClient : : handleDataForm ( const glooxwrapper : : JID & , const glooxwrapper : : DataForm & )
{
DbgXMPP ( " dataForm received " ) ;
}
void XmppClient : : handleOOB ( const glooxwrapper : : JID & , const glooxwrapper : : OOB & )
{
DbgXMPP ( " OOB registration requested " ) ;
}
/*****************************************************
* Requests from GUI *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* Handle requests from the GUI for the list of players .
*
* @ return A JS array containing all known players and their presences
*/
2017-08-23 17:32:42 -07:00
void XmppClient : : GUIGetPlayerList ( const ScriptInterface & scriptInterface , JS : : MutableHandleValue ret )
2013-11-07 12:07:24 -08:00
{
2014-07-26 15:33:16 -07:00
JSContext * cx = scriptInterface . GetContext ( ) ;
JSAutoRequest rq ( cx ) ;
2015-06-19 10:25:28 -07:00
2015-01-24 06:46:52 -08:00
scriptInterface . Eval ( " ([]) " , ret ) ;
2014-01-25 20:25:35 -08:00
2014-01-26 11:01:21 -08:00
// Convert the internal data structure to a Javascript object.
2015-06-19 10:25:28 -07:00
for ( const std : : pair < std : : string , std : : vector < std : : string > > & p : m_PlayerMap )
2014-01-25 20:25:35 -08:00
{
2014-07-26 15:33:16 -07:00
JS : : RootedValue player ( cx ) ;
scriptInterface . Eval ( " ({}) " , & player ) ;
2015-06-19 10:25:28 -07:00
scriptInterface . SetProperty ( player , " name " , wstring_from_utf8 ( p . first ) ) ;
scriptInterface . SetProperty ( player , " presence " , wstring_from_utf8 ( p . second [ 0 ] ) ) ;
scriptInterface . SetProperty ( player , " rating " , wstring_from_utf8 ( p . second [ 1 ] ) ) ;
scriptInterface . SetProperty ( player , " role " , wstring_from_utf8 ( p . second [ 2 ] ) ) ;
2015-01-24 06:46:52 -08:00
scriptInterface . CallFunctionVoid ( ret , " push " , player ) ;
2013-11-07 12:07:24 -08:00
}
}
/**
* Handle requests from the GUI for the list of all active games .
*
* @ return A JS array containing all known games
*/
2017-08-23 17:32:42 -07:00
void XmppClient : : GUIGetGameList ( const ScriptInterface & scriptInterface , JS : : MutableHandleValue ret )
2013-11-07 12:07:24 -08:00
{
2014-07-26 15:33:16 -07:00
JSContext * cx = scriptInterface . GetContext ( ) ;
JSAutoRequest rq ( cx ) ;
2015-06-19 10:25:28 -07:00
2015-01-24 06:46:52 -08:00
scriptInterface . Eval ( " ([]) " , ret ) ;
2018-02-21 09:27:33 -08:00
const char * stats [ ] = { " name " , " ip " , " port " , " stunIP " , " stunPort " , " hostUsername " , " state " ,
" nbp " , " maxnbp " , " players " , " mapName " , " niceMapName " , " mapSize " , " mapType " ,
" victoryCondition " , " startTime " , " mods " } ;
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : m_GameList )
2013-11-07 12:07:24 -08:00
{
2014-07-26 15:33:16 -07:00
JS : : RootedValue game ( cx ) ;
scriptInterface . Eval ( " ({}) " , & game ) ;
2013-11-07 12:07:24 -08:00
2014-09-20 08:35:26 -07:00
for ( size_t i = 0 ; i < ARRAY_SIZE ( stats ) ; + + i )
2015-06-19 10:25:28 -07:00
scriptInterface . SetProperty ( game , stats [ i ] , wstring_from_utf8 ( t - > findAttribute ( stats [ i ] ) . to_string ( ) ) ) ;
2013-11-07 12:07:24 -08:00
2015-01-24 06:46:52 -08:00
scriptInterface . CallFunctionVoid ( ret , " push " , game ) ;
2013-11-07 12:07:24 -08:00
}
}
/**
* Handle requests from the GUI for leaderboard data .
*
* @ return A JS array containing all known leaderboard data
*/
2017-08-23 17:32:42 -07:00
void XmppClient : : GUIGetBoardList ( const ScriptInterface & scriptInterface , JS : : MutableHandleValue ret )
2013-11-07 12:07:24 -08:00
{
2014-07-26 15:33:16 -07:00
JSContext * cx = scriptInterface . GetContext ( ) ;
JSAutoRequest rq ( cx ) ;
2015-01-24 06:46:52 -08:00
scriptInterface . Eval ( " ([]) " , ret ) ;
2015-06-19 10:25:28 -07:00
const char * attributes [ ] = { " name " , " rank " , " rating " } ;
for ( const glooxwrapper : : Tag * const & t : m_BoardList )
2013-11-07 12:07:24 -08:00
{
2014-07-26 15:33:16 -07:00
JS : : RootedValue board ( cx ) ;
scriptInterface . Eval ( " ({}) " , & board ) ;
2013-11-07 12:07:24 -08:00
2014-09-20 08:35:26 -07:00
for ( size_t i = 0 ; i < ARRAY_SIZE ( attributes ) ; + + i )
2015-06-19 10:25:28 -07:00
scriptInterface . SetProperty ( board , attributes [ i ] , wstring_from_utf8 ( t - > findAttribute ( attributes [ i ] ) . to_string ( ) ) ) ;
2013-11-07 12:07:24 -08:00
2015-01-24 06:46:52 -08:00
scriptInterface . CallFunctionVoid ( ret , " push " , board ) ;
2013-11-07 12:07:24 -08:00
}
}
2014-09-20 08:35:26 -07:00
/**
* Handle requests from the GUI for profile data .
*
* @ return A JS array containing the specific user ' s profile data
*/
2017-08-23 17:32:42 -07:00
void XmppClient : : GUIGetProfile ( const ScriptInterface & scriptInterface , JS : : MutableHandleValue ret )
2014-09-20 08:35:26 -07:00
{
JSContext * cx = scriptInterface . GetContext ( ) ;
JSAutoRequest rq ( cx ) ;
2015-01-24 06:46:52 -08:00
scriptInterface . Eval ( " ([]) " , ret ) ;
2014-09-20 08:35:26 -07:00
const char * stats [ ] = { " player " , " rating " , " totalGamesPlayed " , " highestRating " , " wins " , " losses " , " rank " } ;
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : m_Profile )
2014-09-20 08:35:26 -07:00
{
JS : : RootedValue profile ( cx ) ;
scriptInterface . Eval ( " ({}) " , & profile ) ;
for ( size_t i = 0 ; i < ARRAY_SIZE ( stats ) ; + + i )
2015-06-19 10:25:28 -07:00
scriptInterface . SetProperty ( profile , stats [ i ] , wstring_from_utf8 ( t - > findAttribute ( stats [ i ] ) . to_string ( ) ) ) ;
2014-09-20 08:35:26 -07:00
2015-01-24 06:46:52 -08:00
scriptInterface . CallFunctionVoid ( ret , " push " , profile ) ;
2014-09-20 08:35:26 -07:00
}
}
2013-11-07 12:07:24 -08:00
/*****************************************************
* Message interfaces *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-08-28 10:47:43 -07:00
void XmppClient : : CreateGUIMessage (
const std : : string & type ,
const std : : string & level ,
const std : : string & property1_name ,
const std : : string & property1_value ,
const std : : string & property2_name ,
const std : : string & property2_value ,
const std : : time_t time )
{
GUIMessage message ;
message . type = type ;
message . level = level ;
message . property1_name = property1_name ;
message . property1_value = property1_value ;
message . property2_name = property2_name ;
message . property2_value = property2_value ;
message . time = time ;
m_GuiMessageQueue . push_back ( std : : move ( message ) ) ;
}
2017-08-29 09:04:45 -07:00
JS : : Value XmppClient : : GuiMessageToJSVal ( const ScriptInterface & scriptInterface , const GUIMessage & message , const bool historic )
2013-11-07 12:07:24 -08:00
{
2014-07-26 15:33:16 -07:00
JSContext * cx = scriptInterface . GetContext ( ) ;
JSAutoRequest rq ( cx ) ;
2017-08-29 09:04:45 -07:00
JS : : RootedValue ret ( cx ) ;
scriptInterface . Eval ( " ({}) " , & ret ) ;
2017-08-28 10:47:43 -07:00
scriptInterface . SetProperty ( ret , " type " , wstring_from_utf8 ( message . type ) ) ;
2013-11-16 10:38:40 -08:00
if ( ! message . level . empty ( ) )
2017-08-28 10:47:43 -07:00
scriptInterface . SetProperty ( ret , " level " , wstring_from_utf8 ( message . level ) ) ;
if ( ! message . property1_name . empty ( ) )
scriptInterface . SetProperty ( ret , message . property1_name . c_str ( ) , wstring_from_utf8 ( message . property1_value ) ) ;
if ( ! message . property2_name . empty ( ) )
scriptInterface . SetProperty ( ret , message . property2_name . c_str ( ) , wstring_from_utf8 ( message . property2_value ) ) ;
2017-06-18 11:48:18 -07:00
scriptInterface . SetProperty ( ret , " time " , ( double ) message . time ) ;
2017-08-29 09:04:45 -07:00
scriptInterface . SetProperty ( ret , " historic " , historic ) ;
return ret ;
}
JS : : Value XmppClient : : GuiPollNewMessage ( const ScriptInterface & scriptInterface )
{
if ( m_GuiMessageQueue . empty ( ) )
return JS : : UndefinedValue ( ) ;
GUIMessage message = m_GuiMessageQueue . front ( ) ;
2013-11-07 12:07:24 -08:00
m_GuiMessageQueue . pop_front ( ) ;
2017-08-29 09:04:45 -07:00
// Since there can be hundreds of presence changes while playing a game, ignore these for performance
if ( message . type = = " chat " & & message . level ! = " presence " )
m_HistoricGuiMessages . push_back ( message ) ;
return GuiMessageToJSVal ( scriptInterface , message , false ) ;
}
JS : : Value XmppClient : : GuiPollHistoricMessages ( const ScriptInterface & scriptInterface )
{
JSContext * cx = scriptInterface . GetContext ( ) ;
JSAutoRequest rq ( cx ) ;
JS : : RootedObject ret ( cx , JS_NewArrayObject ( cx , 0 ) ) ;
uint32_t i = 0 ;
for ( const GUIMessage & message : m_HistoricGuiMessages )
{
JS : : RootedValue msg ( cx , GuiMessageToJSVal ( scriptInterface , message , true ) ) ;
JS_SetElement ( cx , ret , i + + , msg ) ;
}
return JS : : ObjectValue ( * ret ) ;
2013-11-07 12:07:24 -08:00
}
/**
* Send a standard MUC textual message .
*/
void XmppClient : : SendMUCMessage ( const std : : string & message )
{
m_mucRoom - > send ( message ) ;
}
2015-09-09 22:30:18 -07:00
/**
* Clears all presence updates from the message queue .
* Used when rejoining the lobby , since we don ' t need to handle past presence changes .
*/
void XmppClient : : ClearPresenceUpdates ( )
{
m_GuiMessageQueue . erase (
std : : remove_if ( m_GuiMessageQueue . begin ( ) , m_GuiMessageQueue . end ( ) ,
[ ] ( XmppClient : : GUIMessage & message )
{
2017-08-28 10:47:43 -07:00
return message . type = = " chat " & & message . level = = " presence " ;
2015-09-09 22:30:18 -07:00
}
) , m_GuiMessageQueue . end ( ) ) ;
}
2013-11-07 12:07:24 -08:00
/**
2015-12-31 09:08:23 -08:00
* Handle a room message .
2013-11-07 12:07:24 -08:00
*/
2016-06-14 07:07:52 -07:00
void XmppClient : : handleMUCMessage ( glooxwrapper : : MUCRoom * , const glooxwrapper : : Message & msg , bool priv )
2013-11-07 12:07:24 -08:00
{
DbgXMPP ( msg . from ( ) . resource ( ) < < " said " < < msg . body ( ) ) ;
2017-08-28 10:47:43 -07:00
CreateGUIMessage (
" chat " ,
priv ? " private-message " : " room-message " ,
" from " , msg . from ( ) . resource ( ) . to_string ( ) ,
" text " , msg . body ( ) . to_string ( ) ,
ComputeTimestamp ( msg ) ) ;
2013-11-07 12:07:24 -08:00
}
/**
2015-12-31 09:08:23 -08:00
* Handle a private message .
2013-11-07 12:07:24 -08:00
*/
2017-08-28 10:47:43 -07:00
void XmppClient : : handleMessage ( const glooxwrapper : : Message & msg , glooxwrapper : : MessageSession * )
2013-11-07 12:07:24 -08:00
{
DbgXMPP ( " type " < < msg . subtype ( ) < < " , subject " < < msg . subject ( )
< < " , message " < < msg . body ( ) < < " , thread id " < < msg . thread ( ) ) ;
2017-08-28 10:47:43 -07:00
CreateGUIMessage (
" chat " ,
" private-message " ,
" from " , msg . from ( ) . resource ( ) . to_string ( ) ,
" text " , msg . body ( ) . to_string ( ) ,
ComputeTimestamp ( msg ) ) ;
2013-11-07 12:07:24 -08:00
}
/**
* Handle portions of messages containing custom stanza extensions .
*/
bool XmppClient : : handleIq ( const glooxwrapper : : IQ & iq )
{
DbgXMPP ( " handleIq [ " < < tag_xml ( iq ) < < " ] " ) ;
2015-06-19 10:25:28 -07:00
if ( iq . subtype ( ) = = gloox : : IQ : : Result )
2013-11-07 12:07:24 -08:00
{
2015-06-19 10:25:28 -07:00
const GameListQuery * gq = iq . findExtension < GameListQuery > ( EXTGAMELISTQUERY ) ;
const BoardListQuery * bq = iq . findExtension < BoardListQuery > ( EXTBOARDLISTQUERY ) ;
const ProfileQuery * pq = iq . findExtension < ProfileQuery > ( EXTPROFILEQUERY ) ;
if ( gq )
2013-11-07 12:07:24 -08:00
{
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : m_GameList )
glooxwrapper : : Tag : : free ( t ) ;
2013-11-07 12:07:24 -08:00
m_GameList . clear ( ) ;
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : gq - > m_GameList )
m_GameList . emplace_back ( t - > clone ( ) ) ;
2013-11-07 12:07:24 -08:00
2015-12-31 09:08:23 -08:00
CreateGUIMessage ( " game " , " gamelist " ) ;
2013-11-07 12:07:24 -08:00
}
2015-06-19 10:25:28 -07:00
if ( bq )
2013-11-07 12:07:24 -08:00
{
2014-01-23 15:13:13 -08:00
if ( bq - > m_Command = = " boardlist " )
{
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : m_BoardList )
glooxwrapper : : Tag : : free ( t ) ;
2014-01-23 15:13:13 -08:00
m_BoardList . clear ( ) ;
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : bq - > m_StanzaBoardList )
m_BoardList . emplace_back ( t - > clone ( ) ) ;
2014-01-23 15:13:13 -08:00
2015-12-31 09:08:23 -08:00
CreateGUIMessage ( " game " , " leaderboard " ) ;
2014-01-23 15:13:13 -08:00
}
else if ( bq - > m_Command = = " ratinglist " )
{
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : bq - > m_StanzaBoardList )
2014-01-26 11:01:21 -08:00
{
2015-06-19 10:25:28 -07:00
std : : string name = t - > findAttribute ( " name " ) . to_string ( ) ;
2014-01-26 11:01:21 -08:00
if ( m_PlayerMap . find ( name ) ! = m_PlayerMap . end ( ) )
2015-06-19 10:25:28 -07:00
m_PlayerMap [ name ] [ 1 ] = t - > findAttribute ( " rating " ) . to_string ( ) ;
2014-01-26 11:01:21 -08:00
}
2014-01-23 15:13:13 -08:00
2015-12-31 09:08:23 -08:00
CreateGUIMessage ( " game " , " ratinglist " ) ;
2014-01-23 15:13:13 -08:00
}
2013-11-07 12:07:24 -08:00
}
2014-09-20 08:35:26 -07:00
if ( pq )
{
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : m_Profile )
glooxwrapper : : Tag : : free ( t ) ;
2014-09-20 08:35:26 -07:00
m_Profile . clear ( ) ;
2015-06-19 10:25:28 -07:00
for ( const glooxwrapper : : Tag * const & t : pq - > m_StanzaProfile )
m_Profile . emplace_back ( t - > clone ( ) ) ;
2014-09-20 08:35:26 -07:00
2015-12-31 09:08:23 -08:00
CreateGUIMessage ( " game " , " profile " ) ;
2014-09-20 08:35:26 -07:00
}
2013-11-07 12:07:24 -08:00
}
2018-03-11 17:23:40 -07:00
else if ( iq . subtype ( ) = = gloox : : IQ : : Set )
{
const LobbyAuth * lobbyAuth = iq . findExtension < LobbyAuth > ( EXTLOBBYAUTH ) ;
if ( lobbyAuth )
{
LOGMESSAGE ( " XmppClient: Received lobby auth: %s from %s " , lobbyAuth - > m_Token . to_string ( ) , iq . from ( ) . username ( ) ) ;
glooxwrapper : : IQ response ( gloox : : IQ : : Result , iq . from ( ) , iq . id ( ) ) ;
m_client - > send ( response ) ;
if ( g_NetServer )
g_NetServer - > OnLobbyAuth ( iq . from ( ) . username ( ) , lobbyAuth - > m_Token . to_string ( ) ) ;
2018-11-07 14:56:05 -08:00
else
LOGERROR ( " Received lobby authentication request, but not hosting currently! " ) ;
2018-03-11 17:23:40 -07:00
}
}
2014-09-20 08:35:26 -07:00
else if ( iq . subtype ( ) = = gloox : : IQ : : Error )
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " system " , " error " , " text " , StanzaErrorToString ( iq . error_error ( ) ) ) ;
2013-11-07 12:07:24 -08:00
else
{
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " system " , " error " , " text " , g_L10n . Translate ( " unknown subtype (see logs) " ) ) ;
LOGMESSAGE ( " unknown subtype '%s' " , tag_name ( iq ) . c_str ( ) ) ;
2013-11-07 12:07:24 -08:00
}
return true ;
}
/*****************************************************
2014-01-24 10:20:15 -08:00
* Presence , nickname , and subject *
2013-11-07 12:07:24 -08:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
* Update local data when a user changes presence .
*/
void XmppClient : : handleMUCParticipantPresence ( glooxwrapper : : MUCRoom * , const glooxwrapper : : MUCRoomParticipant participant , const glooxwrapper : : Presence & presence )
{
std : : string nick = participant . nick - > resource ( ) . to_string ( ) ;
gloox : : Presence : : PresenceType presenceType = presence . presence ( ) ;
2014-01-30 07:46:19 -08:00
std : : string presenceString , roleString ;
2014-01-26 11:01:21 -08:00
GetPresenceString ( presenceType , presenceString ) ;
2014-01-30 07:46:19 -08:00
GetRoleString ( participant . role , roleString ) ;
2017-05-04 18:13:12 -07:00
2013-11-07 12:07:24 -08:00
if ( presenceType = = gloox : : Presence : : Unavailable )
{
if ( ! participant . newNick . empty ( ) & & ( participant . flags & ( gloox : : UserNickChanged | gloox : : UserSelf ) ) )
{
// we have a nick change
2014-01-26 11:01:21 -08:00
std : : string newNick = participant . newNick . to_string ( ) ;
2014-01-30 07:46:19 -08:00
m_PlayerMap [ newNick ] . resize ( 3 ) ;
2014-01-26 11:23:50 -08:00
m_PlayerMap [ newNick ] [ 0 ] = presenceString ;
2014-01-30 07:46:19 -08:00
m_PlayerMap [ newNick ] [ 2 ] = roleString ;
2017-08-28 10:47:43 -07:00
2017-02-28 03:16:46 -08:00
DbgXMPP ( nick < < " is now known as " < < participant . newNick . to_string ( ) ) ;
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " chat " , " nick " , " oldnick " , nick , " newnick " , participant . newNick . to_string ( ) ) ;
2017-02-28 03:16:46 -08:00
}
else if ( participant . flags & gloox : : UserKicked )
{
DbgXMPP ( nick < < " was kicked. Reason: " < < participant . reason . to_string ( ) ) ;
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " chat " , " kicked " , " nick " , nick , " reason " , participant . reason . to_string ( ) ) ;
2017-02-28 03:16:46 -08:00
}
else if ( participant . flags & gloox : : UserBanned )
{
DbgXMPP ( nick < < " was banned. Reason: " < < participant . reason . to_string ( ) ) ;
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " chat " , " banned " , " nick " , nick , " reason " , participant . reason . to_string ( ) ) ;
2013-11-07 12:07:24 -08:00
}
else
2017-02-28 03:16:46 -08:00
{
2017-07-31 09:33:14 -07:00
DbgXMPP ( nick < < " left the room (flags " < < participant . flags < < " ) " ) ;
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " chat " , " leave " , " nick " , nick ) ;
2017-02-28 03:16:46 -08:00
}
2013-11-07 12:07:24 -08:00
m_PlayerMap . erase ( nick ) ;
}
else
{
2015-08-30 05:56:48 -07:00
/* During the initialization process, we recieve join messages for everyone
* currently in the room . We don ' t want to display these , so we filter them
* out . We will always be the last to join during initialization .
*/
if ( ! m_initialLoadComplete )
{
if ( m_mucRoom - > nick ( ) . to_string ( ) = = nick )
m_initialLoadComplete = true ;
}
else if ( m_PlayerMap . find ( nick ) = = m_PlayerMap . end ( ) )
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " chat " , " join " , " nick " , nick ) ;
2017-05-04 18:13:12 -07:00
else if ( m_PlayerMap [ nick ] [ 2 ] ! = roleString )
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " chat " , " role " , " nick " , nick , " oldrole " , m_PlayerMap [ nick ] [ 2 ] ) ;
2013-11-07 12:07:24 -08:00
else
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " chat " , " presence " , " nick " , nick ) ;
2013-11-07 12:07:24 -08:00
DbgXMPP ( nick < < " is in the room, presence : " < < ( int ) presenceType ) ;
2014-01-30 07:46:19 -08:00
m_PlayerMap [ nick ] . resize ( 3 ) ;
2014-01-26 11:29:21 -08:00
m_PlayerMap [ nick ] [ 0 ] = presenceString ;
2014-01-30 07:46:19 -08:00
m_PlayerMap [ nick ] [ 2 ] = roleString ;
2013-11-07 12:07:24 -08:00
}
}
2014-01-24 10:20:15 -08:00
/**
* Update local cache when subject changes .
*/
2017-08-28 10:47:43 -07:00
void XmppClient : : handleMUCSubject ( glooxwrapper : : MUCRoom * , const glooxwrapper : : string & nick , const glooxwrapper : : string & subject )
2014-01-24 10:20:15 -08:00
{
2014-01-25 12:49:48 -08:00
m_Subject = subject . c_str ( ) ;
2017-08-28 10:47:43 -07:00
CreateGUIMessage ( " chat " , " subject " , " nick " , nick . c_str ( ) , " subject " , m_Subject ) ;
2014-01-24 10:20:15 -08:00
}
/**
* Get current subject .
*
* @ param topic Variable to store subject in .
*/
void XmppClient : : GetSubject ( std : : string & subject )
{
subject = m_Subject ;
}
2013-11-07 12:07:24 -08:00
/**
* Request nick change , real change via mucRoomHandler .
*
* @ param nick Desired nickname
*/
void XmppClient : : SetNick ( const std : : string & nick )
{
m_mucRoom - > setNick ( nick ) ;
}
/**
* Get current nickname .
*
* @ param nick Variable to store the nickname in .
*/
void XmppClient : : GetNick ( std : : string & nick )
{
nick = m_mucRoom - > nick ( ) . to_string ( ) ;
}
/**
* Kick a player from the current room .
*
* @ param nick Nickname to be kicked
* @ param reason Reason the player was kicked
*/
void XmppClient : : kick ( const std : : string & nick , const std : : string & reason )
{
m_mucRoom - > kick ( nick , reason ) ;
}
/**
* Ban a player from the current room .
*
* @ param nick Nickname to be banned
* @ param reason Reason the player was banned
*/
void XmppClient : : ban ( const std : : string & nick , const std : : string & reason )
{
m_mucRoom - > ban ( nick , reason ) ;
}
/**
* Change the xmpp presence of the client .
*
* @ param presence A string containing the desired presence
*/
void XmppClient : : SetPresence ( const std : : string & presence )
{
# define IF(x,y) if (presence == x) m_mucRoom->setPresence(gloox::Presence::y)
IF ( " available " , Available ) ;
else IF ( " chat " , Chat ) ;
else IF ( " away " , Away ) ;
else IF ( " playing " , DND ) ;
else IF ( " offline " , Unavailable ) ;
// The others are not to be set
# undef IF
2015-01-22 12:36:24 -08:00
else LOGERROR ( " Unknown presence '%s' " , presence . c_str ( ) ) ;
2013-11-07 12:07:24 -08:00
}
/**
* Get the current xmpp presence of the given nick .
*
* @ param nick Nickname to look up presence for
* @ param presence Variable to store the presence in
*/
void XmppClient : : GetPresence ( const std : : string & nick , std : : string & presence )
{
if ( m_PlayerMap . find ( nick ) ! = m_PlayerMap . end ( ) )
2014-01-26 11:01:21 -08:00
presence = m_PlayerMap [ nick ] [ 0 ] ;
2013-11-07 12:07:24 -08:00
else
presence = " offline " ;
}
2014-04-12 16:40:36 -07:00
/**
* Get the current xmpp role of the given nick .
*
* @ param nick Nickname to look up presence for
* @ param role Variable to store the role in
*/
void XmppClient : : GetRole ( const std : : string & nick , std : : string & role )
{
if ( m_PlayerMap . find ( nick ) ! = m_PlayerMap . end ( ) )
role = m_PlayerMap [ nick ] [ 2 ] ;
else
role = " " ;
}
2013-11-07 12:07:24 -08:00
/*****************************************************
* Utilities *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-06-18 11:48:18 -07:00
/**
2017-08-24 13:57:21 -07:00
* Parse and return the timestamp of a historic chat message and return the current time for new chat messages .
* Historic chat messages are implement as DelayedDelivers as specified in XEP - 0203.
* Hence , their timestamp MUST be in UTC and conform to the DateTime format XEP - 0082.
2017-06-18 11:48:18 -07:00
*
* @ returns Seconds since the epoch .
*/
std : : time_t XmppClient : : ComputeTimestamp ( const glooxwrapper : : Message & msg ) const
{
// Only historic messages contain a timestamp!
if ( ! msg . when ( ) )
return std : : time ( nullptr ) ;
2017-08-24 13:57:21 -07:00
// The locale is irrelevant, because the XMPP date format doesn't contain written month names
2018-01-06 08:09:10 -08:00
for ( const std : : string & format : std : : vector < std : : string > { " Y-M-d'T'H:m:sZ " , " Y-M-d'T'H:m:s.SZ " } )
{
2018-04-10 11:13:32 -07:00
UDate dateTime = g_L10n . ParseDateTime ( msg . when ( ) - > stamp ( ) . to_string ( ) , format , icu : : Locale : : getUS ( ) ) ;
2018-01-06 08:09:10 -08:00
if ( dateTime )
return dateTime / 1000.0 ;
}
return std : : time ( nullptr ) ;
2017-06-18 11:48:18 -07:00
}
2013-11-07 12:07:24 -08:00
/**
* Convert a gloox presence type to string .
*
* @ param p Presence to be converted
* @ param presence Variable to store the converted presence string in
*/
void XmppClient : : GetPresenceString ( const gloox : : Presence : : PresenceType p , std : : string & presence ) const
{
switch ( p )
{
# define CASE(x,y) case gloox::Presence::x: presence = y; break
CASE ( Available , " available " ) ;
CASE ( Chat , " chat " ) ;
CASE ( Away , " away " ) ;
CASE ( DND , " playing " ) ;
2015-12-02 06:41:32 -08:00
CASE ( XA , " away " ) ;
2013-11-07 12:07:24 -08:00
CASE ( Unavailable , " offline " ) ;
CASE ( Probe , " probe " ) ;
CASE ( Error , " error " ) ;
CASE ( Invalid , " invalid " ) ;
default :
2015-01-22 12:31:30 -08:00
LOGERROR ( " Unknown presence type '%d' " , ( int ) p ) ;
2013-11-07 12:07:24 -08:00
break ;
# undef CASE
}
}
2014-01-30 07:46:19 -08:00
/**
* Convert a gloox role type to string .
*
* @ param p Role to be converted
* @ param presence Variable to store the converted role string in
*/
void XmppClient : : GetRoleString ( const gloox : : MUCRoomRole r , std : : string & role ) const
{
switch ( r )
{
2016-01-01 10:38:50 -08:00
# define CASE(X, Y) case gloox::X: role = Y; break
2014-01-30 07:46:19 -08:00
CASE ( RoleNone , " none " ) ;
CASE ( RoleVisitor , " visitor " ) ;
CASE ( RoleParticipant , " participant " ) ;
CASE ( RoleModerator , " moderator " ) ;
CASE ( RoleInvalid , " invalid " ) ;
default :
2015-01-22 12:31:30 -08:00
LOGERROR ( " Unknown role type '%d' " , ( int ) r ) ;
2014-01-30 07:46:19 -08:00
break ;
# undef CASE
}
}
2018-10-09 10:50:08 -07:00
/**
* Translates a gloox certificate error codes , i . e . gloox certificate statuses except CertOk .
* Keep in sync with specifications .
*/
std : : string XmppClient : : TLSErrorToString ( gloox : : CertStatus status ) const
{
// TODO: Use translation
std : : map < gloox : : CertStatus , std : : string > certificateErrorStrings = {
{ gloox : : CertInvalid , ( " The certificate is not trusted. " ) } ,
{ gloox : : CertSignerUnknown , ( " The certificate hasn't got a known issuer. " ) } ,
{ gloox : : CertRevoked , ( " The certificate has been revoked. " ) } ,
{ gloox : : CertExpired , ( " The certificate has expired. " ) } ,
{ gloox : : CertNotActive , ( " The certifiacte is not yet active. " ) } ,
{ gloox : : CertWrongPeer , ( " The certificate has not been issued for the peer we're connected to. " ) } ,
{ gloox : : CertSignerNotCa , ( " The signer is not a CA. " ) }
} ;
std : : string result = " " ;
for ( std : : map < gloox : : CertStatus , std : : string > : : iterator it = certificateErrorStrings . begin ( ) ; it ! = certificateErrorStrings . end ( ) ; + + it )
if ( status & it - > first )
result + = " \n " + it - > second ;
return result ;
}
2013-11-07 12:07:24 -08:00
/**
* Convert a gloox stanza error type to string .
2016-01-01 10:38:50 -08:00
* Keep in sync with Gloox documentation
2013-11-07 12:07:24 -08:00
*
* @ param err Error to be converted
* @ return Converted error string
*/
2016-01-04 09:05:39 -08:00
std : : string XmppClient : : StanzaErrorToString ( gloox : : StanzaError err ) const
2013-11-07 12:07:24 -08:00
{
# define CASE(X, Y) case gloox::X: return Y
2016-01-01 10:38:50 -08:00
# define DEBUG_CASE(X, Y) case gloox::X: return g_L10n.Translate("Error") + " (" + Y + ")"
2013-11-07 12:07:24 -08:00
switch ( err )
{
2016-01-01 10:38:50 -08:00
CASE ( StanzaErrorUndefined , g_L10n . Translate ( " No error " ) ) ;
2017-10-28 15:34:57 -07:00
DEBUG_CASE ( StanzaErrorBadRequest , " Server received malformed XML " ) ;
2016-01-01 10:38:50 -08:00
CASE ( StanzaErrorConflict , g_L10n . Translate ( " Player already logged in " ) ) ;
DEBUG_CASE ( StanzaErrorFeatureNotImplemented , " Server does not implement requested feature " ) ;
2014-11-12 17:26:36 -08:00
CASE ( StanzaErrorForbidden , g_L10n . Translate ( " Forbidden " ) ) ;
2016-01-01 10:38:50 -08:00
DEBUG_CASE ( StanzaErrorGone , " Unable to find message receipiant " ) ;
2014-11-12 17:26:36 -08:00
CASE ( StanzaErrorInternalServerError , g_L10n . Translate ( " Internal server error " ) ) ;
2016-01-01 10:38:50 -08:00
DEBUG_CASE ( StanzaErrorItemNotFound , " Message receipiant does not exist " ) ;
DEBUG_CASE ( StanzaErrorJidMalformed , " JID (XMPP address) malformed " ) ;
DEBUG_CASE ( StanzaErrorNotAcceptable , " Receipiant refused message. Possible policy issue " ) ;
2014-11-12 17:26:36 -08:00
CASE ( StanzaErrorNotAllowed , g_L10n . Translate ( " Not allowed " ) ) ;
CASE ( StanzaErrorNotAuthorized , g_L10n . Translate ( " Not authorized " ) ) ;
2016-01-01 10:38:50 -08:00
DEBUG_CASE ( StanzaErrorNotModified , " Requested item has not changed since last request " ) ;
DEBUG_CASE ( StanzaErrorPaymentRequired , " This server requires payment " ) ;
CASE ( StanzaErrorRecipientUnavailable , g_L10n . Translate ( " Recipient temporarily unavailable " ) ) ;
DEBUG_CASE ( StanzaErrorRedirect , " Request redirected " ) ;
2014-11-12 17:26:36 -08:00
CASE ( StanzaErrorRegistrationRequired , g_L10n . Translate ( " Registration required " ) ) ;
2016-01-01 10:38:50 -08:00
DEBUG_CASE ( StanzaErrorRemoteServerNotFound , " Remote server not found " ) ;
DEBUG_CASE ( StanzaErrorRemoteServerTimeout , " Remote server timed out " ) ;
DEBUG_CASE ( StanzaErrorResourceConstraint , " The recipient is unable to process the message due to resource constraints " ) ;
2014-11-12 17:26:36 -08:00
CASE ( StanzaErrorServiceUnavailable , g_L10n . Translate ( " Service unavailable " ) ) ;
2016-01-01 10:38:50 -08:00
DEBUG_CASE ( StanzaErrorSubscribtionRequired , " Service requires subscription " ) ;
DEBUG_CASE ( StanzaErrorUnexpectedRequest , " Attempt to send from invalid stanza address " ) ;
DEBUG_CASE ( StanzaErrorUnknownSender , " Invalid 'from' address " ) ;
2013-11-07 12:07:24 -08:00
default :
2016-01-01 10:38:50 -08:00
return g_L10n . Translate ( " Unknown error " ) ;
2013-11-07 12:07:24 -08:00
}
2016-01-01 10:38:50 -08:00
# undef DEBUG_CASE
2013-11-07 12:07:24 -08:00
# undef CASE
}
2016-01-01 09:18:17 -08:00
/**
* Convert a gloox connection error enum to string
* Keep in sync with Gloox documentation
2016-11-23 05:02:58 -08:00
*
2016-01-01 09:18:17 -08:00
* @ param err Error to be converted
* @ return Converted error string
*/
2016-01-04 09:05:39 -08:00
std : : string XmppClient : : ConnectionErrorToString ( gloox : : ConnectionError err ) const
2016-01-01 09:18:17 -08:00
{
# define CASE(X, Y) case gloox::X: return Y
2016-01-01 10:38:50 -08:00
# define DEBUG_CASE(X, Y) case gloox::X: return g_L10n.Translate("Error") + " (" + Y + ")"
2016-01-01 09:18:17 -08:00
switch ( err )
{
CASE ( ConnNoError , g_L10n . Translate ( " No error " ) ) ;
CASE ( ConnStreamError , g_L10n . Translate ( " Stream error " ) ) ;
CASE ( ConnStreamVersionError , g_L10n . Translate ( " The incoming stream version is unsupported " ) ) ;
CASE ( ConnStreamClosed , g_L10n . Translate ( " The stream has been closed by the server " ) ) ;
2016-01-01 10:38:50 -08:00
DEBUG_CASE ( ConnProxyAuthRequired , " The HTTP/SOCKS5 proxy requires authentication " ) ;
DEBUG_CASE ( ConnProxyAuthFailed , " HTTP/SOCKS5 proxy authentication failed " ) ;
DEBUG_CASE ( ConnProxyNoSupportedAuth , " The HTTP/SOCKS5 proxy requires an unsupported authentication mechanism " ) ;
2017-10-28 15:34:57 -07:00
CASE ( ConnIoError , g_L10n . Translate ( " An I/O error occurred " ) ) ;
DEBUG_CASE ( ConnParseError , " An XML parse error occurred " ) ;
2016-01-01 09:18:17 -08:00
CASE ( ConnConnectionRefused , g_L10n . Translate ( " The connection was refused by the server " ) ) ;
CASE ( ConnDnsError , g_L10n . Translate ( " Resolving the server's hostname failed " ) ) ;
CASE ( ConnOutOfMemory , g_L10n . Translate ( " This system is out of memory " ) ) ;
2016-01-01 10:38:50 -08:00
DEBUG_CASE ( ConnNoSupportedAuth , " The authentication mechanisms the server offered are not supported or no authentication mechanisms were available " ) ;
2018-10-09 10:50:08 -07:00
CASE ( ConnTlsFailed , g_L10n . Translate ( " The server's certificate could not be verified or the TLS handshake did not complete successfully " ) + TLSErrorToString ( m_certStatus ) ) ;
2016-01-04 09:05:43 -08:00
CASE ( ConnTlsNotAvailable , g_L10n . Translate ( " The server did not offer required TLS encryption " ) ) ;
2016-01-01 10:38:50 -08:00
DEBUG_CASE ( ConnCompressionFailed , " Negotiation/initializing compression failed " ) ;
2016-01-01 09:18:17 -08:00
CASE ( ConnAuthenticationFailed , g_L10n . Translate ( " Authentication failed. Incorrect password or account does not exist " ) ) ;
CASE ( ConnUserDisconnected , g_L10n . Translate ( " The user or system requested a disconnect " ) ) ;
CASE ( ConnNotConnected , g_L10n . Translate ( " There is no active connection " ) ) ;
default :
2016-01-01 10:38:50 -08:00
return g_L10n . Translate ( " Unknown error " ) ;
}
# undef DEBUG_CASE
# undef CASE
}
/**
* Convert a gloox registration result enum to string
* Keep in sync with Gloox documentation
2016-11-23 05:02:58 -08:00
*
2016-01-01 10:38:50 -08:00
* @ param err Enum to be converted
* @ return Converted string
*/
2016-01-04 09:05:39 -08:00
std : : string XmppClient : : RegistrationResultToString ( gloox : : RegistrationResult res ) const
2016-01-01 10:38:50 -08:00
{
# define CASE(X, Y) case gloox::X: return Y
# define DEBUG_CASE(X, Y) case gloox::X: return g_L10n.Translate("Error") + " (" + Y + ")"
switch ( res )
{
2018-01-06 16:10:14 -08:00
CASE ( RegistrationSuccess , g_L10n . Translate ( " Your account has been successfully registered " ) ) ;
2016-01-04 09:58:28 -08:00
CASE ( RegistrationNotAcceptable , g_L10n . Translate ( " Not all necessary information provided " ) ) ;
CASE ( RegistrationConflict , g_L10n . Translate ( " Username already exists " ) ) ;
2016-01-04 09:05:39 -08:00
DEBUG_CASE ( RegistrationNotAuthorized , " Account removal timeout or insufficiently secure channel for password change " ) ;
2017-10-28 15:34:57 -07:00
DEBUG_CASE ( RegistrationBadRequest , " Server received an incomplete request " ) ;
2017-05-13 20:18:21 -07:00
DEBUG_CASE ( RegistrationForbidden , " Registration forbidden " ) ;
2016-01-04 09:05:39 -08:00
DEBUG_CASE ( RegistrationRequired , " Account cannot be removed as it does not exist " ) ;
DEBUG_CASE ( RegistrationUnexpectedRequest , " This client is unregistered with the server " ) ;
DEBUG_CASE ( RegistrationNotAllowed , " Server does not permit password changes " ) ;
default :
2017-02-07 07:05:26 -08:00
return " " ;
2016-01-01 09:18:17 -08:00
}
2016-01-01 10:38:50 -08:00
# undef DEBUG_CASE
2016-01-01 09:18:17 -08:00
# undef CASE
}
STUN + XMPP ICE implementation.
Allows lobby players to host games without having to configure their
router.
Differential Revision: https://code.wildfiregames.com/D364
Fixes #2305
Patch By: fcxSanya.
StunClient based on code by SuperTuxKart, relicensed with approval of
the according authors hilnius, hiker, Auria, deveee, Flakebi, leper,
konstin and KroArtem.
Added rfc5245 (ejabberd) support, a GUI option, refactoring and segfault
fixes by myself.
Tested By: user1, Sandarac, Sestroretsk1714, Vladislav, Grugnas,
javiergodas
Partially Reviewed By: leper, Philip, echotangoecho
This was SVN commit r19703.
2017-05-31 23:33:52 -07:00
void XmppClient : : SendStunEndpointToHost ( StunClient : : StunEndpoint * stunEndpoint , const std : : string & hostJIDStr )
{
ENSURE ( stunEndpoint ) ;
char ipStr [ 256 ] = " (error) " ;
ENetAddress addr ;
addr . host = ntohl ( stunEndpoint - > ip ) ;
enet_address_get_host_ip ( & addr , ipStr , ARRAY_SIZE ( ipStr ) ) ;
glooxwrapper : : JID hostJID ( hostJIDStr ) ;
glooxwrapper : : Jingle : : Session session = m_sessionManager - > createSession ( hostJID ) ;
session . sessionInitiate ( ipStr , stunEndpoint - > port ) ;
}
2017-06-05 13:44:00 -07:00
void XmppClient : : handleSessionAction ( gloox : : Jingle : : Action action , glooxwrapper : : Jingle : : Session * UNUSED ( session ) , const glooxwrapper : : Jingle : : Session : : Jingle * jingle )
STUN + XMPP ICE implementation.
Allows lobby players to host games without having to configure their
router.
Differential Revision: https://code.wildfiregames.com/D364
Fixes #2305
Patch By: fcxSanya.
StunClient based on code by SuperTuxKart, relicensed with approval of
the according authors hilnius, hiker, Auria, deveee, Flakebi, leper,
konstin and KroArtem.
Added rfc5245 (ejabberd) support, a GUI option, refactoring and segfault
fixes by myself.
Tested By: user1, Sandarac, Sestroretsk1714, Vladislav, Grugnas,
javiergodas
Partially Reviewed By: leper, Philip, echotangoecho
This was SVN commit r19703.
2017-05-31 23:33:52 -07:00
{
if ( action = = gloox : : Jingle : : SessionInitiate )
handleSessionInitiation ( jingle ) ;
}
2017-06-05 13:44:00 -07:00
void XmppClient : : handleSessionInitiation ( const glooxwrapper : : Jingle : : Session : : Jingle * jingle )
STUN + XMPP ICE implementation.
Allows lobby players to host games without having to configure their
router.
Differential Revision: https://code.wildfiregames.com/D364
Fixes #2305
Patch By: fcxSanya.
StunClient based on code by SuperTuxKart, relicensed with approval of
the according authors hilnius, hiker, Auria, deveee, Flakebi, leper,
konstin and KroArtem.
Added rfc5245 (ejabberd) support, a GUI option, refactoring and segfault
fixes by myself.
Tested By: user1, Sandarac, Sestroretsk1714, Vladislav, Grugnas,
javiergodas
Partially Reviewed By: leper, Philip, echotangoecho
This was SVN commit r19703.
2017-05-31 23:33:52 -07:00
{
glooxwrapper : : Jingle : : ICEUDP : : Candidate candidate = jingle - > getCandidate ( ) ;
if ( candidate . ip . empty ( ) )
{
LOGERROR ( " Failed to retrieve Jingle candidate " ) ;
return ;
}
2018-11-07 14:56:05 -08:00
if ( ! g_NetServer )
{
LOGERROR ( " Received STUN connection request, but not hosting currently! " ) ;
return ;
}
STUN + XMPP ICE implementation.
Allows lobby players to host games without having to configure their
router.
Differential Revision: https://code.wildfiregames.com/D364
Fixes #2305
Patch By: fcxSanya.
StunClient based on code by SuperTuxKart, relicensed with approval of
the according authors hilnius, hiker, Auria, deveee, Flakebi, leper,
konstin and KroArtem.
Added rfc5245 (ejabberd) support, a GUI option, refactoring and segfault
fixes by myself.
Tested By: user1, Sandarac, Sestroretsk1714, Vladislav, Grugnas,
javiergodas
Partially Reviewed By: leper, Philip, echotangoecho
This was SVN commit r19703.
2017-05-31 23:33:52 -07:00
g_NetServer - > SendHolePunchingMessage ( candidate . ip . to_string ( ) , candidate . port ) ;
}