2013-08-21 07:42:42 +00:00
# include "../plugin.h"
# include "xml.h"
//configuration
//#define NOICE //if defined, we only do simple raw udp connections.
2013-11-21 23:02:28 +00:00
# define FILETRANSFERS //IBB only, speeds suck. autoaccept is forced on. no protection from mods stuffcmding sendfile commands. needs more extensive testing
2013-08-21 07:42:42 +00:00
# define QUAKECONNECT //including quake ICE connections (depends upon jingle)
# define VOIP //enables voice chat (depends upon jingle)
2015-04-27 06:19:33 +00:00
//#define VOIP_LEGACY //enables google-only voice chat compat. google have not kept up with the standardisation of jingle (aka: gingle).
2013-08-21 07:42:42 +00:00
//#define VOIP_LEGACY_ONLY //disables jingle feature (advert+detection only)
# define JINGLE //enables jingle signalling
# ifdef JINGLE
# include "../../engine/common/netinc.h"
# else
# undef VOIP
# undef VOIP_LEGACY
# endif
2017-06-21 01:24:25 +00:00
# define JCL_BUILD "5"
2015-06-04 06:15:14 +00:00
//#define DEFAULTDOMAIN "triptohell.info"
2013-08-21 07:42:42 +00:00
# define DEFAULTRESOURCE "Quake"
# define QUAKEMEDIAXMLNS "http: //fteqw.com/protocol/quake"
# define DISCONODE "http: //fteqw.com/ftexmpp" //some sort of client identifier
# define DEFAULTICEMODE ICEM_ICE
2013-11-21 23:02:28 +00:00
# define MEDIATYPE_QUAKE "quake"
# define MEDIATYPE_VIDEO "video"
# define MEDIATYPE_AUDIO "audio"
2013-08-21 07:42:42 +00:00
2015-04-27 06:19:33 +00:00
# define JCL_MAXMSGLEN 0x10000
2013-08-21 07:42:42 +00:00
//values are not on the wire or anything
2013-11-21 23:02:28 +00:00
# define CAP_VOICE (1u<<0) //supports voice
# define CAP_GOOGLE_VOICE (1u<<1) //google supports some old non-standard version of jingle.
# define CAP_VIDEO (1u<<2) //supports video calls
# define CAP_GAMEINVITE (1u<<3) //supports game invites. custom/private protocol
# define CAP_POKE (1u<<4) //can be slapped.
# define CAP_SIFT (1u<<5) //non-jingle file transfers
# define CAP_AVATARS (1u<<6) //can enable querying for user avatars, but cannot disable advertising our own.
2013-08-21 07:42:42 +00:00
2013-11-21 23:02:28 +00:00
//not actually capabilities, but just down to how we handle querying them.
# define CAP_QUERYING (1u<<29) //we've sent a query and are waiting for the response.
# define CAP_QUERIED (1u<<30) //feature capabilities are actually know.
# define CAP_QUERYFAILED (1u<<31) //caps request failed due to bad hash or some such.
2013-08-21 07:42:42 +00:00
2013-11-21 23:02:28 +00:00
//features that default to disabled.
# define CAP_DEFAULTENABLEDCAPS (CAP_VOICE /*|CAP_VIDEO*/ |CAP_GAMEINVITE|CAP_POKE|CAP_AVATARS /*|CAP_SIFT*/ |CAP_GOOGLE_VOICE)
2013-08-21 07:42:42 +00:00
typedef struct bresource_s
{
char bstatus [ 128 ] ; //basic status
char fstatus [ 128 ] ; //full status
char server [ 256 ] ;
int servertype ; //0=none, 1=already a client, 2=joinable
2015-04-27 06:19:33 +00:00
int priority ;
2013-08-21 07:42:42 +00:00
2013-11-21 23:02:28 +00:00
unsigned int buggycaps ;
2013-08-21 07:42:42 +00:00
unsigned int caps ;
char * client_node ; //vendor name
char * client_ver ; //cap hash value
char * client_hash ; //cap hash method
char * client_ext ; //deprecated. additionally queried
struct bresource_s * next ;
char resource [ 1 ] ;
} bresource_t ;
typedef struct buddy_s
{
bresource_t * resources ;
2017-10-12 12:02:25 +00:00
bresource_t * defaultresource ; //this is the one that last replied (must be null for chatrooms, so we only talk to the room in general)
bresource_t * ourselves ; //this is set back to ourselves when in a chatroom
2013-08-21 07:42:42 +00:00
int defaulttimestamp ;
2017-06-21 01:24:25 +00:00
qboolean askfriend ;
2013-08-21 07:42:42 +00:00
qboolean friended ;
2013-11-21 23:02:28 +00:00
qboolean vcardphotochanged ;
2017-10-12 12:02:25 +00:00
enum {
BT_UNKNOWN , //this buddy isn't known...
BT_ROSTER , //this is a friend! or at least on our list of potential friends anyway.
BT_CHATROOM //we're treating this 'guy' as a MUC, each of their resources is a different person. which is weird.
} btype ;
qboolean room_autojoin ;
char * room_nick ;
char * room_password ;
char * room_topic ;
2013-08-21 07:42:42 +00:00
char name [ 256 ] ;
2013-11-21 23:02:28 +00:00
char vcardphotohash [ 41 ] ;
qhandle_t image ;
2013-08-21 07:42:42 +00:00
struct buddy_s * next ;
char accountdomain [ 1 ] ; //no resource on there
} buddy_t ;
typedef struct jclient_s
{
int accountnum ; //a private id to track which client links are associated with
char redirserveraddr [ 64 ] ; //if empty, do an srv lookup.
enum
{
JCL_INACTIVE , //not trying to connect.
JCL_DEAD , //not connected. connection died or something.
JCL_AUTHING , //connected, but not able to send any info on it other than to auth
JCL_ACTIVE //we're connected, we got a buddy list and everything
} status ;
unsigned int timeout ; //reconnect/ping timer
2015-04-27 06:19:33 +00:00
char errormsg [ 256 ] ;
2013-08-21 07:42:42 +00:00
2013-11-21 23:02:28 +00:00
unsigned int enabledcapabilities ;
2013-08-21 07:42:42 +00:00
qhandle_t socket ;
2017-10-12 12:02:25 +00:00
qhandle_t rcon_pipe ; //contains console prints
char rcon_peer [ 256 ] ; //the name of the guy currently receiving console prints
2013-08-21 07:42:42 +00:00
//we buffer output for times when the outgoing socket is full.
//mostly this only happens at the start of the connection when the socket isn't actually open yet.
char * outbuf ;
int outbufpos ;
int outbuflen ;
int outbufmax ;
char bufferedinmessage [ JCL_MAXMSGLEN + 1 ] ; //servers are required to be able to handle messages no shorter than a specific size.
//which means we need to be able to handle messages when they get to us.
//servers can still handle larger messages if they choose, so this might not be enough.
int bufferedinammount ;
char defaultdest [ 256 ] ;
//config info
char serveraddr [ 64 ] ; //if empty, do an srv lookup.
int serverport ;
char domain [ 256 ] ;
char username [ 256 ] ;
char resource [ 256 ] ;
char certificatedomain [ 256 ] ;
int forcetls ; //-1=off, 0=ifpossible, 1=fail if can't upgrade, 2=old-style tls
2013-11-21 23:02:28 +00:00
qboolean savepassword ;
2017-09-20 11:27:13 +00:00
char fulljid [ 256 ] ; //this is our full username@domain/resource string
char barejid [ 256 ] ; //this is our bare username@domain string
2013-08-21 07:42:42 +00:00
char localalias [ 256 ] ; //this is what's shown infront of outgoing messages. >> by default until we can get our name.
2013-11-21 23:02:28 +00:00
char vcardphotohash [ 20 ] ; //20-byte sha1 hash.
enum
{
VCP_UNKNOWN ,
VCP_NONE ,
VCP_KNOWN
} vcardphotohashstatus ;
qboolean vcardphotohashchanged ; //forces a presence send.
2013-08-21 07:42:42 +00:00
int instreampos ;
2017-06-21 01:24:25 +00:00
qboolean connecting ; //still waiting for intial stream tag
2013-08-21 07:42:42 +00:00
qboolean connected ; //fully on server and authed and everything.
qboolean issecure ; //tls enabled (either upgraded or initially)
2013-11-21 23:02:28 +00:00
int streamdebug ; //echo the stream to subconsoles
2013-08-21 07:42:42 +00:00
qboolean preapproval ; //server supports presence preapproval
char curquakeserver [ 2048 ] ;
char defaultnamespace [ 2048 ] ; //should be 'jabber:client' or blank (and spammy with all the extra xmlns attribs)
2017-09-20 11:27:13 +00:00
struct sasl_ctx_s
2013-08-21 07:42:42 +00:00
{
2017-09-20 11:27:13 +00:00
char * username ; //might be different from the account name, but probably isn't.
char * domain ; //might be different from the account domain, but probably isn't.
qboolean issecure ; //tls enabled (either upgraded or initially)
int socket ;
//this stuff should be saved
char password_plain [ 256 ] ; //plain password. scrubbed if we auth using a hashed auth.
qbyte password_hash [ 256 ] ; //safer password, not encrypted, but stored hashed.
size_t password_hash_size ;
char password_validity [ 256 ] ; //internal string used to check that the salt was unchanged
struct saslmethod_s * authmethod ; //null name = oauth2->saslmethod
qboolean allowauth_plainnontls ; //allow plain plain
qboolean allowauth_plaintls ; //allow tls plain
qboolean allowauth_digestmd5 ; //allow digest-md5 auth
qboolean allowauth_scramsha1 ; //allow scram-sha-1 auth
qboolean allowauth_oauth2 ; //use oauth2 where possible
struct
{
char authnonce [ 256 ] ;
} digest ;
struct
{
qboolean plus ;
char authnonce [ 256 ] ;
char authvhash [ 20 ] ;
char authcbindtype [ 20 ] ;
char authcbinding [ 256 ] ;
hashfunc_t * hashfunc ;
size_t hashsize ;
} scram ;
struct
{
char saslmethod [ 64 ] ;
char obtainurl [ 256 ] ;
char refreshurl [ 256 ] ;
char clientid [ 256 ] ;
char clientsecret [ 256 ] ;
char * useraccount ;
char * scope ;
char * accesstoken ; //one-shot access token
char * refreshtoken ; //long-term token that we can use to get new access tokens
char * authtoken ; //short-term authorisation token, usable to get an access token (and a refresh token if we're lucky)
} oauth2 ;
} sasl ;
2013-08-21 07:42:42 +00:00
struct iq_s
{
struct iq_s * next ;
char id [ 64 ] ;
int timeout ;
qboolean ( * callback ) ( struct jclient_s * jcl , struct subtree_s * tree , struct iq_s * iq ) ;
void * usrptr ;
char to [ 1 ] ;
} * pendingiqs ;
# ifdef JINGLE
struct c2c_s
{
struct c2c_s * next ;
qboolean accepted ; //connection is going
qboolean creator ; //true if we're the creator.
unsigned int peercaps ;
2017-10-12 12:02:25 +00:00
qboolean displayed ; //temp flag for displaying jingle sessions with people that are not on our buddy list for whatever reasons
2013-08-21 07:42:42 +00:00
2013-11-21 23:02:28 +00:00
struct
{
char name [ 64 ] ; //uniquely identifies the content within the session.
enum iceproto_e mediatype ;
enum icemode_e method ; //ICE_RAW or ICE_ICE. this is what the peer asked for. updated if we degrade it.
struct icestate_s * ice ;
char * peeraddr ;
int peerport ;
} content [ 3 ] ;
int contents ;
2013-08-21 07:42:42 +00:00
char * with ;
char sid [ 1 ] ;
} * c2c ;
# endif
# ifdef FILETRANSFERS
struct ft_s
{
struct ft_s * next ;
char fname [ MAX_QPATH ] ;
int size ;
2013-10-29 17:38:22 +00:00
int sizedone ;
2013-08-21 07:42:42 +00:00
char * with ;
char md5hash [ 16 ] ;
2013-10-29 17:38:22 +00:00
int privateid ;
char iqid [ 64 ] ;
2013-08-21 07:42:42 +00:00
char sid [ 64 ] ;
int blocksize ;
unsigned short seq ;
qhandle_t file ;
2013-10-29 17:38:22 +00:00
qhandle_t stream ;
qboolean begun ; //handshake
qboolean eof ;
qboolean transmitting ; //we're offering
qboolean allowed ; //if false, don't handshake the transfer
2013-08-21 07:42:42 +00:00
2013-11-21 23:02:28 +00:00
struct
{
char jid [ 128 ] ;
char host [ 40 ] ;
int port ;
} streamhosts [ 16 ] ;
int nexthost ;
2013-08-21 07:42:42 +00:00
enum
{
2013-11-21 23:02:28 +00:00
STRM_IDLE ,
STRM_AUTH ,
STRM_AUTHED ,
STRM_ACTIVE
} streamstatus ;
char indata [ 64 ] ; //only for handshake data
int inlen ;
enum
{
FT_NOTSTARTED ,
2013-08-21 07:42:42 +00:00
FT_IBB , //in-band bytestreams
FT_BYTESTREAM //aka: relay
} method ;
} * ft ;
2013-10-29 17:38:22 +00:00
int privateidseq ;
2013-08-21 07:42:42 +00:00
# endif
2015-04-27 06:19:33 +00:00
//persistant achived info about buddies, to avoid spamming the server every time we connect
//such things might result in lag mid game!
struct buddyinfo_s
{
struct buddyinfo_s * next ;
char * image ;
char * imagehash ;
char * imagemime ;
char accountdomain [ 1 ] ;
} * buddyinfo ;
2013-08-21 07:42:42 +00:00
buddy_t * buddies ;
2015-04-27 06:19:33 +00:00
struct iq_s * avatarupdate ; //we only grab one buddy's photo at a time, this is to avoid too much spam.
2013-08-21 07:42:42 +00:00
} jclient_t ;
# ifdef JINGLE
extern icefuncs_t * piceapi ;
# endif
2017-10-12 12:02:25 +00:00
qboolean NET_DNSLookup_SRV ( const char * host , char * out , int outlen ) ;
2013-08-21 07:42:42 +00:00
//xmpp functionality
2017-10-12 12:02:25 +00:00
struct iq_s * JCL_SendIQNode ( jclient_t * jcl , qboolean ( * callback ) ( jclient_t * jcl , xmltree_t * tree , struct iq_s * iq ) , const char * iqtype , const char * target , xmltree_t * node , qboolean destroynode ) ;
void JCL_AddClientMessagef ( jclient_t * jcl , const char * fmt , . . . ) ;
void JCL_AddClientMessageString ( jclient_t * jcl , const char * msg ) ;
qboolean JCL_FindBuddy ( jclient_t * jcl , const char * jid , buddy_t * * buddy , bresource_t * * bres , qboolean create ) ;
void JCL_ForgetBuddy ( jclient_t * jcl , buddy_t * buddy , bresource_t * bres ) ;
2013-08-21 07:42:42 +00:00
//quake functionality
2017-10-12 12:02:25 +00:00
void JCL_GenLink ( jclient_t * jcl , char * out , int outlen , const char * action , const char * context , const char * contextres , const char * sid , const char * txtfmt , . . . ) ;
void Con_SubPrintf ( const char * subname , const char * format , . . . ) ;
2015-04-27 06:19:33 +00:00
void XMPP_ConversationPrintf ( const char * context , const char * title , char * format , . . . ) ;
2013-08-21 07:42:42 +00:00
//jingle functions
2017-10-12 12:02:25 +00:00
void JCL_Join ( jclient_t * jcl , const char * target , const char * sid , qboolean allow , int protocol ) ;
2013-08-21 07:42:42 +00:00
void JCL_JingleTimeouts ( jclient_t * jcl , qboolean killall ) ;
//jingle iq message handlers
2017-10-12 12:02:25 +00:00
qboolean JCL_HandleGoogleSession ( jclient_t * jcl , xmltree_t * tree , const char * from , const char * id ) ;
qboolean JCL_ParseJingle ( jclient_t * jcl , xmltree_t * tree , const char * from , const char * id ) ;
2013-10-29 17:38:22 +00:00
2013-11-21 23:02:28 +00:00
void XMPP_FT_AcceptFile ( jclient_t * jcl , int fileid , qboolean accept ) ;
qboolean XMPP_FT_OfferAcked ( jclient_t * jcl , xmltree_t * x , struct iq_s * iq ) ;
2017-10-12 12:02:25 +00:00
qboolean XMPP_FT_ParseIQSet ( jclient_t * jcl , const char * iqfrom , const char * iqid , xmltree_t * tree ) ;
void XMPP_FT_SendFile ( jclient_t * jcl , const char * console , const char * to , const char * fname ) ;
2013-11-21 23:02:28 +00:00
void XMPP_FT_Frame ( jclient_t * jcl ) ;
2017-10-12 12:02:25 +00:00
void Base64_Add ( const char * s , int len ) ;
2013-11-21 23:02:28 +00:00
char * Base64_Finish ( void ) ;
2017-10-12 12:02:25 +00:00
int Base64_Decode ( char * out , int outlen , const char * src , int srclen ) ;